pmcf 1.46.4 → 1.47.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, chmod } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
- import { Host } from "pmcf";
5
+ import { types } from "pmcf";
6
6
  import { writeLines, sectionLines } from "../src/utils.mjs";
7
7
  import { prepare } from "../src/cmd.mjs";
8
8
 
@@ -10,7 +10,7 @@ const { root, args, options } = await prepare();
10
10
 
11
11
  const hostName = args[0];
12
12
 
13
- const host = await root.load(hostName, { type: Host });
13
+ const host = await root.load(hostName, { type: types.host });
14
14
 
15
15
  await generateNetworkDefs(host, options.output);
16
16
  await generateMachineInfo(host, options.output);
@@ -105,7 +105,7 @@ async function generateNetworkDefs(host, dir) {
105
105
  })
106
106
  );
107
107
 
108
- if (ni.arpbridge) {
108
+ if (ni.arpbridge.length) {
109
109
  networkSections.push(
110
110
  "",
111
111
  sectionLines("Link", { Promiscuous: "yes" })
@@ -2,13 +2,13 @@
2
2
 
3
3
  import { mkdir, copyFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
- import { Location } from "pmcf";
5
+ import { types } from "pmcf";
6
6
  import { writeLines, sectionLines } from "../src/utils.mjs";
7
7
  import { prepare } from "../src/cmd.mjs";
8
8
 
9
9
  const { root, args, options } = await prepare();
10
10
 
11
- const location = await root.load(args[0], { type: Location });
11
+ const location = await root.load(args[0], { type: types.location });
12
12
 
13
13
  await generateLocationDefs(location, options.output);
14
14
 
@@ -52,7 +52,7 @@ async function generateNamedDefs(owner, targetDir) {
52
52
  };
53
53
  };
54
54
 
55
- for await (const mail of owner.services({ type: "smtp" })) {
55
+ for await (const mail of owner.findServices({ type: "smtp" })) {
56
56
  records.add(
57
57
  createRecord("@", "MX", mail.priority, fullName(mail.owner.domainName))
58
58
  );
@@ -112,7 +112,7 @@ async function generateNamedDefs(owner, targetDir) {
112
112
 
113
113
  if (!hosts.has(host)) {
114
114
  hosts.add(host);
115
- for (const service of host.services()) {
115
+ for (const service of host.findServices()) {
116
116
  if (service.master && service.alias) {
117
117
  zone.records.add(
118
118
  createRecord(service.alias, "CNAME", fullName(host.domainName))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmcf",
3
- "version": "1.46.4",
3
+ "version": "1.47.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -43,7 +43,7 @@
43
43
  "pacc": "^3.3.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@types/node": "^22.13.4",
46
+ "@types/node": "^22.13.5",
47
47
  "ava": "^6.2.0",
48
48
  "c8": "^10.1.3",
49
49
  "documentation": "^14.0.3",
package/src/base.mjs CHANGED
@@ -1,32 +1,39 @@
1
1
  import { join } from "node:path";
2
2
  import { getAttribute } from "pacc";
3
- import { typesByName } from "./types.mjs";
4
- import { asArray } from "./utils.mjs";
3
+ import { addType, primitives } from "./types.mjs";
4
+
5
+ const BaseTypeDefinition = {
6
+ name: "base",
7
+ owners: [],
8
+ properties: {
9
+ owner: { type: "base", collection: false, writeable: false },
10
+ type: { type: "string", collection: false, writeable: false },
11
+ name: {
12
+ type: "string",
13
+ collection: false,
14
+ identifier: true,
15
+ writeable: true
16
+ },
17
+ description: { type: "string", collection: false, writeable: true },
18
+ directory: { type: "string", collection: false, writeable: false }
19
+ }
20
+ };
5
21
 
6
22
  export class Base {
7
23
  owner;
8
- name;
9
24
  description;
25
+ name;
26
+
27
+ static {
28
+ addType(this);
29
+ }
10
30
 
11
31
  static get typeName() {
12
32
  return this.typeDefinition.name;
13
33
  }
14
34
 
15
35
  static get typeDefinition() {
16
- return {
17
- name: "base",
18
- properties: {
19
- type: { type: "string", writeable: false },
20
- name: { type: "string" },
21
- description: { type: "string" },
22
- directory: { type: "string", writeable: false },
23
- owner: {}
24
- }
25
- };
26
- }
27
-
28
- static get nameLookupName() {
29
- return this.typeName + "Named";
36
+ return BaseTypeDefinition;
30
37
  }
31
38
 
32
39
  static get typeFileName() {
@@ -37,16 +44,6 @@ export class Base {
37
44
  return "**/" + this.typeFileName;
38
45
  }
39
46
 
40
- static async prepareData(root, data) {
41
- return this;
42
- }
43
-
44
- static normalizeName(name) {
45
- if (name !== undefined) {
46
- return name.replace(/\/\w+\.json$/, "");
47
- }
48
- }
49
-
50
47
  constructor(owner, data) {
51
48
  this.owner = owner;
52
49
 
@@ -54,62 +51,155 @@ export class Base {
54
51
  case "string":
55
52
  this.name = data;
56
53
  break;
57
- case "object": {
58
- this.name = data.name;
59
- if (data.description) {
60
- this.description = data.description;
61
- }
54
+ case "object":
55
+ this.read(data, BaseTypeDefinition);
56
+ }
57
+
58
+ if (this.name === undefined) {
59
+ this.error("Missing name", this.owner?.toString(), data);
60
+ }
61
+ }
62
+
63
+ ownerFor(property, data) {
64
+ for (const type of property.type.owners) {
65
+ if (this.typeName === type?.name) {
66
+ return this;
67
+ }
68
+ }
69
+ for (const type of property.type.owners) {
70
+ const owner = this[type?.name];
71
+ if (owner) {
72
+ return owner;
62
73
  }
63
74
  }
75
+
76
+ return this;
64
77
  }
65
78
 
66
- read(data) {
67
- for (const [slotName, typeDef] of Object.entries(
68
- this.constructor.typeDefinition.properties
69
- )) {
70
- let slot = data[slotName];
71
- if (slot) {
72
- delete data[slotName];
73
-
74
- const type =
75
- typeof typeDef.type === "string"
76
- ? typesByName[typeDef.type]
77
- : typeDef.type;
78
-
79
- if (typeDef.collection) {
80
- if (Array.isArray(slot) || typeof slot === "string") {
81
- slot = asArray(slot);
82
- if (type) {
83
- for (const item of slot) {
84
- new type(this, item);
79
+ read(data, type) {
80
+ const assign = (property, value) => {
81
+ if (value !== undefined) {
82
+ if (property.collection) {
83
+ const current = this[property.name];
84
+
85
+ switch (typeof current) {
86
+ case "undefined":
87
+ this[property.name] = value;
88
+ break;
89
+ case "object":
90
+ if (Array.isArray(current)) {
91
+ current.push(value);
92
+ } else {
93
+ if (current instanceof Map || current instanceof Set) {
94
+ // TODO
95
+ this[property.name] = value;
96
+ } else {
97
+ this.error("Unknown collection type", property.name, current);
98
+ }
99
+ }
100
+ break;
101
+ case "function":
102
+ if (value instanceof Base) {
103
+ this.addObject(value);
104
+ } else {
105
+ this.error("Unknown collection type", property.name, current);
85
106
  }
107
+ break;
108
+ }
109
+ } else {
110
+ this[property.name] = value;
111
+ }
112
+ }
113
+ };
114
+
115
+ const instantiateAndAssign = (property, value) => {
116
+ if (primitives.has(property.type)) {
117
+ assign(property, value);
118
+ return;
119
+ }
120
+
121
+ switch (typeof value) {
122
+ case "undefined":
123
+ return;
124
+ case "function":
125
+ this.error("Invalid value", property.name, value);
126
+ break;
127
+
128
+ case "boolean":
129
+ case "bigint":
130
+ case "number":
131
+ case "string":
132
+ {
133
+ value = this.expand(value);
134
+ const object = this.typeNamed(property.type.name, value);
135
+
136
+ if (object) {
137
+ assign(property, object);
86
138
  } else {
87
- this[slotName] = slot;
139
+ if (property.type.constructWithIdentifierOnly) {
140
+ new property.type.clazz(this.ownerFor(property, value), value);
141
+ } else {
142
+ this.finalize(() => {
143
+ value = this.expand(value);
144
+ const object =
145
+ this.typeNamed(property.type.name, value) ||
146
+ this.owner.typeNamed(property.type.name, value) ||
147
+ this.root.typeNamed(property.type.name, value); // TODO
148
+
149
+ if (object) {
150
+ assign(property, object);
151
+ } else {
152
+ this.error("Not found", property.name, property.type.name, value);
153
+ }
154
+ });
155
+ }
88
156
  }
157
+ }
158
+ break;
159
+ case "object":
160
+ if (value instanceof property.type.clazz) {
161
+ assign(property, value);
89
162
  } else {
90
- for (const [objectName, objectData] of Object.entries(slot)) {
91
- objectData.name = objectName;
92
- new type(this, objectData);
93
- }
163
+ assign(
164
+ property,
165
+ new property.type.clazz(this.ownerFor(property, value), value)
166
+ );
94
167
  }
95
- } else {
96
- switch (typeDef.type) {
97
- case "undefined":
98
- break;
99
- case "boolean":
100
- case "string":
101
- case "number":
102
- this[slotName] = slot;
103
- break;
168
+ break;
169
+ }
170
+ };
104
171
 
105
- default:
106
- this[slotName] = new type(this, slot);
172
+ for (const property of Object.values(type.properties)) {
173
+ if (property.writeable) {
174
+ const value = data[property.name];
175
+ if (property.collection) {
176
+ if (typeof value === "object") {
177
+ if (Array.isArray(value)) {
178
+ for (const v of value) {
179
+ instantiateAndAssign(property, v);
180
+ }
181
+ } else {
182
+ for (const [objectName, objectData] of Object.entries(value)) {
183
+ objectData[type.identifier.name] = objectName;
184
+ instantiateAndAssign(property, objectData);
185
+ }
186
+ }
187
+ continue;
107
188
  }
108
189
  }
190
+ instantiateAndAssign(property, value);
109
191
  }
110
192
  }
111
193
  }
112
194
 
195
+ typeNamed(typeName, name) {
196
+ return this.owner.typeNamed(typeName, name);
197
+ }
198
+
199
+ addObject(object) {
200
+ return this.owner.addObject(object);
201
+ }
202
+
113
203
  forOwner(owner) {
114
204
  if (this.owner !== owner) {
115
205
  // @ts-ignore
@@ -119,6 +209,16 @@ export class Base {
119
209
  return this;
120
210
  }
121
211
 
212
+ isNamed(name) {
213
+ return name[0] === "/" ? this.fullName === name : this.name === name;
214
+ }
215
+
216
+ relativeName(name) {
217
+ return name?.[0] === "/"
218
+ ? name.substring(this.owner.fullName.length + 1)
219
+ : name;
220
+ }
221
+
122
222
  get typeName() {
123
223
  // @ts-ignore
124
224
  return this.constructor.typeDefinition.name;
@@ -150,16 +250,13 @@ export class Base {
150
250
  }
151
251
 
152
252
  get directory() {
153
- return (
154
- this.#directory ||
155
- (this.owner ? join(this.owner.directory, this.name) : this.name)
156
- );
253
+ return this.#directory || join(this.owner.directory, this.name);
157
254
  }
158
255
 
159
256
  get fullName() {
160
- return this.owner?.fullName && this.name
161
- ? join(this.owner.fullName, this.name)
162
- : this.name;
257
+ return this.name
258
+ ? join(this.owner.fullName, "/", this.name)
259
+ : this.owner.fullName;
163
260
  }
164
261
 
165
262
  expand(object) {
@@ -246,7 +343,29 @@ export class Base {
246
343
  }
247
344
  }
248
345
 
249
- export function extractFrom(object, typeDefinition) {
346
+ export function extractFrom(
347
+ object,
348
+ typeDefinition = object?.constructor?.typeDefinition
349
+ ) {
350
+ if (Array.isArray(object)) {
351
+ if (object.length === 0) {
352
+ return undefined;
353
+ }
354
+
355
+ if (typeDefinition?.identifier) {
356
+ return Object.fromEntries(
357
+ object.map(o => {
358
+ o = extractFrom(o);
359
+ const name = o[typeDefinition.identifier.name];
360
+ delete o[typeDefinition.identifier.name];
361
+ return [name, o];
362
+ })
363
+ );
364
+ }
365
+
366
+ return object.map(o => extractFrom(o));
367
+ }
368
+
250
369
  if (!typeDefinition || object === undefined) {
251
370
  return object;
252
371
  }
@@ -262,19 +381,13 @@ export function extractFrom(object, typeDefinition) {
262
381
  {
263
382
  value = object[name]();
264
383
 
265
- if (Array.isArray(value)) {
266
- if (value.length > 0) {
267
- json[name] = value;
268
- }
269
- } else {
270
- if (typeof value?.next === "function") {
271
- value = [...value];
272
- if (value.length > 0) {
273
- json[name] = value;
274
- }
275
- } else {
276
- json[name] = value;
277
- }
384
+ if (typeof value?.next === "function") {
385
+ value = [...value];
386
+ }
387
+
388
+ value = extractFrom(value, def.type);
389
+ if (value !== undefined) {
390
+ json[name] = value;
278
391
  }
279
392
  }
280
393
  break;
@@ -286,14 +399,17 @@ export function extractFrom(object, typeDefinition) {
286
399
  }
287
400
  } else {
288
401
  if (Array.isArray(value)) {
289
- json[name] = value;
402
+ json[name] = extractFrom(value);
290
403
  } else {
291
- json[name] = Object.fromEntries(
404
+ const resultObject = Object.fromEntries(
292
405
  Object.entries(value).map(([k, v]) => [
293
406
  k,
294
- extractFrom(v, typesByName[def.type])
407
+ v // extractFrom(v, def.type)
295
408
  ])
296
409
  );
410
+ if (Object.keys(resultObject).length > 0) {
411
+ json[name] = resultObject;
412
+ }
297
413
  }
298
414
  }
299
415
  break;
@@ -304,7 +420,7 @@ export function extractFrom(object, typeDefinition) {
304
420
  json[name] = value;
305
421
  }
306
422
  }
307
- typeDefinition = typeDefinition?.extends?.typeDefinition;
423
+ typeDefinition = typeDefinition?.extends;
308
424
  } while (typeDefinition);
309
425
 
310
426
  return json;
package/src/cluster.mjs CHANGED
@@ -1,21 +1,21 @@
1
1
  import { Owner } from "./owner.mjs";
2
2
  import { addType } from "./types.mjs";
3
3
 
4
+ const typeDefinition = {
5
+ name: "cluster",
6
+ owners: [Owner.typeDefinition, "network", "root"],
7
+ priority: 0.7,
8
+ extends: Owner.typeDefinition,
9
+ properties: {}
10
+ };
11
+
4
12
  export class Cluster extends Owner {
5
13
  static {
6
14
  addType(this);
7
15
  }
8
16
 
9
17
  static get typeDefinition() {
10
- return {
11
- name: "cluster",
12
- extends: Owner,
13
- properties: {}
14
- };
18
+ return typeDefinition;
15
19
  }
16
20
 
17
- constructor(owner, data) {
18
- super(owner, data);
19
- owner.addObject(this);
20
- }
21
21
  }
package/src/dns.mjs CHANGED
@@ -1,47 +1,59 @@
1
1
  import { Base } from "./base.mjs";
2
- import { asArray } from "./utils.mjs";
3
2
  import { addType } from "./types.mjs";
4
3
 
4
+ const DNSServiceTypeDefinition = {
5
+ name: "dns",
6
+ owners: ["location", "owner", "network", "cluster", "root"],
7
+ priority: 0.1,
8
+ properties: {
9
+ hasSVRRecords: { type: "boolean", collection: false, writeable: true },
10
+ hasCatalog: { type: "boolean", collection: false, writeable: true },
11
+ recordTTL: { type: "string", collection: false, writeable: true },
12
+ soaUpdates: { type: "number", collection: true, writeable: true },
13
+ forwardsTo: { type: "network", collection: true, writeable: true },
14
+ allowedUpdates: { type: "string", collection: true, writeable: true }
15
+ }
16
+ };
17
+
5
18
  export class DNSService extends Base {
6
19
  allowedUpdates = [];
7
20
  recordTTL = "1W";
8
21
  soaUpdates = [36000, 72000, 600000, 60000];
9
22
  hasSVRRecords = true;
10
23
  hasCatalog = true;
11
- forwardsTo = [];
24
+ #forwardsTo = [];
12
25
 
13
26
  static {
14
27
  addType(this);
15
28
  }
16
29
 
17
30
  static get typeDefinition() {
18
- return {
19
- name: "dns",
20
- properties: {
21
- hasSVRRecords: { type: "boolean" },
22
- hasCatalog: { type: "boolean" },
23
- recordTTL: { type: "string" },
24
- soaUpdates: { type: "number", collection: true },
25
- forwardsTo: { type: "host", collection: true },
26
- allowedUpdates: { type: "string", collection: true }
27
- }
28
- };
31
+ return DNSServiceTypeDefinition;
29
32
  }
30
33
 
31
34
  constructor(owner, data) {
35
+ if (!data.name) {
36
+ data.name = DNSServiceTypeDefinition.name; // TODO
37
+ }
32
38
  super(owner, data);
33
- Object.assign(this, data);
34
- owner.addObject(this);
39
+ this.read(data, DNSServiceTypeDefinition);
40
+ }
41
+
42
+ set forwardsTo(value) {
43
+ this.#forwardsTo.push(value);
44
+ }
45
+
46
+ get forwardsTo() {
47
+ return this.#forwardsTo;
35
48
  }
36
49
 
37
- async *services() {
38
- const filter = { type: "dns" };
50
+ async *findServices() {
51
+ const filter = { type: DNSServiceTypeDefinition.name };
39
52
 
40
- yield* this.owner.services(filter);
53
+ yield* this.owner.findServices(filter);
41
54
 
42
- for (const s of asArray(this.forwardsTo)) {
43
- const owner = await this.owner.root.load(s);
44
- yield* owner.services(filter);
55
+ for (const s of this.forwardsTo) {
56
+ yield* s.findServices(filter);
45
57
  }
46
58
  }
47
59
 
@@ -50,7 +62,7 @@ export class DNSService extends Base {
50
62
  }
51
63
 
52
64
  async resolvedConfig() {
53
- const dnsServices = (await Array.fromAsync(this.services())).sort(
65
+ const dnsServices = (await Array.fromAsync(this.findServices())).sort(
54
66
  (a, b) => a.priority - b.priority
55
67
  );
56
68