pmcf 1.46.3 → 1.47.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/src/owner.mjs CHANGED
@@ -4,53 +4,47 @@ import { Subnet } from "./subnet.mjs";
4
4
  import { addType } from "./types.mjs";
5
5
  import { DNSService } from "./dns.mjs";
6
6
 
7
+ const OwnerTypeDefinition = {
8
+ name: "owner",
9
+ owners: ["owner", "root"],
10
+ priority: 0.9,
11
+ extends: Base.typeDefinition,
12
+ properties: {
13
+ networks: { type: "network", collection: true, writeable: true },
14
+ hosts: { type: "host", collection: true, writeable: true },
15
+ clusters: { type: "cluster", collection: true, writeable: true },
16
+ subnets: { type: Subnet.typeDefinition, collection: true, writeable: true },
17
+ dns: {
18
+ type: DNSService.typeDefinition,
19
+ collection: false,
20
+ writeable: true
21
+ },
22
+ ntp: { type: "string", collection: false, writeable: true },
23
+ domain: { type: "string", collection: false, writeable: true },
24
+ administratorEmail: { type: "string", collection: false, writeable: true }
25
+ }
26
+ };
27
+
28
+ const EMPTY = new Map();
29
+
7
30
  export class Owner extends Base {
8
31
  #membersByType = new Map();
9
32
  #bridges = new Set();
10
33
  #administratorEmail;
11
34
  domain;
12
- ntp = { servers: [] };
35
+ ntp;
13
36
 
14
37
  static {
15
38
  addType(this);
16
39
  }
17
40
 
18
41
  static get typeDefinition() {
19
- return {
20
- name: "owner",
21
- extends: Base,
22
- properties: {
23
- networks: { type: "network", collection: true },
24
- hosts: { type: "host", collection: true },
25
- clusters: { type: "cluster", collection: true },
26
- subnets: { type: Subnet, collection: true },
27
- dns: { type: DNSService, collection: false },
28
- domain: { type: "string" },
29
- administratorEmail: { type: "string" }
30
- }
31
- };
42
+ return OwnerTypeDefinition;
32
43
  }
33
44
 
34
- constructor(owner, data = {}) {
45
+ constructor(owner, data) {
35
46
  super(owner, data);
36
-
37
- if (data.administratorEmail) {
38
- this.#administratorEmail = data.administratorEmail;
39
- delete data.administratorEmail;
40
- }
41
-
42
- if (data.domain) {
43
- this.domain = data.domain;
44
- delete data.domain;
45
- }
46
-
47
- if (data.ntp) {
48
- this.ntp = data.ntp;
49
- delete data.ntp;
50
- }
51
-
52
- this.read(data);
53
-
47
+ this.read(data, OwnerTypeDefinition);
54
48
  owner?.addObject(this);
55
49
  }
56
50
 
@@ -62,6 +56,8 @@ export class Owner extends Base {
62
56
  }
63
57
  }
64
58
 
59
+ this.dns?._traverse(...args);
60
+
65
61
  return true;
66
62
  }
67
63
 
@@ -69,6 +65,10 @@ export class Owner extends Base {
69
65
  }
70
66
 
71
67
  named(name) {
68
+ if (name[0] === "/") {
69
+ name = name.substring(this.fullName.length + 1);
70
+ }
71
+
72
72
  for (const slot of this.#membersByType.values()) {
73
73
  const object = slot.get(name);
74
74
  if (object) {
@@ -78,6 +78,10 @@ export class Owner extends Base {
78
78
  }
79
79
 
80
80
  typeNamed(typeName, name) {
81
+ if (name[0] === "/") {
82
+ name = name.substring(this.fullName.length + 1);
83
+ }
84
+
81
85
  const typeSlot = this.#membersByType.get(typeName);
82
86
  return typeSlot?.get(name) || this.owner?.typeNamed(typeName, name);
83
87
  }
@@ -88,29 +92,26 @@ export class Owner extends Base {
88
92
 
89
93
  typeList(typeName) {
90
94
  const typeSlot = this.#membersByType.get(typeName);
91
- if (typeSlot) {
92
- return typeSlot.values();
93
- }
94
-
95
- return [];
95
+ return (typeSlot || EMPTY).values();
96
96
  }
97
97
 
98
- _addObject(typeName, fullName, object) {
98
+ addTypeObject(typeName, name, object) {
99
99
  let typeSlot = this.#membersByType.get(typeName);
100
100
  if (!typeSlot) {
101
101
  typeSlot = new Map();
102
102
  this.#membersByType.set(typeName, typeSlot);
103
103
  }
104
- typeSlot.set(fullName, object);
104
+
105
+ typeSlot.set(name, object);
105
106
  }
106
107
 
107
108
  addObject(object) {
108
- this._addObject(object.typeName, object.fullName, object);
109
+ this.addTypeObject(object.typeName, object.name, object);
109
110
  }
110
111
 
111
- service(filter) {
112
+ findService(filter) {
112
113
  let best;
113
- for (const service of this.services(filter)) {
114
+ for (const service of this.findServices(filter)) {
114
115
  if (!best || service.priority < best.priority) {
115
116
  best = service;
116
117
  }
@@ -119,9 +120,9 @@ export class Owner extends Base {
119
120
  return best;
120
121
  }
121
122
 
122
- *services(filter) {
123
+ *findServices(filter) {
123
124
  for (const host of this.hosts()) {
124
- for (const service of host.services(filter)) {
125
+ for (const service of host.findServices(filter)) {
125
126
  yield service;
126
127
  }
127
128
  }
@@ -169,17 +170,14 @@ export class Owner extends Base {
169
170
  return this.subnetNamed(cidr) || new Subnet(this, cidr);
170
171
  }
171
172
 
172
- const subnets = [...this.subnets()];
173
-
174
- const subnet = subnets.find(s => s.matchesAddress(address));
175
- if (subnet) {
176
- return subnet;
173
+ const subnet = this.subnetForAddress(address);
174
+ if (!subnet) {
175
+ this.error(
176
+ `Address without subnet ${address}`,
177
+ [...this.subnets()].map(s => s.address)
178
+ );
177
179
  }
178
-
179
- this.error(
180
- `Address without subnet ${address}`,
181
- [...this.subnets()].map(s => s.address)
182
- );
180
+ return subnet;
183
181
  }
184
182
 
185
183
  subnetForAddress(address) {
@@ -236,8 +234,6 @@ export class Owner extends Base {
236
234
 
237
235
  _resolveBridges() {
238
236
  for (const bridge of this.#bridges) {
239
- //this.info(bridgeToJSON(bridge));
240
-
241
237
  const subnets = new Map();
242
238
 
243
239
  for (let network of bridge) {
@@ -253,7 +249,6 @@ export class Owner extends Base {
253
249
  this.error(`Unresolvabale bridge network`, network);
254
250
  }
255
251
  }
256
-
257
252
  // enshure only one subnet address in the bridge
258
253
  for (const subnet of network.subnets()) {
259
254
  const present = subnets.get(subnet.address);
@@ -277,8 +272,20 @@ export class Owner extends Base {
277
272
  }
278
273
  }
279
274
 
275
+ set administratorEmail(value) {
276
+ this.#administratorEmail = value;
277
+ }
278
+
280
279
  get administratorEmail() {
281
- return this.#administratorEmail || "admin@" + this.domain;
280
+ if (this.#administratorEmail) {
281
+ return this.#administratorEmail;
282
+ }
283
+
284
+ if (this.owner) {
285
+ return this.owner.administratorEmail;
286
+ }
287
+
288
+ return "admin@" + this.domain;
282
289
  }
283
290
 
284
291
  *domains() {
@@ -286,14 +293,4 @@ export class Owner extends Base {
286
293
  yield location.domain;
287
294
  }
288
295
  }
289
-
290
- toJSON() {
291
- const json = super.toJSON();
292
-
293
- for (const [typeName, slot] of this.#membersByType) {
294
- json[typeName] = [...slot.keys()].sort();
295
- }
296
-
297
- return json;
298
- }
299
296
  }
package/src/root.mjs CHANGED
@@ -1,8 +1,15 @@
1
1
  import { readFile, glob } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { Base } from "./base.mjs";
4
3
  import { Location } from "./location.mjs";
5
- import { addType, types } from "./types.mjs";
4
+ import { addType, types, resolveTypeLinks } from "./types.mjs";
5
+
6
+ const RootTypeDefinition = {
7
+ name: "root",
8
+ owners: [],
9
+ priority: 1000,
10
+ extends: Location.typeDefinition,
11
+ properties: {}
12
+ };
6
13
 
7
14
  export class Root extends Location {
8
15
  static {
@@ -10,15 +17,12 @@ export class Root extends Location {
10
17
  }
11
18
 
12
19
  static get typeDefinition() {
13
- return {
14
- name: "root",
15
- extends: Location,
16
- properties: {}
17
- };
20
+ return RootTypeDefinition;
18
21
  }
19
22
 
20
23
  constructor(directory) {
21
- super(undefined, { name: "" });
24
+ resolveTypeLinks();
25
+ super(undefined, "");
22
26
  this.directory = directory;
23
27
  this.addObject(this);
24
28
  }
@@ -31,63 +35,53 @@ export class Root extends Location {
31
35
  return this;
32
36
  }
33
37
 
38
+ async _load(name, type) {
39
+ const data = JSON.parse(
40
+ await readFile(
41
+ join(this.directory, name, type.clazz.typeFileName),
42
+ "utf8"
43
+ )
44
+ );
45
+
46
+ const parentName = name.replace(/\/[^\/]+$/, "");
47
+ const owner = name === parentName ? this.root : await this.load(parentName);
48
+
49
+ const fullName = this.fullName + "/" + name;
50
+ data.name = fullName.substring(owner.fullName.length + 1);
51
+
52
+ const object = new type.clazz(owner, data);
53
+
54
+ this.addTypeObject(type.clazz.typeName, name, object);
55
+ return object;
56
+ }
57
+
34
58
  async load(name, options) {
35
- const fullName = Base.normalizeName(name);
36
- let object = this.named(fullName);
59
+ name = name.replace(/\/([^\/]+\.json)?$/, "");
60
+
61
+ const object = this.named(name);
37
62
  if (object) {
38
63
  return object;
39
64
  }
40
65
 
41
- //console.log("LOAD", fullName);
42
-
43
- let path = fullName.split("/");
44
- path.pop();
45
-
46
- let data;
47
- let type = options?.type;
48
- if (type) {
49
- data = JSON.parse(
50
- await readFile(
51
- join(this.directory, fullName, type.typeFileName),
52
- "utf8"
53
- )
54
- );
66
+ if (options?.type) {
67
+ return this._load(name, options.type);
55
68
  } else {
56
- for (type of types) {
69
+ for (const type of Object.values(types)) {
57
70
  try {
58
- data = JSON.parse(
59
- await readFile(
60
- join(this.directory, fullName, type.typeFileName),
61
- "utf8"
62
- )
63
- );
64
- break;
71
+ return await this._load(name, type);
65
72
  } catch {}
66
73
  }
67
-
68
- if (!data) {
69
- return this.load(path.join("/"), options);
70
- }
71
74
  }
72
75
 
73
- const owner = await this.load(path.join("/"));
74
-
75
- const length = owner.fullName.length;
76
- const n = fullName[length] === "/" ? length + 1 : length;
77
- data.name = fullName.substring(n);
78
-
79
- type = await type.prepareData(this, data);
80
-
81
- object = new type(owner, data);
82
-
83
- this._addObject(type.typeName, fullName, object);
84
-
85
- return object;
76
+ const parentName = name.replace(/\/[^\/]$/, "");
77
+ return name === parentName ? this.root : this.load(parentName, options);
86
78
  }
87
79
 
88
80
  async loadAll() {
89
- for (let type of types) {
90
- for await (const name of glob(type.fileNameGlob, {
81
+ for (const type of Object.values(types).sort(
82
+ (a, b) => b.priority - a.priority
83
+ )) {
84
+ for await (const name of glob(type.clazz.fileNameGlob, {
91
85
  cwd: this.directory
92
86
  })) {
93
87
  await this.load(name, { type });
package/src/service.mjs CHANGED
@@ -14,6 +14,24 @@ const ServiceTypes = {
14
14
  dhcp: {}
15
15
  };
16
16
 
17
+ const ServiceTypeDefinition = {
18
+ name: "service",
19
+ owners: ["host"],
20
+ priority: 0.4,
21
+ extends: Base.typeDefinition,
22
+ properties: {
23
+ ipAddresses: { type: "string", collection: true, writeable: true },
24
+ addresses: { type: "string", collection: true, writeable: true },
25
+ port: { type: "number", collection: false, writeable: true },
26
+ protocol: { type: "string", collection: false, writeable: true },
27
+ alias: { type: "string", collection: false, writeable: true },
28
+ type: { type: "string", collection: false, writeable: true },
29
+ master: { type: "boolean", collection: false, writeable: true },
30
+ priority: { type: "number", collection: false, writeable: true },
31
+ weight: { type: "number", collection: false, writeable: true }
32
+ }
33
+ };
34
+
17
35
  export class Service extends Base {
18
36
  alias;
19
37
  #weight;
@@ -27,49 +45,13 @@ export class Service extends Base {
27
45
  }
28
46
 
29
47
  static get typeDefinition() {
30
- return {
31
- name: "service",
32
- extends: Base,
33
- properties: {
34
- ipAddresses: { type: "string", collection: true },
35
- addresses: { type: "string", collection: true },
36
- port: { type: "number" },
37
- protocol: { type: "string" },
38
- alias: { type: "string" },
39
- type: { type: "string" },
40
- master: { type: "boolean" },
41
- priority: { type: "number" },
42
- weight: { type: "number" }
43
- }
44
- };
48
+ return ServiceTypeDefinition;
45
49
  }
46
50
 
47
51
  constructor(owner, data) {
48
52
  super(owner, data);
49
- if (data.weight !== undefined) {
50
- this.#weight = data.weight;
51
- delete data.weight;
52
- }
53
- if (data.priority !== undefined) {
54
- this.#priority = data.priority;
55
- delete data.priority;
56
- }
57
- if (data.type) {
58
- this.#type = data.type;
59
- delete data.type;
60
- }
61
- if (data.port !== undefined) {
62
- this.#port = data.port;
63
- delete data.port;
64
- }
65
- if (data.ipAddresses) {
66
- this.#ipAddresses = data.ipAddresses;
67
- delete data.ipAddresses;
68
- }
69
-
70
- Object.assign(this, data);
71
-
72
- owner.addService(this);
53
+ this.read(data, ServiceTypeDefinition);
54
+ // owner.addService(this);
73
55
  }
74
56
 
75
57
  forOwner(owner) {
@@ -90,6 +72,7 @@ export class Service extends Base {
90
72
  if (this.#ipAddresses) {
91
73
  data.ipAddresses = this.#ipAddresses;
92
74
  }
75
+
93
76
  // @ts-ignore
94
77
  return new this.constructor(owner, data);
95
78
  }
@@ -97,15 +80,8 @@ export class Service extends Base {
97
80
  return this;
98
81
  }
99
82
 
100
- get protocol() {
101
- return ServiceTypes[this.type]?.protocol;
102
- }
103
-
104
- get srvPrefix() {
105
- const st = ServiceTypes[this.type];
106
- if (st?.protocol) {
107
- return `_${this.type}._${st.protocol}`;
108
- }
83
+ set ipAddresses(value) {
84
+ this.#ipAddresses = value;
109
85
  }
110
86
 
111
87
  get ipAddresses() {
@@ -116,23 +92,50 @@ export class Service extends Base {
116
92
  return this.ipAddresses.map(a => `${a}:${this.port}`);
117
93
  }
118
94
 
95
+ set port(value) {
96
+ this.#port = value;
97
+ }
98
+
119
99
  get port() {
120
100
  return this.#port || ServiceTypes[this.type]?.port;
121
101
  }
122
102
 
103
+ set priority(value) {
104
+ this.#priority = value;
105
+ }
106
+
123
107
  get priority() {
124
108
  return this.#priority || this.owner.priority || 99;
125
109
  }
126
110
 
111
+ set weight(value) {
112
+ this.#weight = value;
113
+ }
114
+
127
115
  get weight() {
128
116
  return this.#weight || this.owner.weight || 0;
129
117
  }
130
118
 
131
- get master() {
132
- return this.owner.master;
119
+ set type(value) {
120
+ this.#type = value;
133
121
  }
134
122
 
135
123
  get type() {
136
124
  return this.#type || this.name;
137
125
  }
126
+
127
+ get master() {
128
+ return this.owner.master;
129
+ }
130
+
131
+ get protocol() {
132
+ return ServiceTypes[this.type]?.protocol;
133
+ }
134
+
135
+ get srvPrefix() {
136
+ const st = ServiceTypes[this.type];
137
+ if (st?.protocol) {
138
+ return `_${this.type}._${st.protocol}`;
139
+ }
140
+ }
138
141
  }
package/src/subnet.mjs CHANGED
@@ -2,6 +2,23 @@ import { normalizeCIDR, isLinkLocal } from "./utils.mjs";
2
2
  import { Base } from "./base.mjs";
3
3
  import { addType } from "./types.mjs";
4
4
 
5
+ const SubnetTypeDefinition = {
6
+ name: "subnet",
7
+ owners: ["location", "owner", "network", "root"],
8
+ priority: 0.6,
9
+ constructWithIdentifierOnly: true,
10
+ properties: {
11
+ address: {
12
+ type: "string",
13
+ collection: false,
14
+ writeable: false,
15
+ identifier: true
16
+ },
17
+ networks: { type: "network", collection: true, writeable: true },
18
+ prefixLength: { type: "number", collection: false, writeable: false }
19
+ }
20
+ };
21
+
5
22
  export class Subnet extends Base {
6
23
  networks = new Set();
7
24
 
@@ -10,27 +27,12 @@ export class Subnet extends Base {
10
27
  }
11
28
 
12
29
  static get typeDefinition() {
13
- return {
14
- name: "subnet",
15
- properties: {
16
- address: { type: "string" },
17
- networks: { type: "network", collection: true },
18
- prefixLength: { type: "number", writeable: false }
19
- }
20
- };
30
+ return SubnetTypeDefinition;
21
31
  }
22
32
 
23
33
  constructor(owner, address) {
24
34
  const { cidr } = normalizeCIDR(address);
25
-
26
- if (!cidr) {
27
- const error = Error(`Invalid address`);
28
- error.address = address;
29
- throw error;
30
- }
31
-
32
35
  super(owner, cidr);
33
-
34
36
  owner.addObject(this);
35
37
  }
36
38
 
package/src/types.mjs CHANGED
@@ -1,24 +1,50 @@
1
+ export const types = {};
2
+
1
3
  export function addType(clazz) {
2
- types.push(clazz);
4
+ const type = clazz.typeDefinition;
5
+
6
+ types[type.name] = type;
7
+
8
+ type.clazz = clazz;
9
+ }
10
+
11
+ export const primitives = new Set(["string", "number", "boolean"]);
3
12
 
4
- const typeDefinition = clazz.typeDefinition;
5
- typeDefinition.clazz = clazz;
13
+ export function resolveTypeLinks() {
14
+ for (const type of Object.values(types)) {
15
+ type.owners = type.owners.map(owner =>
16
+ typeof owner === "string" ? types[owner] : owner
17
+ );
6
18
 
7
- typesByName[typeDefinition.name] = clazz;
19
+ for (const [name, property] of Object.entries(type.properties)) {
20
+ property.name = name;
21
+ if (property.identifier) {
22
+ type.identifier = property;
23
+ }
8
24
 
9
- for (const type of types) {
10
- for (const [name, property] of Object.entries(
11
- type.typeDefinition.properties
12
- )) {
13
25
  if (typeof property.type === "string") {
14
- const t = typesByName[property.type];
15
- if (t) {
16
- property.type = t;
26
+ if (!primitives.has(property.type)) {
27
+ const type = types[property.type];
28
+ if (type) {
29
+ property.type = type;
30
+ } else {
31
+ console.error(
32
+ "Unknown type",
33
+ property.type,
34
+ type.name,
35
+ property.name
36
+ );
37
+ }
17
38
  }
18
39
  }
19
40
  }
20
41
  }
21
- }
22
42
 
23
- export const types = [];
24
- export const typesByName = {};
43
+ for (const type of Object.values(types)) {
44
+ if (!type.identifier) {
45
+ if (type.extends?.identifier) {
46
+ type.identifier = type.extends.identifier;
47
+ }
48
+ }
49
+ }
50
+ }
package/types/base.d.mts CHANGED
@@ -1,37 +1,51 @@
1
- export function extractFrom(object: any, typeDefinition: any): any;
1
+ export function extractFrom(object: any, typeDefinition?: any): any;
2
2
  export class Base {
3
3
  static get typeName(): string;
4
4
  static get typeDefinition(): {
5
5
  name: string;
6
+ owners: any[];
6
7
  properties: {
8
+ owner: {
9
+ type: string;
10
+ collection: boolean;
11
+ writeable: boolean;
12
+ };
7
13
  type: {
8
14
  type: string;
15
+ collection: boolean;
9
16
  writeable: boolean;
10
17
  };
11
18
  name: {
12
19
  type: string;
20
+ collection: boolean;
21
+ identifier: boolean;
22
+ writeable: boolean;
13
23
  };
14
24
  description: {
15
25
  type: string;
26
+ collection: boolean;
27
+ writeable: boolean;
16
28
  };
17
29
  directory: {
18
30
  type: string;
31
+ collection: boolean;
19
32
  writeable: boolean;
20
33
  };
21
- owner: {};
22
34
  };
23
35
  };
24
- static get nameLookupName(): string;
25
36
  static get typeFileName(): string;
26
37
  static get fileNameGlob(): string;
27
- static prepareData(root: any, data: any): Promise<typeof Base>;
28
- static normalizeName(name: any): any;
29
38
  constructor(owner: any, data: any);
30
39
  owner: any;
31
- name: any;
32
40
  description: any;
33
- read(data: any): void;
41
+ name: string;
42
+ ownerFor(property: any, data: any): any;
43
+ read(data: any, type: any): void;
44
+ typeNamed(typeName: any, name: any): any;
45
+ addObject(object: any): any;
34
46
  forOwner(owner: any): any;
47
+ isNamed(name: any): boolean;
48
+ relativeName(name: any): any;
35
49
  get typeName(): any;
36
50
  get root(): any;
37
51
  get location(): any;