@syncbridge/common 2.3.0 → 2.4.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/README.md +13 -0
- package/app-factory.js +16 -7
- package/constants.d.ts +1 -1
- package/constants.js +1 -1
- package/decorators/use-component.decorator.d.ts +8 -1
- package/decorators/use-component.decorator.js +10 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/models/enums/plugin-permission.enum.d.ts +19 -0
- package/models/enums/plugin-permission.enum.js +33 -0
- package/models/index.d.ts +1 -0
- package/models/index.js +1 -0
- package/models/metadata/element-component-metadata.d.ts +1 -0
- package/models/metadata/element-component-metadata.js +9 -1
- package/models/metadata/plugin-metadata.d.ts +3 -0
- package/models/metadata/plugin-metadata.js +18 -1
- package/models/profile/profile-component.d.ts +1 -0
- package/models/profile/profile-component.js +7 -0
- package/models/profile/profile.d.ts +1 -0
- package/models/profile/profile.js +7 -0
- package/package.json +1 -1
- package/registry/docs-sidebar.d.ts +7 -0
- package/registry/docs-sidebar.js +18 -3
- package/registry/extension-package.d.ts +5 -1
- package/registry/extension-package.js +12 -2
- package/utils/materialize-metadata.js +6 -3
- package/utils/registry-names.d.ts +13 -0
- package/utils/registry-names.js +19 -0
- package/utils/slot-interfaces.d.ts +10 -0
- package/utils/slot-interfaces.js +11 -0
package/README.md
CHANGED
|
@@ -110,6 +110,19 @@ A component may use components too, with `Component.UseComponent`. A URI
|
|
|
110
110
|
can name another module's component, for example
|
|
111
111
|
`@syncbridge/net:TcpClient`. Declare that module as a peer as well.
|
|
112
112
|
|
|
113
|
+
A slot may take one of several kinds that share no interface - a device
|
|
114
|
+
reached as a client or as a server - with `oneOf`, each a class or its
|
|
115
|
+
URI. Required, one of them must be chosen, and only one can be:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
@App.UseComponent({ oneOf: [HL7Client, HL7Server], required: true })
|
|
119
|
+
declare readonly hl7: HL7Client | HL7Server;
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The app tells which it was given (`this.hl7 instanceof HL7Server`). Where
|
|
123
|
+
the kinds do share what the app uses, an abstract base they both extend
|
|
124
|
+
serves better: one slot typed by it, no branching.
|
|
125
|
+
|
|
113
126
|
## Variables
|
|
114
127
|
|
|
115
128
|
`@DefineVariable` describes a setting. Its type is read from the
|
package/app-factory.js
CHANGED
|
@@ -266,7 +266,13 @@ export var AppFactory;
|
|
|
266
266
|
}
|
|
267
267
|
});
|
|
268
268
|
}
|
|
269
|
-
|
|
269
|
+
/**
|
|
270
|
+
* A running element's values, patched with a new profile's: its hot-plug
|
|
271
|
+
* settings alone - those of a hot-plug group all of them. A list - of
|
|
272
|
+
* values or of groups - is replaced whole, never merged into an object.
|
|
273
|
+
* The paths changed are kept for its 'values-updated' event.
|
|
274
|
+
*/
|
|
275
|
+
function _patchValues(ctx, instance, target, valPath, newValues, newVariables, oldValues, oldVariables, hot = false) {
|
|
270
276
|
if (oldValues)
|
|
271
277
|
Object.assign(target, oldValues);
|
|
272
278
|
const updatedValues = ctx.valueUpdates?.get(instance) || [];
|
|
@@ -274,17 +280,20 @@ export var AppFactory;
|
|
|
274
280
|
if (newVariables) {
|
|
275
281
|
for (const [varKey, varMeta] of Object.entries(newVariables)) {
|
|
276
282
|
const keyPath = valPath ? valPath + '.' + varKey : varKey;
|
|
277
|
-
/** Update only hotplug variables */
|
|
278
|
-
if (varMeta?.hotPlug !== true)
|
|
283
|
+
/** Update only hotplug variables, and all of a hotplug group */
|
|
284
|
+
if (!hot && varMeta?.hotPlug !== true)
|
|
279
285
|
continue;
|
|
280
|
-
if (varMeta.type === 'nested') {
|
|
286
|
+
if (varMeta.type === 'nested' && !varMeta.isArray) {
|
|
281
287
|
target[varKey] = {};
|
|
282
|
-
_patchValues(ctx, instance, target[varKey], keyPath, newValues[varKey], newVariables[varKey]?.variables || {}, oldValues?.[varKey], oldVariables?.[varKey]?.variables);
|
|
288
|
+
_patchValues(ctx, instance, target[varKey], keyPath, newValues?.[varKey], newVariables[varKey]?.variables || {}, oldValues?.[varKey], oldVariables?.[varKey]?.variables, true);
|
|
283
289
|
continue;
|
|
284
290
|
}
|
|
285
|
-
|
|
291
|
+
const value = newValues?.[varKey];
|
|
292
|
+
if (!equal(target[varKey], value)) {
|
|
286
293
|
updatedValues.push(keyPath);
|
|
287
|
-
|
|
294
|
+
/* Its own copy: the profile's left as it is */
|
|
295
|
+
target[varKey] =
|
|
296
|
+
value && typeof value === 'object' ? clone(value) : value;
|
|
288
297
|
}
|
|
289
298
|
}
|
|
290
299
|
}
|
package/constants.d.ts
CHANGED
package/constants.js
CHANGED
|
@@ -5,7 +5,14 @@ import { ComponentBase } from '../classes/component-base.js';
|
|
|
5
5
|
import { ElementComponentMetadata } from '../models/index.js';
|
|
6
6
|
export declare function UseComponent<T extends AppBase | ComponentBase>(options?: UseComponent.Options): (target: T, propertyKey: string | symbol) => void;
|
|
7
7
|
export declare namespace UseComponent {
|
|
8
|
-
interface Options extends Partial<StrictOmit<ElementComponentMetadata, 'classUri' | 'interfaceUri'>> {
|
|
8
|
+
interface Options extends Partial<StrictOmit<ElementComponentMetadata, 'classUri' | 'interfaceUri' | 'oneOf'>> {
|
|
9
9
|
uri?: string;
|
|
10
|
+
/**
|
|
11
|
+
* A slot taking a class of one of these, not of one interface alone:
|
|
12
|
+
* `oneOf: [HL7Client, HL7Server]` - its property typed as either
|
|
13
|
+
*/
|
|
14
|
+
oneOf?: (string | {
|
|
15
|
+
uri: string;
|
|
16
|
+
})[];
|
|
10
17
|
}
|
|
11
18
|
}
|
|
@@ -10,7 +10,14 @@ export function UseComponent(options = {}) {
|
|
|
10
10
|
/** c8 ignore next */
|
|
11
11
|
if (typeof propertyKey !== 'string')
|
|
12
12
|
throw new TypeError('Property key must be a string');
|
|
13
|
-
|
|
13
|
+
/* One of several: each a class, or its uri */
|
|
14
|
+
const oneOf = options.oneOf?.map(c => {
|
|
15
|
+
const u = typeof c === 'string' ? c : c?.uri;
|
|
16
|
+
if (!u)
|
|
17
|
+
throw new TypeError(`${String(propertyKey)}: oneOf takes components - classes with @Component, or their uris`);
|
|
18
|
+
return u;
|
|
19
|
+
});
|
|
20
|
+
let uri = options.uri ?? oneOf?.[0];
|
|
14
21
|
if (!uri) {
|
|
15
22
|
const designType = Reflect.getMetadata('design:type', target, propertyKey);
|
|
16
23
|
if (typeof designType === 'function' && designType.uri) {
|
|
@@ -21,7 +28,8 @@ export function UseComponent(options = {}) {
|
|
|
21
28
|
metadata.components = metadata.components || {};
|
|
22
29
|
metadata.components[propertyKey] = {
|
|
23
30
|
interfaceUri: uri,
|
|
24
|
-
...omit(options, ['classUri', 'interfaceUri']),
|
|
31
|
+
...omit(options, ['classUri', 'interfaceUri', 'oneOf']),
|
|
32
|
+
...(oneOf?.length ? { oneOf } : {}),
|
|
25
33
|
propertyKey,
|
|
26
34
|
};
|
|
27
35
|
};
|
package/index.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export { SECRET_PREFIX, secretDecryptor, secretEncryptor, } from './utils/encryp
|
|
|
36
36
|
export * from './utils/materialize-metadata.js';
|
|
37
37
|
export * from './utils/normalize-metadata.js';
|
|
38
38
|
export * from './utils/registry-names.js';
|
|
39
|
+
export * from './utils/slot-interfaces.js';
|
|
39
40
|
export * from './utils/type-guards.js';
|
|
40
41
|
export * from './utils/uri-to-id.js';
|
|
41
42
|
export * from './utils/worker-permissions.js';
|
package/index.js
CHANGED
|
@@ -36,6 +36,7 @@ export { SECRET_PREFIX, secretDecryptor, secretEncryptor, } from './utils/encryp
|
|
|
36
36
|
export * from './utils/materialize-metadata.js';
|
|
37
37
|
export * from './utils/normalize-metadata.js';
|
|
38
38
|
export * from './utils/registry-names.js';
|
|
39
|
+
export * from './utils/slot-interfaces.js';
|
|
39
40
|
export * from './utils/type-guards.js';
|
|
40
41
|
export * from './utils/uri-to-id.js';
|
|
41
42
|
export * from './utils/worker-permissions.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a plugin reaches beyond its own data directory, its settings, its
|
|
3
|
+
* status, questions and actions, as it declares it needs: shown to the
|
|
4
|
+
* server's admins, given once they approve it. Not declared, none.
|
|
5
|
+
*/
|
|
6
|
+
export declare enum PluginPermission {
|
|
7
|
+
/** `host.workers.addEnvironment`: what every worker starts with */
|
|
8
|
+
WorkerEnvironment = "worker-environment",
|
|
9
|
+
/** `host.logs.registerTarget`: every log record, the workers' messages among them */
|
|
10
|
+
LogRecords = "log-records",
|
|
11
|
+
/** `host.workers.list/get` and the workers' events: their ids, names, statuses */
|
|
12
|
+
Workers = "workers",
|
|
13
|
+
/** The network: the hosts it reaches named in `hosts` */
|
|
14
|
+
Network = "network",
|
|
15
|
+
/** Starting other programs, outside Node's permission model */
|
|
16
|
+
ChildProcesses = "child-processes",
|
|
17
|
+
/** Native addons (.node), outside Node's permission model */
|
|
18
|
+
NativeCode = "native-code"
|
|
19
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { EnumType } from '@opra/common';
|
|
2
|
+
/**
|
|
3
|
+
* What a plugin reaches beyond its own data directory, its settings, its
|
|
4
|
+
* status, questions and actions, as it declares it needs: shown to the
|
|
5
|
+
* server's admins, given once they approve it. Not declared, none.
|
|
6
|
+
*/
|
|
7
|
+
export var PluginPermission;
|
|
8
|
+
(function (PluginPermission) {
|
|
9
|
+
/** `host.workers.addEnvironment`: what every worker starts with */
|
|
10
|
+
PluginPermission["WorkerEnvironment"] = "worker-environment";
|
|
11
|
+
/** `host.logs.registerTarget`: every log record, the workers' messages among them */
|
|
12
|
+
PluginPermission["LogRecords"] = "log-records";
|
|
13
|
+
/** `host.workers.list/get` and the workers' events: their ids, names, statuses */
|
|
14
|
+
PluginPermission["Workers"] = "workers";
|
|
15
|
+
/** The network: the hosts it reaches named in `hosts` */
|
|
16
|
+
PluginPermission["Network"] = "network";
|
|
17
|
+
/** Starting other programs, outside Node's permission model */
|
|
18
|
+
PluginPermission["ChildProcesses"] = "child-processes";
|
|
19
|
+
/** Native addons (.node), outside Node's permission model */
|
|
20
|
+
PluginPermission["NativeCode"] = "native-code";
|
|
21
|
+
})(PluginPermission || (PluginPermission = {}));
|
|
22
|
+
EnumType(PluginPermission, {
|
|
23
|
+
name: 'PluginPermission',
|
|
24
|
+
description: 'What a plugin reaches beyond its own data directory, as it declares it needs and an admin approves it',
|
|
25
|
+
meanings: {
|
|
26
|
+
WorkerEnvironment: 'What every worker starts with: its environment',
|
|
27
|
+
LogRecords: "Every log record, the workers' messages among them - their data too",
|
|
28
|
+
Workers: 'The workers: their ids, names and statuses, and their events',
|
|
29
|
+
Network: 'The network, the hosts it names',
|
|
30
|
+
ChildProcesses: "Starting other programs, outside Node's permission model",
|
|
31
|
+
NativeCode: "Native addons (.node), outside Node's permission model",
|
|
32
|
+
},
|
|
33
|
+
});
|
package/models/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './enums/enum-source.enum.js';
|
|
2
2
|
export * from './enums/log-channel-kind.js';
|
|
3
3
|
export * from './enums/log-level.js';
|
|
4
|
+
export * from './enums/plugin-permission.enum.js';
|
|
4
5
|
export * from './enums/service-status.js';
|
|
5
6
|
export * from './enums/variable-format.enum.js';
|
|
6
7
|
export * from './enums/variable-type.enum.js';
|
package/models/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './enums/enum-source.enum.js';
|
|
2
2
|
export * from './enums/log-channel-kind.js';
|
|
3
3
|
export * from './enums/log-level.js';
|
|
4
|
+
export * from './enums/plugin-permission.enum.js';
|
|
4
5
|
export * from './enums/service-status.js';
|
|
5
6
|
export * from './enums/variable-format.enum.js';
|
|
6
7
|
export * from './enums/variable-type.enum.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { ApiField, ComplexType } from '@opra/common';
|
|
2
|
+
import { ApiField, ArrayType, ComplexType } from '@opra/common';
|
|
3
3
|
import { LogChannelMetadataRecord } from './log-channel-metadata.js';
|
|
4
4
|
import { VariableMetadataRecord } from './variable-metadata.js';
|
|
5
5
|
/**
|
|
@@ -29,6 +29,14 @@ __decorate([
|
|
|
29
29
|
}),
|
|
30
30
|
__metadata("design:type", String)
|
|
31
31
|
], ElementComponentMetadata.prototype, "interfaceUri", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
ApiField({
|
|
34
|
+
label: 'One of',
|
|
35
|
+
description: 'The interfaces the assigned class may implement, one of them: a slot taking either of two kinds (an HL7 client or server). interfaceUri is the first of them, for what reads that alone',
|
|
36
|
+
type: ArrayType('string'),
|
|
37
|
+
}),
|
|
38
|
+
__metadata("design:type", Array)
|
|
39
|
+
], ElementComponentMetadata.prototype, "oneOf", void 0);
|
|
32
40
|
__decorate([
|
|
33
41
|
ApiField({
|
|
34
42
|
label: 'Interface URI',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PluginPermission } from '../enums/plugin-permission.enum.js';
|
|
1
2
|
import { VariableMetadataRecord } from './variable-metadata.js';
|
|
2
3
|
/**
|
|
3
4
|
* A plugin as its class defines it (`@Plugin` of @syncbridge/plugin-api):
|
|
@@ -12,4 +13,6 @@ export declare class PluginMetadata {
|
|
|
12
13
|
description?: string;
|
|
13
14
|
readme?: string;
|
|
14
15
|
settings?: VariableMetadataRecord;
|
|
16
|
+
permissions?: PluginPermission[];
|
|
17
|
+
hosts?: string[];
|
|
15
18
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { ApiField, ComplexType, StringType } from '@opra/common';
|
|
2
|
+
import { ApiField, ArrayType, ComplexType, StringType } from '@opra/common';
|
|
3
|
+
import { PluginPermission } from '../enums/plugin-permission.enum.js';
|
|
3
4
|
import { VariableMetadataRecord } from './variable-metadata.js';
|
|
4
5
|
/**
|
|
5
6
|
* A plugin as its class defines it (`@Plugin` of @syncbridge/plugin-api):
|
|
@@ -58,6 +59,22 @@ __decorate([
|
|
|
58
59
|
}),
|
|
59
60
|
__metadata("design:type", VariableMetadataRecord)
|
|
60
61
|
], PluginMetadata.prototype, "settings", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
ApiField({
|
|
64
|
+
label: 'Permissions',
|
|
65
|
+
description: 'What it reaches beyond its own data directory: shown to the admins, given once they approve it',
|
|
66
|
+
type: ArrayType(PluginPermission),
|
|
67
|
+
}),
|
|
68
|
+
__metadata("design:type", Array)
|
|
69
|
+
], PluginMetadata.prototype, "permissions", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
ApiField({
|
|
72
|
+
label: 'Hosts',
|
|
73
|
+
description: 'The hosts it reaches, with the network permission: shown and approved, not held yet',
|
|
74
|
+
type: ArrayType('string'),
|
|
75
|
+
}),
|
|
76
|
+
__metadata("design:type", Array)
|
|
77
|
+
], PluginMetadata.prototype, "hosts", void 0);
|
|
61
78
|
PluginMetadata = __decorate([
|
|
62
79
|
ComplexType(),
|
|
63
80
|
__metadata("design:paramtypes", [Object])
|
|
@@ -47,6 +47,13 @@ __decorate([
|
|
|
47
47
|
}),
|
|
48
48
|
__metadata("design:type", String)
|
|
49
49
|
], ProfileComponent.prototype, "uri", void 0);
|
|
50
|
+
__decorate([
|
|
51
|
+
ApiField({
|
|
52
|
+
label: 'Written with',
|
|
53
|
+
description: "The version of its component its values were last written with: the engine's to set, on every save - what a migration starts from",
|
|
54
|
+
}),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], ProfileComponent.prototype, "writtenWith", void 0);
|
|
50
57
|
__decorate([
|
|
51
58
|
ApiField({
|
|
52
59
|
label: 'Values',
|
|
@@ -72,6 +72,13 @@ __decorate([
|
|
|
72
72
|
}),
|
|
73
73
|
__metadata("design:type", String)
|
|
74
74
|
], Profile.prototype, "appVersion", void 0);
|
|
75
|
+
__decorate([
|
|
76
|
+
ApiField({
|
|
77
|
+
label: 'Written with',
|
|
78
|
+
description: "The version of its app its values were last written with: the engine's to set, on every save - what a migration starts from",
|
|
79
|
+
}),
|
|
80
|
+
__metadata("design:type", String)
|
|
81
|
+
], Profile.prototype, "writtenWith", void 0);
|
|
75
82
|
__decorate([
|
|
76
83
|
ApiField({
|
|
77
84
|
label: 'Group',
|
package/package.json
CHANGED
|
@@ -19,6 +19,13 @@ import type { LocalizedText } from './localized-text.js';
|
|
|
19
19
|
* the sidebar is - its languages are found by its name (network.md for
|
|
20
20
|
* network.tr.md). A title is text, or text by language. A document not
|
|
21
21
|
* listed is read by a link to it alone.
|
|
22
|
+
*
|
|
23
|
+
* A module's sidebar may list documents beyond its directory, shared by
|
|
24
|
+
* the modules listing them: "../shared/setup.md". Listed by one, a
|
|
25
|
+
* document of no module's directory is that module's - not the package's
|
|
26
|
+
* own, unless the package's sidebar lists it too. A README.md it lists is
|
|
27
|
+
* its page, when it has none of its own; it lists one at most. The
|
|
28
|
+
* package's sidebar lists none of a module's directory.
|
|
22
29
|
*/
|
|
23
30
|
export declare const SIDEBAR_FILE = "sidebar.json";
|
|
24
31
|
/** Text, or text by language: { "tr": "Ağ", "en": "Network" } */
|
package/registry/docs-sidebar.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SbError } from '../classes/sb-error.js';
|
|
2
|
-
import { CLASS_DIR_PATTERN, DOCS_DIR, isDocsLanguage } from './package-docs.js';
|
|
2
|
+
import { CLASS_DIR_PATTERN, DOCS_DIR, isDocsLanguage, README_FILE, } from './package-docs.js';
|
|
3
3
|
/**
|
|
4
4
|
* How a package's documents are listed, where its author tells it: a
|
|
5
5
|
* sidebar.json in docs/ (the package's own) or in docs/<ClassName>/ (an
|
|
@@ -20,6 +20,13 @@ import { CLASS_DIR_PATTERN, DOCS_DIR, isDocsLanguage } from './package-docs.js';
|
|
|
20
20
|
* the sidebar is - its languages are found by its name (network.md for
|
|
21
21
|
* network.tr.md). A title is text, or text by language. A document not
|
|
22
22
|
* listed is read by a link to it alone.
|
|
23
|
+
*
|
|
24
|
+
* A module's sidebar may list documents beyond its directory, shared by
|
|
25
|
+
* the modules listing them: "../shared/setup.md". Listed by one, a
|
|
26
|
+
* document of no module's directory is that module's - not the package's
|
|
27
|
+
* own, unless the package's sidebar lists it too. A README.md it lists is
|
|
28
|
+
* its page, when it has none of its own; it lists one at most. The
|
|
29
|
+
* package's sidebar lists none of a module's directory.
|
|
23
30
|
*/
|
|
24
31
|
export const SIDEBAR_FILE = 'sidebar.json';
|
|
25
32
|
/** Categories within categories, at most */
|
|
@@ -75,8 +82,12 @@ export function parseSidebar(file, source, docs) {
|
|
|
75
82
|
parts.push(part);
|
|
76
83
|
}
|
|
77
84
|
const path = parts.join('/');
|
|
78
|
-
if (!path.startsWith(
|
|
79
|
-
errors.push(`${at}: "${name}"
|
|
85
|
+
if (!path.startsWith(DOCS_DIR + '/')) {
|
|
86
|
+
errors.push(`${at}: "${name}" goes out of ${DOCS_DIR}/ - a sidebar lists the package's documents`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!owner && isModuleDoc(path)) {
|
|
90
|
+
errors.push(`${at}: "${name}" is a module's - the package's sidebar lists its own documents`);
|
|
80
91
|
return;
|
|
81
92
|
}
|
|
82
93
|
if (!docs.has(path)) {
|
|
@@ -145,6 +156,10 @@ export function parseSidebar(file, source, docs) {
|
|
|
145
156
|
errors.push(`${file}: "${unknown.join('", "')}" is not read`);
|
|
146
157
|
result = items(source.items, `${file}: items`, 0);
|
|
147
158
|
}
|
|
159
|
+
/* A module's page: one README, its own or one it takes */
|
|
160
|
+
const readmes = [...listed].filter(p => p.endsWith('/' + README_FILE));
|
|
161
|
+
if (owner && readmes.length > 1)
|
|
162
|
+
errors.push(`${file}: ${readmes.join(', ')} - a module has one ${README_FILE}, its page: list one`);
|
|
148
163
|
if (errors.length)
|
|
149
164
|
throw new SbError(errors.join('\n'));
|
|
150
165
|
return { items: result };
|
|
@@ -26,7 +26,11 @@ export declare class ExtensionPackage {
|
|
|
26
26
|
warnings: string[];
|
|
27
27
|
/**
|
|
28
28
|
* Takes the package's identity and dependencies from its `package.json`
|
|
29
|
-
* and its manifest (see readExtensionManifest)
|
|
29
|
+
* and its manifest (see readExtensionManifest).
|
|
30
|
+
*
|
|
31
|
+
* SyncBridge's modules (`@syncbridge/builtins`, ...) were once peers the
|
|
32
|
+
* host provided; a package that still declares one so depends on it as a
|
|
33
|
+
* registry package, as one built now does in its manifest.
|
|
30
34
|
*/
|
|
31
35
|
assignManifest(pkgJson: ExtensionPackageJson, manifest?: SyncBridgeManifest): void;
|
|
32
36
|
}
|
|
@@ -5,6 +5,7 @@ import { ComponentBase } from '../classes/component-base.js';
|
|
|
5
5
|
import { SbError } from '../classes/sb-error.js';
|
|
6
6
|
import { CLASS_TYPE } from '../constants.js';
|
|
7
7
|
import { AppMetadata, ComponentMetadata, ElementMetadata, PluginMetadata, } from '../models/index.js';
|
|
8
|
+
import { isSyncBridgeModule } from '../utils/registry-names.js';
|
|
8
9
|
import { isAppBase, isComponentBase } from '../utils/type-guards.js';
|
|
9
10
|
/**
|
|
10
11
|
*
|
|
@@ -22,14 +23,23 @@ export class ExtensionPackage {
|
|
|
22
23
|
warnings = [];
|
|
23
24
|
/**
|
|
24
25
|
* Takes the package's identity and dependencies from its `package.json`
|
|
25
|
-
* and its manifest (see readExtensionManifest)
|
|
26
|
+
* and its manifest (see readExtensionManifest).
|
|
27
|
+
*
|
|
28
|
+
* SyncBridge's modules (`@syncbridge/builtins`, ...) were once peers the
|
|
29
|
+
* host provided; a package that still declares one so depends on it as a
|
|
30
|
+
* registry package, as one built now does in its manifest.
|
|
26
31
|
*/
|
|
27
32
|
assignManifest(pkgJson, manifest) {
|
|
28
33
|
this.name = manifest?.name || pkgJson.name;
|
|
29
34
|
this.version = pkgJson.version;
|
|
30
35
|
this.description = pkgJson.description;
|
|
31
36
|
this.registryDependencies = { ...manifest?.dependencies };
|
|
32
|
-
this.peerDependencies = {
|
|
37
|
+
this.peerDependencies = {};
|
|
38
|
+
for (const [name, range] of Object.entries(pkgJson.peerDependencies || {}))
|
|
39
|
+
if (!isSyncBridgeModule(name))
|
|
40
|
+
this.peerDependencies[name] = range;
|
|
41
|
+
else
|
|
42
|
+
this.registryDependencies[name] ??= range;
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
45
|
/**
|
|
@@ -3,6 +3,7 @@ import { SbValidationError } from '../classes/sb-error.js';
|
|
|
3
3
|
import { StackExecutor } from '../classes/stack-executor.js';
|
|
4
4
|
import { AppMetadata, ElementComponentMetadata, ElementComponentMetadataRecord, Profile, ProfileComponent, VariableMetadata, VariableMetadataRecord, VariableType, } from '../models/index.js';
|
|
5
5
|
import { ExtensionRegistry } from '../registry/extension-registry.js';
|
|
6
|
+
import { slotInterfaces, slotTakes } from './slot-interfaces.js';
|
|
6
7
|
/**
|
|
7
8
|
* Materializes metadata from profile.
|
|
8
9
|
*/
|
|
@@ -78,6 +79,9 @@ function _materializeComponents(stackExecutor, srcMetadata, profile, options) {
|
|
|
78
79
|
}
|
|
79
80
|
materializedComponent = {
|
|
80
81
|
interfaceUri: metadataComponent.interfaceUri,
|
|
82
|
+
...(metadataComponent.oneOf
|
|
83
|
+
? { oneOf: metadataComponent.oneOf }
|
|
84
|
+
: {}),
|
|
81
85
|
description: metadataComponent.description,
|
|
82
86
|
required: metadataComponent.required,
|
|
83
87
|
};
|
|
@@ -88,9 +92,8 @@ function _materializeComponents(stackExecutor, srcMetadata, profile, options) {
|
|
|
88
92
|
if (!profileComponent.uri)
|
|
89
93
|
throw new SbValidationError(`"uri" property required`);
|
|
90
94
|
const reg = ExtensionRegistry.getComponent(profileComponent.uri);
|
|
91
|
-
if (!(profileComponent.uri
|
|
92
|
-
|
|
93
|
-
throw new SbValidationError(`Selected component "${profileComponent.uri}" do not implement ${metadataComponent.interfaceUri}.`);
|
|
95
|
+
if (!slotTakes(metadataComponent, profileComponent.uri, reg.metadata.interfaces))
|
|
96
|
+
throw new SbValidationError(`Selected component "${profileComponent.uri}" do not implement ${slotInterfaces(metadataComponent).join(' or ')}.`);
|
|
94
97
|
if (profileComponent.disabled && metadataComponent.required) {
|
|
95
98
|
throw new SbValidationError(`Component "${componentName}" is disabled but it is required.`);
|
|
96
99
|
}
|
|
@@ -12,6 +12,19 @@ export declare const SBEXT_SCOPE = "sbext";
|
|
|
12
12
|
* owner, while `@syncbridge/sqb` is still a valid registry name.
|
|
13
13
|
*/
|
|
14
14
|
export declare const RESERVED_SCOPES: readonly string[];
|
|
15
|
+
/** The SDK: on npm, not in the registry; the host provides them */
|
|
16
|
+
export declare const SDK_PACKAGES: readonly string[];
|
|
17
|
+
/** Whether a name is one of the SDK's packages, which come from npm */
|
|
18
|
+
export declare const isSdkPackage: (name: string) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a name is one of SyncBridge's own registry packages
|
|
21
|
+
* (`@syncbridge/builtins`, `@syncbridge/hl7`, ...). They are registry
|
|
22
|
+
* packages like any other - a server comes with a version of each, and
|
|
23
|
+
* installs a newer one a package needs from the registry - but a package
|
|
24
|
+
* imports one under its own name, `@syncbridge/builtins`, not
|
|
25
|
+
* `sb:@syncbridge/builtins`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const isSyncBridgeModule: (name: string) => boolean;
|
|
15
28
|
/**
|
|
16
29
|
* Checks a registry organization name - the scope, with or without its `@`.
|
|
17
30
|
* Returns the problems found; an empty array means it is valid.
|
package/utils/registry-names.js
CHANGED
|
@@ -13,6 +13,25 @@ export const SBEXT_SCOPE = 'sbext';
|
|
|
13
13
|
* owner, while `@syncbridge/sqb` is still a valid registry name.
|
|
14
14
|
*/
|
|
15
15
|
export const RESERVED_SCOPES = [SBEXT_SCOPE, 'syncbridge'];
|
|
16
|
+
/** The SDK: on npm, not in the registry; the host provides them */
|
|
17
|
+
export const SDK_PACKAGES = [
|
|
18
|
+
'@syncbridge/common',
|
|
19
|
+
'@syncbridge/runtime',
|
|
20
|
+
'@syncbridge/syncbuild',
|
|
21
|
+
'@syncbridge/testing',
|
|
22
|
+
'@syncbridge/plugin-api',
|
|
23
|
+
];
|
|
24
|
+
/** Whether a name is one of the SDK's packages, which come from npm */
|
|
25
|
+
export const isSdkPackage = (name) => SDK_PACKAGES.includes(name);
|
|
26
|
+
/**
|
|
27
|
+
* Whether a name is one of SyncBridge's own registry packages
|
|
28
|
+
* (`@syncbridge/builtins`, `@syncbridge/hl7`, ...). They are registry
|
|
29
|
+
* packages like any other - a server comes with a version of each, and
|
|
30
|
+
* installs a newer one a package needs from the registry - but a package
|
|
31
|
+
* imports one under its own name, `@syncbridge/builtins`, not
|
|
32
|
+
* `sb:@syncbridge/builtins`.
|
|
33
|
+
*/
|
|
34
|
+
export const isSyncBridgeModule = (name) => name.startsWith('@syncbridge/') && !isSdkPackage(name);
|
|
16
35
|
/** npm's limit on the length of a whole package name, scope included */
|
|
17
36
|
const MAX_NAME_LENGTH = 214;
|
|
18
37
|
/** Lower-case, URL-safe, and not starting with `.` or `_` */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ElementComponentMetadata } from '../models/metadata/element-component-metadata.js';
|
|
2
|
+
type Slot = Pick<ElementComponentMetadata, 'interfaceUri' | 'oneOf'>;
|
|
3
|
+
/** The interfaces a slot takes a class of: its oneOf, else its interfaceUri */
|
|
4
|
+
export declare function slotInterfaces(slot: Slot): string[];
|
|
5
|
+
/**
|
|
6
|
+
* Whether a slot takes a class: one of its interfaces it is, or it
|
|
7
|
+
* implements (its metadata's interfaces)
|
|
8
|
+
*/
|
|
9
|
+
export declare function slotTakes(slot: Slot, uri: string, interfaces: readonly string[] | undefined): boolean;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The interfaces a slot takes a class of: its oneOf, else its interfaceUri */
|
|
2
|
+
export function slotInterfaces(slot) {
|
|
3
|
+
return slot.oneOf?.length ? slot.oneOf : [slot.interfaceUri];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Whether a slot takes a class: one of its interfaces it is, or it
|
|
7
|
+
* implements (its metadata's interfaces)
|
|
8
|
+
*/
|
|
9
|
+
export function slotTakes(slot, uri, interfaces) {
|
|
10
|
+
return slotInterfaces(slot).some(i => uri === i || !!interfaces?.includes(i));
|
|
11
|
+
}
|