@zudojs/plugins 0.1.0 → 1.0.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 +21 -0
- package/README.md +130 -29
- package/dist/index.d.ts +34 -13
- package/dist/index.js +28 -10
- package/dist/pluginDependencies/dependencyResolver.core.d.ts +47 -6
- package/dist/pluginDependencies/dependencyResolver.core.js +108 -46
- package/dist/pluginDependencies/index.d.ts +6 -2
- package/dist/pluginDependencies/index.js +4 -1
- package/dist/pluginDependencies/versionCheck.core.d.ts +67 -0
- package/dist/pluginDependencies/versionCheck.core.js +167 -0
- package/dist/pluginDiagnostics/pluginDiagnostic.core.d.ts +9 -0
- package/dist/pluginDiagnostics/pluginDiagnostic.core.js +16 -12
- package/dist/pluginIntegration/index.d.ts +2 -1
- package/dist/pluginIntegration/index.js +1 -1
- package/dist/pluginIntegration/pluginContext.core.d.ts +35 -5
- package/dist/pluginIntegration/pluginContext.core.js +28 -5
- package/dist/pluginLifecycle/pluginLifecycle.core.d.ts +42 -0
- package/dist/pluginLifecycle/pluginLifecycle.core.js +132 -70
- package/dist/pluginManager/pluginManager.core.d.ts +121 -3
- package/dist/pluginManager/pluginManager.core.js +271 -47
- package/dist/pluginRegistry/pluginRegistry.core.d.ts +15 -6
- package/dist/pluginRegistry/pluginRegistry.core.js +6 -1
- package/dist/pluginTypes/index.d.ts +1 -1
- package/dist/pluginTypes/pluginContext.type.d.ts +11 -3
- package/dist/pluginTypes/pluginState.type.js +6 -3
- package/package.json +22 -15
- package/dist/.tsbuildinfo +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/pluginDependencies/dependencyResolver.core.d.ts.map +0 -1
- package/dist/pluginDependencies/dependencyResolver.core.js.map +0 -1
- package/dist/pluginDependencies/index.d.ts.map +0 -1
- package/dist/pluginDependencies/index.js.map +0 -1
- package/dist/pluginDiagnostics/index.d.ts.map +0 -1
- package/dist/pluginDiagnostics/index.js.map +0 -1
- package/dist/pluginDiagnostics/pluginDiagnostic.core.d.ts.map +0 -1
- package/dist/pluginDiagnostics/pluginDiagnostic.core.js.map +0 -1
- package/dist/pluginEvents/index.d.ts.map +0 -1
- package/dist/pluginEvents/index.js.map +0 -1
- package/dist/pluginEvents/pluginEvent.core.d.ts.map +0 -1
- package/dist/pluginEvents/pluginEvent.core.js.map +0 -1
- package/dist/pluginIntegration/index.d.ts.map +0 -1
- package/dist/pluginIntegration/index.js.map +0 -1
- package/dist/pluginIntegration/pluginContext.core.d.ts.map +0 -1
- package/dist/pluginIntegration/pluginContext.core.js.map +0 -1
- package/dist/pluginLifecycle/pluginLifecycle.core.d.ts.map +0 -1
- package/dist/pluginLifecycle/pluginLifecycle.core.js.map +0 -1
- package/dist/pluginManager/pluginManager.core.d.ts.map +0 -1
- package/dist/pluginManager/pluginManager.core.js.map +0 -1
- package/dist/pluginRegistry/index.d.ts.map +0 -1
- package/dist/pluginRegistry/index.js.map +0 -1
- package/dist/pluginRegistry/pluginRegistry.core.d.ts.map +0 -1
- package/dist/pluginRegistry/pluginRegistry.core.js.map +0 -1
- package/dist/pluginTypes/index.d.ts.map +0 -1
- package/dist/pluginTypes/index.js.map +0 -1
- package/dist/pluginTypes/plugin.type.d.ts.map +0 -1
- package/dist/pluginTypes/plugin.type.js.map +0 -1
- package/dist/pluginTypes/pluginContext.type.d.ts.map +0 -1
- package/dist/pluginTypes/pluginContext.type.js.map +0 -1
- package/dist/pluginTypes/pluginDependency.type.d.ts.map +0 -1
- package/dist/pluginTypes/pluginDependency.type.js.map +0 -1
- package/dist/pluginTypes/pluginMetadata.type.d.ts.map +0 -1
- package/dist/pluginTypes/pluginMetadata.type.js.map +0 -1
- package/dist/pluginTypes/pluginState.type.d.ts.map +0 -1
- package/dist/pluginTypes/pluginState.type.js.map +0 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Plugin } from "../pluginTypes/plugin.type.js";
|
|
2
|
+
import { PluginDependencyError } from "@zudojs/errors";
|
|
3
|
+
/**
|
|
4
|
+
* A dependency that is registered but whose version does not satisfy the
|
|
5
|
+
* declared constraint.
|
|
6
|
+
*
|
|
7
|
+
* `PluginDependencyError` hardcodes the message "...which is not
|
|
8
|
+
* registered" and replaces any `metadata` it is handed, so the reason a
|
|
9
|
+
* version check failed could never reach the operator: every version
|
|
10
|
+
* mismatch was reported as a missing plugin. This subclass keeps the
|
|
11
|
+
* type — existing `instanceof PluginDependencyError` handlers still
|
|
12
|
+
* match — and replaces the message with one that names the requirement,
|
|
13
|
+
* what is actually registered, and what to do about it.
|
|
14
|
+
*/
|
|
15
|
+
export declare class PluginDependencyVersionError extends PluginDependencyError {
|
|
16
|
+
/** The plugin that declared the constraint. */
|
|
17
|
+
readonly requiredBy: string;
|
|
18
|
+
/** The dependency whose version was checked. */
|
|
19
|
+
readonly dependencyName: string;
|
|
20
|
+
/** The declared range, e.g. `^2.0.0`. */
|
|
21
|
+
readonly required: string;
|
|
22
|
+
/** The version actually registered, if it declared one. */
|
|
23
|
+
readonly actual: string | undefined;
|
|
24
|
+
constructor(requiredBy: string, dependencyName: string, required: string, actual: string | undefined, reason: string);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A parsed semantic version.
|
|
28
|
+
*
|
|
29
|
+
* Exported because {@link parseVersion} returns one and
|
|
30
|
+
* {@link compareVersions} accepts two: a consumer that cannot name the
|
|
31
|
+
* type cannot use either function from TypeScript.
|
|
32
|
+
*/
|
|
33
|
+
export interface SemVer {
|
|
34
|
+
readonly major: number;
|
|
35
|
+
readonly minor: number;
|
|
36
|
+
readonly patch: number;
|
|
37
|
+
readonly prerelease?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parses a semantic version, returning `undefined` if it is not one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseVersion(version: string): SemVer | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Compares two versions. Prerelease versions sort below their release.
|
|
45
|
+
*/
|
|
46
|
+
export declare function compareVersions(a: SemVer, b: SemVer): number;
|
|
47
|
+
/**
|
|
48
|
+
* Tests a version against a range.
|
|
49
|
+
*
|
|
50
|
+
* Supports the range forms a plugin manifest realistically uses:
|
|
51
|
+
* exact (`1.2.3`), caret (`^1.2.3`), tilde (`~1.2.3`), comparators
|
|
52
|
+
* (`>=1.2.3`, `>`, `<=`, `<`), and `*` for any version. An
|
|
53
|
+
* unrecognisable range is reported by the caller rather than silently
|
|
54
|
+
* passing.
|
|
55
|
+
*/
|
|
56
|
+
export declare function satisfiesVersion(version: string, range: string): boolean | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Verifies every declared dependency version constraint.
|
|
59
|
+
*
|
|
60
|
+
* A dependency that declares no `version` is unconstrained. A plugin
|
|
61
|
+
* whose own `metadata.version` is missing cannot be checked, so a
|
|
62
|
+
* constraint against it is reported rather than quietly passing.
|
|
63
|
+
*
|
|
64
|
+
* @throws {PluginDependencyError} on the first unsatisfied constraint.
|
|
65
|
+
*/
|
|
66
|
+
export declare function assertDependencyVersions(plugins: ReadonlyMap<string, Plugin>): void;
|
|
67
|
+
//# sourceMappingURL=versionCheck.core.d.ts.map
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { PluginDependencyError } from "@zudojs/errors";
|
|
2
|
+
/**
|
|
3
|
+
* A dependency that is registered but whose version does not satisfy the
|
|
4
|
+
* declared constraint.
|
|
5
|
+
*
|
|
6
|
+
* `PluginDependencyError` hardcodes the message "...which is not
|
|
7
|
+
* registered" and replaces any `metadata` it is handed, so the reason a
|
|
8
|
+
* version check failed could never reach the operator: every version
|
|
9
|
+
* mismatch was reported as a missing plugin. This subclass keeps the
|
|
10
|
+
* type — existing `instanceof PluginDependencyError` handlers still
|
|
11
|
+
* match — and replaces the message with one that names the requirement,
|
|
12
|
+
* what is actually registered, and what to do about it.
|
|
13
|
+
*/
|
|
14
|
+
export class PluginDependencyVersionError extends PluginDependencyError {
|
|
15
|
+
/** The plugin that declared the constraint. */
|
|
16
|
+
requiredBy;
|
|
17
|
+
/** The dependency whose version was checked. */
|
|
18
|
+
dependencyName;
|
|
19
|
+
/** The declared range, e.g. `^2.0.0`. */
|
|
20
|
+
required;
|
|
21
|
+
/** The version actually registered, if it declared one. */
|
|
22
|
+
actual;
|
|
23
|
+
constructor(requiredBy, dependencyName, required, actual, reason) {
|
|
24
|
+
super(requiredBy, dependencyName);
|
|
25
|
+
this.name = "PluginDependencyVersionError";
|
|
26
|
+
this.message = reason;
|
|
27
|
+
this.requiredBy = requiredBy;
|
|
28
|
+
this.dependencyName = dependencyName;
|
|
29
|
+
this.required = required;
|
|
30
|
+
this.actual = actual;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
34
|
+
const RANGE_PATTERN = /^\s*(\^|~|>=|<=|>|<|=)?\s*(.+?)\s*$/;
|
|
35
|
+
/**
|
|
36
|
+
* Parses a semantic version, returning `undefined` if it is not one.
|
|
37
|
+
*/
|
|
38
|
+
export function parseVersion(version) {
|
|
39
|
+
const match = SEMVER_PATTERN.exec(version.trim());
|
|
40
|
+
if (!match) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
major: Number(match[1]),
|
|
45
|
+
minor: Number(match[2]),
|
|
46
|
+
patch: Number(match[3]),
|
|
47
|
+
prerelease: match[4],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Compares two versions. Prerelease versions sort below their release.
|
|
52
|
+
*/
|
|
53
|
+
export function compareVersions(a, b) {
|
|
54
|
+
if (a.major !== b.major)
|
|
55
|
+
return a.major - b.major;
|
|
56
|
+
if (a.minor !== b.minor)
|
|
57
|
+
return a.minor - b.minor;
|
|
58
|
+
if (a.patch !== b.patch)
|
|
59
|
+
return a.patch - b.patch;
|
|
60
|
+
if (a.prerelease === b.prerelease)
|
|
61
|
+
return 0;
|
|
62
|
+
if (a.prerelease === undefined)
|
|
63
|
+
return 1;
|
|
64
|
+
if (b.prerelease === undefined)
|
|
65
|
+
return -1;
|
|
66
|
+
return a.prerelease < b.prerelease ? -1 : 1;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Tests a version against a range.
|
|
70
|
+
*
|
|
71
|
+
* Supports the range forms a plugin manifest realistically uses:
|
|
72
|
+
* exact (`1.2.3`), caret (`^1.2.3`), tilde (`~1.2.3`), comparators
|
|
73
|
+
* (`>=1.2.3`, `>`, `<=`, `<`), and `*` for any version. An
|
|
74
|
+
* unrecognisable range is reported by the caller rather than silently
|
|
75
|
+
* passing.
|
|
76
|
+
*/
|
|
77
|
+
export function satisfiesVersion(version, range) {
|
|
78
|
+
const trimmedRange = range.trim();
|
|
79
|
+
if (trimmedRange === "*" || trimmedRange === "" || trimmedRange === "x") {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
const actual = parseVersion(version);
|
|
83
|
+
if (!actual) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const match = RANGE_PATTERN.exec(trimmedRange);
|
|
87
|
+
if (!match) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const operator = match[1] ?? "=";
|
|
91
|
+
const expected = parseVersion(match[2] ?? "");
|
|
92
|
+
if (!expected) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const comparison = compareVersions(actual, expected);
|
|
96
|
+
switch (operator) {
|
|
97
|
+
case "=":
|
|
98
|
+
return comparison === 0;
|
|
99
|
+
case ">":
|
|
100
|
+
return comparison > 0;
|
|
101
|
+
case ">=":
|
|
102
|
+
return comparison >= 0;
|
|
103
|
+
case "<":
|
|
104
|
+
return comparison < 0;
|
|
105
|
+
case "<=":
|
|
106
|
+
return comparison <= 0;
|
|
107
|
+
case "^": {
|
|
108
|
+
if (comparison < 0)
|
|
109
|
+
return false;
|
|
110
|
+
// Caret allows changes that do not modify the left-most non-zero
|
|
111
|
+
// element, matching npm's semantics for 0.x versions.
|
|
112
|
+
if (expected.major > 0)
|
|
113
|
+
return actual.major === expected.major;
|
|
114
|
+
if (expected.minor > 0)
|
|
115
|
+
return actual.major === 0 && actual.minor === expected.minor;
|
|
116
|
+
return (actual.major === 0 &&
|
|
117
|
+
actual.minor === 0 &&
|
|
118
|
+
actual.patch === expected.patch);
|
|
119
|
+
}
|
|
120
|
+
case "~": {
|
|
121
|
+
if (comparison < 0)
|
|
122
|
+
return false;
|
|
123
|
+
return actual.major === expected.major && actual.minor === expected.minor;
|
|
124
|
+
}
|
|
125
|
+
default:
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Verifies every declared dependency version constraint.
|
|
131
|
+
*
|
|
132
|
+
* A dependency that declares no `version` is unconstrained. A plugin
|
|
133
|
+
* whose own `metadata.version` is missing cannot be checked, so a
|
|
134
|
+
* constraint against it is reported rather than quietly passing.
|
|
135
|
+
*
|
|
136
|
+
* @throws {PluginDependencyError} on the first unsatisfied constraint.
|
|
137
|
+
*/
|
|
138
|
+
export function assertDependencyVersions(plugins) {
|
|
139
|
+
for (const [name, plugin] of plugins) {
|
|
140
|
+
const declared = [
|
|
141
|
+
...(plugin.dependencies ?? []),
|
|
142
|
+
...(plugin.optionalDependencies ?? []),
|
|
143
|
+
];
|
|
144
|
+
for (const dependency of declared) {
|
|
145
|
+
if (dependency.version === undefined) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const target = plugins.get(dependency.name);
|
|
149
|
+
if (!target) {
|
|
150
|
+
// Absent optional dependencies are not a version problem.
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const actual = target.metadata.version;
|
|
154
|
+
if (actual === undefined) {
|
|
155
|
+
throw new PluginDependencyVersionError(name, dependency.name, dependency.version, undefined, `Plugin "${name}" requires "${dependency.name}@${dependency.version}", but "${dependency.name}" declares no version. Add a "version" to that plugin's metadata, or drop the constraint from "${name}".`);
|
|
156
|
+
}
|
|
157
|
+
const satisfied = satisfiesVersion(actual, dependency.version);
|
|
158
|
+
if (satisfied === undefined) {
|
|
159
|
+
throw new PluginDependencyVersionError(name, dependency.name, dependency.version, actual, `Plugin "${name}" declares an unsupported version range "${dependency.version}" for "${dependency.name}". Supported forms are an exact version (1.2.3), a caret or tilde range (^1.2.3, ~1.2.3), a comparator (>=1.2.3, >, <=, <) or "*".`);
|
|
160
|
+
}
|
|
161
|
+
if (!satisfied) {
|
|
162
|
+
throw new PluginDependencyVersionError(name, dependency.name, dependency.version, actual, `Plugin "${name}" requires "${dependency.name}@${dependency.version}", but version ${actual} is registered. Register a "${dependency.name}" that satisfies ${dependency.version}, relax the constraint on "${name}", or construct the manager with { checkVersions: false }.`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=versionCheck.core.js.map
|
|
@@ -18,6 +18,8 @@ export interface PluginHealth {
|
|
|
18
18
|
export interface PluginDiagnostic {
|
|
19
19
|
readonly plugin: PluginMetadata;
|
|
20
20
|
readonly state: PluginState;
|
|
21
|
+
/** Whether the plugin failed at any point in its lifecycle. */
|
|
22
|
+
readonly failed: boolean;
|
|
21
23
|
readonly health: PluginHealth;
|
|
22
24
|
readonly dependencies: readonly string[];
|
|
23
25
|
readonly optionalDependencies: readonly string[];
|
|
@@ -51,5 +53,12 @@ export declare function createUnhealthyHealth(details?: unknown): PluginHealth;
|
|
|
51
53
|
export declare function buildDiagnosticReport(plugins: Array<{
|
|
52
54
|
readonly plugin: Plugin;
|
|
53
55
|
readonly state: PluginState;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the plugin failed at any point. A plugin that failed and
|
|
58
|
+
* was then disposed during rollback is still a failure worth
|
|
59
|
+
* reporting, which its current state alone would not show.
|
|
60
|
+
*/
|
|
61
|
+
readonly failed?: boolean;
|
|
62
|
+
readonly error?: unknown;
|
|
54
63
|
}>): PluginDiagnosticReport;
|
|
55
64
|
//# sourceMappingURL=pluginDiagnostic.core.d.ts.map
|
|
@@ -20,21 +20,25 @@ export function createUnhealthyHealth(details) {
|
|
|
20
20
|
* Builds a diagnostic report from registered plugins.
|
|
21
21
|
*/
|
|
22
22
|
export function buildDiagnosticReport(plugins) {
|
|
23
|
-
const pluginDiagnostics = plugins.map(({ plugin, state }) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
:
|
|
29
|
-
|
|
30
|
-
:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
const pluginDiagnostics = plugins.map(({ plugin, state, failed, error }) => {
|
|
24
|
+
const hasFailed = failed === true || state === "failed";
|
|
25
|
+
return {
|
|
26
|
+
plugin: plugin.metadata,
|
|
27
|
+
state,
|
|
28
|
+
failed: hasFailed,
|
|
29
|
+
health: hasFailed
|
|
30
|
+
? createUnhealthyHealth(error instanceof Error ? error.message : error)
|
|
31
|
+
: state === "started"
|
|
32
|
+
? createHealthyHealth()
|
|
33
|
+
: createDegradedHealth(),
|
|
34
|
+
dependencies: plugin.dependencies?.map((d) => d.name) ?? [],
|
|
35
|
+
optionalDependencies: plugin.optionalDependencies?.map((d) => d.name) ?? [],
|
|
36
|
+
};
|
|
37
|
+
});
|
|
34
38
|
const healthy = pluginDiagnostics.filter((d) => d.health.status === "healthy").length;
|
|
35
39
|
const degraded = pluginDiagnostics.filter((d) => d.health.status === "degraded").length;
|
|
36
40
|
const unhealthy = pluginDiagnostics.filter((d) => d.health.status === "unhealthy").length;
|
|
37
|
-
const failed = pluginDiagnostics.filter((d) => d.
|
|
41
|
+
const failed = pluginDiagnostics.filter((d) => d.failed).length;
|
|
38
42
|
return {
|
|
39
43
|
plugins: Object.freeze(pluginDiagnostics),
|
|
40
44
|
total: pluginDiagnostics.length,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Plugin context factory and integration helpers.
|
|
5
5
|
*/
|
|
6
|
-
export {
|
|
6
|
+
export type { CreatePluginContextOptions, OwnedPluginContext, } from "./pluginContext.core.js";
|
|
7
|
+
export { createPluginContext, createOwnedPluginContext, } from "./pluginContext.core.js";
|
|
7
8
|
export type { PluginContext } from "../pluginTypes/pluginContext.type.js";
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { PluginMetadata } from "../pluginTypes/pluginMetadata.type.js";
|
|
2
|
-
import type { PluginContext } from "../pluginTypes/pluginContext.type.js";
|
|
3
|
-
import type { PluginContainer } from "../pluginTypes/pluginContext.type.js";
|
|
4
|
-
import type { PluginConfig } from "../pluginTypes/pluginContext.type.js";
|
|
5
|
-
import type { PluginLogger } from "../pluginTypes/pluginContext.type.js";
|
|
6
|
-
import type { PluginEvents } from "../pluginTypes/pluginContext.type.js";
|
|
2
|
+
import type { PluginContext, PluginContainer, PluginConfig, PluginLogger, PluginEvents, PluginDisposable } from "../pluginTypes/pluginContext.type.js";
|
|
7
3
|
/**
|
|
8
4
|
* Options for creating a plugin context.
|
|
9
5
|
*/
|
|
@@ -12,9 +8,43 @@ export interface CreatePluginContextOptions {
|
|
|
12
8
|
readonly config?: PluginConfig;
|
|
13
9
|
readonly logger?: PluginLogger;
|
|
14
10
|
readonly events?: PluginEvents;
|
|
11
|
+
/**
|
|
12
|
+
* Collection that receives everything the plugin registers for
|
|
13
|
+
* cleanup. The plugin manager passes the registered plugin's own
|
|
14
|
+
* array, which is what the lifecycle disposes; without it, anything
|
|
15
|
+
* registered here would be collected and then never run.
|
|
16
|
+
*/
|
|
17
|
+
readonly disposables?: PluginDisposable[];
|
|
18
|
+
/**
|
|
19
|
+
* Controller whose signal the plugin observes. The manager supplies
|
|
20
|
+
* one it aborts on shutdown so plugins are told to stop.
|
|
21
|
+
*/
|
|
22
|
+
readonly abortController?: AbortController;
|
|
15
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* A plugin context together with the handles needed to tear it down.
|
|
26
|
+
*/
|
|
27
|
+
export interface OwnedPluginContext {
|
|
28
|
+
readonly context: PluginContext;
|
|
29
|
+
/** Everything the plugin registered for cleanup, in registration order. */
|
|
30
|
+
readonly disposables: PluginDisposable[];
|
|
31
|
+
/** Aborts `context.signal`. */
|
|
32
|
+
readonly abort: (reason?: unknown) => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a plugin context and returns it with its teardown handles.
|
|
36
|
+
*
|
|
37
|
+
* The disposables array and the abort function are the two capabilities
|
|
38
|
+
* a context cannot expose to its own consumer but its owner needs, so
|
|
39
|
+
* they are returned alongside rather than trapped in the closure.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createOwnedPluginContext(plugin: PluginMetadata, options?: CreatePluginContextOptions): OwnedPluginContext;
|
|
16
42
|
/**
|
|
17
43
|
* Creates a plugin context for testing and basic usage.
|
|
44
|
+
*
|
|
45
|
+
* Pass `options.disposables` to reach what the plugin registers;
|
|
46
|
+
* otherwise use {@link createOwnedPluginContext}, which returns the
|
|
47
|
+
* collection and the abort handle with the context.
|
|
18
48
|
*/
|
|
19
49
|
export declare function createPluginContext(plugin: PluginMetadata, options?: CreatePluginContextOptions): PluginContext;
|
|
20
50
|
//# sourceMappingURL=pluginContext.core.d.ts.map
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Creates a plugin context
|
|
2
|
+
* Creates a plugin context and returns it with its teardown handles.
|
|
3
|
+
*
|
|
4
|
+
* The disposables array and the abort function are the two capabilities
|
|
5
|
+
* a context cannot expose to its own consumer but its owner needs, so
|
|
6
|
+
* they are returned alongside rather than trapped in the closure.
|
|
3
7
|
*/
|
|
4
|
-
export function
|
|
5
|
-
const abortController = new AbortController();
|
|
6
|
-
const disposables = [];
|
|
7
|
-
|
|
8
|
+
export function createOwnedPluginContext(plugin, options = {}) {
|
|
9
|
+
const abortController = options.abortController ?? new AbortController();
|
|
10
|
+
const disposables = options.disposables ?? [];
|
|
11
|
+
const context = {
|
|
8
12
|
plugin,
|
|
9
13
|
signal: abortController.signal,
|
|
10
14
|
container: options.container,
|
|
@@ -18,5 +22,24 @@ export function createPluginContext(plugin, options = {}) {
|
|
|
18
22
|
disposables.push(disposable);
|
|
19
23
|
},
|
|
20
24
|
};
|
|
25
|
+
return {
|
|
26
|
+
context,
|
|
27
|
+
disposables,
|
|
28
|
+
abort: (reason) => {
|
|
29
|
+
if (!abortController.signal.aborted) {
|
|
30
|
+
abortController.abort(reason);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a plugin context for testing and basic usage.
|
|
37
|
+
*
|
|
38
|
+
* Pass `options.disposables` to reach what the plugin registers;
|
|
39
|
+
* otherwise use {@link createOwnedPluginContext}, which returns the
|
|
40
|
+
* collection and the abort handle with the context.
|
|
41
|
+
*/
|
|
42
|
+
export function createPluginContext(plugin, options = {}) {
|
|
43
|
+
return createOwnedPluginContext(plugin, options).context;
|
|
21
44
|
}
|
|
22
45
|
//# sourceMappingURL=pluginContext.core.js.map
|
|
@@ -1,15 +1,57 @@
|
|
|
1
1
|
import type { Plugin } from "../pluginTypes/plugin.type.js";
|
|
2
2
|
import type { PluginContext } from "../pluginTypes/pluginContext.type.js";
|
|
3
3
|
import type { RegisteredPlugin } from "../pluginRegistry/pluginRegistry.core.js";
|
|
4
|
+
/**
|
|
5
|
+
* Options controlling lifecycle execution.
|
|
6
|
+
*/
|
|
7
|
+
export interface LifecycleControllerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Maximum time a single lifecycle hook may run, in milliseconds.
|
|
10
|
+
*
|
|
11
|
+
* Defaults to `0` (unbounded), preserving existing behaviour. Set a
|
|
12
|
+
* value to bound boot and shutdown: without one, a plugin whose
|
|
13
|
+
* `start()` never settles hangs the whole application with no
|
|
14
|
+
* diagnostic.
|
|
15
|
+
*/
|
|
16
|
+
readonly hookTimeout?: number;
|
|
17
|
+
}
|
|
4
18
|
/**
|
|
5
19
|
* Executes plugin lifecycle phases with state management and event emission.
|
|
6
20
|
*/
|
|
7
21
|
export declare class LifecycleController {
|
|
22
|
+
private readonly options;
|
|
23
|
+
constructor(options?: LifecycleControllerOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Runs a hook under the configured timeout, clearing the timer either
|
|
26
|
+
* way so a completed hook never leaves one armed.
|
|
27
|
+
*/
|
|
28
|
+
private runHook;
|
|
8
29
|
install<TPlugin extends Plugin>(registered: RegisteredPlugin<TPlugin>, context: PluginContext): Promise<void>;
|
|
9
30
|
initialize<TPlugin extends Plugin>(registered: RegisteredPlugin<TPlugin>, context: PluginContext): Promise<void>;
|
|
10
31
|
start<TPlugin extends Plugin>(registered: RegisteredPlugin<TPlugin>, context: PluginContext): Promise<void>;
|
|
11
32
|
stop<TPlugin extends Plugin>(registered: RegisteredPlugin<TPlugin>, context: PluginContext): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Runs one lifecycle phase, moving through its transient state.
|
|
35
|
+
*
|
|
36
|
+
* On failure the plugin moves to `failed` through the state machine
|
|
37
|
+
* rather than around it, so the recorded state is always one the
|
|
38
|
+
* machine actually permits.
|
|
39
|
+
*/
|
|
40
|
+
private runPhase;
|
|
41
|
+
/**
|
|
42
|
+
* Disposes a plugin and everything it registered for cleanup.
|
|
43
|
+
*
|
|
44
|
+
* Disposables run in reverse registration order — the mirror of how
|
|
45
|
+
* they were acquired — and the list is emptied so a second dispose
|
|
46
|
+
* cannot run them again. Every failure is collected; the plugin still
|
|
47
|
+
* reaches a terminal state so it cannot be disposed twice.
|
|
48
|
+
*/
|
|
12
49
|
dispose<TPlugin extends Plugin>(registered: RegisteredPlugin<TPlugin>, context: PluginContext): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Moves a plugin to `failed`, via `stopping` when required.
|
|
52
|
+
*/
|
|
53
|
+
private transitionToFailed;
|
|
54
|
+
private canTransition;
|
|
13
55
|
private ensureTransition;
|
|
14
56
|
}
|
|
15
57
|
//# sourceMappingURL=pluginLifecycle.core.d.ts.map
|