@openbrf/plugin-sdk 0.0.0-stage → 1.2.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/LICENSE +661 -0
- package/LICENSE-EXCEPTION.md +28 -0
- package/README.md +94 -2
- package/dist/abort-signal.d.ts +23 -0
- package/dist/abort-signal.d.ts.map +1 -0
- package/dist/abort-signal.js +2 -0
- package/dist/abort-signal.js.map +1 -0
- package/dist/action-refusal.d.ts +101 -0
- package/dist/action-refusal.d.ts.map +1 -0
- package/dist/action-refusal.js +96 -0
- package/dist/action-refusal.js.map +1 -0
- package/dist/action-schema.d.ts +42 -0
- package/dist/action-schema.d.ts.map +1 -0
- package/dist/action-schema.js +67 -0
- package/dist/action-schema.js.map +1 -0
- package/dist/actions.d.ts +287 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +68 -0
- package/dist/actions.js.map +1 -0
- package/dist/api-version.d.ts +23 -0
- package/dist/api-version.d.ts.map +1 -0
- package/dist/api-version.js +27 -0
- package/dist/api-version.js.map +1 -0
- package/dist/blocks.d.ts +310 -0
- package/dist/blocks.d.ts.map +1 -0
- package/dist/blocks.js +185 -0
- package/dist/blocks.js.map +1 -0
- package/dist/catalog.d.ts +707 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +567 -0
- package/dist/catalog.js.map +1 -0
- package/dist/define-plugin.d.ts +14 -0
- package/dist/define-plugin.d.ts.map +1 -0
- package/dist/define-plugin.js +15 -0
- package/dist/define-plugin.js.map +1 -0
- package/dist/entitlements.d.ts +117 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +62 -0
- package/dist/entitlements.js.map +1 -0
- package/dist/extensions.d.ts +183 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +67 -0
- package/dist/extensions.js.map +1 -0
- package/dist/finances.d.ts +294 -0
- package/dist/finances.d.ts.map +1 -0
- package/dist/finances.js +56 -0
- package/dist/finances.js.map +1 -0
- package/dist/findings.d.ts +156 -0
- package/dist/findings.d.ts.map +1 -0
- package/dist/findings.js +146 -0
- package/dist/findings.js.map +1 -0
- package/dist/host.d.ts +827 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +80 -0
- package/dist/host.js.map +1 -0
- package/dist/imports.d.ts +104 -0
- package/dist/imports.d.ts.map +1 -0
- package/dist/imports.js +70 -0
- package/dist/imports.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/integrity.d.ts +38 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +105 -0
- package/dist/integrity.js.map +1 -0
- package/dist/localized-text.d.ts +18 -0
- package/dist/localized-text.d.ts.map +1 -0
- package/dist/localized-text.js +17 -0
- package/dist/localized-text.js.map +1 -0
- package/dist/manifest.d.ts +1122 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +1118 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +35 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +2 -0
- package/dist/module.js.map +1 -0
- package/dist/package-check.d.ts +66 -0
- package/dist/package-check.d.ts.map +1 -0
- package/dist/package-check.js +755 -0
- package/dist/package-check.js.map +1 -0
- package/dist/permissions.d.ts +224 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +277 -0
- package/dist/permissions.js.map +1 -0
- package/dist/recipients.d.ts +139 -0
- package/dist/recipients.d.ts.map +1 -0
- package/dist/recipients.js +223 -0
- package/dist/recipients.js.map +1 -0
- package/dist/records.d.ts +130 -0
- package/dist/records.d.ts.map +1 -0
- package/dist/records.js +85 -0
- package/dist/records.js.map +1 -0
- package/dist/require-capability.d.ts +37 -0
- package/dist/require-capability.d.ts.map +1 -0
- package/dist/require-capability.js +88 -0
- package/dist/require-capability.js.map +1 -0
- package/dist/settings-schema.d.ts +130 -0
- package/dist/settings-schema.d.ts.map +1 -0
- package/dist/settings-schema.js +272 -0
- package/dist/settings-schema.js.map +1 -0
- package/dist/standard-schema.d.ts +43 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/standard-schema.js +2 -0
- package/dist/standard-schema.js.map +1 -0
- package/package.json +55 -3
- package/src/abort-signal.ts +22 -0
- package/src/action-refusal.ts +178 -0
- package/src/action-schema.ts +89 -0
- package/src/actions.ts +344 -0
- package/src/api-version.ts +28 -0
- package/src/blocks.ts +289 -0
- package/src/catalog.ts +679 -0
- package/src/define-plugin.ts +18 -0
- package/src/entitlements.ts +150 -0
- package/src/extensions.ts +212 -0
- package/src/finances.ts +317 -0
- package/src/findings.ts +163 -0
- package/src/host.ts +894 -0
- package/src/imports.ts +135 -0
- package/src/index.ts +316 -0
- package/src/integrity.ts +125 -0
- package/src/localized-text.ts +19 -0
- package/src/manifest.ts +1412 -0
- package/src/module.ts +38 -0
- package/src/package-check.ts +894 -0
- package/src/permissions.ts +295 -0
- package/src/recipients.ts +294 -0
- package/src/records.ts +172 -0
- package/src/require-capability.ts +106 -0
- package/src/settings-schema.ts +334 -0
- package/src/standard-schema.ts +39 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a paid plugin reads about its licence (ADR 0042).
|
|
3
|
+
*
|
|
4
|
+
* The host holds the licences the board entered and verifies them offline,
|
|
5
|
+
* against the keys the plugin's manifest declares and the instance's own id
|
|
6
|
+
* and key, on the association's calendar day. A plugin gets the finding and
|
|
7
|
+
* never the file, and decides for itself what it withholds without an active
|
|
8
|
+
* entitlement: the host loads and serves it either way.
|
|
9
|
+
*/
|
|
10
|
+
/** The version of the licence file format this contract reads. */
|
|
11
|
+
export declare const LICENCE_FORMAT = "openbrf-licence/1";
|
|
12
|
+
/** The version of the presentation format `present()` writes. */
|
|
13
|
+
export declare const LICENCE_PRESENTATION_FORMAT = "openbrf-licence-presentation/1";
|
|
14
|
+
/** The largest licence file the host accepts, in bytes. */
|
|
15
|
+
export declare const LICENCE_FILE_MAX_BYTES = 16384;
|
|
16
|
+
/** The largest presentation a service has to read, in bytes. */
|
|
17
|
+
export declare const LICENCE_PRESENTATION_MAX_BYTES = 24576;
|
|
18
|
+
/**
|
|
19
|
+
* The finding for the entitlement the manifest names, judged today.
|
|
20
|
+
*
|
|
21
|
+
* Dates are `YYYY-MM-DD` on the association's calendar day, and inclusive: a
|
|
22
|
+
* licence valid until a day is active through the whole of it.
|
|
23
|
+
*
|
|
24
|
+
* - `active`: a licence one of the plugin's keys verified names the
|
|
25
|
+
* entitlement, is bound to this instance, and covers today and this release.
|
|
26
|
+
* - `absent`: no licence the plugin's keys verified names the entitlement.
|
|
27
|
+
* - `expired`: today is after its `validUntil`.
|
|
28
|
+
* - `version-not-covered`: this release was published after its
|
|
29
|
+
* `updatesUntil`. An earlier release stays covered.
|
|
30
|
+
* - `wrong-instance`: it was issued to another instance, by id or by key.
|
|
31
|
+
* - `tampered`: it verified when it was entered and no longer does.
|
|
32
|
+
* - `not-yet-valid`: it was issued after today, which is a wrong clock on
|
|
33
|
+
* one side or the other.
|
|
34
|
+
*
|
|
35
|
+
* Without an active licence the finding is that of the licence entered most
|
|
36
|
+
* recently, so a board that has just entered the wrong file is told about
|
|
37
|
+
* that file.
|
|
38
|
+
*/
|
|
39
|
+
export type EntitlementState = {
|
|
40
|
+
status: "active";
|
|
41
|
+
licenceId: string;
|
|
42
|
+
validUntil: string | null;
|
|
43
|
+
updatesUntil: string | null;
|
|
44
|
+
} | {
|
|
45
|
+
status: "absent";
|
|
46
|
+
} | {
|
|
47
|
+
status: "expired";
|
|
48
|
+
licenceId: string;
|
|
49
|
+
validUntil: string;
|
|
50
|
+
} | {
|
|
51
|
+
status: "version-not-covered";
|
|
52
|
+
licenceId: string;
|
|
53
|
+
updatesUntil: string;
|
|
54
|
+
released: string;
|
|
55
|
+
} | {
|
|
56
|
+
status: "wrong-instance";
|
|
57
|
+
licenceId: string;
|
|
58
|
+
} | {
|
|
59
|
+
status: "tampered";
|
|
60
|
+
} | {
|
|
61
|
+
status: "not-yet-valid";
|
|
62
|
+
licenceId: string;
|
|
63
|
+
issuedOn: string;
|
|
64
|
+
};
|
|
65
|
+
export type EntitlementStatus = EntitlementState["status"];
|
|
66
|
+
export declare const ENTITLEMENT_STATUSES: readonly ["active", "absent", "expired", "version-not-covered", "wrong-instance", "tampered", "not-yet-valid"];
|
|
67
|
+
/**
|
|
68
|
+
* A licensed plugin's view of its entitlement.
|
|
69
|
+
*
|
|
70
|
+
* Read on every call rather than once at boot, so a licence the board enters,
|
|
71
|
+
* one that ends, and the next one taking over all reach a running plugin
|
|
72
|
+
* without a restart.
|
|
73
|
+
*/
|
|
74
|
+
export interface PluginEntitlements {
|
|
75
|
+
/** The entitlement the manifest names, judged today. */
|
|
76
|
+
get(): Promise<EntitlementState>;
|
|
77
|
+
/**
|
|
78
|
+
* The active licence with a fresh signature by the instance key, for one of
|
|
79
|
+
* the audiences the manifest declares, as the text to send that service.
|
|
80
|
+
* Null without an active licence.
|
|
81
|
+
*
|
|
82
|
+
* Signed without a network call; sending it is the plugin's. A presentation
|
|
83
|
+
* is good at its audience for five minutes and once, so make one per
|
|
84
|
+
* request rather than keeping one.
|
|
85
|
+
*
|
|
86
|
+
* Throws a RangeError for an audience the manifest does not declare, since
|
|
87
|
+
* that is a defect in the plugin rather than a state of the licence.
|
|
88
|
+
*/
|
|
89
|
+
present(audience: string): Promise<string | null>;
|
|
90
|
+
}
|
|
91
|
+
export interface TestEntitlementsOptions {
|
|
92
|
+
/** What `get()` answers. */
|
|
93
|
+
state: EntitlementState;
|
|
94
|
+
/** The manifest's `licence.audiences`. None by default. */
|
|
95
|
+
audiences?: readonly string[];
|
|
96
|
+
/** What `present()` answers while the state is active. */
|
|
97
|
+
presentation?: (audience: string) => string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* A stand-in for `host.entitlements` in a plugin's own tests.
|
|
101
|
+
*
|
|
102
|
+
* It answers with the state it was given and keeps the host's two rules about
|
|
103
|
+
* presenting: an undeclared audience is refused, and nothing is presented
|
|
104
|
+
* without an active licence. A plugin that wants to test the service end of a
|
|
105
|
+
* presentation signs licences with a throwaway key generated in the test;
|
|
106
|
+
* nothing here needs one.
|
|
107
|
+
*/
|
|
108
|
+
export declare function createTestEntitlements(options: TestEntitlementsOptions): PluginEntitlements & {
|
|
109
|
+
state: EntitlementState;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* The refusal of an audience the manifest does not declare, worded once for
|
|
113
|
+
* the host and its test double. The audience is left out: it is whatever the
|
|
114
|
+
* plugin passed.
|
|
115
|
+
*/
|
|
116
|
+
export declare function undeclaredAudienceError(): RangeError;
|
|
117
|
+
//# sourceMappingURL=entitlements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../src/entitlements.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,kEAAkE;AAClE,eAAO,MAAM,cAAc,sBAAsB,CAAC;AAElD,iEAAiE;AACjE,eAAO,MAAM,2BAA2B,mCAAmC,CAAC;AAE5E,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,gEAAgE;AAChE,eAAO,MAAM,8BAA8B,QAAS,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,gBAAgB,GACxB;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC5D;IACE,MAAM,EAAE,qBAAqB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,GACtB;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AAE3D,eAAO,MAAM,oBAAoB,gHAQgB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,GAAG,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,uBAAuB;IACtC,4BAA4B;IAC5B,KAAK,EAAE,gBAAgB,CAAC;IACxB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,kBAAkB,GAAG;IAAE,KAAK,EAAE,gBAAgB,CAAA;CAAE,CAkBlD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,UAAU,CAKpD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a paid plugin reads about its licence (ADR 0042).
|
|
3
|
+
*
|
|
4
|
+
* The host holds the licences the board entered and verifies them offline,
|
|
5
|
+
* against the keys the plugin's manifest declares and the instance's own id
|
|
6
|
+
* and key, on the association's calendar day. A plugin gets the finding and
|
|
7
|
+
* never the file, and decides for itself what it withholds without an active
|
|
8
|
+
* entitlement: the host loads and serves it either way.
|
|
9
|
+
*/
|
|
10
|
+
/** The version of the licence file format this contract reads. */
|
|
11
|
+
export const LICENCE_FORMAT = "openbrf-licence/1";
|
|
12
|
+
/** The version of the presentation format `present()` writes. */
|
|
13
|
+
export const LICENCE_PRESENTATION_FORMAT = "openbrf-licence-presentation/1";
|
|
14
|
+
/** The largest licence file the host accepts, in bytes. */
|
|
15
|
+
export const LICENCE_FILE_MAX_BYTES = 16_384;
|
|
16
|
+
/** The largest presentation a service has to read, in bytes. */
|
|
17
|
+
export const LICENCE_PRESENTATION_MAX_BYTES = 24_576;
|
|
18
|
+
export const ENTITLEMENT_STATUSES = [
|
|
19
|
+
"active",
|
|
20
|
+
"absent",
|
|
21
|
+
"expired",
|
|
22
|
+
"version-not-covered",
|
|
23
|
+
"wrong-instance",
|
|
24
|
+
"tampered",
|
|
25
|
+
"not-yet-valid",
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* A stand-in for `host.entitlements` in a plugin's own tests.
|
|
29
|
+
*
|
|
30
|
+
* It answers with the state it was given and keeps the host's two rules about
|
|
31
|
+
* presenting: an undeclared audience is refused, and nothing is presented
|
|
32
|
+
* without an active licence. A plugin that wants to test the service end of a
|
|
33
|
+
* presentation signs licences with a throwaway key generated in the test;
|
|
34
|
+
* nothing here needs one.
|
|
35
|
+
*/
|
|
36
|
+
export function createTestEntitlements(options) {
|
|
37
|
+
const audiences = new Set(options.audiences ?? []);
|
|
38
|
+
const double = {
|
|
39
|
+
state: options.state,
|
|
40
|
+
get: () => Promise.resolve(double.state),
|
|
41
|
+
present: (audience) => {
|
|
42
|
+
if (!audiences.has(audience)) {
|
|
43
|
+
return Promise.reject(undeclaredAudienceError());
|
|
44
|
+
}
|
|
45
|
+
if (double.state.status !== "active") {
|
|
46
|
+
return Promise.resolve(null);
|
|
47
|
+
}
|
|
48
|
+
return Promise.resolve(options.presentation?.(audience) ?? `test-presentation:${audience}`);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
return double;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The refusal of an audience the manifest does not declare, worded once for
|
|
55
|
+
* the host and its test double. The audience is left out: it is whatever the
|
|
56
|
+
* plugin passed.
|
|
57
|
+
*/
|
|
58
|
+
export function undeclaredAudienceError() {
|
|
59
|
+
return new RangeError("present() was asked for an audience the plugin's manifest does not " +
|
|
60
|
+
"declare under licence.audiences.");
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=entitlements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.js","sourceRoot":"","sources":["../src/entitlements.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAElD,iEAAiE;AACjE,MAAM,CAAC,MAAM,2BAA2B,GAAG,gCAAgC,CAAC;AAE5E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAE7C,gEAAgE;AAChE,MAAM,CAAC,MAAM,8BAA8B,GAAG,MAAM,CAAC;AA4CrD,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,qBAAqB;IACrB,gBAAgB;IAChB,UAAU;IACV,eAAe;CACgC,CAAC;AAoClD;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAgC;IAEhC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,GAAG,EAAE,GAA8B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QACnE,OAAO,EAAE,CAAC,QAAgB,EAA0B,EAAE;YACpD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7B,OAAO,OAAO,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAAC,CAAC;YACnD,CAAC;YACD,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACrC,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO,OAAO,CAAC,OAAO,CACpB,OAAO,CAAC,YAAY,EAAE,CAAC,QAAQ,CAAC,IAAI,qBAAqB,QAAQ,EAAE,CACpE,CAAC;QACJ,CAAC;KACF,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,IAAI,UAAU,CACnB,qEAAqE;QACnE,kCAAkC,CACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { PluginAbortSignal } from "./abort-signal.ts";
|
|
2
|
+
import type { StandardSchemaV1 } from "./standard-schema.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Plugins that extend plugins (ADR 0030).
|
|
5
|
+
*
|
|
6
|
+
* An extension point (utökningspunkt) is a named slot a plugin declares in its
|
|
7
|
+
* manifest and offers to other plugins; that plugin is the point's host plugin.
|
|
8
|
+
* An addon (utökning) is a plugin that extends another at one of its points,
|
|
9
|
+
* and what it registers there is a contribution (bidrag): a title and a
|
|
10
|
+
* handler. Each is a plugin in every other respect, installed, consented to and
|
|
11
|
+
* switched on by itself, and neither receives the other's permissions.
|
|
12
|
+
*
|
|
13
|
+
* The host carries every call between them. It validates the input against the
|
|
14
|
+
* host plugin's schema before the call and the output after it, copies both as
|
|
15
|
+
* JSON values so that nothing but data crosses, and answers for what the addon
|
|
16
|
+
* did in the result rather than by throwing: a screen that loses one source
|
|
17
|
+
* should go on with the others.
|
|
18
|
+
*
|
|
19
|
+
* Swedish domain terms follow GLOSSARY.md.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* What an extension point id may look like, in the manifest and at the host.
|
|
23
|
+
*
|
|
24
|
+
* Lowercase letters, digits and single hyphens, two to thirty-two characters:
|
|
25
|
+
* the shape a plugin id has, shorter, because it is shown beside one.
|
|
26
|
+
*/
|
|
27
|
+
export declare const EXTENSION_POINT_ID_PATTERN: RegExp;
|
|
28
|
+
/** How many points a plugin may declare, and how many an addon may extend. */
|
|
29
|
+
export declare const MAX_EXTENSION_POINTS = 8;
|
|
30
|
+
/** How long a contribution may take when its point names no time of its own. */
|
|
31
|
+
export declare const EXTENSION_TIMEOUT_MS = 5000;
|
|
32
|
+
/** The longest a point may give a contribution. */
|
|
33
|
+
export declare const EXTENSION_MAX_TIMEOUT_MS = 15000;
|
|
34
|
+
/**
|
|
35
|
+
* The longest answer a contribution may give, in characters of its JSON text.
|
|
36
|
+
* A longer one is `output-invalid`, refused before it is parsed or validated.
|
|
37
|
+
*/
|
|
38
|
+
export declare const EXTENSION_MAX_OUTPUT_LENGTH = 1048576;
|
|
39
|
+
/**
|
|
40
|
+
* Why `invoke` answered without a value.
|
|
41
|
+
*
|
|
42
|
+
* Everything an addon can do wrong, and the one state the host plugin cannot
|
|
43
|
+
* see from its own side, so a host plugin can tell a broken addon from one that
|
|
44
|
+
* is switched off without either being thrown at it.
|
|
45
|
+
*/
|
|
46
|
+
export declare const EXTENSION_RESULT_REASONS: readonly [
|
|
47
|
+
/**
|
|
48
|
+
* The addon is not installed, not loaded, switched off, or has registered no
|
|
49
|
+
* contribution at this point.
|
|
50
|
+
*/
|
|
51
|
+
"not-serving",
|
|
52
|
+
/**
|
|
53
|
+
* The handler did not answer in time. Its signal was aborted.
|
|
54
|
+
*
|
|
55
|
+
* The time bounds only how long the host waits for the handler's promise.
|
|
56
|
+
* Work the handler does synchronously, and a `toJSON` or getter in its
|
|
57
|
+
* answer, runs on the host's one thread with no limit, as any plugin code
|
|
58
|
+
* does: a handler that loops stalls the instance.
|
|
59
|
+
*/
|
|
60
|
+
"timed-out",
|
|
61
|
+
/** The handler threw. What it threw is in the server log, never here. */
|
|
62
|
+
"failed",
|
|
63
|
+
/**
|
|
64
|
+
* The answer does not meet the point's output schema, has no JSON form, or
|
|
65
|
+
* is longer than EXTENSION_MAX_OUTPUT_LENGTH as JSON.
|
|
66
|
+
*/
|
|
67
|
+
"output-invalid"];
|
|
68
|
+
export type ExtensionResultReason = (typeof EXTENSION_RESULT_REASONS)[number];
|
|
69
|
+
/** What the host plugin hands `define` for one point its manifest declares. */
|
|
70
|
+
export interface ExtensionPointDefinition<Input = unknown, Output = unknown> {
|
|
71
|
+
/**
|
|
72
|
+
* What the host plugin sends a contribution. A strict object at every depth,
|
|
73
|
+
* built with the host's zod, with no record, catchall, `unknown` or `any`
|
|
74
|
+
* anywhere inside it.
|
|
75
|
+
*/
|
|
76
|
+
readonly input: StandardSchemaV1<unknown, Input>;
|
|
77
|
+
/**
|
|
78
|
+
* What a contribution must answer. A strict object at every depth, built with
|
|
79
|
+
* the host's zod, with no record, catchall, `unknown` or `any` anywhere inside
|
|
80
|
+
* it: a key the schema does not name is a key an addon could hand over
|
|
81
|
+
* something nobody consented to in.
|
|
82
|
+
*/
|
|
83
|
+
readonly output: StandardSchemaV1<unknown, Output>;
|
|
84
|
+
/**
|
|
85
|
+
* How long a contribution may take, in milliseconds.
|
|
86
|
+
* EXTENSION_TIMEOUT_MS by default, at most EXTENSION_MAX_TIMEOUT_MS.
|
|
87
|
+
*/
|
|
88
|
+
readonly timeoutMs?: number;
|
|
89
|
+
}
|
|
90
|
+
/** What the host tells a contribution about the call it is answering. */
|
|
91
|
+
export interface ExtensionContext {
|
|
92
|
+
/** The plugin whose point is being answered. */
|
|
93
|
+
readonly hostPlugin: string;
|
|
94
|
+
readonly point: string;
|
|
95
|
+
/** The point's major version, which is the one the addon named. */
|
|
96
|
+
readonly version: number;
|
|
97
|
+
/** For the logs, so a call can be followed from one plugin to the other. */
|
|
98
|
+
readonly requestId: string;
|
|
99
|
+
readonly now: Date;
|
|
100
|
+
/** Aborted when the call times out or the host plugin gives up on it. */
|
|
101
|
+
readonly signal: PluginAbortSignal;
|
|
102
|
+
}
|
|
103
|
+
/** What an addon hands `contribute` for one point its manifest extends. */
|
|
104
|
+
export interface ExtensionContributionDefinition<Input = unknown, Output = unknown> {
|
|
105
|
+
/**
|
|
106
|
+
* A bare key in the addon's own namespace, as an action's is. The host
|
|
107
|
+
* qualifies it as `plugin-<addon>:<key>`.
|
|
108
|
+
*/
|
|
109
|
+
readonly titleKey: string;
|
|
110
|
+
readonly descriptionKey?: string;
|
|
111
|
+
/**
|
|
112
|
+
* Answers one call.
|
|
113
|
+
*
|
|
114
|
+
* Runs with the addon's own host object, under the permissions the board
|
|
115
|
+
* consented to for the addon, and with no person behind it: an extension
|
|
116
|
+
* point is a call from one plugin to another, not somebody's request. It
|
|
117
|
+
* must accept input fields it does not know, because a host plugin may add
|
|
118
|
+
* an optional field to a point without raising its version.
|
|
119
|
+
*/
|
|
120
|
+
readonly handler: (input: Input, context: ExtensionContext) => Promise<Output>;
|
|
121
|
+
}
|
|
122
|
+
/** One contribution that is serving at a point, as its host plugin sees it. */
|
|
123
|
+
export interface ExtensionContributionSummary {
|
|
124
|
+
/** The addon's plugin id, which `invoke` takes. */
|
|
125
|
+
readonly addon: string;
|
|
126
|
+
/** Qualified into the addon's namespace, `plugin-<addon>:<key>`. */
|
|
127
|
+
readonly titleKey: string;
|
|
128
|
+
readonly descriptionKey: string | null;
|
|
129
|
+
}
|
|
130
|
+
/** What `invoke` answers: a value, or why there is none. */
|
|
131
|
+
export type ExtensionResult<Output> = {
|
|
132
|
+
readonly ok: true;
|
|
133
|
+
readonly value: Output;
|
|
134
|
+
} | {
|
|
135
|
+
readonly ok: false;
|
|
136
|
+
readonly reason: ExtensionResultReason;
|
|
137
|
+
};
|
|
138
|
+
/** The plugin and the point an addon contributes to. */
|
|
139
|
+
export interface ExtensionTarget {
|
|
140
|
+
readonly plugin: string;
|
|
141
|
+
readonly point: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Extension points, as a plugin sees them.
|
|
145
|
+
*
|
|
146
|
+
* A host plugin uses `define`, `list` and `invoke`; an addon uses
|
|
147
|
+
* `contribute`. `define` and `contribute` are declarations, callable from the
|
|
148
|
+
* module factory onwards and buffered until the application exists, as
|
|
149
|
+
* `host.actions.register` is. A definition or contribution the host cannot
|
|
150
|
+
* accept - a point or target the manifest does not declare, a schema that is
|
|
151
|
+
* not a strict object built with the host's zod, a second one for the same
|
|
152
|
+
* point - is found when the buffer is flushed and stops the plugin with
|
|
153
|
+
* `extension-refused`, rather than throwing here.
|
|
154
|
+
*
|
|
155
|
+
* `list` and `invoke` keep the ordinary gate and work from `onModuleInit`
|
|
156
|
+
* onwards.
|
|
157
|
+
*/
|
|
158
|
+
export interface PluginExtensions {
|
|
159
|
+
/** Host plugin: the schemas of a point its manifest declares. */
|
|
160
|
+
define<Input, Output>(point: string, definition: ExtensionPointDefinition<Input, Output>): void;
|
|
161
|
+
/**
|
|
162
|
+
* Host plugin: the contributions to one of its points that are serving now,
|
|
163
|
+
* in addon id order.
|
|
164
|
+
*
|
|
165
|
+
* Throws for a point the manifest does not declare or the plugin has not
|
|
166
|
+
* defined.
|
|
167
|
+
*/
|
|
168
|
+
list(point: string): Promise<readonly ExtensionContributionSummary[]>;
|
|
169
|
+
/**
|
|
170
|
+
* Host plugin: calls one addon's contribution.
|
|
171
|
+
*
|
|
172
|
+
* Throws for a mistake of the host plugin's own: a point its manifest does
|
|
173
|
+
* not declare or it has not defined, or an input its own schema refuses. An
|
|
174
|
+
* aborted `signal` rejects with the signal's reason. Everything the addon
|
|
175
|
+
* does is answered in the result.
|
|
176
|
+
*/
|
|
177
|
+
invoke<Output>(point: string, addon: string, input: unknown, options?: {
|
|
178
|
+
signal?: PluginAbortSignal;
|
|
179
|
+
}): Promise<ExtensionResult<Output>>;
|
|
180
|
+
/** Addon: the handler for a point its manifest extends. Once per point. */
|
|
181
|
+
contribute<Input, Output>(target: ExtensionTarget, contribution: ExtensionContributionDefinition<Input, Output>): void;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=extensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugins that extend plugins (ADR 0030).
|
|
3
|
+
*
|
|
4
|
+
* An extension point (utökningspunkt) is a named slot a plugin declares in its
|
|
5
|
+
* manifest and offers to other plugins; that plugin is the point's host plugin.
|
|
6
|
+
* An addon (utökning) is a plugin that extends another at one of its points,
|
|
7
|
+
* and what it registers there is a contribution (bidrag): a title and a
|
|
8
|
+
* handler. Each is a plugin in every other respect, installed, consented to and
|
|
9
|
+
* switched on by itself, and neither receives the other's permissions.
|
|
10
|
+
*
|
|
11
|
+
* The host carries every call between them. It validates the input against the
|
|
12
|
+
* host plugin's schema before the call and the output after it, copies both as
|
|
13
|
+
* JSON values so that nothing but data crosses, and answers for what the addon
|
|
14
|
+
* did in the result rather than by throwing: a screen that loses one source
|
|
15
|
+
* should go on with the others.
|
|
16
|
+
*
|
|
17
|
+
* Swedish domain terms follow GLOSSARY.md.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* What an extension point id may look like, in the manifest and at the host.
|
|
21
|
+
*
|
|
22
|
+
* Lowercase letters, digits and single hyphens, two to thirty-two characters:
|
|
23
|
+
* the shape a plugin id has, shorter, because it is shown beside one.
|
|
24
|
+
*/
|
|
25
|
+
export const EXTENSION_POINT_ID_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
26
|
+
/** How many points a plugin may declare, and how many an addon may extend. */
|
|
27
|
+
export const MAX_EXTENSION_POINTS = 8;
|
|
28
|
+
/** How long a contribution may take when its point names no time of its own. */
|
|
29
|
+
export const EXTENSION_TIMEOUT_MS = 5_000;
|
|
30
|
+
/** The longest a point may give a contribution. */
|
|
31
|
+
export const EXTENSION_MAX_TIMEOUT_MS = 15_000;
|
|
32
|
+
/**
|
|
33
|
+
* The longest answer a contribution may give, in characters of its JSON text.
|
|
34
|
+
* A longer one is `output-invalid`, refused before it is parsed or validated.
|
|
35
|
+
*/
|
|
36
|
+
export const EXTENSION_MAX_OUTPUT_LENGTH = 1_048_576;
|
|
37
|
+
/**
|
|
38
|
+
* Why `invoke` answered without a value.
|
|
39
|
+
*
|
|
40
|
+
* Everything an addon can do wrong, and the one state the host plugin cannot
|
|
41
|
+
* see from its own side, so a host plugin can tell a broken addon from one that
|
|
42
|
+
* is switched off without either being thrown at it.
|
|
43
|
+
*/
|
|
44
|
+
export const EXTENSION_RESULT_REASONS = [
|
|
45
|
+
/**
|
|
46
|
+
* The addon is not installed, not loaded, switched off, or has registered no
|
|
47
|
+
* contribution at this point.
|
|
48
|
+
*/
|
|
49
|
+
"not-serving",
|
|
50
|
+
/**
|
|
51
|
+
* The handler did not answer in time. Its signal was aborted.
|
|
52
|
+
*
|
|
53
|
+
* The time bounds only how long the host waits for the handler's promise.
|
|
54
|
+
* Work the handler does synchronously, and a `toJSON` or getter in its
|
|
55
|
+
* answer, runs on the host's one thread with no limit, as any plugin code
|
|
56
|
+
* does: a handler that loops stalls the instance.
|
|
57
|
+
*/
|
|
58
|
+
"timed-out",
|
|
59
|
+
/** The handler threw. What it threw is in the server log, never here. */
|
|
60
|
+
"failed",
|
|
61
|
+
/**
|
|
62
|
+
* The answer does not meet the point's output schema, has no JSON form, or
|
|
63
|
+
* is longer than EXTENSION_MAX_OUTPUT_LENGTH as JSON.
|
|
64
|
+
*/
|
|
65
|
+
"output-invalid",
|
|
66
|
+
];
|
|
67
|
+
//# sourceMappingURL=extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,+BAA+B,CAAC;AAE1E,8EAA8E;AAC9E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAE1C,mDAAmD;AACnD,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,SAAS,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC;;;OAGG;IACH,aAAa;IACb;;;;;;;OAOG;IACH,WAAW;IACX,yEAAyE;IACzE,QAAQ;IACR;;;OAGG;IACH,gBAAgB;CACR,CAAC"}
|