@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,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raising a plugin route above the capability floor the host gives it.
|
|
3
|
+
*
|
|
4
|
+
* The host reads one metadata key off a controller and off each of its routes,
|
|
5
|
+
* and answers a request only for a person who holds every capability listed
|
|
6
|
+
* there. A plugin's controllers get a floor from the plugin's own permissions
|
|
7
|
+
* (see "Routes" in the plugin contract); `RequireCapability` is how a route
|
|
8
|
+
* asks for more.
|
|
9
|
+
*
|
|
10
|
+
* Written without `@nestjs/common` on purpose, for the reason `definePlugin` is
|
|
11
|
+
* an identity function: the SDK is bundled into the plugin, and an import of
|
|
12
|
+
* its own would have to be resolved from the data volume. NestJS's
|
|
13
|
+
* `SetMetadata` does no more than this, on the same global `Reflect` the host's
|
|
14
|
+
* copy of NestJS writes to.
|
|
15
|
+
*/
|
|
16
|
+
/** The metadata key the host reads a route's or a controller's capabilities from. */
|
|
17
|
+
export const REQUIRED_CAPABILITIES_KEY = "openbrf:required-capabilities";
|
|
18
|
+
/**
|
|
19
|
+
* Every capability name the host knows, which is what a route may require.
|
|
20
|
+
*
|
|
21
|
+
* Listed here, not derived, because the SDK cannot import the host. The host
|
|
22
|
+
* checks the list against its own capabilities in a test, so a capability
|
|
23
|
+
* added there is not missing here for long; until the SDK is released with it,
|
|
24
|
+
* a plugin that needs the new name is a type error rather than a typo caught
|
|
25
|
+
* at request time.
|
|
26
|
+
*/
|
|
27
|
+
export const PLUGIN_ROUTE_CAPABILITIES = [
|
|
28
|
+
"association:manage",
|
|
29
|
+
"association:read",
|
|
30
|
+
"addressBook:read",
|
|
31
|
+
"addressBook:write",
|
|
32
|
+
"memberRegister:read",
|
|
33
|
+
"apartmentRegister:read",
|
|
34
|
+
"protectedData:reveal",
|
|
35
|
+
"registerReport:export",
|
|
36
|
+
"invitation:send",
|
|
37
|
+
"signupRequest:decide",
|
|
38
|
+
"self:manage",
|
|
39
|
+
"residentDirectory:read",
|
|
40
|
+
"issues:handle",
|
|
41
|
+
"issues:report",
|
|
42
|
+
"issues:configure",
|
|
43
|
+
"documents:manage",
|
|
44
|
+
"site:manage",
|
|
45
|
+
"news:comment",
|
|
46
|
+
"boardPosition:manage",
|
|
47
|
+
"bookings:book",
|
|
48
|
+
"bookings:manage",
|
|
49
|
+
"bookings:configure",
|
|
50
|
+
"events:manage",
|
|
51
|
+
"events:attend",
|
|
52
|
+
"systemRole:manage",
|
|
53
|
+
"motions:submit",
|
|
54
|
+
"motions:handle",
|
|
55
|
+
"meetings:manage",
|
|
56
|
+
"memberCharges:manage",
|
|
57
|
+
"fees:manage",
|
|
58
|
+
"dataProtection:manage",
|
|
59
|
+
"boardMailbox:handle",
|
|
60
|
+
"sublets:apply",
|
|
61
|
+
"sublets:handle",
|
|
62
|
+
"keyOrders:place",
|
|
63
|
+
"keyOrders:handle",
|
|
64
|
+
"chat:participate",
|
|
65
|
+
"chat:moderate",
|
|
66
|
+
"apartmentBinder:manage",
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* Declares the capabilities a route requires. Several are combined with AND.
|
|
70
|
+
*
|
|
71
|
+
* Applied to a controller it covers every route in it. It can only add to the
|
|
72
|
+
* floor the host gives the plugin: a route cannot require less.
|
|
73
|
+
*/
|
|
74
|
+
export function RequireCapability(...capabilities) {
|
|
75
|
+
return ((target, _key, descriptor) => {
|
|
76
|
+
const define = Reflect.defineMetadata;
|
|
77
|
+
if (define === undefined) {
|
|
78
|
+
throw new Error("RequireCapability needs reflect-metadata, which @nestjs/common loads: import it before the controller.");
|
|
79
|
+
}
|
|
80
|
+
if (descriptor !== undefined) {
|
|
81
|
+
define(REQUIRED_CAPABILITIES_KEY, capabilities, descriptor.value);
|
|
82
|
+
return descriptor;
|
|
83
|
+
}
|
|
84
|
+
define(REQUIRED_CAPABILITIES_KEY, capabilities, target);
|
|
85
|
+
return target;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=require-capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-capability.js","sourceRoot":"","sources":["../src/require-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,qFAAqF;AACrF,MAAM,CAAC,MAAM,yBAAyB,GAAG,+BAA+B,CAAC;AAEzE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,oBAAoB;IACpB,kBAAkB;IAClB,kBAAkB;IAClB,mBAAmB;IACnB,qBAAqB;IACrB,wBAAwB;IACxB,sBAAsB;IACtB,uBAAuB;IACvB,iBAAiB;IACjB,sBAAsB;IACtB,aAAa;IACb,wBAAwB;IACxB,eAAe;IACf,eAAe;IACf,kBAAkB;IAClB,kBAAkB;IAClB,aAAa;IACb,cAAc;IACd,sBAAsB;IACtB,eAAe;IACf,iBAAiB;IACjB,oBAAoB;IACpB,eAAe;IACf,eAAe;IACf,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,iBAAiB;IACjB,sBAAsB;IACtB,aAAa;IACb,uBAAuB;IACvB,qBAAqB;IACrB,eAAe;IACf,gBAAgB;IAChB,iBAAiB;IACjB,kBAAkB;IAClB,kBAAkB;IAClB,eAAe;IACf,wBAAwB;CAChB,CAAC;AAUX;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,GAAG,YAAqC;IAExC,OAAO,CAAC,CACN,MAAc,EACd,IAAsB,EACtB,UAA+B,EAC/B,EAAE;QACF,MAAM,MAAM,GAAI,OAAsC,CAAC,cAAc,CAAC;QACtE,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,wGAAwG,CACzG,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,CAAC,yBAAyB,EAAE,YAAY,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;YAClE,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,MAAM,CAAC,yBAAyB,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAqC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const pluginSettingsFieldSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
3
|
+
key: z.ZodString;
|
|
4
|
+
labelKey: z.ZodString;
|
|
5
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
6
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
+
type: z.ZodLiteral<"text">;
|
|
8
|
+
default: z.ZodOptional<z.ZodString>;
|
|
9
|
+
minLength: z.ZodOptional<z.ZodInt>;
|
|
10
|
+
maxLength: z.ZodOptional<z.ZodInt>;
|
|
11
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
12
|
+
key: z.ZodString;
|
|
13
|
+
labelKey: z.ZodString;
|
|
14
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
15
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
16
|
+
type: z.ZodLiteral<"number">;
|
|
17
|
+
default: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
integer: z.ZodDefault<z.ZodBoolean>;
|
|
21
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
22
|
+
key: z.ZodString;
|
|
23
|
+
labelKey: z.ZodString;
|
|
24
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
25
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
26
|
+
type: z.ZodLiteral<"boolean">;
|
|
27
|
+
default: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
29
|
+
key: z.ZodString;
|
|
30
|
+
labelKey: z.ZodString;
|
|
31
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
32
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
33
|
+
type: z.ZodLiteral<"select">;
|
|
34
|
+
default: z.ZodOptional<z.ZodString>;
|
|
35
|
+
options: z.ZodArray<z.ZodObject<{
|
|
36
|
+
value: z.ZodString;
|
|
37
|
+
labelKey: z.ZodString;
|
|
38
|
+
}, z.core.$strip>>;
|
|
39
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
40
|
+
key: z.ZodString;
|
|
41
|
+
labelKey: z.ZodString;
|
|
42
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
43
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
44
|
+
type: z.ZodLiteral<"secret">;
|
|
45
|
+
minLength: z.ZodOptional<z.ZodInt>;
|
|
46
|
+
maxLength: z.ZodOptional<z.ZodInt>;
|
|
47
|
+
}, z.core.$strip>], "type">;
|
|
48
|
+
export type PluginSettingsField = z.infer<typeof pluginSettingsFieldSchema>;
|
|
49
|
+
export declare const pluginSettingsSchema: z.ZodObject<{
|
|
50
|
+
fields: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
51
|
+
key: z.ZodString;
|
|
52
|
+
labelKey: z.ZodString;
|
|
53
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
54
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
55
|
+
type: z.ZodLiteral<"text">;
|
|
56
|
+
default: z.ZodOptional<z.ZodString>;
|
|
57
|
+
minLength: z.ZodOptional<z.ZodInt>;
|
|
58
|
+
maxLength: z.ZodOptional<z.ZodInt>;
|
|
59
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
60
|
+
key: z.ZodString;
|
|
61
|
+
labelKey: z.ZodString;
|
|
62
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
63
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
64
|
+
type: z.ZodLiteral<"number">;
|
|
65
|
+
default: z.ZodOptional<z.ZodNumber>;
|
|
66
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
67
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
68
|
+
integer: z.ZodDefault<z.ZodBoolean>;
|
|
69
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
70
|
+
key: z.ZodString;
|
|
71
|
+
labelKey: z.ZodString;
|
|
72
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
73
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
74
|
+
type: z.ZodLiteral<"boolean">;
|
|
75
|
+
default: z.ZodOptional<z.ZodBoolean>;
|
|
76
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
77
|
+
key: z.ZodString;
|
|
78
|
+
labelKey: z.ZodString;
|
|
79
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
80
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
81
|
+
type: z.ZodLiteral<"select">;
|
|
82
|
+
default: z.ZodOptional<z.ZodString>;
|
|
83
|
+
options: z.ZodArray<z.ZodObject<{
|
|
84
|
+
value: z.ZodString;
|
|
85
|
+
labelKey: z.ZodString;
|
|
86
|
+
}, z.core.$strip>>;
|
|
87
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
88
|
+
key: z.ZodString;
|
|
89
|
+
labelKey: z.ZodString;
|
|
90
|
+
hintKey: z.ZodOptional<z.ZodString>;
|
|
91
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
92
|
+
type: z.ZodLiteral<"secret">;
|
|
93
|
+
minLength: z.ZodOptional<z.ZodInt>;
|
|
94
|
+
maxLength: z.ZodOptional<z.ZodInt>;
|
|
95
|
+
}, z.core.$strip>], "type">>;
|
|
96
|
+
}, z.core.$strip>;
|
|
97
|
+
export type PluginSettingsSchema = z.infer<typeof pluginSettingsSchema>;
|
|
98
|
+
export type PluginSettingsValues = Record<string, string | number | boolean>;
|
|
99
|
+
/**
|
|
100
|
+
* The declaration without its secret fields: what the admin screen is given
|
|
101
|
+
* values for, and what the host validates the stored plain values against.
|
|
102
|
+
*/
|
|
103
|
+
export declare function withoutSecretFields(schema: PluginSettingsSchema): PluginSettingsSchema;
|
|
104
|
+
/**
|
|
105
|
+
* The validator for one secret value, as it is written.
|
|
106
|
+
*
|
|
107
|
+
* The bounds the field declares, and never the empty string: a secret is
|
|
108
|
+
* cleared by saying so rather than by storing nothing as a value, so a
|
|
109
|
+
* plugin reading it is never handed "" where it expects a key.
|
|
110
|
+
*/
|
|
111
|
+
export declare function secretValueValidator(field: Extract<PluginSettingsField, {
|
|
112
|
+
type: "secret";
|
|
113
|
+
}>): z.ZodString;
|
|
114
|
+
/**
|
|
115
|
+
* Builds the validator for one settings declaration.
|
|
116
|
+
*
|
|
117
|
+
* Produced from the declaration rather than written by the plugin, so the
|
|
118
|
+
* form the host renders and the values the host stores can never disagree
|
|
119
|
+
* about what is acceptable. Unknown keys are stripped: a plugin that drops a
|
|
120
|
+
* setting in a new version must not leave the old value behind to be handed
|
|
121
|
+
* back to it as something it no longer understands.
|
|
122
|
+
*
|
|
123
|
+
* A secret field reads as a string, which is how `host.settings.read()` hands
|
|
124
|
+
* it to the plugin. What the admin screen writes it with is the host's own
|
|
125
|
+
* rule, since the screen never holds the value it would be validating.
|
|
126
|
+
*/
|
|
127
|
+
export declare function settingsValidator(schema: PluginSettingsSchema): z.ZodType<PluginSettingsValues>;
|
|
128
|
+
/** The values a fresh installation starts with, from the declared defaults. */
|
|
129
|
+
export declare function defaultSettings(schema: PluginSettingsSchema): PluginSettingsValues;
|
|
130
|
+
//# sourceMappingURL=settings-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-schema.d.ts","sourceRoot":"","sources":["../src/settings-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsFxB,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAMpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAoG5E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAe7B,CAAC;AAEL,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAE7E;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,oBAAoB,GAC3B,oBAAoB,CAItB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC,GACtD,CAAC,CAAC,SAAS,CAMb;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,oBAAoB,GAC3B,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAQjC;AAuDD,+EAA+E;AAC/E,wBAAgB,eAAe,CAC7B,MAAM,EAAE,oBAAoB,GAC3B,oBAAoB,CAQtB"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* A plugin's settings form, declared rather than drawn.
|
|
4
|
+
*
|
|
5
|
+
* The host renders it, validates against it, and stores the result. A plugin
|
|
6
|
+
* therefore never ships a settings screen of its own, which is what keeps
|
|
7
|
+
* plugin settings looking like the rest of the product and keeps the values
|
|
8
|
+
* validated on the server rather than only in a form the plugin drew.
|
|
9
|
+
*
|
|
10
|
+
* Labels are i18n KEYS, resolved in the plugin's own namespace
|
|
11
|
+
* (`plugin-<id>`, merged at runtime from its locales/{sv,en}.json). Inline
|
|
12
|
+
* label text is deliberately not accepted: a manifest carrying English prose
|
|
13
|
+
* would put an untranslatable string on a Swedish-first screen, and the
|
|
14
|
+
* runtime merge already exists to hold that text.
|
|
15
|
+
*/
|
|
16
|
+
const settingsKeySchema = z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1)
|
|
19
|
+
.max(64)
|
|
20
|
+
// A settings key becomes a JSON object key and a form field name. Keeping it
|
|
21
|
+
// to an identifier shape means a stored value can never collide with a
|
|
22
|
+
// prototype member or need escaping in either place.
|
|
23
|
+
.regex(/^[a-z][a-zA-Z0-9_]*$/, "must be a lowerCamelCase identifier");
|
|
24
|
+
const translationKeySchema = z.string().min(1).max(200);
|
|
25
|
+
const baseFieldSchema = z.object({
|
|
26
|
+
key: settingsKeySchema,
|
|
27
|
+
labelKey: translationKeySchema,
|
|
28
|
+
/** Help text under the field. Never the only carrier of a requirement. */
|
|
29
|
+
hintKey: translationKeySchema.optional(),
|
|
30
|
+
required: z.boolean().default(false),
|
|
31
|
+
});
|
|
32
|
+
const textFieldSchema = baseFieldSchema.extend({
|
|
33
|
+
type: z.literal("text"),
|
|
34
|
+
default: z.string().optional(),
|
|
35
|
+
minLength: z.int().min(0).optional(),
|
|
36
|
+
maxLength: z.int().min(1).max(10_000).optional(),
|
|
37
|
+
});
|
|
38
|
+
const numberFieldSchema = baseFieldSchema.extend({
|
|
39
|
+
type: z.literal("number"),
|
|
40
|
+
default: z.number().optional(),
|
|
41
|
+
min: z.number().optional(),
|
|
42
|
+
max: z.number().optional(),
|
|
43
|
+
integer: z.boolean().default(false),
|
|
44
|
+
});
|
|
45
|
+
const booleanFieldSchema = baseFieldSchema.extend({
|
|
46
|
+
type: z.literal("boolean"),
|
|
47
|
+
default: z.boolean().optional(),
|
|
48
|
+
});
|
|
49
|
+
const selectFieldSchema = baseFieldSchema.extend({
|
|
50
|
+
type: z.literal("select"),
|
|
51
|
+
default: z.string().optional(),
|
|
52
|
+
options: z
|
|
53
|
+
.array(z.object({
|
|
54
|
+
value: z.string().min(1).max(200),
|
|
55
|
+
labelKey: translationKeySchema,
|
|
56
|
+
}))
|
|
57
|
+
.min(1)
|
|
58
|
+
.max(50),
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* A credential: an API key, a token, a customer number the plugin signs in
|
|
62
|
+
* with (ADR 0037).
|
|
63
|
+
*
|
|
64
|
+
* Write-only from the admin screen: the host never returns the value to the
|
|
65
|
+
* browser, only whether one is set, and the board replaces or clears it. It is
|
|
66
|
+
* stored encrypted, apart from the other values, and handed back in clear only
|
|
67
|
+
* by `host.settings.read()` on the server. No default, because a credential
|
|
68
|
+
* written in a published manifest is not a secret.
|
|
69
|
+
*/
|
|
70
|
+
const secretFieldSchema = baseFieldSchema.extend({
|
|
71
|
+
type: z.literal("secret"),
|
|
72
|
+
minLength: z.int().min(1).optional(),
|
|
73
|
+
maxLength: z.int().min(1).max(10_000).optional(),
|
|
74
|
+
});
|
|
75
|
+
export const pluginSettingsFieldSchema = z.discriminatedUnion("type", [
|
|
76
|
+
textFieldSchema,
|
|
77
|
+
numberFieldSchema,
|
|
78
|
+
booleanFieldSchema,
|
|
79
|
+
selectFieldSchema,
|
|
80
|
+
secretFieldSchema,
|
|
81
|
+
]);
|
|
82
|
+
/**
|
|
83
|
+
* Checks a field's bounds against each other and against its default.
|
|
84
|
+
*
|
|
85
|
+
* Done while the manifest is parsed rather than left to the validator, because
|
|
86
|
+
* neither of the two paths a default takes goes through the field's
|
|
87
|
+
* constraints. `.default(value)` short-circuits an absent value and returns it
|
|
88
|
+
* without applying anything downstream, and `defaultSettings` reads the
|
|
89
|
+
* declaration directly. A number field declaring `min: 10, default: 5` would
|
|
90
|
+
* otherwise hand the plugin a 5 it said it would never see, and
|
|
91
|
+
* `minLength: 10, maxLength: 2` would declare a field no submitted value can
|
|
92
|
+
* satisfy - both of which are the plugin's mistake to hear about at install
|
|
93
|
+
* time rather than the board's to discover at a save.
|
|
94
|
+
*/
|
|
95
|
+
function checkFieldConstraints(field, index, ctx) {
|
|
96
|
+
const reject = (message, key) => {
|
|
97
|
+
ctx.addIssue({
|
|
98
|
+
code: "custom",
|
|
99
|
+
message,
|
|
100
|
+
path: ["fields", index, key],
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
switch (field.type) {
|
|
104
|
+
case "text": {
|
|
105
|
+
if (field.minLength !== undefined &&
|
|
106
|
+
field.maxLength !== undefined &&
|
|
107
|
+
field.minLength > field.maxLength) {
|
|
108
|
+
reject("minLength must not be greater than maxLength", "minLength");
|
|
109
|
+
}
|
|
110
|
+
if (field.default === undefined) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (field.minLength !== undefined &&
|
|
114
|
+
field.default.length < field.minLength) {
|
|
115
|
+
reject("the default is shorter than minLength", "default");
|
|
116
|
+
}
|
|
117
|
+
if (field.maxLength !== undefined &&
|
|
118
|
+
field.default.length > field.maxLength) {
|
|
119
|
+
reject("the default is longer than maxLength", "default");
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
case "number": {
|
|
124
|
+
if (field.min !== undefined &&
|
|
125
|
+
field.max !== undefined &&
|
|
126
|
+
field.min > field.max) {
|
|
127
|
+
reject("min must not be greater than max", "min");
|
|
128
|
+
}
|
|
129
|
+
if (field.default === undefined) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (field.integer && !Number.isInteger(field.default)) {
|
|
133
|
+
reject("the default is not an integer", "default");
|
|
134
|
+
}
|
|
135
|
+
if (field.min !== undefined && field.default < field.min) {
|
|
136
|
+
reject("the default is below min", "default");
|
|
137
|
+
}
|
|
138
|
+
if (field.max !== undefined && field.default > field.max) {
|
|
139
|
+
reject("the default is above max", "default");
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
case "select": {
|
|
144
|
+
if (field.default !== undefined &&
|
|
145
|
+
!field.options.some((option) => option.value === field.default)) {
|
|
146
|
+
reject("the default is not one of the declared options", "default");
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
case "secret": {
|
|
151
|
+
if (field.minLength !== undefined &&
|
|
152
|
+
field.maxLength !== undefined &&
|
|
153
|
+
field.minLength > field.maxLength) {
|
|
154
|
+
reject("minLength must not be greater than maxLength", "minLength");
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
case "boolean":
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export const pluginSettingsSchema = z
|
|
163
|
+
.object({
|
|
164
|
+
fields: z
|
|
165
|
+
.array(pluginSettingsFieldSchema)
|
|
166
|
+
.max(50)
|
|
167
|
+
.refine((fields) => new Set(fields.map((field) => field.key)).size === fields.length, { message: "field keys must be unique" }),
|
|
168
|
+
})
|
|
169
|
+
.superRefine((declaration, ctx) => {
|
|
170
|
+
declaration.fields.forEach((field, index) => {
|
|
171
|
+
checkFieldConstraints(field, index, ctx);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
/**
|
|
175
|
+
* The declaration without its secret fields: what the admin screen is given
|
|
176
|
+
* values for, and what the host validates the stored plain values against.
|
|
177
|
+
*/
|
|
178
|
+
export function withoutSecretFields(schema) {
|
|
179
|
+
return {
|
|
180
|
+
fields: schema.fields.filter((field) => field.type !== "secret"),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The validator for one secret value, as it is written.
|
|
185
|
+
*
|
|
186
|
+
* The bounds the field declares, and never the empty string: a secret is
|
|
187
|
+
* cleared by saying so rather than by storing nothing as a value, so a
|
|
188
|
+
* plugin reading it is never handed "" where it expects a key.
|
|
189
|
+
*/
|
|
190
|
+
export function secretValueValidator(field) {
|
|
191
|
+
let secret = z.string().min(Math.max(1, field.minLength ?? 1));
|
|
192
|
+
if (field.maxLength !== undefined) {
|
|
193
|
+
secret = secret.max(field.maxLength);
|
|
194
|
+
}
|
|
195
|
+
return secret;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Builds the validator for one settings declaration.
|
|
199
|
+
*
|
|
200
|
+
* Produced from the declaration rather than written by the plugin, so the
|
|
201
|
+
* form the host renders and the values the host stores can never disagree
|
|
202
|
+
* about what is acceptable. Unknown keys are stripped: a plugin that drops a
|
|
203
|
+
* setting in a new version must not leave the old value behind to be handed
|
|
204
|
+
* back to it as something it no longer understands.
|
|
205
|
+
*
|
|
206
|
+
* A secret field reads as a string, which is how `host.settings.read()` hands
|
|
207
|
+
* it to the plugin. What the admin screen writes it with is the host's own
|
|
208
|
+
* rule, since the screen never holds the value it would be validating.
|
|
209
|
+
*/
|
|
210
|
+
export function settingsValidator(schema) {
|
|
211
|
+
const shape = {};
|
|
212
|
+
for (const field of schema.fields) {
|
|
213
|
+
shape[field.key] = fieldValidator(field);
|
|
214
|
+
}
|
|
215
|
+
return z.object(shape).strip();
|
|
216
|
+
}
|
|
217
|
+
function fieldValidator(field) {
|
|
218
|
+
switch (field.type) {
|
|
219
|
+
case "text": {
|
|
220
|
+
let text = z.string();
|
|
221
|
+
if (field.minLength !== undefined) {
|
|
222
|
+
text = text.min(field.minLength);
|
|
223
|
+
}
|
|
224
|
+
if (field.maxLength !== undefined) {
|
|
225
|
+
text = text.max(field.maxLength);
|
|
226
|
+
}
|
|
227
|
+
// A required text field rejects the empty string. Without this a form
|
|
228
|
+
// submitted with the field untouched would satisfy "required" with a
|
|
229
|
+
// value the plugin has to treat as missing anyway.
|
|
230
|
+
return optionality(field.required ? text.min(1) : text, field);
|
|
231
|
+
}
|
|
232
|
+
case "number": {
|
|
233
|
+
let number = field.integer ? z.number().int() : z.number();
|
|
234
|
+
if (field.min !== undefined) {
|
|
235
|
+
number = number.min(field.min);
|
|
236
|
+
}
|
|
237
|
+
if (field.max !== undefined) {
|
|
238
|
+
number = number.max(field.max);
|
|
239
|
+
}
|
|
240
|
+
return optionality(number, field);
|
|
241
|
+
}
|
|
242
|
+
case "boolean":
|
|
243
|
+
return optionality(z.boolean(), field);
|
|
244
|
+
case "select":
|
|
245
|
+
return optionality(z.enum(field.options.map((option) => option.value)), field);
|
|
246
|
+
case "secret":
|
|
247
|
+
return optionality(secretValueValidator(field), field);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Applies the declared default and, for an optional field, tolerates absence.
|
|
252
|
+
*
|
|
253
|
+
* A default makes the field satisfied whether or not it was submitted, which
|
|
254
|
+
* is why it is applied before the optional wrapper rather than after.
|
|
255
|
+
*/
|
|
256
|
+
function optionality(validator, field) {
|
|
257
|
+
if ("default" in field && field.default !== undefined) {
|
|
258
|
+
return validator.default(field.default);
|
|
259
|
+
}
|
|
260
|
+
return field.required ? validator : validator.optional();
|
|
261
|
+
}
|
|
262
|
+
/** The values a fresh installation starts with, from the declared defaults. */
|
|
263
|
+
export function defaultSettings(schema) {
|
|
264
|
+
const values = {};
|
|
265
|
+
for (const field of schema.fields) {
|
|
266
|
+
if ("default" in field && field.default !== undefined) {
|
|
267
|
+
values[field.key] = field.default;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return values;
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=settings-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-schema.js","sourceRoot":"","sources":["../src/settings-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Standard Schema interface, version 1, as this package needs it.
|
|
3
|
+
*
|
|
4
|
+
* Declared here rather than imported, for the reason the rest of this package
|
|
5
|
+
* declares its own types: what an action's input and output are validated by is
|
|
6
|
+
* the host's business, and naming a validator in the published contract would
|
|
7
|
+
* make every plugin author's build depend on the one the host happens to use
|
|
8
|
+
* today. A structural interface says what the host requires - something that can
|
|
9
|
+
* validate an unknown value and report issues - and any library implementing the
|
|
10
|
+
* standard satisfies it, zod included.
|
|
11
|
+
*
|
|
12
|
+
* It is a subset: the parts the registry actually calls. `validate` is the one
|
|
13
|
+
* that matters, and note that it RETURNS its issues rather than throwing, which
|
|
14
|
+
* is the difference between this and a validator's own `parse`.
|
|
15
|
+
*
|
|
16
|
+
* The full specification is at standardschema.dev.
|
|
17
|
+
*/
|
|
18
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
19
|
+
readonly "~standard": {
|
|
20
|
+
/** The version of the standard this schema implements. */
|
|
21
|
+
readonly version: 1;
|
|
22
|
+
/** The library that produced the schema, for diagnostics. */
|
|
23
|
+
readonly vendor: string;
|
|
24
|
+
readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
|
|
25
|
+
readonly types?: {
|
|
26
|
+
readonly input: Input;
|
|
27
|
+
readonly output: Output;
|
|
28
|
+
} | undefined;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export type StandardSchemaResult<Output> = {
|
|
32
|
+
readonly value: Output;
|
|
33
|
+
readonly issues?: undefined;
|
|
34
|
+
} | {
|
|
35
|
+
readonly issues: readonly StandardSchemaIssue[];
|
|
36
|
+
};
|
|
37
|
+
export interface StandardSchemaIssue {
|
|
38
|
+
readonly message: string;
|
|
39
|
+
readonly path?: readonly (PropertyKey | {
|
|
40
|
+
readonly key: PropertyKey;
|
|
41
|
+
})[];
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=standard-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standard-schema.d.ts","sourceRoot":"","sources":["../src/standard-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK;IAC/D,QAAQ,CAAC,WAAW,EAAE;QACpB,0DAA0D;QAC1D,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACpB,6DAA6D;QAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,CACjB,KAAK,EAAE,OAAO,KACX,oBAAoB,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1E,QAAQ,CAAC,KAAK,CAAC,EACb;YAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;KAClE,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,CAAC,MAAM,IACnC;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG;QAAE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;KAAE,CAAC,EAAE,CAAC;CAC1E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standard-schema.js","sourceRoot":"","sources":["../src/standard-schema.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openbrf/plugin-sdk",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "The public plugin contract for Open BRF: manifest schema, permission set and host service interfaces.",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@nestjs/common": "^12.2.0",
|
|
15
|
+
"typescript": "^7.0.2",
|
|
16
|
+
"vite-plus": "1.3.0",
|
|
17
|
+
"zod": "^4.6.7"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src",
|
|
24
|
+
"!src/**/*.spec.ts",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"LICENSE-EXCEPTION.md"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"provenance": true,
|
|
31
|
+
"registry": "https://registry.npmjs.org/"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/openbrf/openbrf.git",
|
|
36
|
+
"directory": "packages/plugin-sdk"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@nestjs/common": "^12.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"@nestjs/common": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"zod": "^4.6.7"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/openbrf/openbrf/issues"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc -p tsconfig.json",
|
|
54
|
+
"lint": "vp lint .",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p type-tests/tsconfig.dom.json && tsc -p type-tests/tsconfig.language.json",
|
|
56
|
+
"test": "vp test run"
|
|
57
|
+
}
|
|
6
58
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `AbortSignal` the host hands a plugin, and takes from one.
|
|
3
|
+
*
|
|
4
|
+
* The host passes a real `AbortSignal`. This package compiles against the
|
|
5
|
+
* language alone and cannot name the platform's type, so it is looked up in
|
|
6
|
+
* the plugin's own program: where that has an `AbortSignal`, from the DOM
|
|
7
|
+
* library or from Node's types, this is it, and the signal can be passed on to
|
|
8
|
+
* `fetch` and to anything else that takes one. Where it has none, this is the
|
|
9
|
+
* part of one a plugin can use without them.
|
|
10
|
+
*/
|
|
11
|
+
export type PluginAbortSignal = typeof globalThis extends {
|
|
12
|
+
readonly AbortSignal: { readonly prototype: infer Signal };
|
|
13
|
+
}
|
|
14
|
+
? Signal
|
|
15
|
+
: PluginAbortSignalShape;
|
|
16
|
+
|
|
17
|
+
/** What every `AbortSignal` has, for a program without the platform's types. */
|
|
18
|
+
interface PluginAbortSignalShape {
|
|
19
|
+
readonly aborted: boolean;
|
|
20
|
+
addEventListener(type: "abort", listener: () => void): void;
|
|
21
|
+
removeEventListener(type: "abort", listener: () => void): void;
|
|
22
|
+
}
|