pmcf 1.16.0 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { writeFile, mkdir, copyFile, glob } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
- import { writeLines, sectionLines } from "../src/model.mjs";
5
+ import { writeLines, sectionLines } from "../src/utils.mjs";
6
6
  import { prepare } from "../src/cmd.mjs";
7
7
 
8
8
  const { world, args, options } = prepare();
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { mkdir, copyFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
- import { writeLines, sectionLines } from "../src/model.mjs";
5
+ import { writeLines, sectionLines } from "../src/utils.mjs";
6
6
  import { prepare } from "../src/cmd.mjs";
7
7
 
8
8
  const { world, args, options } = prepare();
@@ -21,7 +21,7 @@ console.log("replaces", `mf-location-${location.name}`);
21
21
  console.log("description", `location definitions for ${location.name}`);
22
22
 
23
23
  async function generateLocationDefs(location, dir) {
24
- const sl = (await Array.fromAsync(location.dns.services())).map(s=>s.ipAddress);
24
+ const sl = (await Array.fromAsync(location.dns.services())).map(s=>s.ipAddresses).flat()
25
25
  const s1 = sl.shift();
26
26
 
27
27
  await writeLines(
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { join } from "node:path";
4
4
  import { createHmac } from "node:crypto";
5
- import { writeLines } from "../src/model.mjs";
5
+ import { writeLines, isIPv4Address } from "../src/utils.mjs";
6
6
  import { prepare } from "../src/cmd.mjs";
7
7
 
8
8
  const { world, args, options } = prepare();
@@ -103,7 +103,7 @@ async function generateNamedDefs(location, targetDir) {
103
103
  const host = networkInterface.host;
104
104
  zone.records.add(
105
105
  `${host.hostName.padEnd(NAME_LEN, " ")} ${ttl} IN ${
106
- address.indexOf(".") >= 0 ? "A " : "AAAA"
106
+ isIPv4Address(address) ? "A " : "AAAA"
107
107
  } ${normalizeIPAddress(address)}`
108
108
  );
109
109
 
@@ -132,7 +132,7 @@ async function generateNamedDefs(location, targetDir) {
132
132
 
133
133
  const reverseZone = networkInterface.network.subnet?.reverseZone;
134
134
 
135
- if (reverseZone && address.indexOf(".") >= 0) {
135
+ if (reverseZone && isIPv4Address(address)) {
136
136
  reverseZone.records.add(
137
137
  `${(reverseArpaAddress(address) + ".").padEnd(
138
138
  NAME_LEN,
@@ -159,7 +159,9 @@ async function generateNamedDefs(location, targetDir) {
159
159
  zoneConfig.push(` file \"${zone.file}\";`);
160
160
 
161
161
  zoneConfig.push(
162
- ` allow-update { ${dns.allowedUpdates.length ? dns.allowedUpdates.join(";") : "none"}; };`
162
+ ` allow-update { ${
163
+ dns.allowedUpdates.length ? dns.allowedUpdates.join(";") : "none"
164
+ }; };`
163
165
  );
164
166
  zoneConfig.push(` notify yes;`);
165
167
  zoneConfig.push(`};`);
@@ -177,7 +179,7 @@ async function generateNamedDefs(location, targetDir) {
177
179
  }
178
180
 
179
181
  export function reverseAddress(address) {
180
- if (address.indexOf(".") >= 0) {
182
+ if (isIPv4Address(address)) {
181
183
  return address.split(".").reverse().join(".");
182
184
  }
183
185
 
@@ -191,12 +193,12 @@ export function reverseAddress(address) {
191
193
  export function reverseArpaAddress(address) {
192
194
  return (
193
195
  reverseAddress(address) +
194
- (address.indexOf(".") >= 0 ? ".in-addr.arpa" : ".ip6.arpa")
196
+ (isIPv4Address(address) ? ".in-addr.arpa" : ".ip6.arpa")
195
197
  );
196
198
  }
197
199
 
198
200
  export function normalizeIPAddress(address) {
199
- if (address.indexOf(".") >= 0) {
201
+ if (isIPv4Address(address)) {
200
202
  return address;
201
203
  }
202
204
  address = address.replace(/\/\d+$/, "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmcf",
3
- "version": "1.16.0",
3
+ "version": "1.17.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -40,7 +40,7 @@
40
40
  "lint:typescript": "tsc --allowJs --checkJs --noEmit --resolveJsonModule --target es2024 --lib esnext -m esnext --module nodenext --moduleResolution nodenext ./src**/*.mjs"
41
41
  },
42
42
  "devDependencies": {
43
- "@types/node": "^22.10.7",
43
+ "@types/node": "^22.10.9",
44
44
  "ava": "^6.2.0",
45
45
  "c8": "^10.1.3",
46
46
  "documentation": "^14.0.3",
package/src/model.mjs CHANGED
@@ -1,6 +1,7 @@
1
- import { readFile, writeFile, mkdir, glob } from "node:fs/promises";
1
+ import { readFile, glob } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { getAttribute } from "pacc";
4
+ import { asArray, bridgeToJSON } from "./utils.mjs";
4
5
 
5
6
  export class Base {
6
7
  owner;
@@ -139,6 +140,37 @@ export class Owner extends Base {
139
140
  #networks = new Map();
140
141
  #subnets = new Map();
141
142
  #bridges = new Set();
143
+ #dns;
144
+ #administratorEmail;
145
+ domain;
146
+ ntp = { servers: [] };
147
+
148
+ constructor(owner, data) {
149
+ super(owner, data);
150
+
151
+ let dns;
152
+ if (data?.dns) {
153
+ dns = data.dns;
154
+ delete data.dns;
155
+ }
156
+
157
+ this.#dns = new DNSService(this, dns);
158
+
159
+ if (data?.networks) {
160
+ const networks = data.networks;
161
+ delete data.networks;
162
+
163
+ for (const [name, data] of Object.entries(networks)) {
164
+ data.name = name;
165
+ new Network(this, data);
166
+ }
167
+ }
168
+ Object.assign(this, data);
169
+ }
170
+
171
+ get dns() {
172
+ return this.#dns;
173
+ }
142
174
 
143
175
  async *hosts() {
144
176
  for (const host of this.#hosts.values()) {
@@ -244,6 +276,14 @@ export class Owner extends Base {
244
276
  }
245
277
  }
246
278
 
279
+ async *networkAddresses() {
280
+ for await (const host of this.hosts()) {
281
+ for (const networkAddresses of host.networkAddresses()) {
282
+ yield networkAddresses;
283
+ }
284
+ }
285
+ }
286
+
247
287
  #resolveActions = [];
248
288
 
249
289
  resolveLater(action) {
@@ -268,6 +308,14 @@ export class Owner extends Base {
268
308
  return this.#subnets.values();
269
309
  }
270
310
 
311
+ get administratorEmail() {
312
+ return this.#administratorEmail || "admin@" + this.domain;
313
+ }
314
+
315
+ get propertyNames() {
316
+ return [...super.propertyNames, "domain", "administratorEmail", "dns"];
317
+ }
318
+
271
319
  toJSON() {
272
320
  return {
273
321
  ...super.toJSON(),
@@ -410,14 +458,6 @@ export class World extends Owner {
410
458
  async host(name) {
411
459
  return this.load(name, { type: Host });
412
460
  }
413
-
414
- async *networkAddresses() {
415
- for await (const host of this.hosts()) {
416
- for (const networkAddresses of host.networkAddresses()) {
417
- yield networkAddresses;
418
- }
419
- }
420
- }
421
461
  }
422
462
 
423
463
  class DNSService extends Base {
@@ -425,6 +465,10 @@ class DNSService extends Base {
425
465
  recordTTL = "1W";
426
466
  forwardsTo = [];
427
467
 
468
+ static get typeName() {
469
+ return "dns";
470
+ }
471
+
428
472
  constructor(owner, data) {
429
473
  super(owner, data);
430
474
  Object.assign(this, data);
@@ -447,37 +491,10 @@ class DNSService extends Base {
447
491
  }
448
492
 
449
493
  export class Location extends Owner {
450
- domain;
451
- #dns;
452
- ntp = { servers: [] };
453
- #administratorEmail;
454
-
455
494
  static get typeName() {
456
495
  return "location";
457
496
  }
458
497
 
459
- constructor(owner, data) {
460
- super(owner, data);
461
-
462
- let dns;
463
- if (data.dns) {
464
- dns = data.dns;
465
- delete data.dns;
466
- }
467
-
468
- this.#dns = new DNSService(this, dns);
469
- const networks = data.networks;
470
- delete data.networks;
471
- Object.assign(this, data);
472
-
473
- if (networks) {
474
- for (const [name, data] of Object.entries(networks)) {
475
- data.name = name;
476
- new Network(this, data);
477
- }
478
- }
479
- }
480
-
481
498
  async *hosts() {
482
499
  for await (const host of this.owner.hosts()) {
483
500
  if (host.location === this) {
@@ -485,26 +502,6 @@ export class Location extends Owner {
485
502
  }
486
503
  }
487
504
  }
488
-
489
- get dns() {
490
- return this.#dns;
491
- }
492
-
493
- async *networkAddresses() {
494
- for await (const host of this.hosts()) {
495
- for (const networkAddresses of host.networkAddresses()) {
496
- yield networkAddresses;
497
- }
498
- }
499
- }
500
-
501
- get administratorEmail() {
502
- return this.#administratorEmail || "admin@" + this.domain;
503
- }
504
-
505
- get propertyNames() {
506
- return [...super.propertyNames, "domain", "administratorEmail", "dns"];
507
- }
508
505
  }
509
506
 
510
507
  export class Network extends Owner {
@@ -671,14 +668,14 @@ export class Host extends Base {
671
668
  }
672
669
  }
673
670
 
674
- Object.assign(this, { networkInterfaces: {} }, data);
675
-
676
- owner.addHost(this);
671
+ Object.assign(this, data);
677
672
 
678
673
  for (const [name, iface] of Object.entries(this.networkInterfaces)) {
679
674
  iface.name = name;
680
- this.networkInterfaces[name] = new NetworkInterface(this, iface);
675
+ new NetworkInterface(this, iface);
681
676
  }
677
+
678
+ owner.addHost(this);
682
679
  }
683
680
 
684
681
  get deployment() {
@@ -777,10 +774,12 @@ export class Host extends Base {
777
774
  }
778
775
  }
779
776
 
777
+ addNetworkInterface(networkInterface) {
778
+ this.networkInterfaces[networkInterface.name] = networkInterface;
779
+ }
780
+
780
781
  *networkAddresses() {
781
- for (const [name, networkInterface] of Object.entries(
782
- this.networkInterfaces
783
- )) {
782
+ for (const networkInterface of Object.values(this.networkInterfaces)) {
784
783
  for (const attribute of ["ipv4", "ipv6", "link-local-ipv6"]) {
785
784
  if (networkInterface[attribute]) {
786
785
  yield { address: networkInterface[attribute], networkInterface };
@@ -849,6 +848,8 @@ export class NetworkInterface extends Base {
849
848
  }
850
849
 
851
850
  Object.assign(this, data);
851
+
852
+ owner.addNetworkInterface(this);
852
853
  }
853
854
 
854
855
  get host() {
@@ -1005,40 +1006,17 @@ export class Service extends Base {
1005
1006
  }
1006
1007
  }
1007
1008
 
1008
- const _types = [Location, Network, Subnet, Host, Service];
1009
+ const _types = [Location, Network, Subnet, Host, Service, DNSService];
1009
1010
  const _typesByName = Object.fromEntries(_types.map(t => [t.typeName, t]));
1010
1011
 
1011
- export async function writeLines(dir, name, lines) {
1012
- await mkdir(dir, { recursive: true });
1013
- return writeFile(
1014
- join(dir, name),
1015
- [...lines]
1016
- .flat()
1017
- .filter(line => line !== undefined)
1018
- .map(l => l + "\n")
1019
- .join(""),
1020
- "utf8"
1021
- );
1022
- }
1023
-
1024
- export function sectionLines(sectionName, values) {
1025
- const lines = [`[${sectionName}]`];
1026
-
1027
- for (const [name, value] of Object.entries(values)) {
1028
- lines.push(`${name}=${value}`);
1029
- }
1030
-
1031
- return lines;
1032
- }
1033
-
1034
- function extractFrom(object, propertyNames) {
1012
+ export function extractFrom(object, propertyNames) {
1035
1013
  const json = {};
1036
1014
  for (const p of propertyNames) {
1037
1015
  const value = object[p];
1038
1016
 
1039
1017
  if (value !== undefined) {
1040
- if (value instanceof Base) {
1041
- json[p] = { name: object.name };
1018
+ if (value instanceof Base && value.name) {
1019
+ json[p] = { name: value.name };
1042
1020
  } else {
1043
1021
  json[p] = value;
1044
1022
  }
@@ -1046,11 +1024,3 @@ function extractFrom(object, propertyNames) {
1046
1024
  }
1047
1025
  return json;
1048
1026
  }
1049
-
1050
- function bridgeToJSON(bridge) {
1051
- return [...bridge].map(n => n.name || `(${n})`).sort();
1052
- }
1053
-
1054
- function asArray(value) {
1055
- return Array.isArray(value) ? value : value === undefined ? [] : [value];
1056
- }
package/src/utils.mjs ADDED
@@ -0,0 +1,41 @@
1
+ import { writeFile, mkdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ export async function writeLines(dir, name, lines) {
5
+ await mkdir(dir, { recursive: true });
6
+ return writeFile(
7
+ join(dir, name),
8
+ [...lines]
9
+ .flat()
10
+ .filter(line => line !== undefined)
11
+ .map(l => l + "\n")
12
+ .join(""),
13
+ "utf8"
14
+ );
15
+ }
16
+
17
+ export function sectionLines(sectionName, values) {
18
+ const lines = [`[${sectionName}]`];
19
+
20
+ for (const [name, value] of Object.entries(values)) {
21
+ lines.push(`${name}=${value}`);
22
+ }
23
+
24
+ return lines;
25
+ }
26
+
27
+ export function bridgeToJSON(bridge) {
28
+ return [...bridge].map(n => n.name || `(${n})`).sort();
29
+ }
30
+
31
+ export function asArray(value) {
32
+ return Array.isArray(value) ? value : value === undefined ? [] : [value];
33
+ }
34
+
35
+ export function isIPv4Address(address) {
36
+ return address.indexOf(".") >= 0;
37
+ }
38
+
39
+ export function isIPv6Address(address) {
40
+ return address.indexOf(":") >= 0;
41
+ }
package/types/model.d.mts CHANGED
@@ -1,5 +1,4 @@
1
- export function writeLines(dir: any, name: any, lines: any): Promise<void>;
2
- export function sectionLines(sectionName: any, values: any): string[];
1
+ export function extractFrom(object: any, propertyNames: any): {};
3
2
  export class Base {
4
3
  static get typeName(): string;
5
4
  static get typeFileName(): string;
@@ -28,6 +27,11 @@ export class Base {
28
27
  #private;
29
28
  }
30
29
  export class Owner extends Base {
30
+ domain: any;
31
+ ntp: {
32
+ servers: any[];
33
+ };
34
+ get dns(): DNSService;
31
35
  hosts(): AsyncGenerator<any, void, unknown>;
32
36
  addObject(object: any): void;
33
37
  addHost(host: any): void;
@@ -38,11 +42,13 @@ export class Owner extends Base {
38
42
  addNetwork(network: any): void;
39
43
  addBridge(network: any, destinationNetworks: any): any;
40
44
  _resolveBridges(): void;
45
+ networkAddresses(): AsyncGenerator<any, void, unknown>;
41
46
  resolveLater(action: any): void;
42
47
  resolve(): void;
43
48
  addSubnet(subnet: any): void;
44
49
  subnet(name: any): any;
45
50
  subnets(): MapIterator<any>;
51
+ get administratorEmail(): any;
46
52
  toJSON(): {
47
53
  networks: any[];
48
54
  subnets: any[];
@@ -53,7 +59,7 @@ export class Owner extends Base {
53
59
  }
54
60
  export class World extends Owner {
55
61
  static get types(): {
56
- [k: string]: typeof Location | typeof Host | typeof Network | typeof Subnet | typeof Service;
62
+ [k: string]: typeof Location | typeof Host | typeof DNSService | typeof Network | typeof Subnet | typeof Service;
57
63
  };
58
64
  constructor(directory: any);
59
65
  get fullName(): string;
@@ -66,21 +72,9 @@ export class World extends Owner {
66
72
  domains(): AsyncGenerator<any, void, unknown>;
67
73
  location(name: any): Promise<any>;
68
74
  host(name: any): Promise<any>;
69
- networkAddresses(): AsyncGenerator<{
70
- address: any;
71
- networkInterface: any;
72
- }, void, unknown>;
73
75
  #private;
74
76
  }
75
77
  export class Location extends Owner {
76
- domain: any;
77
- ntp: {
78
- servers: any[];
79
- };
80
- get dns(): DNSService;
81
- networkAddresses(): AsyncGenerator<any, void, unknown>;
82
- get administratorEmail(): any;
83
- #private;
84
78
  }
85
79
  export class Network extends Owner {
86
80
  kind: any;
@@ -115,6 +109,7 @@ export class Host extends Base {
115
109
  get domainName(): string;
116
110
  addService(service: any): void;
117
111
  services(filter: any): Generator<any, void, unknown>;
112
+ addNetworkInterface(networkInterface: any): void;
118
113
  networkAddresses(): Generator<{
119
114
  address: any;
120
115
  networkInterface: any;
@@ -0,0 +1,6 @@
1
+ export function writeLines(dir: any, name: any, lines: any): Promise<void>;
2
+ export function sectionLines(sectionName: any, values: any): string[];
3
+ export function bridgeToJSON(bridge: any): any[];
4
+ export function asArray(value: any): any[];
5
+ export function isIPv4Address(address: any): boolean;
6
+ export function isIPv6Address(address: any): boolean;