pmcf 1.19.0 → 1.21.0

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.
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmcf",
3
- "version": "1.19.0",
3
+ "version": "1.21.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
7
  "exports": {
8
8
  ".": {
9
- "default": "./src/model.mjs"
9
+ "default": "./src/module.mjs"
10
10
  }
11
11
  },
12
12
  "description": "Poor mans configuration management",
@@ -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.9",
43
+ "@types/node": "^22.10.10",
44
44
  "ava": "^6.2.0",
45
45
  "c8": "^10.1.3",
46
46
  "documentation": "^14.0.3",
package/src/base.mjs ADDED
@@ -0,0 +1,169 @@
1
+ import { join } from "node:path";
2
+ import { getAttribute } from "pacc";
3
+
4
+ export class Base {
5
+ owner;
6
+ name;
7
+ description;
8
+
9
+ static get typeName() {
10
+ return "base";
11
+ }
12
+
13
+ static get typeFileName() {
14
+ return this.typeName + ".json";
15
+ }
16
+
17
+ static get fileNameGlob() {
18
+ return "**/" + this.typeFileName;
19
+ }
20
+
21
+ static async prepareData(world, data) {
22
+ return this;
23
+ }
24
+
25
+ static baseName(name) {
26
+ if (!name) {
27
+ return undefined;
28
+ }
29
+
30
+ return name.replace(/\/\w+\.json$/, "");
31
+ }
32
+
33
+ constructor(owner, data) {
34
+ this.owner = owner;
35
+
36
+ if (data) {
37
+ this.name = data.name;
38
+ if (data.description) {
39
+ this.description = data.description;
40
+ }
41
+ }
42
+ }
43
+
44
+ withOwner(owner) {
45
+ if (this.owner !== owner) {
46
+ return new this.constructor(owner, this);
47
+ }
48
+
49
+ return this;
50
+ }
51
+
52
+ get typeName() {
53
+ return this.constructor.typeName;
54
+ }
55
+
56
+ get world() {
57
+ return this.owner.world;
58
+ }
59
+
60
+ get location() {
61
+ return this.owner.location;
62
+ }
63
+
64
+ get host() {
65
+ return this.owner.host;
66
+ }
67
+
68
+ async network(name) {
69
+ return this.owner.network(name);
70
+ }
71
+
72
+ #directory;
73
+ set directory(directory) {
74
+ this.#directory = directory;
75
+ }
76
+
77
+ get directory() {
78
+ return this.#directory || join(this.owner.directory, this.name);
79
+ }
80
+
81
+ get fullName() {
82
+ return this.owner?.fullName
83
+ ? join(this.owner.fullName, this.name)
84
+ : this.name;
85
+ }
86
+
87
+ expand(object) {
88
+ switch (typeof object) {
89
+ case "string":
90
+ return object.replaceAll(/\$\{([^\}]*)\}/g, (match, m1) => {
91
+ return getAttribute(this, m1) || "${" + m1 + "}";
92
+ });
93
+
94
+ case "object":
95
+ if (Array.isArray(object)) {
96
+ return object.map(e => this.expand(e));
97
+ }
98
+
99
+ if (object instanceof Set) {
100
+ return new Set([...object].map(e => this.expand(e)));
101
+ }
102
+
103
+ /*return Object.fromEntries(
104
+ Object.entries(object).map(([k, v]) => [k, this.expand(v)])
105
+ );*/
106
+ }
107
+
108
+ return object;
109
+ }
110
+
111
+ #finalize;
112
+
113
+ finalize(action) {
114
+ if (!this.#finalize) {
115
+ this.#finalize = [];
116
+ }
117
+ this.#finalize.push(action);
118
+ }
119
+
120
+ execFinalize() {
121
+ if (this.#finalize) {
122
+ //this.info("finalize");
123
+ let i = 0;
124
+ for (const action of this.#finalize) {
125
+ if (action) {
126
+ this.#finalize[i] = undefined;
127
+ action();
128
+ }
129
+ i++;
130
+ }
131
+ }
132
+ }
133
+
134
+ error(...args) {
135
+ console.error(`${this.toString()}:`, ...args);
136
+ }
137
+
138
+ info(...args) {
139
+ console.info(`${this.toString()}:`, ...args);
140
+ }
141
+
142
+ toString() {
143
+ return `${this.fullName}(${this.typeName})`;
144
+ }
145
+
146
+ get propertyNames() {
147
+ return ["name", "description", "directory", "owner"];
148
+ }
149
+
150
+ toJSON() {
151
+ return extractFrom(this, this.propertyNames);
152
+ }
153
+ }
154
+
155
+ export function extractFrom(object, propertyNames) {
156
+ const json = {};
157
+ for (const p of propertyNames) {
158
+ const value = object[p];
159
+
160
+ if (value !== undefined) {
161
+ if (value instanceof Base && value.name) {
162
+ json[p] = { name: value.name };
163
+ } else {
164
+ json[p] = value;
165
+ }
166
+ }
167
+ }
168
+ return json;
169
+ }
package/src/dns.mjs ADDED
@@ -0,0 +1,32 @@
1
+ import { Base } from "./base.mjs";
2
+ import { asArray } from "./utils.mjs";
3
+
4
+ export class DNSService extends Base {
5
+ allowedUpdates = [];
6
+ recordTTL = "1W";
7
+ forwardsTo = [];
8
+
9
+ static get typeName() {
10
+ return "dns";
11
+ }
12
+
13
+ constructor(owner, data) {
14
+ super(owner, data);
15
+ Object.assign(this, data);
16
+ }
17
+
18
+ async *services() {
19
+ const filter = { type: "dns" };
20
+
21
+ yield* this.owner.services(filter);
22
+
23
+ for (const s of asArray(this.forwardsTo)) {
24
+ const owner = await this.owner.world.load(s);
25
+ yield* owner.services(filter);
26
+ }
27
+ }
28
+
29
+ get propertyNames() {
30
+ return ["recordTTL", "forwardsTo", "allowedUpdates"];
31
+ }
32
+ }
package/src/model.mjs CHANGED
@@ -1,139 +1,8 @@
1
1
  import { readFile, glob } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { getAttribute } from "pacc";
4
3
  import { asArray, bridgeToJSON } from "./utils.mjs";
5
-
6
- export class Base {
7
- owner;
8
- name;
9
- description;
10
-
11
- static get typeName() {
12
- return "base";
13
- }
14
-
15
- static get typeFileName() {
16
- return this.typeName + ".json";
17
- }
18
-
19
- static get fileNameGlob() {
20
- return "**/" + this.typeFileName;
21
- }
22
-
23
- static async prepareData(world, data) {
24
- return this;
25
- }
26
-
27
- static baseName(name) {
28
- if (!name) {
29
- return undefined;
30
- }
31
-
32
- return name.replace(/\/\w+\.json$/, "");
33
- }
34
-
35
- constructor(owner, data) {
36
- this.owner = owner;
37
-
38
- if (data) {
39
- this.name = data.name;
40
- if (data.description) {
41
- this.description = data.description;
42
- }
43
- }
44
- }
45
-
46
- withOwner(owner) {
47
- if (this.owner !== owner) {
48
- return new this.constructor(owner, this);
49
- }
50
-
51
- return this;
52
- }
53
-
54
- get typeName() {
55
- return this.constructor.typeName;
56
- }
57
-
58
- get world() {
59
- return this.owner.world;
60
- }
61
-
62
- get location() {
63
- if (this instanceof Location) {
64
- return this;
65
- }
66
- return this.owner.location;
67
- }
68
-
69
- get host() {
70
- if (this instanceof Host) {
71
- return this;
72
- }
73
- return this.owner.host;
74
- }
75
-
76
- async network(name) {
77
- return this.owner.network(name);
78
- }
79
-
80
- #directory;
81
- set directory(directory) {
82
- this.#directory = directory;
83
- }
84
-
85
- get directory() {
86
- return this.#directory || join(this.owner.directory, this.name);
87
- }
88
-
89
- get fullName() {
90
- return this.owner ? join(this.owner.fullName, this.name) : this.name;
91
- }
92
-
93
- expand(object) {
94
- switch (typeof object) {
95
- case "string":
96
- return object.replaceAll(/\$\{([^\}]*)\}/g, (match, m1) => {
97
- return getAttribute(this, m1) || "${" + m1 + "}";
98
- });
99
-
100
- case "object":
101
- if (Array.isArray(object)) {
102
- return object.map(e => this.expand(e));
103
- }
104
-
105
- if (object instanceof Set) {
106
- return new Set([...object].map(e => this.expand(e)));
107
- }
108
-
109
- /*return Object.fromEntries(
110
- Object.entries(object).map(([k, v]) => [k, this.expand(v)])
111
- );*/
112
- }
113
-
114
- return object;
115
- }
116
-
117
- error(...args) {
118
- console.error(`${this.toString()}:`, ...args);
119
- }
120
-
121
- info(...args) {
122
- console.info(`${this.toString()}:`, ...args);
123
- }
124
-
125
- toString() {
126
- return `${this.fullName}(${this.typeName})`;
127
- }
128
-
129
- get propertyNames() {
130
- return ["name", "description", "directory", "owner"];
131
- }
132
-
133
- toJSON() {
134
- return extractFrom(this, this.propertyNames);
135
- }
136
- }
4
+ import { Base } from "./base.mjs";
5
+ import { DNSService } from "./dns.mjs";
137
6
 
138
7
  export class Owner extends Base {
139
8
  #hosts = new Map();
@@ -166,6 +35,18 @@ export class Owner extends Base {
166
35
  }
167
36
  }
168
37
  Object.assign(this, data);
38
+
39
+ this.finalize(() => {
40
+ for (const network of this.#networks.values()) {
41
+ network.execFinalize();
42
+ }
43
+ });
44
+
45
+ this.finalize(() => {
46
+ for (const host of this.#hosts.values()) {
47
+ host.execFinalize();
48
+ }
49
+ });
169
50
  }
170
51
 
171
52
  get dns() {
@@ -248,7 +129,7 @@ export class Owner extends Base {
248
129
  other.bridge = bridge;
249
130
  } else {
250
131
  bridge.add(name);
251
- this.resolveLater(() => this._resolveBridges());
132
+ this.finalize(() => this._resolveBridges());
252
133
  }
253
134
  }
254
135
 
@@ -284,18 +165,6 @@ export class Owner extends Base {
284
165
  }
285
166
  }
286
167
 
287
- #resolveActions = [];
288
-
289
- resolveLater(action) {
290
- this.#resolveActions.push(action);
291
- }
292
-
293
- resolve() {
294
- for (const action of this.#resolveActions) {
295
- action();
296
- }
297
- }
298
-
299
168
  addSubnet(subnet) {
300
169
  this.#subnets.set(subnet.name, subnet);
301
170
  }
@@ -414,6 +283,8 @@ export class World extends Owner {
414
283
  await this.load(name, { type });
415
284
  }
416
285
  }
286
+
287
+ this.execFinalize();
417
288
  }
418
289
 
419
290
  addObject(object) {
@@ -460,41 +331,15 @@ export class World extends Owner {
460
331
  }
461
332
  }
462
333
 
463
- class DNSService extends Base {
464
- allowedUpdates = [];
465
- recordTTL = "1W";
466
- forwardsTo = [];
467
-
468
- static get typeName() {
469
- return "dns";
470
- }
471
-
472
- constructor(owner, data) {
473
- super(owner, data);
474
- Object.assign(this, data);
475
- }
476
-
477
- async *services() {
478
- const filter = { type: "dns" };
479
-
480
- yield* this.owner.services(filter);
481
-
482
- for (const s of asArray(this.forwardsTo)) {
483
- const owner = await this.owner.world.load(s);
484
- yield* owner.services(filter);
485
- }
486
- }
487
-
488
- get propertyNames() {
489
- return ["recordTTL", "forwardsTo", "allowedUpdates"];
490
- }
491
- }
492
-
493
334
  export class Location extends Owner {
494
335
  static get typeName() {
495
336
  return "location";
496
337
  }
497
338
 
339
+ get location() {
340
+ return this;
341
+ }
342
+
498
343
  async *hosts() {
499
344
  for await (const host of this.owner.hosts()) {
500
345
  if (host.location === this) {
@@ -676,6 +521,12 @@ export class Host extends Base {
676
521
  }
677
522
 
678
523
  owner.addHost(this);
524
+
525
+ this.finalize(() => {
526
+ for (const ni of Object.values(this.networkInterfaces)) {
527
+ ni.execFinalize();
528
+ }
529
+ });
679
530
  }
680
531
 
681
532
  get deployment() {
@@ -757,6 +608,10 @@ export class Host extends Base {
757
608
  return this.hostName + "." + this.domain;
758
609
  }
759
610
 
611
+ get host() {
612
+ return this;
613
+ }
614
+
760
615
  addService(service) {
761
616
  this.#services.push(service);
762
617
  }
@@ -777,7 +632,7 @@ export class Host extends Base {
777
632
  addNetworkInterface(networkInterface) {
778
633
  this.networkInterfaces[networkInterface.name] = networkInterface;
779
634
 
780
- if(networkInterface.network) {
635
+ if (networkInterface.network) {
781
636
  networkInterface.network.addHost(this);
782
637
  }
783
638
  }
@@ -841,9 +696,9 @@ export class NetworkInterface extends Base {
841
696
  #metric;
842
697
  #ssid;
843
698
  #psk;
699
+ #network;
844
700
  arpbridge;
845
701
  hwaddr;
846
- network;
847
702
 
848
703
  constructor(owner, data) {
849
704
  super(owner, data);
@@ -866,18 +721,20 @@ export class NetworkInterface extends Base {
866
721
  }
867
722
 
868
723
  if (data.network) {
869
- const network = owner.owner.network(data.network);
724
+ let network = owner.owner.network(data.network);
870
725
 
871
726
  if (network) {
872
- data.network = network;
727
+ this.network = network;
873
728
  } else {
874
- this.error("Missing network", data.network);
729
+ network = data.network;
730
+ this.finalize(() => (this.network = network));
875
731
  }
732
+
733
+ delete data.network;
734
+ } else if (owner.owner instanceof Network) {
735
+ this.network = owner.owner;
876
736
  }
877
- else if(owner.owner instanceof Network) {
878
- data.network = owner.owner;
879
- }
880
-
737
+
881
738
  Object.assign(this, data);
882
739
 
883
740
  owner.addNetworkInterface(this);
@@ -885,6 +742,25 @@ export class NetworkInterface extends Base {
885
742
  //this.arpbridge = owner.addARPBridge(this, data.arpbridge);
886
743
  }
887
744
 
745
+ get network() {
746
+ return this.#network;
747
+ }
748
+
749
+ set network(networkOrName) {
750
+ if (!(networkOrName instanceof Network)) {
751
+ let network = this.owner.owner.network(networkOrName);
752
+
753
+ if (network) {
754
+ this.#network = network;
755
+ return;
756
+ } else {
757
+ this.error("Unknown network", networkOrName);
758
+ }
759
+ }
760
+
761
+ this.#network = networkOrName;
762
+ }
763
+
888
764
  get host() {
889
765
  return this.owner;
890
766
  }
@@ -1070,19 +946,3 @@ export class Service extends Base {
1070
946
 
1071
947
  const _types = [Location, Network, Subnet, Host, Service, DNSService];
1072
948
  const _typesByName = Object.fromEntries(_types.map(t => [t.typeName, t]));
1073
-
1074
- export function extractFrom(object, propertyNames) {
1075
- const json = {};
1076
- for (const p of propertyNames) {
1077
- const value = object[p];
1078
-
1079
- if (value !== undefined) {
1080
- if (value instanceof Base && value.name) {
1081
- json[p] = { name: value.name };
1082
- } else {
1083
- json[p] = value;
1084
- }
1085
- }
1086
- }
1087
- return json;
1088
- }
package/src/module.mjs ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./base.mjs";
2
+ export * from "./dns.mjs";
3
+ export * from "./model.mjs";
@@ -0,0 +1,30 @@
1
+ export function extractFrom(object: any, propertyNames: any): {};
2
+ export class Base {
3
+ static get typeName(): string;
4
+ static get typeFileName(): string;
5
+ static get fileNameGlob(): string;
6
+ static prepareData(world: any, data: any): Promise<typeof Base>;
7
+ static baseName(name: any): any;
8
+ constructor(owner: any, data: any);
9
+ owner: any;
10
+ name: any;
11
+ description: any;
12
+ withOwner(owner: any): any;
13
+ get typeName(): any;
14
+ get world(): any;
15
+ get location(): any;
16
+ get host(): any;
17
+ network(name: any): Promise<any>;
18
+ set directory(directory: any);
19
+ get directory(): any;
20
+ get fullName(): any;
21
+ expand(object: any): any;
22
+ finalize(action: any): void;
23
+ execFinalize(): void;
24
+ error(...args: any[]): void;
25
+ info(...args: any[]): void;
26
+ toString(): string;
27
+ get propertyNames(): string[];
28
+ toJSON(): {};
29
+ #private;
30
+ }
@@ -0,0 +1,7 @@
1
+ export class DNSService extends Base {
2
+ allowedUpdates: any[];
3
+ recordTTL: string;
4
+ forwardsTo: any[];
5
+ services(): AsyncGenerator<any, void, any>;
6
+ }
7
+ import { Base } from "./base.mjs";
package/types/model.d.mts CHANGED
@@ -1,31 +1,3 @@
1
- export function extractFrom(object: any, propertyNames: any): {};
2
- export class Base {
3
- static get typeName(): string;
4
- static get typeFileName(): string;
5
- static get fileNameGlob(): string;
6
- static prepareData(world: any, data: any): Promise<typeof Base>;
7
- static baseName(name: any): any;
8
- constructor(owner: any, data: any);
9
- owner: any;
10
- name: any;
11
- description: any;
12
- withOwner(owner: any): any;
13
- get typeName(): any;
14
- get world(): any;
15
- get location(): any;
16
- get host(): any;
17
- network(name: any): Promise<any>;
18
- set directory(directory: any);
19
- get directory(): any;
20
- get fullName(): any;
21
- expand(object: any): any;
22
- error(...args: any[]): void;
23
- info(...args: any[]): void;
24
- toString(): string;
25
- get propertyNames(): string[];
26
- toJSON(): {};
27
- #private;
28
- }
29
1
  export class Owner extends Base {
30
2
  domain: any;
31
3
  ntp: {
@@ -43,8 +15,6 @@ export class Owner extends Base {
43
15
  addBridge(network: any, destinationNetworks: any): any;
44
16
  _resolveBridges(): void;
45
17
  networkAddresses(): AsyncGenerator<any, void, unknown>;
46
- resolveLater(action: any): void;
47
- resolve(): void;
48
18
  addSubnet(subnet: any): void;
49
19
  subnet(name: any): any;
50
20
  subnets(): MapIterator<any>;
@@ -59,7 +29,7 @@ export class Owner extends Base {
59
29
  }
60
30
  export class World extends Owner {
61
31
  static get types(): {
62
- [k: string]: typeof Location | typeof Host | typeof DNSService | typeof Network | typeof Subnet | typeof Service;
32
+ [k: string]: typeof DNSService | typeof Network | typeof Host | typeof Location | typeof Subnet | typeof Service;
63
33
  };
64
34
  constructor(directory: any);
65
35
  get fullName(): string;
@@ -75,6 +45,7 @@ export class World extends Owner {
75
45
  #private;
76
46
  }
77
47
  export class Location extends Owner {
48
+ get location(): this;
78
49
  }
79
50
  export class Network extends Owner {
80
51
  kind: any;
@@ -107,6 +78,7 @@ export class Host extends Base {
107
78
  get modelName(): any;
108
79
  get hostName(): any;
109
80
  get domainName(): string;
81
+ get host(): this;
110
82
  addService(service: any): void;
111
83
  services(filter: any): Generator<any, void, unknown>;
112
84
  addNetworkInterface(networkInterface: any): void;
@@ -126,7 +98,8 @@ export class Host extends Base {
126
98
  export class NetworkInterface extends Base {
127
99
  arpbridge: any;
128
100
  hwaddr: any;
129
- network: any;
101
+ set network(networkOrName: any);
102
+ get network(): any;
130
103
  get scope(): any;
131
104
  get metric(): any;
132
105
  get ssid(): any;
@@ -149,10 +122,5 @@ export class Service extends Base {
149
122
  get type(): any;
150
123
  #private;
151
124
  }
152
- declare class DNSService extends Base {
153
- allowedUpdates: any[];
154
- recordTTL: string;
155
- forwardsTo: any[];
156
- services(): AsyncGenerator<any, void, any>;
157
- }
158
- export {};
125
+ import { Base } from "./base.mjs";
126
+ import { DNSService } from "./dns.mjs";
@@ -0,0 +1,3 @@
1
+ export * from "./base.mjs";
2
+ export * from "./dns.mjs";
3
+ export * from "./model.mjs";