webloom-framework 0.2.0 → 0.3.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 +11 -8
- package/dist/{chunk-URY3E6UH.js → chunk-76BGPI6M.js} +771 -312
- package/dist/chunk-76BGPI6M.js.map +1 -0
- package/dist/{createPluginHost-ChJNBTsX.d.ts → createPluginHost-BVsUCDKN.d.ts} +67 -88
- package/dist/index.d.ts +15 -200
- package/dist/index.js +276 -884
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/resourceRegistry-BFnjmeGE.d.ts +267 -0
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +2 -2
- package/docs/api.md +20 -19
- package/docs/migration-baseline.md +2 -2
- package/docs/proposals/browser-runtime-v1/implementation-plan.md +7 -1
- package/docs/proposals/browser-runtime-v1/requirements.md +6 -1
- package/docs/proposals/browser-runtime-v1/verification.md +20 -13
- package/docs/proposals/shared-worker-call-first/SWCF-009-typed-transfer-follow-up.md +34 -0
- package/docs/proposals/shared-worker-call-first/implementation-plan.md +567 -0
- package/package.json +1 -1
- package/dist/chunk-URY3E6UH.js.map +0 -1
- package/dist/resourceRegistry-MNM1c7od.d.ts +0 -157
|
@@ -24,16 +24,12 @@ var PermissionDeniedError = class extends Error {
|
|
|
24
24
|
};
|
|
25
25
|
function createRemoteServiceMessageCodec(options = {}) {
|
|
26
26
|
const prefix = options.prefix ?? "webloom.remote-service";
|
|
27
|
-
const protocolVersion = options.protocolVersion ?? "
|
|
27
|
+
const protocolVersion = options.protocolVersion ?? "webloom.remote-service.v2";
|
|
28
28
|
const types = /* @__PURE__ */ new Map([
|
|
29
29
|
["call", `${prefix}.call`],
|
|
30
30
|
["result", `${prefix}.result`],
|
|
31
31
|
["error", `${prefix}.error`],
|
|
32
|
-
["cancel", `${prefix}.cancel`]
|
|
33
|
-
["handshake", `${prefix}.handshake`],
|
|
34
|
-
["snapshot", `${prefix}.snapshot`],
|
|
35
|
-
["invalidate", `${prefix}.invalidate`],
|
|
36
|
-
["disconnect", `${prefix}.disconnect`]
|
|
32
|
+
["cancel", `${prefix}.cancel`]
|
|
37
33
|
]);
|
|
38
34
|
return Object.freeze({
|
|
39
35
|
protocolVersion,
|
|
@@ -53,11 +49,14 @@ function createRemoteServiceMessageCodec(options = {}) {
|
|
|
53
49
|
}
|
|
54
50
|
});
|
|
55
51
|
}
|
|
56
|
-
var
|
|
57
|
-
code
|
|
58
|
-
|
|
52
|
+
var RemoteServiceError = class extends Error {
|
|
53
|
+
code;
|
|
54
|
+
details;
|
|
55
|
+
constructor(code, message, details) {
|
|
59
56
|
super(message);
|
|
60
|
-
this.name = "
|
|
57
|
+
this.name = "RemoteServiceError";
|
|
58
|
+
this.code = code;
|
|
59
|
+
this.details = details;
|
|
61
60
|
}
|
|
62
61
|
};
|
|
63
62
|
var UpgradeGateRejectedError = class extends Error {
|
|
@@ -3147,51 +3146,82 @@ function createRuntimeUnitImplementationRegistry(implementations = []) {
|
|
|
3147
3146
|
};
|
|
3148
3147
|
}
|
|
3149
3148
|
|
|
3150
|
-
// src/
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
function mergeSignals2(...signals) {
|
|
3161
|
-
const activeSignals = signals.filter((signal) => signal !== void 0);
|
|
3162
|
-
if (activeSignals.length === 0) {
|
|
3163
|
-
return { signal: new AbortController().signal, dispose: () => void 0 };
|
|
3164
|
-
}
|
|
3165
|
-
if (activeSignals.length === 1) {
|
|
3166
|
-
const [signal] = activeSignals;
|
|
3167
|
-
if (signal) return { signal, dispose: () => void 0 };
|
|
3149
|
+
// src/runtime/runtimeTypes.ts
|
|
3150
|
+
var RuntimeInitializationError = class extends Error {
|
|
3151
|
+
code = "runtime_initialization_failed";
|
|
3152
|
+
details;
|
|
3153
|
+
constructor(details) {
|
|
3154
|
+
super(
|
|
3155
|
+
`Runtime initialization failed${details.pluginId ? ` for ${details.pluginId}` : ""} during ${details.phase}: ${details.error}`
|
|
3156
|
+
);
|
|
3157
|
+
this.name = "RuntimeInitializationError";
|
|
3158
|
+
this.details = details;
|
|
3168
3159
|
}
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3160
|
+
};
|
|
3161
|
+
var RuntimeUnavailableError = class extends Error {
|
|
3162
|
+
code = "transport_unavailable";
|
|
3163
|
+
constructor(message = "Runtime is unavailable") {
|
|
3164
|
+
super(message);
|
|
3165
|
+
this.name = "RuntimeUnavailableError";
|
|
3174
3166
|
}
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3167
|
+
};
|
|
3168
|
+
|
|
3169
|
+
// src/runtime/runtimeProtocol.ts
|
|
3170
|
+
var RUNTIME_PROTOCOL_VERSION = "webloom.runtime.v2";
|
|
3171
|
+
var RUNTIME_SNAPSHOT_TYPE = "webloom.runtime.snapshot";
|
|
3172
|
+
var RUNTIME_ERROR_TYPE = "webloom.runtime.error";
|
|
3173
|
+
function isRecord2(input) {
|
|
3174
|
+
return typeof input === "object" && input !== null;
|
|
3175
|
+
}
|
|
3176
|
+
function isNonEmptyString(input) {
|
|
3177
|
+
return typeof input === "string" && input.length > 0;
|
|
3178
|
+
}
|
|
3179
|
+
function isRuntimeKind2(input) {
|
|
3180
|
+
return input === "window-main" || input === "shared-worker";
|
|
3181
|
+
}
|
|
3182
|
+
function isPluginStateKind(input) {
|
|
3183
|
+
return input === "registered" || input === "starting" || input === "stopping" || input === "enabled" || input === "disabled" || input === "blocked" || input === "error-disabled" || input === "cleanup-pending" || input === "unknown";
|
|
3184
|
+
}
|
|
3185
|
+
function isRemoteServiceReference(input) {
|
|
3186
|
+
if (!isRecord2(input)) return false;
|
|
3187
|
+
const reference = input;
|
|
3188
|
+
return isNonEmptyString(reference.capabilityId) && isNonEmptyString(reference.contractVersion) && isRuntimeKind2(reference.runtime) && isNonEmptyString(reference.runtimeInstanceId) && isNonEmptyString(reference.serviceInstanceId) && (reference.status === "starting" || reference.status === "ready" || reference.status === "unavailable" || reference.status === "failed") && isRecord2(reference.attributes) && !Array.isArray(reference.attributes) && (reference.grantId === void 0 || isNonEmptyString(reference.grantId)) && (reference.authorizationRevision === void 0 || Number.isSafeInteger(reference.authorizationRevision) && reference.authorizationRevision >= 0);
|
|
3189
|
+
}
|
|
3190
|
+
function isRuntimeSnapshotUnit(input) {
|
|
3191
|
+
if (!isRecord2(input)) return false;
|
|
3192
|
+
const unit = input;
|
|
3193
|
+
return isNonEmptyString(unit.pluginId) && isNonEmptyString(unit.unitId) && isRuntimeKind2(unit.runtime) && isPluginStateKind(unit.state) && (unit.instanceId === void 0 || isNonEmptyString(unit.instanceId));
|
|
3194
|
+
}
|
|
3195
|
+
function createRuntimeMessageCodec() {
|
|
3196
|
+
return createRemoteServiceMessageCodec({
|
|
3197
|
+
prefix: "webloom.runtime",
|
|
3198
|
+
protocolVersion: RUNTIME_PROTOCOL_VERSION
|
|
3187
3199
|
});
|
|
3200
|
+
}
|
|
3201
|
+
function isRuntimeSnapshot(input) {
|
|
3202
|
+
if (!isRecord2(input)) return false;
|
|
3203
|
+
const message = input;
|
|
3204
|
+
return message.type === RUNTIME_SNAPSHOT_TYPE && isNonEmptyString(message.protocolVersion) && isNonEmptyString(message.runtimeId) && isRuntimeKind2(message.runtimeKind) && isNonEmptyString(message.runtimeInstanceId) && Number.isSafeInteger(message.revision) && message.revision >= 0 && Array.isArray(message.units) && message.units.every(isRuntimeSnapshotUnit) && Array.isArray(message.services) && message.services.every(isRemoteServiceReference) && (message.state === "starting" || message.state === "ready" || message.state === "stopping" || message.state === "failed" || message.state === "disposed");
|
|
3205
|
+
}
|
|
3206
|
+
function isRuntimeSnapshotProtocol(input) {
|
|
3207
|
+
return input.protocolVersion === RUNTIME_PROTOCOL_VERSION;
|
|
3208
|
+
}
|
|
3209
|
+
function isRuntimeError(input) {
|
|
3210
|
+
if (!isRecord2(input)) return false;
|
|
3211
|
+
const message = input;
|
|
3212
|
+
return message.type === RUNTIME_ERROR_TYPE && isNonEmptyString(message.protocolVersion) && (message.code === "runtime_initialization_failed" || message.code === "protocol_mismatch" || message.code === "transport_unavailable") && isNonEmptyString(message.message) && (message.pluginId === void 0 || isNonEmptyString(message.pluginId)) && (message.unitId === void 0 || isNonEmptyString(message.unitId)) && (message.phase === void 0 || message.phase === "startup" || message.phase === "snapshot");
|
|
3213
|
+
}
|
|
3214
|
+
function unitSnapshotFromState(state, runtime) {
|
|
3188
3215
|
return {
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
}
|
|
3216
|
+
pluginId: state.pluginId,
|
|
3217
|
+
unitId: state.unitId,
|
|
3218
|
+
runtime,
|
|
3219
|
+
...state.instanceId !== void 0 ? { instanceId: state.instanceId } : {},
|
|
3220
|
+
state: state.kind
|
|
3193
3221
|
};
|
|
3194
3222
|
}
|
|
3223
|
+
|
|
3224
|
+
// src/transport/serviceBridge.ts
|
|
3195
3225
|
function stableAttributes(value) {
|
|
3196
3226
|
const normalize = (input) => {
|
|
3197
3227
|
if (Array.isArray(input)) return input.map(normalize);
|
|
@@ -3202,34 +3232,127 @@ function stableAttributes(value) {
|
|
|
3202
3232
|
};
|
|
3203
3233
|
return JSON.stringify(normalize(value));
|
|
3204
3234
|
}
|
|
3235
|
+
function isPlainRecord(value) {
|
|
3236
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3237
|
+
const prototype = Object.getPrototypeOf(value);
|
|
3238
|
+
return prototype === Object.prototype || prototype === null;
|
|
3239
|
+
}
|
|
3240
|
+
function isSafeAttributeValue(value, seen) {
|
|
3241
|
+
if (value === null || value === void 0 || typeof value === "string" || typeof value === "boolean") return true;
|
|
3242
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
3243
|
+
if (typeof value !== "object") return false;
|
|
3244
|
+
if (seen.has(value)) return false;
|
|
3245
|
+
seen.add(value);
|
|
3246
|
+
const valid = Array.isArray(value) ? value.every((item) => isSafeAttributeValue(item, seen)) : isPlainRecord(value) && !Reflect.ownKeys(value).some((key) => typeof key !== "string") && Object.values(value).every((item) => isSafeAttributeValue(item, seen));
|
|
3247
|
+
seen.delete(value);
|
|
3248
|
+
return valid;
|
|
3249
|
+
}
|
|
3250
|
+
function isValidAttributes(value) {
|
|
3251
|
+
return isPlainRecord(value) && isSafeAttributeValue(value, /* @__PURE__ */ new Set());
|
|
3252
|
+
}
|
|
3253
|
+
function isValidSnapshot(snapshot) {
|
|
3254
|
+
if (!snapshot || typeof snapshot !== "object" || typeof snapshot.protocolVersion !== "string" || snapshot.protocolVersion.length === 0 || typeof snapshot.runtimeInstanceId !== "string" || snapshot.runtimeInstanceId.length === 0 || !Number.isSafeInteger(snapshot.revision) || snapshot.revision < 0 || !Array.isArray(snapshot.services)) return false;
|
|
3255
|
+
if (snapshot.runtimeId !== void 0 && (typeof snapshot.runtimeId !== "string" || snapshot.runtimeId.length === 0)) return false;
|
|
3256
|
+
if (snapshot.runtimeKind !== void 0 && snapshot.runtimeKind !== "window-main" && snapshot.runtimeKind !== "shared-worker") return false;
|
|
3257
|
+
if (snapshot.state !== void 0 && snapshot.state !== "starting" && snapshot.state !== "ready" && snapshot.state !== "stopping" && snapshot.state !== "failed" && snapshot.state !== "disposed") return false;
|
|
3258
|
+
const bindingKeys = /* @__PURE__ */ new Set();
|
|
3259
|
+
const serviceCapabilityKeys = /* @__PURE__ */ new Set();
|
|
3260
|
+
const readyLookupKeys = /* @__PURE__ */ new Set();
|
|
3261
|
+
for (const service of snapshot.services) {
|
|
3262
|
+
if (!service || typeof service !== "object" || typeof service.capabilityId !== "string" || service.capabilityId.length === 0 || typeof service.contractVersion !== "string" || service.contractVersion.length === 0 || service.runtime !== "window-main" && service.runtime !== "shared-worker" || service.runtimeInstanceId !== snapshot.runtimeInstanceId || typeof service.serviceInstanceId !== "string" || service.serviceInstanceId.length === 0 || service.status !== "starting" && service.status !== "ready" && service.status !== "unavailable" && service.status !== "failed" || !isValidAttributes(service.attributes) || service.grantId !== void 0 && (typeof service.grantId !== "string" || service.grantId.length === 0) || service.authorizationRevision !== void 0 && (!Number.isSafeInteger(service.authorizationRevision) || service.authorizationRevision < 0)) return false;
|
|
3263
|
+
if (snapshot.runtimeKind !== void 0 && service.runtime !== snapshot.runtimeKind) return false;
|
|
3264
|
+
const key = bindingKey(service);
|
|
3265
|
+
if (bindingKeys.has(key)) return false;
|
|
3266
|
+
bindingKeys.add(key);
|
|
3267
|
+
const serviceCapabilityKey = `${service.capabilityId}\0${service.serviceInstanceId}`;
|
|
3268
|
+
if (serviceCapabilityKeys.has(serviceCapabilityKey)) return false;
|
|
3269
|
+
serviceCapabilityKeys.add(serviceCapabilityKey);
|
|
3270
|
+
if (service.status === "ready") {
|
|
3271
|
+
const lookupKey = `${service.capabilityId}\0${service.contractVersion}\0${service.runtime}`;
|
|
3272
|
+
if (readyLookupKeys.has(lookupKey)) return false;
|
|
3273
|
+
readyLookupKeys.add(lookupKey);
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
return true;
|
|
3277
|
+
}
|
|
3205
3278
|
function referenceKey(reference) {
|
|
3206
3279
|
return [
|
|
3207
3280
|
reference.capabilityId,
|
|
3208
|
-
reference.providerInstanceId,
|
|
3209
|
-
reference.runtime,
|
|
3210
3281
|
reference.contractVersion,
|
|
3211
|
-
reference.
|
|
3212
|
-
reference.
|
|
3213
|
-
reference.
|
|
3214
|
-
reference.
|
|
3282
|
+
reference.runtime,
|
|
3283
|
+
reference.runtimeInstanceId,
|
|
3284
|
+
reference.serviceInstanceId,
|
|
3285
|
+
reference.status,
|
|
3215
3286
|
stableAttributes(reference.attributes),
|
|
3216
3287
|
reference.grantId ?? "null",
|
|
3217
|
-
reference.authorizationRevision ?? "null"
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3288
|
+
reference.authorizationRevision ?? "null"
|
|
3289
|
+
].join("\0");
|
|
3290
|
+
}
|
|
3291
|
+
function bindingKey(reference) {
|
|
3292
|
+
return [
|
|
3293
|
+
reference.capabilityId,
|
|
3294
|
+
reference.contractVersion,
|
|
3295
|
+
reference.runtime,
|
|
3296
|
+
reference.runtimeInstanceId,
|
|
3297
|
+
reference.serviceInstanceId
|
|
3221
3298
|
].join("\0");
|
|
3222
3299
|
}
|
|
3223
3300
|
function lookupMatches(reference, lookup) {
|
|
3224
|
-
return reference.
|
|
3301
|
+
return reference.capabilityId === lookup.capabilityId && reference.contractVersion === lookup.contractVersion && (lookup.runtime === void 0 || reference.runtime === lookup.runtime);
|
|
3302
|
+
}
|
|
3303
|
+
function ensureTimeout(value, label) {
|
|
3304
|
+
const timeout = value ?? 3e4;
|
|
3305
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
3306
|
+
throw new TypeError(`${label} must be a finite number greater than zero`);
|
|
3307
|
+
}
|
|
3308
|
+
return timeout;
|
|
3309
|
+
}
|
|
3310
|
+
function mergeSignals2(...signals) {
|
|
3311
|
+
const active = signals.filter((signal) => signal !== void 0);
|
|
3312
|
+
if (active.length === 0) return { signal: new AbortController().signal, dispose: () => void 0 };
|
|
3313
|
+
const alreadyAborted = active.find((signal) => signal.aborted);
|
|
3314
|
+
if (alreadyAborted) {
|
|
3315
|
+
const controller2 = new AbortController();
|
|
3316
|
+
controller2.abort(alreadyAborted.reason);
|
|
3317
|
+
return { signal: controller2.signal, dispose: () => void 0 };
|
|
3318
|
+
}
|
|
3319
|
+
const controller = new AbortController();
|
|
3320
|
+
const listeners = active.map((signal) => {
|
|
3321
|
+
const listener = () => {
|
|
3322
|
+
try {
|
|
3323
|
+
controller.abort(signal.reason);
|
|
3324
|
+
} catch {
|
|
3325
|
+
controller.abort();
|
|
3326
|
+
}
|
|
3327
|
+
};
|
|
3328
|
+
signal.addEventListener("abort", listener, { once: true });
|
|
3329
|
+
return { signal, listener };
|
|
3330
|
+
});
|
|
3331
|
+
return {
|
|
3332
|
+
signal: controller.signal,
|
|
3333
|
+
dispose: () => {
|
|
3334
|
+
for (const { signal, listener } of listeners) signal.removeEventListener("abort", listener);
|
|
3335
|
+
}
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3338
|
+
function abortedError(signal, fallback = "request_cancelled") {
|
|
3339
|
+
const reason = signal.reason;
|
|
3340
|
+
if (reason instanceof Error && typeof reason.code === "string") return reason;
|
|
3341
|
+
if (reason instanceof Error && fallback === "request_cancelled") {
|
|
3342
|
+
return new RemoteServiceError("request_cancelled", reason.message);
|
|
3343
|
+
}
|
|
3344
|
+
return new RemoteServiceError(fallback, fallback === "call_timeout" ? "Remote service call timed out" : "Remote service request was cancelled");
|
|
3345
|
+
}
|
|
3346
|
+
function callTimeoutError(timeoutMs) {
|
|
3347
|
+
return new RemoteServiceError("call_timeout", `Remote service call exceeded its ${timeoutMs}ms deadline`, { timeoutMs });
|
|
3225
3348
|
}
|
|
3226
3349
|
function createServiceBridge(options) {
|
|
3227
|
-
|
|
3228
|
-
let
|
|
3229
|
-
let
|
|
3230
|
-
let
|
|
3231
|
-
let
|
|
3232
|
-
|
|
3350
|
+
const defaultCallTimeoutMs = ensureTimeout(options.defaultCallTimeoutMs, "defaultCallTimeoutMs");
|
|
3351
|
+
let currentState = "empty";
|
|
3352
|
+
let currentRuntimeInstanceId;
|
|
3353
|
+
let currentRevision;
|
|
3354
|
+
let currentServices = [];
|
|
3355
|
+
let terminalError;
|
|
3233
3356
|
const proxies = /* @__PURE__ */ new Set();
|
|
3234
3357
|
const listeners = /* @__PURE__ */ new Set();
|
|
3235
3358
|
const notify = () => {
|
|
@@ -3240,155 +3363,162 @@ function createServiceBridge(options) {
|
|
|
3240
3363
|
}
|
|
3241
3364
|
}
|
|
3242
3365
|
};
|
|
3243
|
-
const revokeProxy = (
|
|
3244
|
-
if (
|
|
3245
|
-
|
|
3246
|
-
|
|
3366
|
+
const revokeProxy = (record, reason, code = "service_revoked") => {
|
|
3367
|
+
if (record.revoked) return;
|
|
3368
|
+
record.revoked = true;
|
|
3369
|
+
record.reason = reason;
|
|
3247
3370
|
try {
|
|
3248
|
-
|
|
3371
|
+
record.controller.abort(new RemoteServiceError(code, reason));
|
|
3249
3372
|
} catch {
|
|
3250
|
-
|
|
3373
|
+
record.controller.abort();
|
|
3251
3374
|
}
|
|
3252
3375
|
};
|
|
3253
|
-
const
|
|
3254
|
-
for (const
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3376
|
+
const revokeAll = (reason, code = "service_revoked") => {
|
|
3377
|
+
for (const record of proxies) revokeProxy(record, reason, code);
|
|
3378
|
+
};
|
|
3379
|
+
const currentMatches = (lookup) => currentServices.filter((reference) => reference.status === "ready" && lookupMatches(reference, lookup));
|
|
3380
|
+
const findBinding = (record, lookup) => {
|
|
3381
|
+
if (record.boundReference) return record.boundReference;
|
|
3382
|
+
if (terminalError) throw terminalError;
|
|
3383
|
+
const matches = currentMatches(lookup);
|
|
3384
|
+
if (matches.length > 1) {
|
|
3385
|
+
throw new RemoteServiceError(
|
|
3386
|
+
"capability_unavailable",
|
|
3387
|
+
`Service "${lookup.capabilityId}" version "${lookup.contractVersion}" has multiple ready instances`
|
|
3388
|
+
);
|
|
3389
|
+
}
|
|
3390
|
+
const reference = matches[0];
|
|
3391
|
+
if (reference) {
|
|
3392
|
+
record.boundReference = Object.freeze({
|
|
3393
|
+
...reference,
|
|
3394
|
+
attributes: Object.freeze({ ...reference.attributes })
|
|
3395
|
+
});
|
|
3396
|
+
return record.boundReference;
|
|
3259
3397
|
}
|
|
3398
|
+
return void 0;
|
|
3260
3399
|
};
|
|
3261
|
-
const
|
|
3262
|
-
|
|
3263
|
-
|
|
3400
|
+
const waitForBinding = (record, lookup, signal) => {
|
|
3401
|
+
const immediate = findBinding(record, lookup);
|
|
3402
|
+
if (immediate) return Promise.resolve(immediate);
|
|
3403
|
+
if (record.revoked) return Promise.reject(new RemoteServiceError("service_revoked", record.reason));
|
|
3404
|
+
if (signal.aborted) return Promise.reject(abortedError(signal));
|
|
3405
|
+
return new Promise((resolve, reject) => {
|
|
3406
|
+
let settled = false;
|
|
3407
|
+
const finish = (callback) => {
|
|
3408
|
+
if (settled) return;
|
|
3409
|
+
settled = true;
|
|
3410
|
+
removeListener();
|
|
3411
|
+
callback();
|
|
3412
|
+
};
|
|
3413
|
+
const check = () => {
|
|
3414
|
+
if (record.revoked) {
|
|
3415
|
+
finish(() => reject(new RemoteServiceError("service_revoked", record.reason)));
|
|
3416
|
+
return;
|
|
3417
|
+
}
|
|
3418
|
+
if (signal.aborted) {
|
|
3419
|
+
finish(() => reject(abortedError(signal)));
|
|
3420
|
+
return;
|
|
3421
|
+
}
|
|
3422
|
+
try {
|
|
3423
|
+
const reference = findBinding(record, lookup);
|
|
3424
|
+
if (reference) finish(() => resolve(reference));
|
|
3425
|
+
} catch (error) {
|
|
3426
|
+
finish(() => reject(error));
|
|
3427
|
+
}
|
|
3428
|
+
};
|
|
3429
|
+
const onAbort = () => check();
|
|
3430
|
+
const listener = () => check();
|
|
3431
|
+
const removeListener = () => {
|
|
3432
|
+
signal.removeEventListener("abort", onAbort);
|
|
3433
|
+
listeners.delete(listener);
|
|
3434
|
+
};
|
|
3435
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3436
|
+
listeners.add(listener);
|
|
3437
|
+
check();
|
|
3438
|
+
});
|
|
3264
3439
|
};
|
|
3265
3440
|
const bridge = {
|
|
3266
3441
|
get state() {
|
|
3267
3442
|
return currentState;
|
|
3268
3443
|
},
|
|
3269
|
-
get
|
|
3270
|
-
return
|
|
3444
|
+
get runtimeInstanceId() {
|
|
3445
|
+
return currentRuntimeInstanceId;
|
|
3271
3446
|
},
|
|
3272
|
-
get
|
|
3273
|
-
return
|
|
3274
|
-
},
|
|
3275
|
-
handshake(input) {
|
|
3276
|
-
if (input.protocolVersion !== options.protocolVersion) {
|
|
3277
|
-
clearReferences("service bridge protocol mismatch");
|
|
3278
|
-
currentConnectionId = void 0;
|
|
3279
|
-
currentAuthorityInstanceId = void 0;
|
|
3280
|
-
lastRevision = void 0;
|
|
3281
|
-
hasBaseline = false;
|
|
3282
|
-
currentState = "disconnected";
|
|
3283
|
-
notify();
|
|
3284
|
-
return { accepted: false, reason: "protocol-mismatch" };
|
|
3285
|
-
}
|
|
3286
|
-
const isNewBinding = currentConnectionId !== input.connectionId || currentAuthorityInstanceId !== input.authorityInstanceId;
|
|
3287
|
-
if (isNewBinding) {
|
|
3288
|
-
clearReferences("remote service authority or connection changed");
|
|
3289
|
-
lastRevision = void 0;
|
|
3290
|
-
hasBaseline = false;
|
|
3291
|
-
}
|
|
3292
|
-
currentConnectionId = input.connectionId;
|
|
3293
|
-
currentAuthorityInstanceId = input.authorityInstanceId;
|
|
3294
|
-
currentState = "handshaking";
|
|
3295
|
-
notify();
|
|
3296
|
-
return { accepted: true };
|
|
3447
|
+
get defaultCallTimeoutMs() {
|
|
3448
|
+
return defaultCallTimeoutMs;
|
|
3297
3449
|
},
|
|
3298
3450
|
applySnapshot(snapshot) {
|
|
3299
|
-
if (
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
reason: "wrong-connection",
|
|
3303
|
-
receivedRevision: snapshot.snapshotRevision
|
|
3304
|
-
};
|
|
3451
|
+
if (currentState === "disposed") return { accepted: false, reason: "disposed" };
|
|
3452
|
+
if (snapshot.protocolVersion !== options.protocolVersion) {
|
|
3453
|
+
return { accepted: false, reason: "protocol-mismatch", receivedRevision: snapshot.revision };
|
|
3305
3454
|
}
|
|
3306
|
-
if (snapshot
|
|
3307
|
-
return {
|
|
3308
|
-
accepted: false,
|
|
3309
|
-
reason: "wrong-authority",
|
|
3310
|
-
receivedRevision: snapshot.snapshotRevision
|
|
3311
|
-
};
|
|
3455
|
+
if (!isValidSnapshot(snapshot)) {
|
|
3456
|
+
return { accepted: false, reason: "invalid-snapshot", receivedRevision: snapshot.revision };
|
|
3312
3457
|
}
|
|
3313
|
-
if (
|
|
3314
|
-
return {
|
|
3315
|
-
accepted: false,
|
|
3316
|
-
reason: "stale-revision",
|
|
3317
|
-
receivedRevision: snapshot.snapshotRevision
|
|
3318
|
-
};
|
|
3458
|
+
if (currentRuntimeInstanceId === snapshot.runtimeInstanceId && currentRevision !== void 0 && snapshot.revision <= currentRevision) {
|
|
3459
|
+
return { accepted: false, reason: "stale-revision", receivedRevision: snapshot.revision };
|
|
3319
3460
|
}
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
}
|
|
3461
|
+
const runtimeChanged = currentRuntimeInstanceId !== void 0 && currentRuntimeInstanceId !== snapshot.runtimeInstanceId;
|
|
3462
|
+
if (runtimeChanged) revokeAll("Runtime instance changed; proxy cannot be rebound", "service_stale");
|
|
3463
|
+
const nextServices = snapshot.services.map((service) => Object.freeze({
|
|
3464
|
+
...service,
|
|
3465
|
+
attributes: Object.freeze({ ...service.attributes })
|
|
3466
|
+
}));
|
|
3467
|
+
const nextReady = new Map(nextServices.filter((service) => service.status === "ready").map((service) => [bindingKey(service), service]));
|
|
3468
|
+
if (!runtimeChanged) {
|
|
3469
|
+
for (const record of proxies) {
|
|
3470
|
+
const bound = record.boundReference;
|
|
3471
|
+
if (!bound) continue;
|
|
3472
|
+
const replacement = nextReady.get(bindingKey(bound));
|
|
3473
|
+
if (!replacement || referenceKey(replacement) !== referenceKey(bound)) {
|
|
3474
|
+
revokeProxy(record, "Bound service instance was replaced or revoked", "service_revoked");
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3336
3477
|
}
|
|
3337
|
-
|
|
3478
|
+
currentRuntimeInstanceId = snapshot.runtimeInstanceId;
|
|
3479
|
+
currentRevision = snapshot.revision;
|
|
3480
|
+
currentServices = Object.freeze(nextServices);
|
|
3481
|
+
terminalError = void 0;
|
|
3482
|
+
if (snapshot.state === "failed") {
|
|
3338
3483
|
currentState = "stale";
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
}
|
|
3349
|
-
const next = /* @__PURE__ */ new Map();
|
|
3350
|
-
for (const reference of snapshot.services) {
|
|
3351
|
-
if (reference.authorityInstanceId !== currentAuthorityInstanceId || reference.connectionId !== void 0 && reference.connectionId !== snapshot.connectionId || reference.snapshotRevision !== snapshot.snapshotRevision) continue;
|
|
3352
|
-
next.set(referenceKey(reference), Object.freeze({ ...reference }));
|
|
3484
|
+
terminalError = new RemoteServiceError("runtime_initialization_failed", "Remote Runtime initialization failed");
|
|
3485
|
+
} else if (snapshot.state === "disposed" || snapshot.state === "stopping") {
|
|
3486
|
+
currentState = "stale";
|
|
3487
|
+
terminalError = new RemoteServiceError("transport_unavailable", "Remote Runtime is no longer accepting calls");
|
|
3488
|
+
revokeAll("Remote Runtime is no longer accepting calls", "transport_unavailable");
|
|
3489
|
+
} else if (snapshot.state === "ready") {
|
|
3490
|
+
currentState = "ready";
|
|
3491
|
+
} else {
|
|
3492
|
+
currentState = "empty";
|
|
3353
3493
|
}
|
|
3354
|
-
references.clear();
|
|
3355
|
-
for (const [key, reference] of next) references.set(key, reference);
|
|
3356
|
-
const validKeys = new Set(
|
|
3357
|
-
[...next.values()].filter((reference) => reference.status === "ready").map(referenceKey)
|
|
3358
|
-
);
|
|
3359
|
-
invalidateProxies("remote service reference is no longer ready", validKeys);
|
|
3360
|
-
lastRevision = snapshot.snapshotRevision;
|
|
3361
|
-
hasBaseline = true;
|
|
3362
|
-
currentState = "ready";
|
|
3363
3494
|
notify();
|
|
3364
|
-
return {
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3495
|
+
return { accepted: true, state: currentState, revision: snapshot.revision };
|
|
3496
|
+
},
|
|
3497
|
+
markProtocolMismatch(reason = "Remote Runtime protocol version mismatch") {
|
|
3498
|
+
if (currentState === "disposed") return;
|
|
3499
|
+
terminalError = new RemoteServiceError("protocol_mismatch", reason);
|
|
3500
|
+
revokeAll(reason, "protocol_mismatch");
|
|
3501
|
+
currentState = "stale";
|
|
3502
|
+
notify();
|
|
3503
|
+
},
|
|
3504
|
+
markInitializationFailed(reason = "Remote Runtime initialization failed") {
|
|
3505
|
+
if (currentState === "disposed") return;
|
|
3506
|
+
terminalError = new RemoteServiceError("runtime_initialization_failed", reason);
|
|
3507
|
+
revokeAll(reason, "runtime_initialization_failed");
|
|
3508
|
+
currentState = "stale";
|
|
3509
|
+
notify();
|
|
3369
3510
|
},
|
|
3370
3511
|
getProxy(lookup, scope) {
|
|
3371
|
-
if (currentState !== "ready") return void 0;
|
|
3372
|
-
const matches = [...references.values()].filter((candidate) => lookupMatches(candidate, lookup));
|
|
3373
|
-
if (matches.length > 1) {
|
|
3374
|
-
throw new RemoteServiceUnavailableError(
|
|
3375
|
-
`Service reference is ambiguous for "${lookup.capabilityId}" version "${lookup.contractVersion}"`
|
|
3376
|
-
);
|
|
3377
|
-
}
|
|
3378
|
-
const reference = matches[0];
|
|
3379
|
-
if (!reference) return void 0;
|
|
3380
3512
|
const record = {
|
|
3381
|
-
reference,
|
|
3382
3513
|
revoked: false,
|
|
3383
|
-
reason: "
|
|
3514
|
+
reason: "Remote service proxy revoked",
|
|
3384
3515
|
controller: new AbortController()
|
|
3385
3516
|
};
|
|
3386
3517
|
proxies.add(record);
|
|
3387
3518
|
let removeScopeRevoke;
|
|
3388
3519
|
let removeScopeDispose;
|
|
3389
|
-
let proxy;
|
|
3390
3520
|
const revokeFromScope = (reason) => {
|
|
3391
|
-
revokeProxy(record, reason);
|
|
3521
|
+
revokeProxy(record, reason, "service_revoked");
|
|
3392
3522
|
proxies.delete(record);
|
|
3393
3523
|
removeScopeRevoke?.();
|
|
3394
3524
|
removeScopeDispose?.();
|
|
@@ -3396,70 +3526,68 @@ function createServiceBridge(options) {
|
|
|
3396
3526
|
if (scope) {
|
|
3397
3527
|
try {
|
|
3398
3528
|
removeScopeRevoke = scope.onRevoke(revokeFromScope);
|
|
3399
|
-
removeScopeDispose = scope.onDispose(revokeFromScope, `service-proxy:${
|
|
3529
|
+
removeScopeDispose = scope.onDispose(revokeFromScope, `service-proxy:${lookup.capabilityId}`);
|
|
3400
3530
|
} catch {
|
|
3401
|
-
|
|
3531
|
+
revokeFromScope("Remote service proxy scope is already revoked");
|
|
3402
3532
|
}
|
|
3403
3533
|
}
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
return void 0;
|
|
3409
|
-
}
|
|
3410
|
-
proxy = {
|
|
3411
|
-
reference,
|
|
3534
|
+
const proxy = {
|
|
3535
|
+
get reference() {
|
|
3536
|
+
return record.boundReference;
|
|
3537
|
+
},
|
|
3412
3538
|
get revoked() {
|
|
3413
3539
|
return record.revoked;
|
|
3414
3540
|
},
|
|
3415
3541
|
async call(request, callOptions = {}) {
|
|
3416
|
-
if (record.revoked) throw new
|
|
3417
|
-
const
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
}
|
|
3429
|
-
const context = {
|
|
3430
|
-
// requestId 只作为旧调用方的业务 operationId 别名;真正的
|
|
3431
|
-
// MessagePort callId 由传输层每次调用单独生成。
|
|
3432
|
-
operationId: callOptions.operationId ?? callOptions.requestId ?? makeRequestId(),
|
|
3433
|
-
connectionId,
|
|
3434
|
-
reference,
|
|
3435
|
-
grantId: reference.grantId,
|
|
3436
|
-
signal: merged.signal
|
|
3437
|
-
};
|
|
3438
|
-
let result;
|
|
3439
|
-
try {
|
|
3440
|
-
result = Promise.resolve(options.transport.call(request, context));
|
|
3441
|
-
} catch (error) {
|
|
3442
|
-
merged.dispose();
|
|
3443
|
-
throw error;
|
|
3444
|
-
}
|
|
3542
|
+
if (record.revoked) throw new RemoteServiceError("service_revoked", record.reason);
|
|
3543
|
+
const timeoutMs = ensureTimeout(callOptions.timeoutMs ?? defaultCallTimeoutMs, "timeoutMs");
|
|
3544
|
+
const deadlineAt = Date.now() + timeoutMs;
|
|
3545
|
+
const timeoutController = new AbortController();
|
|
3546
|
+
const timeout = setTimeout(() => {
|
|
3547
|
+
try {
|
|
3548
|
+
timeoutController.abort(callTimeoutError(timeoutMs));
|
|
3549
|
+
} catch {
|
|
3550
|
+
timeoutController.abort();
|
|
3551
|
+
}
|
|
3552
|
+
}, timeoutMs);
|
|
3553
|
+
const merged = mergeSignals2(scope?.signal, record.controller.signal, callOptions.signal, timeoutController.signal);
|
|
3445
3554
|
try {
|
|
3555
|
+
const reference = await waitForBinding(record, lookup, merged.signal);
|
|
3556
|
+
if (merged.signal.aborted) throw abortedError(merged.signal);
|
|
3557
|
+
const context = {
|
|
3558
|
+
operationId: callOptions.operationId ?? callOptions.requestId,
|
|
3559
|
+
reference,
|
|
3560
|
+
grantId: reference.grantId,
|
|
3561
|
+
signal: merged.signal,
|
|
3562
|
+
deadlineAt,
|
|
3563
|
+
timeoutMs
|
|
3564
|
+
};
|
|
3565
|
+
let result;
|
|
3566
|
+
try {
|
|
3567
|
+
result = Promise.resolve(options.transport.call(request, context));
|
|
3568
|
+
} catch (error) {
|
|
3569
|
+
throw error;
|
|
3570
|
+
}
|
|
3446
3571
|
const value = await Promise.race([
|
|
3447
3572
|
result,
|
|
3448
3573
|
new Promise((_, reject) => {
|
|
3449
3574
|
if (merged.signal.aborted) {
|
|
3450
|
-
reject(merged.signal
|
|
3575
|
+
reject(abortedError(merged.signal));
|
|
3451
3576
|
return;
|
|
3452
3577
|
}
|
|
3453
|
-
const onAbort = () => reject(merged.signal
|
|
3578
|
+
const onAbort = () => reject(abortedError(merged.signal));
|
|
3454
3579
|
merged.signal.addEventListener("abort", onAbort, { once: true });
|
|
3455
3580
|
result.finally(() => merged.signal.removeEventListener("abort", onAbort)).catch(() => void 0);
|
|
3456
3581
|
})
|
|
3457
3582
|
]);
|
|
3458
|
-
if (record.revoked
|
|
3459
|
-
throw new RemoteServiceUnavailableError("Remote service request belongs to an invalidated proxy");
|
|
3460
|
-
}
|
|
3583
|
+
if (record.revoked) throw new RemoteServiceError("service_revoked", record.reason);
|
|
3461
3584
|
return value;
|
|
3585
|
+
} catch (error) {
|
|
3586
|
+
if (merged.signal.aborted) throw abortedError(merged.signal);
|
|
3587
|
+
if (error instanceof RemoteServiceError) throw error;
|
|
3588
|
+
throw error;
|
|
3462
3589
|
} finally {
|
|
3590
|
+
clearTimeout(timeout);
|
|
3463
3591
|
merged.dispose();
|
|
3464
3592
|
if (record.revoked) {
|
|
3465
3593
|
proxies.delete(record);
|
|
@@ -3468,124 +3596,203 @@ function createServiceBridge(options) {
|
|
|
3468
3596
|
}
|
|
3469
3597
|
}
|
|
3470
3598
|
},
|
|
3471
|
-
revoke(reason = "
|
|
3472
|
-
|
|
3473
|
-
proxies.delete(record);
|
|
3474
|
-
removeScopeRevoke?.();
|
|
3475
|
-
removeScopeDispose?.();
|
|
3599
|
+
revoke(reason = "Remote service proxy revoked") {
|
|
3600
|
+
revokeFromScope(reason);
|
|
3476
3601
|
}
|
|
3477
3602
|
};
|
|
3478
3603
|
return proxy;
|
|
3479
3604
|
},
|
|
3480
3605
|
requireProxy(lookup, scope) {
|
|
3481
|
-
|
|
3482
|
-
if (!proxy) {
|
|
3483
|
-
throw new RemoteServiceUnavailableError(
|
|
3484
|
-
`Service "${lookup.capabilityId}" version "${lookup.contractVersion}" is not ready`
|
|
3485
|
-
);
|
|
3486
|
-
}
|
|
3487
|
-
return proxy;
|
|
3606
|
+
return bridge.getProxy(lookup, scope);
|
|
3488
3607
|
},
|
|
3489
|
-
invalidate(reason = "
|
|
3490
|
-
|
|
3608
|
+
invalidate(reason = "Remote service directory invalidated") {
|
|
3609
|
+
if (currentState === "disposed") return;
|
|
3610
|
+
currentServices = [];
|
|
3611
|
+
currentRevision = void 0;
|
|
3612
|
+
terminalError = void 0;
|
|
3613
|
+
revokeAll(reason, "service_revoked");
|
|
3491
3614
|
currentState = "stale";
|
|
3492
|
-
lastRevision = void 0;
|
|
3493
|
-
hasBaseline = false;
|
|
3494
3615
|
notify();
|
|
3495
3616
|
},
|
|
3496
|
-
disconnect(reason = "
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3617
|
+
disconnect(reason = "Remote service transport disconnected") {
|
|
3618
|
+
if (currentState === "disposed") return;
|
|
3619
|
+
currentServices = [];
|
|
3620
|
+
currentRevision = void 0;
|
|
3621
|
+
currentRuntimeInstanceId = void 0;
|
|
3622
|
+
terminalError = new RemoteServiceError("transport_unavailable", reason);
|
|
3623
|
+
revokeAll(reason, "transport_unavailable");
|
|
3624
|
+
currentState = "stale";
|
|
3625
|
+
notify();
|
|
3626
|
+
},
|
|
3627
|
+
dispose(reason = "Remote service bridge disposed") {
|
|
3628
|
+
if (currentState === "disposed") return;
|
|
3629
|
+
currentServices = [];
|
|
3630
|
+
currentRevision = void 0;
|
|
3631
|
+
currentRuntimeInstanceId = void 0;
|
|
3632
|
+
terminalError = new RemoteServiceError("transport_unavailable", reason);
|
|
3633
|
+
revokeAll(reason, "transport_unavailable");
|
|
3634
|
+
currentState = "disposed";
|
|
3503
3635
|
notify();
|
|
3636
|
+
proxies.clear();
|
|
3504
3637
|
},
|
|
3505
3638
|
subscribe(listener) {
|
|
3506
3639
|
listeners.add(listener);
|
|
3507
3640
|
return () => listeners.delete(listener);
|
|
3508
3641
|
},
|
|
3509
3642
|
services() {
|
|
3510
|
-
return
|
|
3643
|
+
return currentServices;
|
|
3511
3644
|
}
|
|
3512
3645
|
};
|
|
3513
3646
|
return bridge;
|
|
3514
3647
|
}
|
|
3515
|
-
var createRemoteServiceBridge = createServiceBridge;
|
|
3516
3648
|
|
|
3517
3649
|
// src/transport/messagePortServiceTransport.ts
|
|
3518
3650
|
var nextTransportId = 0;
|
|
3519
3651
|
var nextCallSequence = 0;
|
|
3520
|
-
function makeTransportId() {
|
|
3521
|
-
nextTransportId += 1;
|
|
3522
|
-
return nextTransportId;
|
|
3523
|
-
}
|
|
3524
3652
|
function makeCallId(transportId) {
|
|
3525
3653
|
nextCallSequence += 1;
|
|
3526
3654
|
return `remote-call:${transportId}:${nextCallSequence}`;
|
|
3527
3655
|
}
|
|
3656
|
+
function finiteTimeout(value) {
|
|
3657
|
+
const timeout = value ?? 3e4;
|
|
3658
|
+
if (!Number.isFinite(timeout) || timeout <= 0) throw new TypeError("Remote service timeout must be finite and greater than zero");
|
|
3659
|
+
return timeout;
|
|
3660
|
+
}
|
|
3528
3661
|
function errorFromWire(input) {
|
|
3529
|
-
|
|
3530
|
-
typeof input?.
|
|
3662
|
+
return new RemoteServiceError(
|
|
3663
|
+
typeof input?.code === "string" && input.code.length > 0 ? input.code : "handler_failed",
|
|
3664
|
+
typeof input?.message === "string" ? input.message : "Remote service call failed",
|
|
3665
|
+
input?.details
|
|
3531
3666
|
);
|
|
3532
|
-
if (typeof input?.name === "string" && input.name.length > 0) error.name = input.name;
|
|
3533
|
-
if (typeof input?.code === "string" && input.code.length > 0) {
|
|
3534
|
-
Object.defineProperty(error, "code", {
|
|
3535
|
-
configurable: true,
|
|
3536
|
-
enumerable: true,
|
|
3537
|
-
value: input.code,
|
|
3538
|
-
writable: false
|
|
3539
|
-
});
|
|
3540
|
-
}
|
|
3541
|
-
return error;
|
|
3542
3667
|
}
|
|
3543
3668
|
function abortReason(signal) {
|
|
3544
|
-
|
|
3669
|
+
const reason = signal.reason;
|
|
3670
|
+
if (reason instanceof Error && typeof reason.code === "string") return reason;
|
|
3671
|
+
if (reason instanceof Error) return new RemoteServiceError("request_cancelled", reason.message);
|
|
3672
|
+
return new RemoteServiceError("request_cancelled", "Remote service request was cancelled");
|
|
3673
|
+
}
|
|
3674
|
+
function addMessageListener(port, listener) {
|
|
3675
|
+
port.addEventListener("message", listener);
|
|
3676
|
+
return () => port.removeEventListener("message", listener);
|
|
3677
|
+
}
|
|
3678
|
+
function postBestEffort(port, message, transfer = []) {
|
|
3679
|
+
try {
|
|
3680
|
+
port.postMessage(message, [...transfer]);
|
|
3681
|
+
} catch {
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
function postRequest(port, message, transfer = []) {
|
|
3685
|
+
try {
|
|
3686
|
+
port.postMessage(message, [...transfer]);
|
|
3687
|
+
} catch (error) {
|
|
3688
|
+
const name = error && typeof error === "object" && typeof error.name === "string" ? error.name : "DataCloneError";
|
|
3689
|
+
const messageText = error instanceof Error ? error.message : String(error);
|
|
3690
|
+
throw new RemoteServiceError(
|
|
3691
|
+
"request_clone_failed",
|
|
3692
|
+
`Remote service request could not be posted: ${messageText}`,
|
|
3693
|
+
{ name, message: messageText }
|
|
3694
|
+
);
|
|
3695
|
+
}
|
|
3545
3696
|
}
|
|
3546
3697
|
function isResponseMessage(input, codec) {
|
|
3547
3698
|
if (!input || typeof input !== "object") return false;
|
|
3548
3699
|
const message = input;
|
|
3549
|
-
return (message.type === codec.type("result") || message.type === codec.type("error")) && typeof message.
|
|
3700
|
+
return (message.type === codec.type("result") || message.type === codec.type("error")) && typeof message.protocolVersion === "string" && typeof message.callId === "string" && typeof message.serviceInstanceId === "string";
|
|
3550
3701
|
}
|
|
3551
3702
|
function createMessagePortServiceTransport(options) {
|
|
3552
3703
|
const pending = /* @__PURE__ */ new Map();
|
|
3553
3704
|
let disposed = false;
|
|
3554
|
-
const transportId =
|
|
3705
|
+
const transportId = ++nextTransportId;
|
|
3555
3706
|
const codec = options.codec ?? createRemoteServiceMessageCodec();
|
|
3707
|
+
const defaultCallTimeoutMs = finiteTimeout(options.defaultCallTimeoutMs);
|
|
3708
|
+
const rejectPending = (error, sendCancel) => {
|
|
3709
|
+
for (const [callId, call] of pending) {
|
|
3710
|
+
pending.delete(callId);
|
|
3711
|
+
call.removeAbort();
|
|
3712
|
+
call.disposeDeadline();
|
|
3713
|
+
if (sendCancel) {
|
|
3714
|
+
postBestEffort(options.port, codec.encode({
|
|
3715
|
+
type: codec.type("cancel"),
|
|
3716
|
+
protocolVersion: codec.protocolVersion,
|
|
3717
|
+
callId,
|
|
3718
|
+
serviceInstanceId: call.serviceInstanceId
|
|
3719
|
+
}));
|
|
3720
|
+
}
|
|
3721
|
+
call.reject(error);
|
|
3722
|
+
}
|
|
3723
|
+
};
|
|
3556
3724
|
const onMessage = (event) => {
|
|
3557
3725
|
const decoded = codec.decode(event.data);
|
|
3558
3726
|
if (!isResponseMessage(decoded, codec)) return;
|
|
3559
3727
|
const call = pending.get(decoded.callId);
|
|
3560
3728
|
if (!call) return;
|
|
3561
|
-
if (decoded.
|
|
3729
|
+
if (decoded.serviceInstanceId !== call.serviceInstanceId) return;
|
|
3562
3730
|
pending.delete(decoded.callId);
|
|
3563
3731
|
call.removeAbort();
|
|
3732
|
+
call.disposeDeadline();
|
|
3733
|
+
if (decoded.protocolVersion !== codec.protocolVersion) {
|
|
3734
|
+
call.reject(new RemoteServiceError("protocol_mismatch", "Remote service protocol version mismatch"));
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3564
3737
|
if (decoded.type === codec.type("error")) {
|
|
3565
3738
|
call.reject(errorFromWire(decoded.error));
|
|
3566
3739
|
} else {
|
|
3567
3740
|
call.resolve(decoded.result);
|
|
3568
3741
|
}
|
|
3569
3742
|
};
|
|
3570
|
-
|
|
3743
|
+
const onMessageError = () => rejectPending(new RemoteServiceError("transport_unavailable", "Remote service message could not be decoded"), false);
|
|
3744
|
+
const removeMessage = addMessageListener(options.port, onMessage);
|
|
3745
|
+
options.port.addEventListener("messageerror", onMessageError);
|
|
3571
3746
|
options.port.start();
|
|
3572
3747
|
const dispose = () => {
|
|
3573
3748
|
if (disposed) return;
|
|
3574
3749
|
disposed = true;
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3750
|
+
removeMessage();
|
|
3751
|
+
options.port.removeEventListener("messageerror", onMessageError);
|
|
3752
|
+
rejectPending(new RemoteServiceError("transport_unavailable", "Remote service transport disposed"), true);
|
|
3753
|
+
if (options.closeOnDispose) {
|
|
3754
|
+
try {
|
|
3755
|
+
options.port.close();
|
|
3756
|
+
} catch {
|
|
3757
|
+
}
|
|
3580
3758
|
}
|
|
3581
|
-
if (options.closeOnDispose) options.port.close();
|
|
3582
3759
|
};
|
|
3583
3760
|
const transport = {
|
|
3584
3761
|
call(request, context) {
|
|
3585
|
-
if (disposed) return Promise.reject(new
|
|
3762
|
+
if (disposed) return Promise.reject(new RemoteServiceError("transport_unavailable", "Remote service transport disposed"));
|
|
3586
3763
|
if (context.signal.aborted) return Promise.reject(abortReason(context.signal));
|
|
3587
3764
|
const callId = makeCallId(transportId);
|
|
3588
|
-
const
|
|
3765
|
+
const serviceInstanceId = context.reference.serviceInstanceId;
|
|
3766
|
+
const timeoutMs = finiteTimeout(context.timeoutMs ?? defaultCallTimeoutMs);
|
|
3767
|
+
const deadlineAt = context.deadlineAt ?? Date.now() + timeoutMs;
|
|
3768
|
+
const timeoutController = new AbortController();
|
|
3769
|
+
const remaining = Math.max(0, deadlineAt - Date.now());
|
|
3770
|
+
const deadlineTimer = setTimeout(() => {
|
|
3771
|
+
try {
|
|
3772
|
+
timeoutController.abort(new RemoteServiceError("call_timeout", "Remote service call timed out"));
|
|
3773
|
+
} catch {
|
|
3774
|
+
timeoutController.abort();
|
|
3775
|
+
}
|
|
3776
|
+
}, remaining);
|
|
3777
|
+
const merged = (() => {
|
|
3778
|
+
const controller = new AbortController();
|
|
3779
|
+
const signals = [context.signal, timeoutController.signal];
|
|
3780
|
+
const listeners = signals.map((signal) => {
|
|
3781
|
+
const listener = () => {
|
|
3782
|
+
try {
|
|
3783
|
+
controller.abort(signal.reason);
|
|
3784
|
+
} catch {
|
|
3785
|
+
controller.abort();
|
|
3786
|
+
}
|
|
3787
|
+
};
|
|
3788
|
+
signal.addEventListener("abort", listener, { once: true });
|
|
3789
|
+
return { signal, listener };
|
|
3790
|
+
});
|
|
3791
|
+
return {
|
|
3792
|
+
signal: controller.signal,
|
|
3793
|
+
dispose: () => listeners.forEach(({ signal, listener }) => signal.removeEventListener("abort", listener))
|
|
3794
|
+
};
|
|
3795
|
+
})();
|
|
3589
3796
|
return new Promise((resolve, reject) => {
|
|
3590
3797
|
let settled = false;
|
|
3591
3798
|
const finish = (callback) => {
|
|
@@ -3593,46 +3800,52 @@ function createMessagePortServiceTransport(options) {
|
|
|
3593
3800
|
settled = true;
|
|
3594
3801
|
callback();
|
|
3595
3802
|
};
|
|
3803
|
+
const sendCancel = () => postBestEffort(options.port, codec.encode({
|
|
3804
|
+
type: codec.type("cancel"),
|
|
3805
|
+
protocolVersion: codec.protocolVersion,
|
|
3806
|
+
callId,
|
|
3807
|
+
serviceInstanceId
|
|
3808
|
+
}));
|
|
3596
3809
|
const onAbort = () => {
|
|
3597
3810
|
if (!pending.delete(callId)) return;
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
connectionId: context.connectionId,
|
|
3604
|
-
providerInstanceId
|
|
3605
|
-
};
|
|
3606
|
-
options.port.postMessage(codec.encode(cancel));
|
|
3607
|
-
} catch {
|
|
3608
|
-
}
|
|
3811
|
+
sendCancel();
|
|
3812
|
+
const reason = merged.signal.reason instanceof RemoteServiceError ? merged.signal.reason : merged.signal.reason?.code === "call_timeout" ? merged.signal.reason : abortReason(merged.signal);
|
|
3813
|
+
finish(() => reject(reason));
|
|
3814
|
+
merged.dispose();
|
|
3815
|
+
clearTimeout(deadlineTimer);
|
|
3609
3816
|
};
|
|
3610
3817
|
const pendingCall = {
|
|
3611
|
-
|
|
3612
|
-
|
|
3818
|
+
protocolVersion: codec.protocolVersion,
|
|
3819
|
+
serviceInstanceId,
|
|
3613
3820
|
resolve: (value) => finish(() => resolve(value)),
|
|
3614
3821
|
reject: (error) => finish(() => reject(error)),
|
|
3615
|
-
removeAbort: () =>
|
|
3822
|
+
removeAbort: () => merged.signal.removeEventListener("abort", onAbort),
|
|
3823
|
+
disposeDeadline: () => {
|
|
3824
|
+
merged.dispose();
|
|
3825
|
+
clearTimeout(deadlineTimer);
|
|
3826
|
+
}
|
|
3616
3827
|
};
|
|
3617
3828
|
pending.set(callId, pendingCall);
|
|
3618
|
-
|
|
3829
|
+
merged.signal.addEventListener("abort", onAbort, { once: true });
|
|
3830
|
+
const message = {
|
|
3831
|
+
type: codec.type("call"),
|
|
3832
|
+
protocolVersion: codec.protocolVersion,
|
|
3833
|
+
callId,
|
|
3834
|
+
capabilityId: context.reference.capabilityId,
|
|
3835
|
+
contractVersion: context.reference.contractVersion,
|
|
3836
|
+
serviceInstanceId,
|
|
3837
|
+
...context.operationId ? { operationId: context.operationId } : {},
|
|
3838
|
+
...context.grantId ?? context.reference.grantId ? { grantId: context.grantId ?? context.reference.grantId } : {},
|
|
3839
|
+
request
|
|
3840
|
+
};
|
|
3619
3841
|
try {
|
|
3620
|
-
const message = {
|
|
3621
|
-
type: codec.type("call"),
|
|
3622
|
-
callId,
|
|
3623
|
-
connectionId: context.connectionId,
|
|
3624
|
-
providerInstanceId,
|
|
3625
|
-
...context.operationId ? { operationId: context.operationId } : {},
|
|
3626
|
-
...context.grantId ?? context.reference.grantId ? { grantId: context.grantId ?? context.reference.grantId } : {},
|
|
3627
|
-
reference: context.reference,
|
|
3628
|
-
request
|
|
3629
|
-
};
|
|
3630
3842
|
const transfer = options.transferForRequest?.(request, context) ?? [];
|
|
3631
|
-
options.port
|
|
3632
|
-
if (
|
|
3843
|
+
postRequest(options.port, codec.encode(message), transfer);
|
|
3844
|
+
if (merged.signal.aborted) onAbort();
|
|
3633
3845
|
} catch (error) {
|
|
3634
3846
|
pending.delete(callId);
|
|
3635
3847
|
pendingCall.removeAbort();
|
|
3848
|
+
pendingCall.disposeDeadline();
|
|
3636
3849
|
pendingCall.reject(error);
|
|
3637
3850
|
}
|
|
3638
3851
|
});
|
|
@@ -3641,6 +3854,252 @@ function createMessagePortServiceTransport(options) {
|
|
|
3641
3854
|
};
|
|
3642
3855
|
return transport;
|
|
3643
3856
|
}
|
|
3857
|
+
function ensureTimeout2(value) {
|
|
3858
|
+
const timeout = value ?? 3e4;
|
|
3859
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
3860
|
+
throw new TypeError("defaultCallTimeoutMs must be a finite number greater than zero");
|
|
3861
|
+
}
|
|
3862
|
+
return timeout;
|
|
3863
|
+
}
|
|
3864
|
+
function addMessageListener2(port, listener) {
|
|
3865
|
+
port.addEventListener("message", listener);
|
|
3866
|
+
return () => port.removeEventListener("message", listener);
|
|
3867
|
+
}
|
|
3868
|
+
function ensureMessageEventTarget(port) {
|
|
3869
|
+
const target = port;
|
|
3870
|
+
if (target.addEventListener && target.removeEventListener) return;
|
|
3871
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3872
|
+
const original = target.onmessage;
|
|
3873
|
+
target.addEventListener = function add(type, listener) {
|
|
3874
|
+
if (type === "message") listeners.add(listener);
|
|
3875
|
+
};
|
|
3876
|
+
target.removeEventListener = function remove(type, listener) {
|
|
3877
|
+
if (type === "message") listeners.delete(listener);
|
|
3878
|
+
};
|
|
3879
|
+
target.onmessage = (event) => {
|
|
3880
|
+
original?.call(port, event);
|
|
3881
|
+
for (const listener of [...listeners]) listener(event);
|
|
3882
|
+
};
|
|
3883
|
+
}
|
|
3884
|
+
function makeWorker(options) {
|
|
3885
|
+
const workerOptions = {
|
|
3886
|
+
type: "module",
|
|
3887
|
+
...options.name ? { name: options.name } : {},
|
|
3888
|
+
...options.credentials ? { credentials: options.credentials } : {}
|
|
3889
|
+
};
|
|
3890
|
+
if (options.workerFactory) return options.workerFactory(options.url, workerOptions);
|
|
3891
|
+
const WorkerConstructor = globalThis.SharedWorker;
|
|
3892
|
+
if (!WorkerConstructor) throw new RuntimeUnavailableError("SharedWorker is not supported by this browser");
|
|
3893
|
+
return new WorkerConstructor(options.url, workerOptions);
|
|
3894
|
+
}
|
|
3895
|
+
function connectSharedWorkerInternal(options) {
|
|
3896
|
+
if (!options || typeof options.id !== "string" || options.id.trim() === "") {
|
|
3897
|
+
throw new Error("SharedWorker runtime id must be a non-empty string");
|
|
3898
|
+
}
|
|
3899
|
+
const defaultCallTimeoutMs = ensureTimeout2(options.defaultCallTimeoutMs);
|
|
3900
|
+
const worker = makeWorker(options);
|
|
3901
|
+
const port = worker.port;
|
|
3902
|
+
if (!port) throw new RuntimeUnavailableError("SharedWorker did not expose a MessagePort");
|
|
3903
|
+
ensureMessageEventTarget(port);
|
|
3904
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3905
|
+
const codec = createRuntimeMessageCodec();
|
|
3906
|
+
let disposed = false;
|
|
3907
|
+
let runtimeInstanceId;
|
|
3908
|
+
let removeRuntimeMessage;
|
|
3909
|
+
let transport;
|
|
3910
|
+
let restoreWorkerError = () => void 0;
|
|
3911
|
+
let currentSnapshot = {
|
|
3912
|
+
runtimeId: options.id,
|
|
3913
|
+
runtimeKind: "shared-worker",
|
|
3914
|
+
runtimeInstanceId: "",
|
|
3915
|
+
state: "starting",
|
|
3916
|
+
revision: 0,
|
|
3917
|
+
units: [],
|
|
3918
|
+
services: []
|
|
3919
|
+
};
|
|
3920
|
+
const emit = (next) => {
|
|
3921
|
+
currentSnapshot = Object.freeze({
|
|
3922
|
+
...next,
|
|
3923
|
+
units: Object.freeze([...next.units]),
|
|
3924
|
+
services: Object.freeze([...next.services])
|
|
3925
|
+
});
|
|
3926
|
+
for (const listener of [...listeners]) {
|
|
3927
|
+
try {
|
|
3928
|
+
listener(currentSnapshot);
|
|
3929
|
+
} catch {
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
};
|
|
3933
|
+
const bridgeTransport = {
|
|
3934
|
+
call(request, context) {
|
|
3935
|
+
if (!transport) {
|
|
3936
|
+
return Promise.reject(new RuntimeUnavailableError("SharedWorker connection is unavailable"));
|
|
3937
|
+
}
|
|
3938
|
+
return transport.call(request, context);
|
|
3939
|
+
}
|
|
3940
|
+
};
|
|
3941
|
+
const bridge = createServiceBridge({
|
|
3942
|
+
protocolVersion: RUNTIME_PROTOCOL_VERSION,
|
|
3943
|
+
transport: bridgeTransport,
|
|
3944
|
+
defaultCallTimeoutMs
|
|
3945
|
+
});
|
|
3946
|
+
const cleanup = () => {
|
|
3947
|
+
removeRuntimeMessage?.();
|
|
3948
|
+
removeRuntimeMessage = void 0;
|
|
3949
|
+
port.removeEventListener("messageerror", onWorkerError);
|
|
3950
|
+
transport?.dispose();
|
|
3951
|
+
transport = void 0;
|
|
3952
|
+
try {
|
|
3953
|
+
port.close();
|
|
3954
|
+
} catch {
|
|
3955
|
+
}
|
|
3956
|
+
if (worker.removeEventListener) worker.removeEventListener("error", onWorkerError);
|
|
3957
|
+
restoreWorkerError();
|
|
3958
|
+
restoreWorkerError = () => void 0;
|
|
3959
|
+
};
|
|
3960
|
+
const disconnect = (reason, failure) => {
|
|
3961
|
+
if (disposed) return;
|
|
3962
|
+
cleanup();
|
|
3963
|
+
bridge.disconnect(reason);
|
|
3964
|
+
emit({
|
|
3965
|
+
...currentSnapshot,
|
|
3966
|
+
state: "disconnected",
|
|
3967
|
+
runtimeInstanceId: "",
|
|
3968
|
+
revision: 0,
|
|
3969
|
+
units: [],
|
|
3970
|
+
services: [],
|
|
3971
|
+
error: reason
|
|
3972
|
+
});
|
|
3973
|
+
runtimeInstanceId = void 0;
|
|
3974
|
+
};
|
|
3975
|
+
const onRuntimeError = (message) => {
|
|
3976
|
+
const error = new RuntimeInitializationError({
|
|
3977
|
+
pluginId: message.pluginId,
|
|
3978
|
+
unitId: message.unitId,
|
|
3979
|
+
phase: message.phase === "snapshot" ? "snapshot" : "startup",
|
|
3980
|
+
error: message.message
|
|
3981
|
+
});
|
|
3982
|
+
if (message.code === "protocol_mismatch") bridge.markProtocolMismatch(message.message);
|
|
3983
|
+
else if (message.code === "runtime_initialization_failed") bridge.markInitializationFailed(message.message);
|
|
3984
|
+
else bridge.disconnect(message.message);
|
|
3985
|
+
emit({ ...currentSnapshot, state: "failed", error: error.message, units: [], services: [] });
|
|
3986
|
+
};
|
|
3987
|
+
const onSnapshot = (snapshot) => {
|
|
3988
|
+
if (snapshot.runtimeId !== options.id || snapshot.runtimeKind !== "shared-worker") return;
|
|
3989
|
+
if (!isRuntimeSnapshotProtocol(snapshot)) {
|
|
3990
|
+
bridge.markProtocolMismatch(`Runtime protocol ${snapshot.protocolVersion} is not supported`);
|
|
3991
|
+
emit({
|
|
3992
|
+
...currentSnapshot,
|
|
3993
|
+
state: "failed",
|
|
3994
|
+
error: `Runtime protocol ${snapshot.protocolVersion} is not supported`,
|
|
3995
|
+
units: [],
|
|
3996
|
+
services: []
|
|
3997
|
+
});
|
|
3998
|
+
return;
|
|
3999
|
+
}
|
|
4000
|
+
const applied = bridge.applySnapshot(snapshot);
|
|
4001
|
+
if (!applied.accepted) return;
|
|
4002
|
+
runtimeInstanceId = snapshot.runtimeInstanceId;
|
|
4003
|
+
emit({
|
|
4004
|
+
runtimeId: snapshot.runtimeId,
|
|
4005
|
+
runtimeKind: snapshot.runtimeKind,
|
|
4006
|
+
runtimeInstanceId: snapshot.runtimeInstanceId,
|
|
4007
|
+
state: snapshot.state === "ready" ? "ready" : snapshot.state,
|
|
4008
|
+
revision: snapshot.revision,
|
|
4009
|
+
units: snapshot.units,
|
|
4010
|
+
services: snapshot.services
|
|
4011
|
+
});
|
|
4012
|
+
};
|
|
4013
|
+
const onWorkerError = (event) => {
|
|
4014
|
+
const detail = event.type ? `SharedWorker error: ${event.type}` : "SharedWorker error";
|
|
4015
|
+
disconnect(detail);
|
|
4016
|
+
};
|
|
4017
|
+
const onMessage = (event) => {
|
|
4018
|
+
if (disposed) return;
|
|
4019
|
+
if (isRuntimeError(event.data)) {
|
|
4020
|
+
onRuntimeError(event.data);
|
|
4021
|
+
return;
|
|
4022
|
+
}
|
|
4023
|
+
if (isRuntimeSnapshot(event.data)) {
|
|
4024
|
+
onSnapshot(event.data);
|
|
4025
|
+
return;
|
|
4026
|
+
}
|
|
4027
|
+
const decoded = codec.decode(event.data);
|
|
4028
|
+
if (decoded?.type === codec.type("error")) ;
|
|
4029
|
+
};
|
|
4030
|
+
try {
|
|
4031
|
+
options.onConnection?.({ worker, port });
|
|
4032
|
+
transport = createMessagePortServiceTransport({
|
|
4033
|
+
port,
|
|
4034
|
+
codec,
|
|
4035
|
+
defaultCallTimeoutMs,
|
|
4036
|
+
closeOnDispose: false
|
|
4037
|
+
});
|
|
4038
|
+
removeRuntimeMessage = addMessageListener2(port, onMessage);
|
|
4039
|
+
port.addEventListener("messageerror", onWorkerError);
|
|
4040
|
+
if (worker.addEventListener) worker.addEventListener("error", onWorkerError);
|
|
4041
|
+
else {
|
|
4042
|
+
const previous = worker.onerror;
|
|
4043
|
+
const fallbackHandler = (event) => {
|
|
4044
|
+
previous?.(event);
|
|
4045
|
+
onWorkerError(event);
|
|
4046
|
+
};
|
|
4047
|
+
worker.onerror = fallbackHandler;
|
|
4048
|
+
restoreWorkerError = () => {
|
|
4049
|
+
if (worker.onerror === fallbackHandler) worker.onerror = previous;
|
|
4050
|
+
};
|
|
4051
|
+
}
|
|
4052
|
+
port.start();
|
|
4053
|
+
} catch (error) {
|
|
4054
|
+
cleanup();
|
|
4055
|
+
throw error;
|
|
4056
|
+
}
|
|
4057
|
+
const handle = {
|
|
4058
|
+
runtimeKind: "shared-worker",
|
|
4059
|
+
runtimeId: options.id,
|
|
4060
|
+
get runtimeInstanceId() {
|
|
4061
|
+
return runtimeInstanceId;
|
|
4062
|
+
},
|
|
4063
|
+
serviceBridge: bridge,
|
|
4064
|
+
state: () => currentSnapshot,
|
|
4065
|
+
capability(capabilityId, capabilityOptions = {}) {
|
|
4066
|
+
const proxy = bridge.requireProxy({
|
|
4067
|
+
capabilityId,
|
|
4068
|
+
contractVersion: capabilityOptions.contractVersion ?? `${capabilityId}.v1`
|
|
4069
|
+
});
|
|
4070
|
+
return proxy;
|
|
4071
|
+
},
|
|
4072
|
+
subscribe(listener) {
|
|
4073
|
+
listeners.add(listener);
|
|
4074
|
+
listener(currentSnapshot);
|
|
4075
|
+
return () => listeners.delete(listener);
|
|
4076
|
+
},
|
|
4077
|
+
dispose(reason = "SharedWorker connection disposed") {
|
|
4078
|
+
if (disposed) return Promise.resolve();
|
|
4079
|
+
disposed = true;
|
|
4080
|
+
emit({ ...currentSnapshot, state: "stopping" });
|
|
4081
|
+
bridge.dispose(reason);
|
|
4082
|
+
cleanup();
|
|
4083
|
+
runtimeInstanceId = void 0;
|
|
4084
|
+
emit({
|
|
4085
|
+
...currentSnapshot,
|
|
4086
|
+
state: "disposed",
|
|
4087
|
+
runtimeInstanceId: "",
|
|
4088
|
+
revision: 0,
|
|
4089
|
+
units: [],
|
|
4090
|
+
services: []
|
|
4091
|
+
});
|
|
4092
|
+
return Promise.resolve();
|
|
4093
|
+
}
|
|
4094
|
+
};
|
|
4095
|
+
return handle;
|
|
4096
|
+
}
|
|
4097
|
+
function connectSharedWorker(options) {
|
|
4098
|
+
return connectSharedWorkerInternal(options);
|
|
4099
|
+
}
|
|
4100
|
+
function connectSharedWorkerForTesting(options, workerFactory) {
|
|
4101
|
+
return connectSharedWorkerInternal({ ...options, workerFactory });
|
|
4102
|
+
}
|
|
3644
4103
|
|
|
3645
4104
|
// src/lifecycle/pluginIntentController.ts
|
|
3646
4105
|
function makeAuthorityInstanceId() {
|
|
@@ -3797,6 +4256,6 @@ function createPluginIntentController(options = {}) {
|
|
|
3797
4256
|
return controller;
|
|
3798
4257
|
}
|
|
3799
4258
|
|
|
3800
|
-
export { LIFECYCLE_ERROR_TEXT, LifecycleScopeRevokedError, PermissionDeniedError, PermissionLeaseRevokedError, PluginGraphValidationError, RESOURCE_OWNER, RESOURCE_REGISTRY_CAPABILITY, RUNTIME_MESSAGE_BUS,
|
|
3801
|
-
//# sourceMappingURL=chunk-
|
|
3802
|
-
//# sourceMappingURL=chunk-
|
|
4259
|
+
export { LIFECYCLE_ERROR_TEXT, LifecycleScopeRevokedError, PermissionDeniedError, PermissionLeaseRevokedError, PluginGraphValidationError, RESOURCE_OWNER, RESOURCE_REGISTRY_CAPABILITY, RUNTIME_ERROR_TYPE, RUNTIME_MESSAGE_BUS, RUNTIME_PROTOCOL_VERSION, RUNTIME_SNAPSHOT_TYPE, RemoteServiceError, RuntimeInitializationError, RuntimeUnavailableError, SCOPED_TASK_SCHEDULER_CAPABILITY, StartupCapabilityError, StartupPluginError, UpgradeGateRejectedError, buildPluginGraph, connectSharedWorker, connectSharedWorkerForTesting, createCapabilityRegistry, createInMemoryPluginConfigStore, createLifecycleScope, createMessageBus, createMessagePortServiceTransport, createPermissionLease, createPluginHost, createPluginIntentController, createRemoteServiceMessageCodec, createResourceRegistry, createResourceScope, createResourceStore, createRuntimeMessageCodec, createRuntimeUnitImplementationRegistry, createScopedMessageBus, createScopedTaskScheduler, createServiceBridge, dependenciesOfManifest, isRuntimeError, isRuntimeSnapshot, isRuntimeSnapshotProtocol, lifecycleErrorText, providesOfManifest, registerOwnedResource, reverseDependentsOf, unitSnapshotFromState, validatePluginGraph, validateRuntimeUnitDependencyContracts };
|
|
4260
|
+
//# sourceMappingURL=chunk-76BGPI6M.js.map
|
|
4261
|
+
//# sourceMappingURL=chunk-76BGPI6M.js.map
|