webloom-framework 0.1.0 → 0.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/README.md +83 -36
- package/dist/{chunk-KGNF36DZ.js → chunk-URY3E6UH.js} +333 -267
- package/dist/chunk-URY3E6UH.js.map +1 -0
- package/dist/{createPluginHost-CcW9sPNs.d.ts → createPluginHost-ChJNBTsX.d.ts} +76 -77
- package/dist/index.d.ts +300 -10
- package/dist/index.js +1556 -146
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/{resourceRegistry-BAnqKcp7.d.ts → resourceRegistry-MNM1c7od.d.ts} +1 -1
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +2 -2
- package/docs/api.md +156 -14
- package/docs/migration-baseline.md +20 -12
- package/docs/proposals/browser-runtime-v1/implementation-plan.md +527 -0
- package/docs/proposals/browser-runtime-v1/requirements.md +349 -0
- package/docs/proposals/browser-runtime-v1/verification.md +49 -0
- package/package.json +4 -1
- package/dist/chunk-KGNF36DZ.js.map +0 -1
|
@@ -136,11 +136,8 @@ var PluginGraphValidationError = class extends Error {
|
|
|
136
136
|
function unique(values) {
|
|
137
137
|
return [...new Set(values)];
|
|
138
138
|
}
|
|
139
|
-
function
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
function isPluginLifetime(value) {
|
|
143
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
139
|
+
function isRuntimeKind(value) {
|
|
140
|
+
return value === "window-main" || value === "shared-worker";
|
|
144
141
|
}
|
|
145
142
|
function isRecord(value) {
|
|
146
143
|
return typeof value === "object" && value !== null;
|
|
@@ -228,11 +225,11 @@ function validateRuntimeUnitDependencyContracts(manifests) {
|
|
|
228
225
|
if (typeof dependency.contractVersion !== "string" || dependency.contractVersion.trim() === "") {
|
|
229
226
|
errors.push("contractVersion\uFF08\u5951\u7EA6\u7248\u672C\uFF09\u4E0D\u80FD\u4E3A\u7A7A");
|
|
230
227
|
}
|
|
231
|
-
if (!
|
|
232
|
-
errors.push("
|
|
228
|
+
if (!isRuntimeKind(dependency.sourceRuntime)) {
|
|
229
|
+
errors.push("sourceRuntime\uFF08\u63D0\u4F9B\u8005 Runtime\uFF09\u65E0\u6548");
|
|
233
230
|
}
|
|
234
|
-
if (!
|
|
235
|
-
errors.push("
|
|
231
|
+
if (unitValue.runtime !== void 0 && !isRuntimeKind(unitValue.runtime)) {
|
|
232
|
+
errors.push("runtime\uFF08\u76EE\u6807 Runtime\uFF09\u65E0\u6548");
|
|
236
233
|
}
|
|
237
234
|
if (dependency.reason !== void 0 && typeof dependency.reason !== "string") {
|
|
238
235
|
errors.push("reason\uFF08\u4F9D\u8D56\u8BF4\u660E\uFF09\u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
@@ -250,15 +247,34 @@ function validateRuntimeUnitDependencyContracts(manifests) {
|
|
|
250
247
|
});
|
|
251
248
|
});
|
|
252
249
|
}
|
|
250
|
+
for (const manifest of manifests) {
|
|
251
|
+
for (const unit of manifest.units ?? []) {
|
|
252
|
+
if (!isRuntimeKind(unit.runtime)) {
|
|
253
|
+
diagnostics.push({
|
|
254
|
+
code: "plugin.runtime_invalid",
|
|
255
|
+
ids: [manifest.id, unit.id],
|
|
256
|
+
message: `\u63D2\u4EF6 "${manifest.id}" \u7684\u8FD0\u884C\u5355\u5143 "${unit.id}" \u58F0\u660E\u4E86\u4E0D\u652F\u6301\u7684 Runtime\uFF1Bv1 \u4EC5\u652F\u6301 window-main/shared-worker`
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
253
261
|
return diagnostics;
|
|
254
262
|
}
|
|
255
|
-
function
|
|
263
|
+
function unitRuntime(unit) {
|
|
264
|
+
return unit.runtime;
|
|
265
|
+
}
|
|
266
|
+
function isRuntimeUnit(unit) {
|
|
267
|
+
return typeof unit.id === "string" && unit.id.length > 0 && isRuntimeKind(unit.runtime);
|
|
268
|
+
}
|
|
269
|
+
function selectedRuntimeUnits(manifest, runtime) {
|
|
256
270
|
const units = manifest.units ?? [];
|
|
257
271
|
if (units.length === 0) return [];
|
|
258
|
-
if (
|
|
259
|
-
|
|
272
|
+
if (runtime !== void 0) return units.filter(
|
|
273
|
+
(unit) => isRuntimeUnit(unit) && unit.runtime === runtime
|
|
274
|
+
);
|
|
275
|
+
return units.length === 1 && isRuntimeUnit(units[0]) ? [units[0]] : [];
|
|
260
276
|
}
|
|
261
|
-
function dependenciesOfManifest(manifest,
|
|
277
|
+
function dependenciesOfManifest(manifest, runtime) {
|
|
262
278
|
const byCapability = /* @__PURE__ */ new Map();
|
|
263
279
|
const add = (dependency) => {
|
|
264
280
|
const previous = byCapability.get(dependency.capability);
|
|
@@ -267,8 +283,8 @@ function dependenciesOfManifest(manifest, execution) {
|
|
|
267
283
|
}
|
|
268
284
|
};
|
|
269
285
|
const units = manifest.units ?? [];
|
|
270
|
-
if (units.length > 1 &&
|
|
271
|
-
const selectedUnits = selectedRuntimeUnits(manifest,
|
|
286
|
+
if (units.length > 1 && runtime === void 0) return [];
|
|
287
|
+
const selectedUnits = selectedRuntimeUnits(manifest, runtime);
|
|
272
288
|
if (units.length > 0 && selectedUnits.length === 0) return [];
|
|
273
289
|
if (units.length === 0) {
|
|
274
290
|
for (const dependency of manifest.dependencies ?? []) add(dependency);
|
|
@@ -279,12 +295,12 @@ function dependenciesOfManifest(manifest, execution) {
|
|
|
279
295
|
}
|
|
280
296
|
return [...byCapability.values()];
|
|
281
297
|
}
|
|
282
|
-
function providesOfManifest(manifest,
|
|
298
|
+
function providesOfManifest(manifest, runtime) {
|
|
283
299
|
const units = manifest.units ?? [];
|
|
284
|
-
if (units.length > 1 &&
|
|
300
|
+
if (units.length > 1 && runtime === void 0) return [];
|
|
285
301
|
return unique([
|
|
286
302
|
...units.length === 0 ? manifest.provides ?? [] : [],
|
|
287
|
-
...selectedRuntimeUnits(manifest,
|
|
303
|
+
...selectedRuntimeUnits(manifest, runtime).flatMap((unit) => unit.provides ?? [])
|
|
288
304
|
]);
|
|
289
305
|
}
|
|
290
306
|
function runtimeUnitProviders(manifests, capability) {
|
|
@@ -292,11 +308,11 @@ function runtimeUnitProviders(manifests, capability) {
|
|
|
292
308
|
for (const manifest of manifests) {
|
|
293
309
|
for (const unit of manifest.units ?? []) {
|
|
294
310
|
if (!unit.provides?.includes(capability)) continue;
|
|
311
|
+
if (!isRuntimeUnit(unit)) continue;
|
|
295
312
|
providers.push({
|
|
296
313
|
pluginId: manifest.id,
|
|
297
314
|
unitId: unit.id,
|
|
298
|
-
|
|
299
|
-
lifetime: unit.lifetime,
|
|
315
|
+
runtime: unit.runtime,
|
|
300
316
|
contractVersion: unit.providedContracts?.[capability]
|
|
301
317
|
});
|
|
302
318
|
}
|
|
@@ -305,12 +321,12 @@ function runtimeUnitProviders(manifests, capability) {
|
|
|
305
321
|
}
|
|
306
322
|
function matchingRuntimeUnitProviders(manifests, dependency) {
|
|
307
323
|
return runtimeUnitProviders(manifests, dependency.capability).filter(
|
|
308
|
-
(provider) => provider.
|
|
324
|
+
(provider) => provider.runtime === dependency.sourceRuntime && provider.contractVersion === dependency.contractVersion
|
|
309
325
|
);
|
|
310
326
|
}
|
|
311
|
-
function runtimeDependenciesForValidation(manifest,
|
|
327
|
+
function runtimeDependenciesForValidation(manifest, runtime) {
|
|
312
328
|
const units = manifest.units ?? [];
|
|
313
|
-
const selected =
|
|
329
|
+
const selected = runtime === void 0 ? units : units.filter((unit) => unitRuntime(unit) === runtime);
|
|
314
330
|
return selected.flatMap((unit) => unit.dependencies ?? []);
|
|
315
331
|
}
|
|
316
332
|
function buildPluginGraph(manifests, options = {}) {
|
|
@@ -322,9 +338,9 @@ function buildPluginGraph(manifests, options = {}) {
|
|
|
322
338
|
const unitGraph = {};
|
|
323
339
|
const enabled = options.enabledPluginIds;
|
|
324
340
|
for (const manifest of manifests) {
|
|
325
|
-
const selectedUnits = selectedRuntimeUnits(manifest, options.
|
|
326
|
-
const provided = providesOfManifest(manifest, options.
|
|
327
|
-
const dependencyEntries = dependenciesOfManifest(manifest, options.
|
|
341
|
+
const selectedUnits = selectedRuntimeUnits(manifest, options.runtime);
|
|
342
|
+
const provided = providesOfManifest(manifest, options.runtime);
|
|
343
|
+
const dependencyEntries = dependenciesOfManifest(manifest, options.runtime);
|
|
328
344
|
dependencyDetails[manifest.id] = dependencyEntries.map((dependency) => ({ ...dependency }));
|
|
329
345
|
const deps = unique(dependencyEntries.map((dependency) => dependency.capability));
|
|
330
346
|
optionalDependencies[manifest.id] = unique(
|
|
@@ -337,7 +353,7 @@ function buildPluginGraph(manifests, options = {}) {
|
|
|
337
353
|
unitGraph[unitKey] = {
|
|
338
354
|
pluginId: manifest.id,
|
|
339
355
|
unitId: unit.id,
|
|
340
|
-
|
|
356
|
+
runtime: unit.runtime,
|
|
341
357
|
dependencies: unique((unit.dependencies ?? []).map((dependency) => dependency.capability)),
|
|
342
358
|
dependencyDetails: (unit.dependencies ?? []).map((dependency) => ({ ...dependency })),
|
|
343
359
|
provides: unique(unit.provides ?? []),
|
|
@@ -391,7 +407,7 @@ function buildPluginGraph(manifests, options = {}) {
|
|
|
391
407
|
visited.add(pluginId);
|
|
392
408
|
return;
|
|
393
409
|
}
|
|
394
|
-
for (const dependency of dependenciesOfManifest(manifest, options.
|
|
410
|
+
for (const dependency of dependenciesOfManifest(manifest, options.runtime)) {
|
|
395
411
|
if (dependency.optional) continue;
|
|
396
412
|
const provider = firstProvider.get(dependency.capability);
|
|
397
413
|
if (provider) visit(provider);
|
|
@@ -439,9 +455,12 @@ function validatePluginGraph(manifests, options = {}) {
|
|
|
439
455
|
}
|
|
440
456
|
}
|
|
441
457
|
for (const manifest of manifests) {
|
|
442
|
-
const strictDependencies = runtimeDependenciesForValidation(manifest, options.
|
|
458
|
+
const strictDependencies = runtimeDependenciesForValidation(manifest, options.runtime);
|
|
443
459
|
for (const dependency of strictDependencies) {
|
|
444
460
|
if (dependency.optional) continue;
|
|
461
|
+
if (options.externalRuntimeDependencies && options.runtime !== void 0 && dependency.sourceRuntime !== options.runtime) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
445
464
|
const matches = matchingRuntimeUnitProviders(manifests, dependency);
|
|
446
465
|
if (matches.length > 0) {
|
|
447
466
|
if (matches.length > 1 && !multiProvider.has(dependency.capability)) {
|
|
@@ -474,9 +493,9 @@ function validatePluginGraph(manifests, options = {}) {
|
|
|
474
493
|
}
|
|
475
494
|
for (const manifest of manifests) {
|
|
476
495
|
const strictDependencyCapabilities = new Set(
|
|
477
|
-
runtimeDependenciesForValidation(manifest, options.
|
|
496
|
+
runtimeDependenciesForValidation(manifest, options.runtime).map((dependency) => dependency.capability)
|
|
478
497
|
);
|
|
479
|
-
for (const dependency of dependenciesOfManifest(manifest, options.
|
|
498
|
+
for (const dependency of dependenciesOfManifest(manifest, options.runtime)) {
|
|
480
499
|
if (dependency.optional) continue;
|
|
481
500
|
if (strictDependencyCapabilities.has(dependency.capability)) continue;
|
|
482
501
|
const provided = (graph.providers?.[dependency.capability]?.length ?? 0) > 0 || builtins.has(dependency.capability);
|
|
@@ -2189,24 +2208,26 @@ function lifecycleStateFor(state, desired) {
|
|
|
2189
2208
|
return desired ? "waiting" : "disabled";
|
|
2190
2209
|
}
|
|
2191
2210
|
}
|
|
2192
|
-
function selectedUnit(manifest,
|
|
2211
|
+
function selectedUnit(manifest, runtime) {
|
|
2193
2212
|
const units = manifest.units ?? [];
|
|
2194
2213
|
if (units.length > 1) {
|
|
2195
|
-
const matches =
|
|
2214
|
+
const matches = runtime === void 0 ? [] : units.filter(
|
|
2215
|
+
(unit) => unit.runtime === runtime
|
|
2216
|
+
);
|
|
2196
2217
|
return matches.length === 1 ? matches[0] : void 0;
|
|
2197
2218
|
}
|
|
2198
2219
|
if (units.length === 1) {
|
|
2199
|
-
|
|
2220
|
+
const unit = units[0];
|
|
2221
|
+
return unit && (runtime === void 0 || unit.runtime === runtime) && unit.runtime !== void 0 ? unit : void 0;
|
|
2200
2222
|
}
|
|
2223
|
+
const targetRuntime = runtime ?? "window-main";
|
|
2201
2224
|
return {
|
|
2202
2225
|
id: manifest.id,
|
|
2203
|
-
|
|
2204
|
-
lifetime: manifest.meta.lifetime ?? defaultLifetime,
|
|
2226
|
+
runtime: targetRuntime,
|
|
2205
2227
|
dependencies: manifest.dependencies?.map((dependency) => ({
|
|
2206
2228
|
capability: dependency.capability,
|
|
2207
2229
|
contractVersion: dependency.contractVersion ?? `${dependency.capability}.v1`,
|
|
2208
|
-
|
|
2209
|
-
scope: dependency.scope ?? defaultLifetime,
|
|
2230
|
+
sourceRuntime: dependency.sourceRuntime ?? targetRuntime,
|
|
2210
2231
|
...dependency.reason !== void 0 ? { reason: dependency.reason } : {},
|
|
2211
2232
|
...dependency.optional !== void 0 ? { optional: dependency.optional } : {}
|
|
2212
2233
|
})),
|
|
@@ -2216,17 +2237,12 @@ function selectedUnit(manifest, execution, defaultLifetime) {
|
|
|
2216
2237
|
contribution: manifest.contribution
|
|
2217
2238
|
};
|
|
2218
2239
|
}
|
|
2219
|
-
function dependenciesFor(manifest,
|
|
2220
|
-
return dependenciesOfManifest(manifest,
|
|
2240
|
+
function dependenciesFor(manifest, runtime) {
|
|
2241
|
+
return dependenciesOfManifest(manifest, runtime);
|
|
2221
2242
|
}
|
|
2222
2243
|
function setupFor(manifest, unit, options) {
|
|
2223
2244
|
return options.runtimeUnitImplementationRegistry?.get(manifest.id, unit.id);
|
|
2224
2245
|
}
|
|
2225
|
-
function stableValue(value) {
|
|
2226
|
-
if (Array.isArray(value)) return `[${value.map(stableValue).join(",")}]`;
|
|
2227
|
-
if (!value || typeof value !== "object") return JSON.stringify(value);
|
|
2228
|
-
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableValue(item)}`).join(",")}}`;
|
|
2229
|
-
}
|
|
2230
2246
|
var StartupCapabilityError = class extends Error {
|
|
2231
2247
|
details;
|
|
2232
2248
|
constructor(details, phase = "startup") {
|
|
@@ -2244,6 +2260,7 @@ var StartupPluginError = class extends Error {
|
|
|
2244
2260
|
}
|
|
2245
2261
|
};
|
|
2246
2262
|
function createPluginHost(options = {}) {
|
|
2263
|
+
const runtimeKind = options.runtime;
|
|
2247
2264
|
const listeners = /* @__PURE__ */ new Set();
|
|
2248
2265
|
let versionCounter = 0;
|
|
2249
2266
|
let hostDisposed = false;
|
|
@@ -2290,7 +2307,6 @@ function createPluginHost(options = {}) {
|
|
|
2290
2307
|
let intentSnapshot = options.pluginIntentCoordinator?.snapshot();
|
|
2291
2308
|
let removeConfigSubscription = () => void 0;
|
|
2292
2309
|
let removeIntentSubscription = () => void 0;
|
|
2293
|
-
let removeScopeSubscription = () => void 0;
|
|
2294
2310
|
let reconcilePromise;
|
|
2295
2311
|
const desiredEnabledFor = (pluginId, manifest) => {
|
|
2296
2312
|
if (manifest?.meta.startup === "required" || manifest?.meta.canDisable === false) return true;
|
|
@@ -2300,8 +2316,10 @@ function createPluginHost(options = {}) {
|
|
|
2300
2316
|
return configStore.read()[pluginId] ?? manifest?.meta.defaultEnabled ?? false;
|
|
2301
2317
|
};
|
|
2302
2318
|
const desiredRevisionFor = (pluginId) => intentSnapshot?.desiredRevision[pluginId];
|
|
2303
|
-
const selected = (manifest) => selectedUnit(manifest,
|
|
2304
|
-
const graph = () => buildPluginGraph([
|
|
2319
|
+
const selected = (manifest) => selectedUnit(manifest, runtimeKind);
|
|
2320
|
+
const graph = () => buildPluginGraph([
|
|
2321
|
+
...knownManifests.values()
|
|
2322
|
+
], { runtime: runtimeKind, enabledPluginIds: enabledSet });
|
|
2305
2323
|
const validateManifest = (manifest) => {
|
|
2306
2324
|
if (!manifest || typeof manifest.id !== "string" || manifest.id.trim() === "") throw new Error("Plugin id must be a non-empty string");
|
|
2307
2325
|
if (typeof manifest.name !== "string" || manifest.name.trim() === "") throw new Error(`Plugin "${manifest.id}" name must be a non-empty string`);
|
|
@@ -2312,12 +2330,17 @@ function createPluginHost(options = {}) {
|
|
|
2312
2330
|
throw new Error(`Plugin "${manifest.id}" required startup metadata is inconsistent`);
|
|
2313
2331
|
}
|
|
2314
2332
|
if (manifest.units !== void 0 && !Array.isArray(manifest.units)) throw new Error(`Plugin "${manifest.id}" units must be an array`);
|
|
2333
|
+
if ((manifest.units?.length ?? 0) > 1 && runtimeKind === void 0) {
|
|
2334
|
+
throw new Error(`Plugin "${manifest.id}" runtime must be explicit for multi-unit manifests`);
|
|
2335
|
+
}
|
|
2315
2336
|
if (manifest.units && manifest.units.length > 0) {
|
|
2316
2337
|
const ids = /* @__PURE__ */ new Set();
|
|
2317
2338
|
for (const unit of manifest.units) {
|
|
2318
2339
|
if (!unit.id || ids.has(unit.id)) throw new Error(`Plugin "${manifest.id}" has duplicate or empty unit id`);
|
|
2319
2340
|
ids.add(unit.id);
|
|
2320
|
-
if (
|
|
2341
|
+
if (unit.runtime !== "window-main" && unit.runtime !== "shared-worker") {
|
|
2342
|
+
throw new Error(`Plugin "${manifest.id}" unit "${unit.id}" declares an unsupported Runtime`);
|
|
2343
|
+
}
|
|
2321
2344
|
}
|
|
2322
2345
|
}
|
|
2323
2346
|
options.manifestValidator?.(manifest);
|
|
@@ -2325,9 +2348,18 @@ function createPluginHost(options = {}) {
|
|
|
2325
2348
|
const isRequired = (manifest) => manifest.meta.startup === "required" || manifest.meta.canDisable === false;
|
|
2326
2349
|
const missingDependencies = (manifest) => {
|
|
2327
2350
|
const missing = [];
|
|
2328
|
-
|
|
2351
|
+
const selectedUnitForDependencies = selected(manifest);
|
|
2352
|
+
const localRuntime = selectedUnitForDependencies?.runtime ?? runtimeKind;
|
|
2353
|
+
for (const dependency of dependenciesFor(manifest, runtimeKind)) {
|
|
2329
2354
|
if (dependency.optional) continue;
|
|
2330
|
-
|
|
2355
|
+
const sourceRuntime = dependency.sourceRuntime ?? localRuntime;
|
|
2356
|
+
const contractVersion = dependency.contractVersion ?? `${dependency.capability}.v1`;
|
|
2357
|
+
if (sourceRuntime !== void 0 && sourceRuntime !== localRuntime) {
|
|
2358
|
+
const available = options.remoteServiceReferences?.().some((reference) => reference.status === "ready" && reference.runtime === sourceRuntime && reference.capabilityId === dependency.capability && reference.contractVersion === contractVersion) ?? false;
|
|
2359
|
+
if (!available) missing.push(dependency.capability);
|
|
2360
|
+
} else if (!capabilities.has(dependency.capability)) {
|
|
2361
|
+
missing.push(dependency.capability);
|
|
2362
|
+
}
|
|
2331
2363
|
}
|
|
2332
2364
|
return [...new Set(missing)];
|
|
2333
2365
|
};
|
|
@@ -2335,21 +2367,21 @@ function createPluginHost(options = {}) {
|
|
|
2335
2367
|
const unit = selected(manifest);
|
|
2336
2368
|
if (!unit) {
|
|
2337
2369
|
const units = manifest.units ?? [];
|
|
2338
|
-
if (units.length > 0 &&
|
|
2370
|
+
if (units.length > 0 && runtimeKind !== void 0 && !units.some((item) => item.runtime === runtimeKind)) {
|
|
2339
2371
|
const snapshots = runtimeSnapshots?.() ?? [];
|
|
2340
2372
|
if (snapshots.some((item) => item.pluginId === manifest.id)) return `runtime:${manifest.id}:unknown`;
|
|
2341
2373
|
}
|
|
2342
2374
|
return units.length > 0 ? `runtime:${manifest.id}:unit-unavailable` : void 0;
|
|
2343
2375
|
}
|
|
2376
|
+
const unavailable = options.runtimeUnitAvailability?.({
|
|
2377
|
+
pluginId: manifest.id,
|
|
2378
|
+
unitId: unit.id,
|
|
2379
|
+
runtime: unit.runtime
|
|
2380
|
+
});
|
|
2381
|
+
if (unavailable) return unavailable;
|
|
2344
2382
|
if (!setupFor(manifest, unit, options)) return `runtime:${manifest.id}:${unit.id}:implementation-unavailable`;
|
|
2345
2383
|
return void 0;
|
|
2346
2384
|
};
|
|
2347
|
-
const resolveScope = (record, unit) => {
|
|
2348
|
-
const resolver = options.scopeResolver;
|
|
2349
|
-
if (!resolver) return { available: true, parent: rootScope, attributes: rootAttributes, key: stableValue(rootAttributes) };
|
|
2350
|
-
return resolver.resolve({ pluginId: record.manifest.id, unitId: unit.id, lifetime: unit.lifetime, manifest: record.manifest });
|
|
2351
|
-
};
|
|
2352
|
-
const scopeKeyForResolution = (resolution, unit) => resolution.key ?? `${resolution.parent?.identity.scopeId ?? "root"}:${unit.lifetime}:${stableValue(resolution.attributes ?? rootAttributes)}`;
|
|
2353
2385
|
const revokeContributions = (record) => {
|
|
2354
2386
|
for (const contribution of record.contributions) {
|
|
2355
2387
|
if (!contribution.active) continue;
|
|
@@ -2677,19 +2709,25 @@ function createPluginHost(options = {}) {
|
|
|
2677
2709
|
bumpVersion();
|
|
2678
2710
|
return;
|
|
2679
2711
|
}
|
|
2680
|
-
const resolution = resolveScope(record, unit);
|
|
2681
|
-
if (!resolution.available) {
|
|
2682
|
-
record.state = "blocked";
|
|
2683
|
-
record.blockedBy = [resolution.reason ?? `scope:${unit.lifetime}:unavailable`];
|
|
2684
|
-
bumpVersion();
|
|
2685
|
-
return;
|
|
2686
|
-
}
|
|
2687
2712
|
const instanceId = makeInstanceId(record.manifest.id, unit.id);
|
|
2688
2713
|
let scope;
|
|
2689
2714
|
try {
|
|
2690
|
-
const
|
|
2691
|
-
|
|
2692
|
-
|
|
2715
|
+
const attributes = Object.freeze({
|
|
2716
|
+
...rootAttributes,
|
|
2717
|
+
...options.runtimeUnitAttributes?.({
|
|
2718
|
+
pluginId: record.manifest.id,
|
|
2719
|
+
unitId: unit.id,
|
|
2720
|
+
runtime: unit.runtime,
|
|
2721
|
+
manifest: record.manifest
|
|
2722
|
+
}) ?? {}
|
|
2723
|
+
});
|
|
2724
|
+
const parent = options.runtimeUnitParentScope?.({
|
|
2725
|
+
pluginId: record.manifest.id,
|
|
2726
|
+
unitId: unit.id,
|
|
2727
|
+
runtime: unit.runtime,
|
|
2728
|
+
manifest: record.manifest
|
|
2729
|
+
}) ?? rootScope;
|
|
2730
|
+
scope = parent.child("runtime-unit", { pluginId: record.manifest.id, attributes });
|
|
2693
2731
|
} catch (error) {
|
|
2694
2732
|
record.state = "blocked";
|
|
2695
2733
|
record.blockedBy = [errorMessage4(error)];
|
|
@@ -2701,7 +2739,6 @@ function createPluginHost(options = {}) {
|
|
|
2701
2739
|
record.instanceId = instanceId;
|
|
2702
2740
|
record.unitId = unit.id;
|
|
2703
2741
|
instanceAttributes.set(instanceId, scope.identity.attributes);
|
|
2704
|
-
record.scopeKey = scopeKeyForResolution(resolution, unit);
|
|
2705
2742
|
record.error = void 0;
|
|
2706
2743
|
record.blockedBy = void 0;
|
|
2707
2744
|
bumpVersion();
|
|
@@ -2712,7 +2749,7 @@ function createPluginHost(options = {}) {
|
|
|
2712
2749
|
const result = await setup(context);
|
|
2713
2750
|
record.teardown = typeof result === "function" ? result : void 0;
|
|
2714
2751
|
await registerContribution(record, unit, instanceId, scope);
|
|
2715
|
-
const declared = providesOfManifest(record.manifest,
|
|
2752
|
+
const declared = providesOfManifest(record.manifest, runtimeKind);
|
|
2716
2753
|
const missingDeclarations = declared.filter((key) => !record.capabilities.has(key));
|
|
2717
2754
|
if (missingDeclarations.length > 0) {
|
|
2718
2755
|
throw new Error(`Plugin "${record.manifest.id}" did not provide declared capabilities: ${missingDeclarations.join(", ")}`);
|
|
@@ -2743,7 +2780,13 @@ function createPluginHost(options = {}) {
|
|
|
2743
2780
|
record.instanceId = void 0;
|
|
2744
2781
|
record.unitId = void 0;
|
|
2745
2782
|
bumpVersion();
|
|
2746
|
-
throw new StartupPluginError({
|
|
2783
|
+
throw new StartupPluginError({
|
|
2784
|
+
pluginId: record.manifest.id,
|
|
2785
|
+
unitId: unit.id,
|
|
2786
|
+
capabilities: providesOfManifest(record.manifest, runtimeKind),
|
|
2787
|
+
state: record.state,
|
|
2788
|
+
error: record.error
|
|
2789
|
+
});
|
|
2747
2790
|
}
|
|
2748
2791
|
})();
|
|
2749
2792
|
starting.set(pluginId, task);
|
|
@@ -2780,32 +2823,29 @@ function createPluginHost(options = {}) {
|
|
|
2780
2823
|
if (reconcilePromise) return reconcilePromise;
|
|
2781
2824
|
reconcilePromise = (async () => {
|
|
2782
2825
|
const manifests = [...knownManifests.values()];
|
|
2783
|
-
const ordered = orderManifestsByDependencies(manifests,
|
|
2784
|
-
const rotationTargets = manifests.filter((manifest) => {
|
|
2785
|
-
const record = records.get(manifest.id);
|
|
2786
|
-
if (!record || !desiredEnabledFor(manifest.id, manifest) || record.state !== "enabled" && record.state !== "starting") return false;
|
|
2787
|
-
const unit = selected(record.manifest);
|
|
2788
|
-
if (!unit || !options.scopeResolver) return false;
|
|
2789
|
-
const resolution = resolveScope(record, unit);
|
|
2790
|
-
return !resolution.available || record.scopeKey !== scopeKeyForResolution(resolution, unit);
|
|
2791
|
-
});
|
|
2792
|
-
const rotationPlan = [];
|
|
2793
|
-
const planned = /* @__PURE__ */ new Set();
|
|
2794
|
-
for (const target of rotationTargets) {
|
|
2795
|
-
for (const item of collectDisablePlan(target.id)) {
|
|
2796
|
-
if (planned.has(item.manifest.id)) continue;
|
|
2797
|
-
planned.add(item.manifest.id);
|
|
2798
|
-
rotationPlan.push(item);
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
for (const item of rotationPlan) {
|
|
2802
|
-
await stopPlugin(item, "scope resolution changed", true);
|
|
2803
|
-
}
|
|
2826
|
+
const ordered = orderManifestsByDependencies(manifests, runtimeKind);
|
|
2804
2827
|
for (const manifest of ordered) {
|
|
2805
2828
|
const record = records.get(manifest.id);
|
|
2806
2829
|
if (!record) continue;
|
|
2830
|
+
if (record && (record.state === "enabled" || record.state === "starting")) {
|
|
2831
|
+
const missing = missingDependencies(manifest);
|
|
2832
|
+
const unavailable = runtimeUnavailable(manifest);
|
|
2833
|
+
if (missing.length > 0 || unavailable) {
|
|
2834
|
+
await stopPlugin(
|
|
2835
|
+
record,
|
|
2836
|
+
"runtime dependency unavailable",
|
|
2837
|
+
true,
|
|
2838
|
+
[.../* @__PURE__ */ new Set([...missing, ...unavailable ? [unavailable] : []])]
|
|
2839
|
+
);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2807
2842
|
if (desiredEnabledFor(manifest.id, manifest) && record.state !== "enabled" && record.state !== "starting") {
|
|
2808
|
-
|
|
2843
|
+
try {
|
|
2844
|
+
await enable(manifest.id);
|
|
2845
|
+
} catch (error) {
|
|
2846
|
+
if (!isRequired(manifest)) continue;
|
|
2847
|
+
throw error;
|
|
2848
|
+
}
|
|
2809
2849
|
}
|
|
2810
2850
|
}
|
|
2811
2851
|
for (const manifest of manifests) {
|
|
@@ -2819,10 +2859,14 @@ function createPluginHost(options = {}) {
|
|
|
2819
2859
|
});
|
|
2820
2860
|
return reconcilePromise;
|
|
2821
2861
|
};
|
|
2822
|
-
function orderManifestsByDependencies(manifests,
|
|
2862
|
+
function orderManifestsByDependencies(manifests, runtime) {
|
|
2823
2863
|
const byId = new Map(manifests.map((manifest) => [manifest.id, manifest]));
|
|
2824
2864
|
const providers = /* @__PURE__ */ new Map();
|
|
2825
|
-
for (const manifest of manifests)
|
|
2865
|
+
for (const manifest of manifests) {
|
|
2866
|
+
for (const capability of providesOfManifest(manifest, runtime)) {
|
|
2867
|
+
if (!providers.has(capability)) providers.set(capability, manifest.id);
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2826
2870
|
const visited = /* @__PURE__ */ new Set();
|
|
2827
2871
|
const visiting = /* @__PURE__ */ new Set();
|
|
2828
2872
|
const ordered = [];
|
|
@@ -2830,7 +2874,7 @@ function createPluginHost(options = {}) {
|
|
|
2830
2874
|
if (visited.has(manifest.id)) return;
|
|
2831
2875
|
if (visiting.has(manifest.id)) return;
|
|
2832
2876
|
visiting.add(manifest.id);
|
|
2833
|
-
for (const dependency of dependenciesFor(manifest,
|
|
2877
|
+
for (const dependency of dependenciesFor(manifest, runtime)) {
|
|
2834
2878
|
if (dependency.optional) continue;
|
|
2835
2879
|
const provider = providers.get(dependency.capability);
|
|
2836
2880
|
const providerManifest = provider ? byId.get(provider) : void 0;
|
|
@@ -2868,7 +2912,14 @@ function createPluginHost(options = {}) {
|
|
|
2868
2912
|
...record.unitId ? { unitId: record.unitId } : {},
|
|
2869
2913
|
...record.blockedBy ? { blockedBy: [...record.blockedBy] } : {},
|
|
2870
2914
|
...record.cleanup ? { cleanup: record.cleanup } : {},
|
|
2871
|
-
units: unit ? [{
|
|
2915
|
+
units: unit ? [{
|
|
2916
|
+
pluginId,
|
|
2917
|
+
unitId: unit.id,
|
|
2918
|
+
runtime: unit.runtime,
|
|
2919
|
+
kind: record.state,
|
|
2920
|
+
...record.instanceId ? { instanceId: record.instanceId } : {},
|
|
2921
|
+
...record.error ? { error: record.error } : {}
|
|
2922
|
+
}] : []
|
|
2872
2923
|
};
|
|
2873
2924
|
},
|
|
2874
2925
|
scope: (pluginId) => records.get(pluginId)?.scope,
|
|
@@ -2889,7 +2940,14 @@ function createPluginHost(options = {}) {
|
|
|
2889
2940
|
},
|
|
2890
2941
|
validateManifestSet(manifests) {
|
|
2891
2942
|
for (const manifest of manifests) validateManifest(manifest);
|
|
2892
|
-
validatePluginGraph([...manifests], {
|
|
2943
|
+
validatePluginGraph([...manifests], {
|
|
2944
|
+
runtime: runtimeKind,
|
|
2945
|
+
builtinCapabilities: /* @__PURE__ */ new Set([
|
|
2946
|
+
...capabilities.keys(),
|
|
2947
|
+
...(options.remoteServiceReferences?.() ?? []).map((reference) => reference.capabilityId)
|
|
2948
|
+
]),
|
|
2949
|
+
externalRuntimeDependencies: options.externalRuntimeDependencies
|
|
2950
|
+
});
|
|
2893
2951
|
},
|
|
2894
2952
|
provide(key, value) {
|
|
2895
2953
|
if (hostDisposed) throw new LifecycleScopeRevokedError("Plugin host is disposed");
|
|
@@ -2912,7 +2970,15 @@ function createPluginHost(options = {}) {
|
|
|
2912
2970
|
validateManifest(manifest);
|
|
2913
2971
|
if (knownManifests.has(manifest.id)) throw new Error(`Plugin "${manifest.id}" is already registered`);
|
|
2914
2972
|
}
|
|
2915
|
-
validatePluginGraph(current, {
|
|
2973
|
+
validatePluginGraph(current, {
|
|
2974
|
+
runtime: runtimeKind,
|
|
2975
|
+
builtinCapabilities: /* @__PURE__ */ new Set([
|
|
2976
|
+
...capabilities.keys(),
|
|
2977
|
+
...(options.remoteServiceReferences?.() ?? []).map((reference) => reference.capabilityId)
|
|
2978
|
+
]),
|
|
2979
|
+
allowMissingDependencies: false,
|
|
2980
|
+
externalRuntimeDependencies: options.externalRuntimeDependencies
|
|
2981
|
+
});
|
|
2916
2982
|
for (const manifest of current) {
|
|
2917
2983
|
knownManifests.set(manifest.id, manifest);
|
|
2918
2984
|
records.set(manifest.id, { manifest, state: "registered", disposeCallbacks: [], capabilities: /* @__PURE__ */ new Set(), contributions: [] });
|
|
@@ -2984,6 +3050,13 @@ function createPluginHost(options = {}) {
|
|
|
2984
3050
|
);
|
|
2985
3051
|
return { ok: true };
|
|
2986
3052
|
},
|
|
3053
|
+
suspend: async (pluginId, reason = "runtime identity changed") => {
|
|
3054
|
+
const record = records.get(pluginId);
|
|
3055
|
+
if (!record) return;
|
|
3056
|
+
if (record.state !== "enabled" && record.state !== "starting") return;
|
|
3057
|
+
beginStop(record, reason, true);
|
|
3058
|
+
await stopPlugin(record, reason, true);
|
|
3059
|
+
},
|
|
2987
3060
|
unregister: async (pluginId) => {
|
|
2988
3061
|
const record = records.get(pluginId);
|
|
2989
3062
|
if (!record) return;
|
|
@@ -2999,7 +3072,6 @@ function createPluginHost(options = {}) {
|
|
|
2999
3072
|
hostDisposed = true;
|
|
3000
3073
|
removeConfigSubscription();
|
|
3001
3074
|
removeIntentSubscription();
|
|
3002
|
-
removeScopeSubscription();
|
|
3003
3075
|
disposePromise = (async () => {
|
|
3004
3076
|
for (const record of [...records.values()].reverse()) {
|
|
3005
3077
|
if (record.state === "enabled" || record.state === "starting") {
|
|
@@ -3049,13 +3121,6 @@ function createPluginHost(options = {}) {
|
|
|
3049
3121
|
void reconcile().catch(() => void 0);
|
|
3050
3122
|
bumpVersion();
|
|
3051
3123
|
});
|
|
3052
|
-
if (options.scopeResolver?.subscribe) {
|
|
3053
|
-
removeScopeSubscription = options.scopeResolver.subscribe(() => {
|
|
3054
|
-
void reconcile().catch(() => void 0);
|
|
3055
|
-
resourceStore.refreshRuntimeBindings();
|
|
3056
|
-
bumpVersion();
|
|
3057
|
-
});
|
|
3058
|
-
}
|
|
3059
3124
|
return host;
|
|
3060
3125
|
}
|
|
3061
3126
|
|
|
@@ -3082,161 +3147,6 @@ function createRuntimeUnitImplementationRegistry(implementations = []) {
|
|
|
3082
3147
|
};
|
|
3083
3148
|
}
|
|
3084
3149
|
|
|
3085
|
-
// src/lifecycle/pluginIntentController.ts
|
|
3086
|
-
function makeAuthorityInstanceId() {
|
|
3087
|
-
try {
|
|
3088
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
3089
|
-
return `authority:${crypto.randomUUID()}`;
|
|
3090
|
-
}
|
|
3091
|
-
} catch {
|
|
3092
|
-
}
|
|
3093
|
-
return `authority:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
|
|
3094
|
-
}
|
|
3095
|
-
function cloneSnapshot(snapshot) {
|
|
3096
|
-
return {
|
|
3097
|
-
revision: snapshot.revision,
|
|
3098
|
-
desiredEnabled: { ...snapshot.desiredEnabled },
|
|
3099
|
-
desiredRevision: { ...snapshot.desiredRevision }
|
|
3100
|
-
};
|
|
3101
|
-
}
|
|
3102
|
-
function normalizeSnapshot(initial) {
|
|
3103
|
-
const revision = Number.isSafeInteger(initial?.revision) && (initial?.revision ?? 0) >= 0 ? initial.revision : 0;
|
|
3104
|
-
const desiredEnabled = {};
|
|
3105
|
-
for (const [pluginId, value] of Object.entries(initial?.desiredEnabled ?? {})) {
|
|
3106
|
-
if (typeof value === "boolean") desiredEnabled[pluginId] = value;
|
|
3107
|
-
}
|
|
3108
|
-
const desiredRevision = {};
|
|
3109
|
-
for (const [pluginId, value] of Object.entries(initial?.desiredRevision ?? {})) {
|
|
3110
|
-
if (Number.isSafeInteger(value) && value >= 0) desiredRevision[pluginId] = value;
|
|
3111
|
-
}
|
|
3112
|
-
return { revision, desiredEnabled, desiredRevision };
|
|
3113
|
-
}
|
|
3114
|
-
function commandFingerprint(command) {
|
|
3115
|
-
return [
|
|
3116
|
-
command.authorityInstanceId,
|
|
3117
|
-
command.expectedRevision,
|
|
3118
|
-
command.pluginId,
|
|
3119
|
-
command.desiredEnabled ? "true" : "false"
|
|
3120
|
-
].join("\0");
|
|
3121
|
-
}
|
|
3122
|
-
function commandError(message) {
|
|
3123
|
-
return { status: "command-conflict", commandId: "", message };
|
|
3124
|
-
}
|
|
3125
|
-
function createPluginIntentController(options = {}) {
|
|
3126
|
-
const authorityInstanceId = options.authorityInstanceId ?? makeAuthorityInstanceId();
|
|
3127
|
-
const maxCommandRecords = Math.max(1, Math.floor(options.maxCommandRecords ?? 256));
|
|
3128
|
-
let current = normalizeSnapshot(options.initial);
|
|
3129
|
-
const commandRecords = /* @__PURE__ */ new Map();
|
|
3130
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
3131
|
-
let queue = Promise.resolve();
|
|
3132
|
-
const notify = () => {
|
|
3133
|
-
const snapshot = cloneSnapshot(current);
|
|
3134
|
-
for (const listener of [...listeners]) {
|
|
3135
|
-
try {
|
|
3136
|
-
listener(snapshot);
|
|
3137
|
-
} catch {
|
|
3138
|
-
}
|
|
3139
|
-
}
|
|
3140
|
-
};
|
|
3141
|
-
const process = async (command) => {
|
|
3142
|
-
const candidate = command;
|
|
3143
|
-
const commandId = typeof candidate?.commandId === "string" ? candidate.commandId : "";
|
|
3144
|
-
if (!candidate || typeof candidate.authorityInstanceId !== "string" || candidate.authorityInstanceId.length === 0 || typeof candidate.pluginId !== "string" || candidate.pluginId.length === 0 || typeof candidate.desiredEnabled !== "boolean" || !Number.isSafeInteger(candidate.expectedRevision) || (candidate.expectedRevision ?? -1) < 0 || commandId.length === 0) {
|
|
3145
|
-
const result = commandError("commandId\u3001pluginId \u548C expectedRevision \u5FC5\u987B\u662F\u6709\u6548\u503C");
|
|
3146
|
-
return { ...result, commandId };
|
|
3147
|
-
}
|
|
3148
|
-
if (command.authorityInstanceId !== authorityInstanceId) {
|
|
3149
|
-
return {
|
|
3150
|
-
status: "stale-authority",
|
|
3151
|
-
commandId: command.commandId,
|
|
3152
|
-
expectedAuthorityInstanceId: authorityInstanceId
|
|
3153
|
-
};
|
|
3154
|
-
}
|
|
3155
|
-
const fingerprint = commandFingerprint(command);
|
|
3156
|
-
const existing = commandRecords.get(command.commandId);
|
|
3157
|
-
if (existing) {
|
|
3158
|
-
if (existing.fingerprint !== fingerprint) {
|
|
3159
|
-
return {
|
|
3160
|
-
status: "command-conflict",
|
|
3161
|
-
commandId: command.commandId,
|
|
3162
|
-
message: "\u76F8\u540C commandId \u7684\u547D\u4EE4\u5185\u5BB9\u4E0D\u540C\uFF0C\u62D2\u7EDD\u8986\u76D6\u539F\u547D\u4EE4"
|
|
3163
|
-
};
|
|
3164
|
-
}
|
|
3165
|
-
return {
|
|
3166
|
-
status: "duplicate",
|
|
3167
|
-
commandId: command.commandId,
|
|
3168
|
-
snapshot: cloneSnapshot(existing.result.snapshot),
|
|
3169
|
-
persisted: true
|
|
3170
|
-
};
|
|
3171
|
-
}
|
|
3172
|
-
if (command.expectedRevision !== current.revision) {
|
|
3173
|
-
return {
|
|
3174
|
-
status: "revision-conflict",
|
|
3175
|
-
commandId: command.commandId,
|
|
3176
|
-
snapshot: cloneSnapshot(current)
|
|
3177
|
-
};
|
|
3178
|
-
}
|
|
3179
|
-
const next = {
|
|
3180
|
-
revision: current.revision + 1,
|
|
3181
|
-
desiredEnabled: {
|
|
3182
|
-
...current.desiredEnabled,
|
|
3183
|
-
[command.pluginId]: command.desiredEnabled
|
|
3184
|
-
},
|
|
3185
|
-
desiredRevision: {
|
|
3186
|
-
...current.desiredRevision,
|
|
3187
|
-
[command.pluginId]: (current.desiredRevision[command.pluginId] ?? 0) + 1
|
|
3188
|
-
}
|
|
3189
|
-
};
|
|
3190
|
-
try {
|
|
3191
|
-
await options.persist?.(cloneSnapshot(next));
|
|
3192
|
-
} catch (error) {
|
|
3193
|
-
return {
|
|
3194
|
-
status: "persistence-failed",
|
|
3195
|
-
commandId: command.commandId,
|
|
3196
|
-
message: error instanceof Error ? error.message : String(error),
|
|
3197
|
-
snapshot: cloneSnapshot(current)
|
|
3198
|
-
};
|
|
3199
|
-
}
|
|
3200
|
-
current = next;
|
|
3201
|
-
const accepted = {
|
|
3202
|
-
status: "accepted",
|
|
3203
|
-
commandId: command.commandId,
|
|
3204
|
-
snapshot: cloneSnapshot(current),
|
|
3205
|
-
persisted: true
|
|
3206
|
-
};
|
|
3207
|
-
commandRecords.set(command.commandId, {
|
|
3208
|
-
fingerprint,
|
|
3209
|
-
result: accepted
|
|
3210
|
-
});
|
|
3211
|
-
while (commandRecords.size > maxCommandRecords) {
|
|
3212
|
-
const oldest = commandRecords.keys().next().value;
|
|
3213
|
-
if (oldest === void 0) break;
|
|
3214
|
-
commandRecords.delete(oldest);
|
|
3215
|
-
}
|
|
3216
|
-
notify();
|
|
3217
|
-
return {
|
|
3218
|
-
status: "accepted",
|
|
3219
|
-
commandId: accepted.commandId,
|
|
3220
|
-
snapshot: cloneSnapshot(accepted.snapshot),
|
|
3221
|
-
persisted: true
|
|
3222
|
-
};
|
|
3223
|
-
};
|
|
3224
|
-
const controller = {
|
|
3225
|
-
authorityInstanceId,
|
|
3226
|
-
snapshot: () => cloneSnapshot(current),
|
|
3227
|
-
submit(command) {
|
|
3228
|
-
const result = queue.then(() => process(command));
|
|
3229
|
-
queue = result.then(() => void 0, () => void 0);
|
|
3230
|
-
return result;
|
|
3231
|
-
},
|
|
3232
|
-
subscribe(listener) {
|
|
3233
|
-
listeners.add(listener);
|
|
3234
|
-
return () => listeners.delete(listener);
|
|
3235
|
-
}
|
|
3236
|
-
};
|
|
3237
|
-
return controller;
|
|
3238
|
-
}
|
|
3239
|
-
|
|
3240
3150
|
// src/transport/serviceBridge.ts
|
|
3241
3151
|
function makeRequestId() {
|
|
3242
3152
|
try {
|
|
@@ -3296,9 +3206,10 @@ function referenceKey(reference) {
|
|
|
3296
3206
|
return [
|
|
3297
3207
|
reference.capabilityId,
|
|
3298
3208
|
reference.providerInstanceId,
|
|
3299
|
-
reference.
|
|
3209
|
+
reference.runtime,
|
|
3300
3210
|
reference.contractVersion,
|
|
3301
3211
|
reference.authorityInstanceId,
|
|
3212
|
+
reference.connectionId ?? "local",
|
|
3302
3213
|
reference.scopeId,
|
|
3303
3214
|
reference.handoverGeneration,
|
|
3304
3215
|
stableAttributes(reference.attributes),
|
|
@@ -3310,7 +3221,7 @@ function referenceKey(reference) {
|
|
|
3310
3221
|
].join("\0");
|
|
3311
3222
|
}
|
|
3312
3223
|
function lookupMatches(reference, lookup) {
|
|
3313
|
-
return reference.status === "ready" && reference.capabilityId === lookup.capabilityId && reference.contractVersion === lookup.contractVersion && (lookup.
|
|
3224
|
+
return reference.status === "ready" && reference.capabilityId === lookup.capabilityId && reference.contractVersion === lookup.contractVersion && (lookup.runtime === void 0 || reference.runtime === lookup.runtime) && (lookup.scopeId === void 0 || reference.scopeId === lookup.scopeId);
|
|
3314
3225
|
}
|
|
3315
3226
|
function createServiceBridge(options) {
|
|
3316
3227
|
let currentState = "disconnected";
|
|
@@ -3437,7 +3348,7 @@ function createServiceBridge(options) {
|
|
|
3437
3348
|
}
|
|
3438
3349
|
const next = /* @__PURE__ */ new Map();
|
|
3439
3350
|
for (const reference of snapshot.services) {
|
|
3440
|
-
if (reference.authorityInstanceId !== currentAuthorityInstanceId || reference.snapshotRevision !== snapshot.snapshotRevision) continue;
|
|
3351
|
+
if (reference.authorityInstanceId !== currentAuthorityInstanceId || reference.connectionId !== void 0 && reference.connectionId !== snapshot.connectionId || reference.snapshotRevision !== snapshot.snapshotRevision) continue;
|
|
3441
3352
|
next.set(referenceKey(reference), Object.freeze({ ...reference }));
|
|
3442
3353
|
}
|
|
3443
3354
|
references.clear();
|
|
@@ -3731,6 +3642,161 @@ function createMessagePortServiceTransport(options) {
|
|
|
3731
3642
|
return transport;
|
|
3732
3643
|
}
|
|
3733
3644
|
|
|
3645
|
+
// src/lifecycle/pluginIntentController.ts
|
|
3646
|
+
function makeAuthorityInstanceId() {
|
|
3647
|
+
try {
|
|
3648
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
3649
|
+
return `authority:${crypto.randomUUID()}`;
|
|
3650
|
+
}
|
|
3651
|
+
} catch {
|
|
3652
|
+
}
|
|
3653
|
+
return `authority:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
|
|
3654
|
+
}
|
|
3655
|
+
function cloneSnapshot(snapshot) {
|
|
3656
|
+
return {
|
|
3657
|
+
revision: snapshot.revision,
|
|
3658
|
+
desiredEnabled: { ...snapshot.desiredEnabled },
|
|
3659
|
+
desiredRevision: { ...snapshot.desiredRevision }
|
|
3660
|
+
};
|
|
3661
|
+
}
|
|
3662
|
+
function normalizeSnapshot(initial) {
|
|
3663
|
+
const revision = Number.isSafeInteger(initial?.revision) && (initial?.revision ?? 0) >= 0 ? initial.revision : 0;
|
|
3664
|
+
const desiredEnabled = {};
|
|
3665
|
+
for (const [pluginId, value] of Object.entries(initial?.desiredEnabled ?? {})) {
|
|
3666
|
+
if (typeof value === "boolean") desiredEnabled[pluginId] = value;
|
|
3667
|
+
}
|
|
3668
|
+
const desiredRevision = {};
|
|
3669
|
+
for (const [pluginId, value] of Object.entries(initial?.desiredRevision ?? {})) {
|
|
3670
|
+
if (Number.isSafeInteger(value) && value >= 0) desiredRevision[pluginId] = value;
|
|
3671
|
+
}
|
|
3672
|
+
return { revision, desiredEnabled, desiredRevision };
|
|
3673
|
+
}
|
|
3674
|
+
function commandFingerprint(command) {
|
|
3675
|
+
return [
|
|
3676
|
+
command.authorityInstanceId,
|
|
3677
|
+
command.expectedRevision,
|
|
3678
|
+
command.pluginId,
|
|
3679
|
+
command.desiredEnabled ? "true" : "false"
|
|
3680
|
+
].join("\0");
|
|
3681
|
+
}
|
|
3682
|
+
function commandError(message) {
|
|
3683
|
+
return { status: "command-conflict", commandId: "", message };
|
|
3684
|
+
}
|
|
3685
|
+
function createPluginIntentController(options = {}) {
|
|
3686
|
+
const authorityInstanceId = options.authorityInstanceId ?? makeAuthorityInstanceId();
|
|
3687
|
+
const maxCommandRecords = Math.max(1, Math.floor(options.maxCommandRecords ?? 256));
|
|
3688
|
+
let current = normalizeSnapshot(options.initial);
|
|
3689
|
+
const commandRecords = /* @__PURE__ */ new Map();
|
|
3690
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3691
|
+
let queue = Promise.resolve();
|
|
3692
|
+
const notify = () => {
|
|
3693
|
+
const snapshot = cloneSnapshot(current);
|
|
3694
|
+
for (const listener of [...listeners]) {
|
|
3695
|
+
try {
|
|
3696
|
+
listener(snapshot);
|
|
3697
|
+
} catch {
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
};
|
|
3701
|
+
const process = async (command) => {
|
|
3702
|
+
const candidate = command;
|
|
3703
|
+
const commandId = typeof candidate?.commandId === "string" ? candidate.commandId : "";
|
|
3704
|
+
if (!candidate || typeof candidate.authorityInstanceId !== "string" || candidate.authorityInstanceId.length === 0 || typeof candidate.pluginId !== "string" || candidate.pluginId.length === 0 || typeof candidate.desiredEnabled !== "boolean" || !Number.isSafeInteger(candidate.expectedRevision) || (candidate.expectedRevision ?? -1) < 0 || commandId.length === 0) {
|
|
3705
|
+
const result = commandError("commandId\u3001pluginId \u548C expectedRevision \u5FC5\u987B\u662F\u6709\u6548\u503C");
|
|
3706
|
+
return { ...result, commandId };
|
|
3707
|
+
}
|
|
3708
|
+
if (command.authorityInstanceId !== authorityInstanceId) {
|
|
3709
|
+
return {
|
|
3710
|
+
status: "stale-authority",
|
|
3711
|
+
commandId: command.commandId,
|
|
3712
|
+
expectedAuthorityInstanceId: authorityInstanceId
|
|
3713
|
+
};
|
|
3714
|
+
}
|
|
3715
|
+
const fingerprint = commandFingerprint(command);
|
|
3716
|
+
const existing = commandRecords.get(command.commandId);
|
|
3717
|
+
if (existing) {
|
|
3718
|
+
if (existing.fingerprint !== fingerprint) {
|
|
3719
|
+
return {
|
|
3720
|
+
status: "command-conflict",
|
|
3721
|
+
commandId: command.commandId,
|
|
3722
|
+
message: "\u76F8\u540C commandId \u7684\u547D\u4EE4\u5185\u5BB9\u4E0D\u540C\uFF0C\u62D2\u7EDD\u8986\u76D6\u539F\u547D\u4EE4"
|
|
3723
|
+
};
|
|
3724
|
+
}
|
|
3725
|
+
return {
|
|
3726
|
+
status: "duplicate",
|
|
3727
|
+
commandId: command.commandId,
|
|
3728
|
+
snapshot: cloneSnapshot(existing.result.snapshot),
|
|
3729
|
+
persisted: true
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
if (command.expectedRevision !== current.revision) {
|
|
3733
|
+
return {
|
|
3734
|
+
status: "revision-conflict",
|
|
3735
|
+
commandId: command.commandId,
|
|
3736
|
+
snapshot: cloneSnapshot(current)
|
|
3737
|
+
};
|
|
3738
|
+
}
|
|
3739
|
+
const next = {
|
|
3740
|
+
revision: current.revision + 1,
|
|
3741
|
+
desiredEnabled: {
|
|
3742
|
+
...current.desiredEnabled,
|
|
3743
|
+
[command.pluginId]: command.desiredEnabled
|
|
3744
|
+
},
|
|
3745
|
+
desiredRevision: {
|
|
3746
|
+
...current.desiredRevision,
|
|
3747
|
+
[command.pluginId]: (current.desiredRevision[command.pluginId] ?? 0) + 1
|
|
3748
|
+
}
|
|
3749
|
+
};
|
|
3750
|
+
try {
|
|
3751
|
+
await options.persist?.(cloneSnapshot(next));
|
|
3752
|
+
} catch (error) {
|
|
3753
|
+
return {
|
|
3754
|
+
status: "persistence-failed",
|
|
3755
|
+
commandId: command.commandId,
|
|
3756
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3757
|
+
snapshot: cloneSnapshot(current)
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3760
|
+
current = next;
|
|
3761
|
+
const accepted = {
|
|
3762
|
+
status: "accepted",
|
|
3763
|
+
commandId: command.commandId,
|
|
3764
|
+
snapshot: cloneSnapshot(current),
|
|
3765
|
+
persisted: true
|
|
3766
|
+
};
|
|
3767
|
+
commandRecords.set(command.commandId, {
|
|
3768
|
+
fingerprint,
|
|
3769
|
+
result: accepted
|
|
3770
|
+
});
|
|
3771
|
+
while (commandRecords.size > maxCommandRecords) {
|
|
3772
|
+
const oldest = commandRecords.keys().next().value;
|
|
3773
|
+
if (oldest === void 0) break;
|
|
3774
|
+
commandRecords.delete(oldest);
|
|
3775
|
+
}
|
|
3776
|
+
notify();
|
|
3777
|
+
return {
|
|
3778
|
+
status: "accepted",
|
|
3779
|
+
commandId: accepted.commandId,
|
|
3780
|
+
snapshot: cloneSnapshot(accepted.snapshot),
|
|
3781
|
+
persisted: true
|
|
3782
|
+
};
|
|
3783
|
+
};
|
|
3784
|
+
const controller = {
|
|
3785
|
+
authorityInstanceId,
|
|
3786
|
+
snapshot: () => cloneSnapshot(current),
|
|
3787
|
+
submit(command) {
|
|
3788
|
+
const result = queue.then(() => process(command));
|
|
3789
|
+
queue = result.then(() => void 0, () => void 0);
|
|
3790
|
+
return result;
|
|
3791
|
+
},
|
|
3792
|
+
subscribe(listener) {
|
|
3793
|
+
listeners.add(listener);
|
|
3794
|
+
return () => listeners.delete(listener);
|
|
3795
|
+
}
|
|
3796
|
+
};
|
|
3797
|
+
return controller;
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3734
3800
|
export { LIFECYCLE_ERROR_TEXT, LifecycleScopeRevokedError, PermissionDeniedError, PermissionLeaseRevokedError, PluginGraphValidationError, RESOURCE_OWNER, RESOURCE_REGISTRY_CAPABILITY, RUNTIME_MESSAGE_BUS, RemoteServiceUnavailableError, SCOPED_TASK_SCHEDULER_CAPABILITY, StartupCapabilityError, StartupPluginError, UpgradeGateRejectedError, buildPluginGraph, createCapabilityRegistry, createInMemoryPluginConfigStore, createLifecycleScope, createMessageBus, createMessagePortServiceTransport, createPermissionLease, createPluginHost, createPluginIntentController, createRemoteServiceBridge, createRemoteServiceMessageCodec, createResourceRegistry, createResourceScope, createResourceStore, createRuntimeUnitImplementationRegistry, createScopedMessageBus, createScopedTaskScheduler, createServiceBridge, dependenciesOfManifest, lifecycleErrorText, providesOfManifest, registerOwnedResource, reverseDependentsOf, validatePluginGraph, validateRuntimeUnitDependencyContracts };
|
|
3735
|
-
//# sourceMappingURL=chunk-
|
|
3736
|
-
//# sourceMappingURL=chunk-
|
|
3801
|
+
//# sourceMappingURL=chunk-URY3E6UH.js.map
|
|
3802
|
+
//# sourceMappingURL=chunk-URY3E6UH.js.map
|