pmcf 1.98.2 → 1.99.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.
- package/package.json +2 -2
- package/src/base.mjs +3 -1
- package/src/host-utils.mjs +6 -1
- package/src/location.mjs +2 -2
- package/src/module.mjs +3 -3
- package/src/owner.mjs +0 -22
- package/src/service.mjs +28 -3
- package/src/{dhcp.mjs → services/dhcp.mjs} +15 -12
- package/src/{dns.mjs → services/dns.mjs} +24 -20
- package/src/{ntp.mjs → services/ntp.mjs} +11 -9
- package/types/cluster.d.mts +0 -112
- package/types/location.d.mts +0 -224
- package/types/module.d.mts +3 -3
- package/types/network.d.mts +0 -112
- package/types/owner.d.mts +0 -112
- package/types/root.d.mts +0 -224
- package/types/service.d.mts +135 -0
- package/types/{dhcp.d.mts → services/dhcp.d.mts} +4 -2
- package/types/{dns.d.mts → services/dns.d.mts} +3 -2
- package/types/{ntp.d.mts → services/ntp.d.mts} +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmcf",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.99.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"pkg-dir": "^8.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^22.13.
|
|
46
|
+
"@types/node": "^22.13.14",
|
|
47
47
|
"ava": "^6.2.0",
|
|
48
48
|
"c8": "^10.1.3",
|
|
49
49
|
"documentation": "^14.0.3",
|
package/src/base.mjs
CHANGED
|
@@ -179,9 +179,11 @@ export class Base {
|
|
|
179
179
|
if (value instanceof property.type.clazz) {
|
|
180
180
|
assign(property, value);
|
|
181
181
|
} else {
|
|
182
|
+
const factory = property.type.factoryFor?.(value) || property.type.clazz;
|
|
183
|
+
|
|
182
184
|
assign(
|
|
183
185
|
property,
|
|
184
|
-
new
|
|
186
|
+
new factory(this.ownerFor(property, value), value)
|
|
185
187
|
);
|
|
186
188
|
}
|
|
187
189
|
break;
|
package/src/host-utils.mjs
CHANGED
|
@@ -25,6 +25,11 @@ export async function generateNetworkDefs(host, packageData) {
|
|
|
25
25
|
const networkDir = join(packageData.dir, "etc/systemd/network");
|
|
26
26
|
|
|
27
27
|
for (const ni of host.networkInterfaces.values()) {
|
|
28
|
+
switch (ni.kind) {
|
|
29
|
+
case "loopback":
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
if (ni.name !== "eth0" && ni.hwaddr) {
|
|
29
34
|
await writeLines(networkDir, `${ni.name}.link`, [
|
|
30
35
|
sectionLines("Match", { MACAddress: ni.hwaddr }),
|
|
@@ -46,7 +51,7 @@ export async function generateNetworkDefs(host, packageData) {
|
|
|
46
51
|
|
|
47
52
|
switch (ni.kind) {
|
|
48
53
|
case "ethernet":
|
|
49
|
-
case "
|
|
54
|
+
case "wlan":
|
|
50
55
|
const routeSectionExtra = ni.destination
|
|
51
56
|
? { Destination: ni.destination }
|
|
52
57
|
: { Gateway: ni.gatewayAddress };
|
package/src/location.mjs
CHANGED
|
@@ -68,7 +68,7 @@ export class Location extends Owner {
|
|
|
68
68
|
await writeLines(
|
|
69
69
|
join(dir, "etc/systemd/resolved.conf.d"),
|
|
70
70
|
`${this.name}.conf`,
|
|
71
|
-
sectionLines(...this.dns.systemdConfig)
|
|
71
|
+
sectionLines(...this.findService({type: "dns"}).systemdConfig)
|
|
72
72
|
);
|
|
73
73
|
|
|
74
74
|
await writeLines(
|
|
@@ -84,7 +84,7 @@ export class Location extends Owner {
|
|
|
84
84
|
await writeLines(
|
|
85
85
|
join(dir, "etc/systemd/timesyncd.conf.d"),
|
|
86
86
|
`${this.name}.conf`,
|
|
87
|
-
sectionLines(...this.ntp.systemdConfig)
|
|
87
|
+
sectionLines(...this.findService({type: "ntp"}).systemdConfig)
|
|
88
88
|
);
|
|
89
89
|
|
|
90
90
|
const locationDir = join(dir, "etc", "location");
|
package/src/module.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * from "./base.mjs";
|
|
2
2
|
export * from "./service.mjs";
|
|
3
|
-
export * from "./dns.mjs";
|
|
4
|
-
export * from "./ntp.mjs";
|
|
5
|
-
export * from "./dhcp.mjs";
|
|
3
|
+
export * from "./services/dns.mjs";
|
|
4
|
+
export * from "./services/ntp.mjs";
|
|
5
|
+
export * from "./services/dhcp.mjs";
|
|
6
6
|
export * from "./cluster.mjs";
|
|
7
7
|
export * from "./owner.mjs";
|
|
8
8
|
export * from "./location.mjs";
|
package/src/owner.mjs
CHANGED
|
@@ -2,9 +2,6 @@ import { asIterator, normalizeCIDR } from "./utils.mjs";
|
|
|
2
2
|
import { Base } from "./base.mjs";
|
|
3
3
|
import { Subnet } from "./subnet.mjs";
|
|
4
4
|
import { addType, types } from "./types.mjs";
|
|
5
|
-
import { DNSService } from "./dns.mjs";
|
|
6
|
-
import { NTPService } from "./ntp.mjs";
|
|
7
|
-
import { DHCPService } from "./dhcp.mjs";
|
|
8
5
|
|
|
9
6
|
const OwnerTypeDefinition = {
|
|
10
7
|
name: "owner",
|
|
@@ -16,21 +13,6 @@ const OwnerTypeDefinition = {
|
|
|
16
13
|
hosts: { type: "host", collection: true, writeable: true },
|
|
17
14
|
clusters: { type: "cluster", collection: true, writeable: true },
|
|
18
15
|
subnets: { type: Subnet.typeDefinition, collection: true, writeable: true },
|
|
19
|
-
dns: {
|
|
20
|
-
type: DNSService.typeDefinition,
|
|
21
|
-
collection: false,
|
|
22
|
-
writeable: true
|
|
23
|
-
},
|
|
24
|
-
ntp: {
|
|
25
|
-
type: NTPService.typeDefinition,
|
|
26
|
-
collection: false,
|
|
27
|
-
writeable: true
|
|
28
|
-
},
|
|
29
|
-
dhcp: {
|
|
30
|
-
type: DHCPService.typeDefinition,
|
|
31
|
-
collection: false,
|
|
32
|
-
writeable: true
|
|
33
|
-
},
|
|
34
16
|
|
|
35
17
|
country: { type: "string", collection: false, writeable: true },
|
|
36
18
|
domain: { type: "string", collection: false, writeable: true },
|
|
@@ -69,10 +51,6 @@ export class Owner extends Base {
|
|
|
69
51
|
}
|
|
70
52
|
}
|
|
71
53
|
|
|
72
|
-
this.dns?._traverse(...args);
|
|
73
|
-
this.ntp?._traverse(...args);
|
|
74
|
-
this.dhcp?._traverse(...args);
|
|
75
|
-
|
|
76
54
|
return true;
|
|
77
55
|
}
|
|
78
56
|
|
package/src/service.mjs
CHANGED
|
@@ -8,8 +8,10 @@ import {
|
|
|
8
8
|
dnsFormatParameters,
|
|
9
9
|
dnsMergeParameters
|
|
10
10
|
} from "./dns-utils.mjs";
|
|
11
|
+
import { DHCPService, DNSService, NTPService } from "./module.mjs";
|
|
11
12
|
|
|
12
13
|
const ServiceTypes = {
|
|
14
|
+
ntp: { protocol: "udp", port: 123, tls: false },
|
|
13
15
|
dns: { protocol: "udp", port: 53, tls: false },
|
|
14
16
|
ldap: { protocol: "tcp", port: 389, tls: false },
|
|
15
17
|
ldaps: { protocol: "tcp", port: 636, tls: true },
|
|
@@ -46,18 +48,36 @@ const ServiceTypes = {
|
|
|
46
48
|
parameters: {
|
|
47
49
|
sys: "waMa=0",
|
|
48
50
|
adVF: "0x100",
|
|
49
|
-
dk0: "adVN=MF-TM-999"
|
|
50
|
-
|
|
51
|
+
dk0: "adVN=MF-TM-999"
|
|
52
|
+
// adVF: "0x82"
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
};
|
|
55
57
|
|
|
56
|
-
const ServiceTypeDefinition = {
|
|
58
|
+
export const ServiceTypeDefinition = {
|
|
57
59
|
name: "service",
|
|
58
60
|
owners: ["host", "cluster"],
|
|
59
61
|
priority: 0.4,
|
|
60
62
|
extends: Base.typeDefinition,
|
|
63
|
+
factoryFor(value) {
|
|
64
|
+
const type = value.type || value.name;
|
|
65
|
+
|
|
66
|
+
if (type === "dns") {
|
|
67
|
+
delete value.type;
|
|
68
|
+
return DNSService;
|
|
69
|
+
}
|
|
70
|
+
if (type === "ntp") {
|
|
71
|
+
delete value.type;
|
|
72
|
+
return NTPService;
|
|
73
|
+
}
|
|
74
|
+
if (type === "dhcp") {
|
|
75
|
+
delete value.type;
|
|
76
|
+
return DHCPService;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return Service;
|
|
80
|
+
},
|
|
61
81
|
properties: {
|
|
62
82
|
...networkAddressProperties,
|
|
63
83
|
ipAddresses: { type: "string", collection: true, writeable: true },
|
|
@@ -126,6 +146,11 @@ export class Service extends Base {
|
|
|
126
146
|
return this;
|
|
127
147
|
}
|
|
128
148
|
|
|
149
|
+
get network()
|
|
150
|
+
{
|
|
151
|
+
return this.server.network;
|
|
152
|
+
}
|
|
153
|
+
|
|
129
154
|
get server() {
|
|
130
155
|
return this.owner;
|
|
131
156
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { FileContentProvider } from "npm-pkgbuild";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import {
|
|
4
|
+
Service,
|
|
5
|
+
ServiceTypeDefinition,
|
|
6
|
+
serviceAddresses
|
|
7
|
+
} from "../service.mjs";
|
|
8
|
+
import { addType } from "../types.mjs";
|
|
9
|
+
import { writeLines } from "../utils.mjs";
|
|
7
10
|
|
|
8
11
|
const DHCPServiceTypeDefinition = {
|
|
9
12
|
name: "dhcp",
|
|
10
|
-
owners:
|
|
13
|
+
owners: ServiceTypeDefinition.owners,
|
|
11
14
|
priority: 0.1,
|
|
12
15
|
properties: {}
|
|
13
16
|
};
|
|
14
17
|
|
|
15
|
-
export class DHCPService extends
|
|
18
|
+
export class DHCPService extends Service {
|
|
16
19
|
static {
|
|
17
20
|
addType(this);
|
|
18
21
|
}
|
|
@@ -22,13 +25,12 @@ export class DHCPService extends Base {
|
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
constructor(owner, data) {
|
|
25
|
-
if (!data.name) {
|
|
26
|
-
data.name = DHCPServiceTypeDefinition.name; // TODO
|
|
27
|
-
}
|
|
28
28
|
super(owner, data);
|
|
29
29
|
this.read(data, DHCPServiceTypeDefinition);
|
|
30
|
+
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
get type() {
|
|
33
|
+
return DHCPServiceTypeDefinition.name;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
async *preparePackages(dir) {
|
|
@@ -41,7 +43,8 @@ export class DHCPService extends Base {
|
|
|
41
43
|
name: `kea-${name}`,
|
|
42
44
|
description: `kea definitions for ${this.fullName}`,
|
|
43
45
|
access: "private",
|
|
44
|
-
dependencies: ["kea"]
|
|
46
|
+
dependencies: ["kea"],
|
|
47
|
+
replaces: ["kea-sw"] // TODO remove
|
|
45
48
|
}
|
|
46
49
|
};
|
|
47
50
|
|
|
@@ -131,7 +134,7 @@ export class DHCPService extends Base {
|
|
|
131
134
|
const hwmap = new Map();
|
|
132
135
|
const hostNames = new Set();
|
|
133
136
|
|
|
134
|
-
for await (const { networkInterface } of this.
|
|
137
|
+
for await (const { networkInterface } of this.network.networkAddresses()) {
|
|
135
138
|
if (networkInterface.hwaddr) {
|
|
136
139
|
if (!hostNames.has(networkInterface.hostName)) {
|
|
137
140
|
hwmap.set(networkInterface.hwaddr, networkInterface);
|
|
@@ -6,16 +6,19 @@ import {
|
|
|
6
6
|
isIPv6Address,
|
|
7
7
|
normalizeIPAddress,
|
|
8
8
|
isLinkLocal
|
|
9
|
-
} from "
|
|
10
|
-
import { DNSRecord, dnsFullName } from "
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
} from "../utils.mjs";
|
|
10
|
+
import { DNSRecord, dnsFullName } from "../dns-utils.mjs";
|
|
11
|
+
import { addType } from "../types.mjs";
|
|
12
|
+
import {
|
|
13
|
+
Service,
|
|
14
|
+
ServiceTypeDefinition,
|
|
15
|
+
serviceAddresses
|
|
16
|
+
} from "../service.mjs";
|
|
17
|
+
import { subnets } from "../subnet.mjs";
|
|
15
18
|
|
|
16
19
|
const DNSServiceTypeDefinition = {
|
|
17
20
|
name: "dns",
|
|
18
|
-
owners:
|
|
21
|
+
owners: ServiceTypeDefinition.owners,
|
|
19
22
|
priority: 0.1,
|
|
20
23
|
properties: {
|
|
21
24
|
source: { type: "network", collection: true, writeable: true },
|
|
@@ -58,7 +61,7 @@ function addressesStatement(prefix, objects, generateEmpty = false) {
|
|
|
58
61
|
return [];
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
export class DNSService extends
|
|
64
|
+
export class DNSService extends Service {
|
|
62
65
|
allowedUpdates = [];
|
|
63
66
|
recordTTL = "1W";
|
|
64
67
|
hasSVRRecords = true;
|
|
@@ -85,13 +88,12 @@ export class DNSService extends Base {
|
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
constructor(owner, data) {
|
|
88
|
-
if (!data.name) {
|
|
89
|
-
data.name = DNSServiceTypeDefinition.name; // TODO
|
|
90
|
-
}
|
|
91
91
|
super(owner, data);
|
|
92
92
|
this.read(data, DNSServiceTypeDefinition);
|
|
93
|
+
}
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
get type() {
|
|
96
|
+
return DNSServiceTypeDefinition.name;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
get soaUpdates() {
|
|
@@ -252,7 +254,9 @@ async function generateZoneDefs(dns, packageData) {
|
|
|
252
254
|
|
|
253
255
|
const configs = [];
|
|
254
256
|
|
|
255
|
-
|
|
257
|
+
const location = dns.location;
|
|
258
|
+
|
|
259
|
+
for (const host of location.hosts()) {
|
|
256
260
|
for (const domain of host.foreignDomainNames) {
|
|
257
261
|
const zone = {
|
|
258
262
|
id: domain,
|
|
@@ -324,7 +328,7 @@ async function generateZoneDefs(dns, packageData) {
|
|
|
324
328
|
subnet,
|
|
325
329
|
networkInterface,
|
|
326
330
|
domainNames
|
|
327
|
-
} of
|
|
331
|
+
} of location.networkAddresses()) {
|
|
328
332
|
const host = networkInterface.host;
|
|
329
333
|
if (
|
|
330
334
|
!addresses.has(address) &&
|
|
@@ -379,12 +383,12 @@ async function generateZoneDefs(dns, packageData) {
|
|
|
379
383
|
|
|
380
384
|
for (const service of host.findServices()) {
|
|
381
385
|
//for (const domainName of domainNames) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
386
|
+
for (const record of service.dnsRecordsForDomainName(
|
|
387
|
+
host.domainName,
|
|
388
|
+
dns.hasSVRRecords
|
|
389
|
+
)) {
|
|
390
|
+
zone.records.add(record);
|
|
391
|
+
}
|
|
388
392
|
//}
|
|
389
393
|
}
|
|
390
394
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { addType } from "../types.mjs";
|
|
2
|
+
import {
|
|
3
|
+
Service,
|
|
4
|
+
ServiceTypeDefinition,
|
|
5
|
+
serviceAddresses
|
|
6
|
+
} from "../service.mjs";
|
|
4
7
|
|
|
5
8
|
const NTPServiceTypeDefinition = {
|
|
6
9
|
name: "ntp",
|
|
7
|
-
owners:
|
|
10
|
+
owners: ServiceTypeDefinition.owners,
|
|
8
11
|
priority: 0.1,
|
|
9
12
|
properties: {
|
|
10
13
|
source: { type: "network", collection: true, writeable: true }
|
|
@@ -13,7 +16,7 @@ const NTPServiceTypeDefinition = {
|
|
|
13
16
|
|
|
14
17
|
const NTP_SERVICE_FILTER = { type: NTPServiceTypeDefinition.name };
|
|
15
18
|
|
|
16
|
-
export class NTPService extends
|
|
19
|
+
export class NTPService extends Service {
|
|
17
20
|
#source = [];
|
|
18
21
|
|
|
19
22
|
static {
|
|
@@ -25,13 +28,12 @@ export class NTPService extends Base {
|
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
constructor(owner, data) {
|
|
28
|
-
if (!data.name) {
|
|
29
|
-
data.name = NTPServiceTypeDefinition.name; // TODO
|
|
30
|
-
}
|
|
31
31
|
super(owner, data);
|
|
32
32
|
this.read(data, NTPServiceTypeDefinition);
|
|
33
|
+
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
get type() {
|
|
36
|
+
return NTPServiceTypeDefinition.name;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
set source(value) {
|
package/types/cluster.d.mts
CHANGED
|
@@ -91,118 +91,6 @@ export class Cluster extends Host {
|
|
|
91
91
|
collection: boolean;
|
|
92
92
|
writeable: boolean;
|
|
93
93
|
};
|
|
94
|
-
dns: {
|
|
95
|
-
type: {
|
|
96
|
-
name: string;
|
|
97
|
-
owners: string[];
|
|
98
|
-
priority: number;
|
|
99
|
-
properties: {
|
|
100
|
-
source: {
|
|
101
|
-
type: string;
|
|
102
|
-
collection: boolean;
|
|
103
|
-
writeable: boolean;
|
|
104
|
-
};
|
|
105
|
-
trusted: {
|
|
106
|
-
type: string;
|
|
107
|
-
collection: boolean;
|
|
108
|
-
writeable: boolean;
|
|
109
|
-
};
|
|
110
|
-
protected: {
|
|
111
|
-
type: string;
|
|
112
|
-
collection: boolean;
|
|
113
|
-
writeable: boolean;
|
|
114
|
-
};
|
|
115
|
-
open: {
|
|
116
|
-
type: string;
|
|
117
|
-
collection: boolean;
|
|
118
|
-
writeable: boolean;
|
|
119
|
-
};
|
|
120
|
-
hasSVRRecords: {
|
|
121
|
-
type: string;
|
|
122
|
-
collection: boolean;
|
|
123
|
-
writeable: boolean;
|
|
124
|
-
};
|
|
125
|
-
hasCatalog: {
|
|
126
|
-
type: string;
|
|
127
|
-
collection: boolean;
|
|
128
|
-
writeable: boolean;
|
|
129
|
-
};
|
|
130
|
-
hasLinkLocalAdresses: {
|
|
131
|
-
type: string;
|
|
132
|
-
collection: boolean;
|
|
133
|
-
writeable: boolean;
|
|
134
|
-
};
|
|
135
|
-
notify: {
|
|
136
|
-
type: string;
|
|
137
|
-
collection: boolean;
|
|
138
|
-
writeable: boolean;
|
|
139
|
-
};
|
|
140
|
-
recordTTL: {
|
|
141
|
-
type: string;
|
|
142
|
-
collection: boolean;
|
|
143
|
-
writeable: boolean;
|
|
144
|
-
};
|
|
145
|
-
serial: {
|
|
146
|
-
type: string;
|
|
147
|
-
collection: boolean;
|
|
148
|
-
writeable: boolean;
|
|
149
|
-
};
|
|
150
|
-
refresh: {
|
|
151
|
-
type: string;
|
|
152
|
-
collection: boolean;
|
|
153
|
-
writeable: boolean;
|
|
154
|
-
};
|
|
155
|
-
retry: {
|
|
156
|
-
type: string;
|
|
157
|
-
collection: boolean;
|
|
158
|
-
writeable: boolean;
|
|
159
|
-
};
|
|
160
|
-
expire: {
|
|
161
|
-
type: string;
|
|
162
|
-
collection: boolean;
|
|
163
|
-
writeable: boolean;
|
|
164
|
-
};
|
|
165
|
-
minimum: {
|
|
166
|
-
type: string;
|
|
167
|
-
collection: boolean;
|
|
168
|
-
writeable: boolean;
|
|
169
|
-
};
|
|
170
|
-
allowedUpdates: {
|
|
171
|
-
type: string;
|
|
172
|
-
collection: boolean;
|
|
173
|
-
writeable: boolean;
|
|
174
|
-
};
|
|
175
|
-
};
|
|
176
|
-
};
|
|
177
|
-
collection: boolean;
|
|
178
|
-
writeable: boolean;
|
|
179
|
-
};
|
|
180
|
-
ntp: {
|
|
181
|
-
type: {
|
|
182
|
-
name: string;
|
|
183
|
-
owners: string[];
|
|
184
|
-
priority: number;
|
|
185
|
-
properties: {
|
|
186
|
-
source: {
|
|
187
|
-
type: string;
|
|
188
|
-
collection: boolean;
|
|
189
|
-
writeable: boolean;
|
|
190
|
-
};
|
|
191
|
-
};
|
|
192
|
-
};
|
|
193
|
-
collection: boolean;
|
|
194
|
-
writeable: boolean;
|
|
195
|
-
};
|
|
196
|
-
dhcp: {
|
|
197
|
-
type: {
|
|
198
|
-
name: string;
|
|
199
|
-
owners: string[];
|
|
200
|
-
priority: number;
|
|
201
|
-
properties: {};
|
|
202
|
-
};
|
|
203
|
-
collection: boolean;
|
|
204
|
-
writeable: boolean;
|
|
205
|
-
};
|
|
206
94
|
country: {
|
|
207
95
|
type: string;
|
|
208
96
|
collection: boolean;
|