webloom-framework 0.1.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/dist/index.js ADDED
@@ -0,0 +1,583 @@
1
+ import { createRemoteServiceMessageCodec, UpgradeGateRejectedError } from './chunk-KGNF36DZ.js';
2
+ 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 } from './chunk-KGNF36DZ.js';
3
+
4
+ // src/contracts/plugin.ts
5
+ function runtimeCapabilityContractVersion(capability) {
6
+ return `${capability}.v1`;
7
+ }
8
+ function defineRuntimeUnitDependencies(dependencies, defaults = {}) {
9
+ return dependencies.map((dependency) => ({
10
+ capability: dependency.capability,
11
+ contractVersion: runtimeCapabilityContractVersion(dependency.capability),
12
+ sourceExecution: defaults.sourceExecution ?? "default",
13
+ scope: defaults.scope ?? "root",
14
+ ...dependency.reason !== void 0 ? { reason: dependency.reason } : {},
15
+ ...dependency.optional !== void 0 ? { optional: dependency.optional } : {}
16
+ }));
17
+ }
18
+ function defineRuntimeUnitProvidedContracts(capabilities) {
19
+ return Object.fromEntries(
20
+ capabilities.map((capability) => [capability, runtimeCapabilityContractVersion(capability)])
21
+ );
22
+ }
23
+
24
+ // src/lifecycle/permissionVerifier.ts
25
+ function verifyPermissionLease(options) {
26
+ options.lease.assert(options.permission);
27
+ if (options.binding) options.lease.assertBinding(options.binding);
28
+ }
29
+
30
+ // src/lifecycle/scopedRegistry.ts
31
+ function definitionId(value) {
32
+ if (!value || typeof value !== "object") return void 0;
33
+ const id = value.id;
34
+ return typeof id === "string" && id.length > 0 ? id : void 0;
35
+ }
36
+ function defaultRegistrationRules() {
37
+ return [{ method: "register", idArgument: 0, unregisterMethod: "unregister" }];
38
+ }
39
+ function createScopedRegistryFacade(target, scope, options) {
40
+ const rules = options.registrations ?? defaultRegistrationRules();
41
+ const byMethod = new Map(rules.map((rule) => [rule.method, rule]));
42
+ const unregisterMethods = new Set(
43
+ rules.map((rule) => rule.unregisterMethod).filter((method) => Boolean(method))
44
+ );
45
+ const ownedRegistrations = /* @__PURE__ */ new Set();
46
+ const objectTarget = target;
47
+ const remember = (rule, id, result, args) => {
48
+ const unregisterMethod = rule.unregisterMethod;
49
+ if (typeof result !== "function" && (!unregisterMethod || id === void 0)) return result;
50
+ const key = rule.method + ":" + (id ?? "returned");
51
+ let active = true;
52
+ let removeScopeRevoke = () => void 0;
53
+ let removeScopeCleanup = () => void 0;
54
+ const entry = {};
55
+ const clearOwnership = (removeDispose = true) => {
56
+ if (!active) return false;
57
+ active = false;
58
+ ownedRegistrations.delete(entry);
59
+ removeScopeRevoke();
60
+ if (removeDispose) removeScopeCleanup();
61
+ return true;
62
+ };
63
+ const invokeUnregister = (...offArgs) => {
64
+ if (typeof result === "function") {
65
+ return result.apply(target, offArgs);
66
+ }
67
+ const unregister = objectTarget[unregisterMethod];
68
+ if (typeof unregister !== "function") return void 0;
69
+ const unregisterArgument = rule.unregisterArgument ?? rule.idArgument ?? 0;
70
+ const unregisterArgs = [...args];
71
+ unregisterArgs[unregisterArgument] = id;
72
+ return unregister.apply(target, unregisterArgs);
73
+ };
74
+ const cleanup = async (_reason) => {
75
+ if (!active) {
76
+ if (entry.revokedCleanup) await entry.revokedCleanup;
77
+ return;
78
+ }
79
+ if (!clearOwnership()) return;
80
+ await invokeUnregister();
81
+ };
82
+ const revokeNow = (_reason) => {
83
+ if (!clearOwnership(false)) return;
84
+ try {
85
+ const pending = invokeUnregister();
86
+ if (pending && typeof pending.then === "function") {
87
+ const revokedCleanup = Promise.resolve(pending).then(() => void 0);
88
+ entry.revokedCleanup = revokedCleanup;
89
+ revokedCleanup.catch(() => void 0);
90
+ }
91
+ } catch (error) {
92
+ entry.revokedCleanup = Promise.reject(error);
93
+ entry.revokedCleanup.catch(() => void 0);
94
+ }
95
+ };
96
+ entry.id = id;
97
+ entry.unregisterMethod = unregisterMethod;
98
+ entry.revokeNow = revokeNow;
99
+ Object.defineProperty(entry, "active", {
100
+ enumerable: true,
101
+ configurable: false,
102
+ get: () => active,
103
+ set: (value) => {
104
+ active = value;
105
+ }
106
+ });
107
+ entry.removeScopeCleanup = removeScopeCleanup;
108
+ ownedRegistrations.add(entry);
109
+ removeScopeCleanup = scope.onDispose(
110
+ cleanup,
111
+ options.name + ":" + key,
112
+ "after-teardown"
113
+ );
114
+ entry.removeScopeCleanup = removeScopeCleanup;
115
+ removeScopeRevoke = scope.onRevoke(revokeNow);
116
+ if (typeof result === "function") {
117
+ return (...offArgs) => {
118
+ if (!clearOwnership()) return void 0;
119
+ return result.apply(target, offArgs);
120
+ };
121
+ }
122
+ return result;
123
+ };
124
+ return new Proxy(target, {
125
+ get(current, property, receiver) {
126
+ const value = Reflect.get(current, property, receiver);
127
+ if (typeof value !== "function") return value;
128
+ const rule = typeof property === "string" ? byMethod.get(property) : void 0;
129
+ if (rule) {
130
+ return (...args) => {
131
+ scope.assertActive();
132
+ const callArgs = [...args];
133
+ if (rule.bindPluginIdArgument !== void 0 && scope.identity.pluginId) {
134
+ const claimedPluginId = callArgs[rule.bindPluginIdArgument];
135
+ if (claimedPluginId !== void 0 && claimedPluginId !== scope.identity.pluginId) {
136
+ throw new Error(
137
+ 'Registry owner "' + String(claimedPluginId) + '" does not match plugin instance "' + scope.identity.pluginId + '"'
138
+ );
139
+ }
140
+ callArgs[rule.bindPluginIdArgument] = scope.identity.pluginId;
141
+ }
142
+ if (rule.ownerPluginIdProperty && scope.identity.pluginId) {
143
+ const definition = callArgs[rule.idArgument ?? 0];
144
+ if (definition && typeof definition === "object") {
145
+ const claimedPluginId = definition[rule.ownerPluginIdProperty];
146
+ if (claimedPluginId !== void 0 && claimedPluginId !== scope.identity.pluginId) {
147
+ throw new Error(
148
+ 'Registry owner "' + String(claimedPluginId) + '" does not match plugin instance "' + scope.identity.pluginId + '"'
149
+ );
150
+ }
151
+ }
152
+ }
153
+ const result = value.apply(target, callArgs);
154
+ const id = rule.idArgument === void 0 ? void 0 : definitionId(callArgs[rule.idArgument]);
155
+ return remember(rule, id, result, callArgs);
156
+ };
157
+ }
158
+ if (typeof property === "string" && unregisterMethods.has(property)) {
159
+ return (...args) => {
160
+ scope.assertActive();
161
+ const id = definitionId(args[0]) ?? (typeof args[0] === "string" ? args[0] : void 0);
162
+ const owned = [...ownedRegistrations].find(
163
+ (entry) => entry.active && entry.id === id && entry.unregisterMethod === property
164
+ );
165
+ if (!owned) {
166
+ throw new Error(
167
+ 'Registry resource "' + (id ?? "unknown") + '" is not owned by this plugin instance'
168
+ );
169
+ }
170
+ const result = value.apply(target, args);
171
+ owned.active = false;
172
+ ownedRegistrations.delete(owned);
173
+ owned.removeScopeCleanup();
174
+ return result;
175
+ };
176
+ }
177
+ return value.bind(target);
178
+ }
179
+ });
180
+ }
181
+
182
+ // src/lifecycle/upgradeGate.ts
183
+ function validGeneration(value) {
184
+ return Number.isSafeInteger(value) && value >= 0;
185
+ }
186
+ function uniqueStrings(values) {
187
+ return [...new Set(values.filter((value) => typeof value === "string" && value.length > 0))];
188
+ }
189
+ function makeSessionId() {
190
+ try {
191
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
192
+ return `upgrade-session:${crypto.randomUUID()}`;
193
+ }
194
+ } catch {
195
+ }
196
+ return `upgrade-session:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
197
+ }
198
+ function buildIsCompatible(options, buildId) {
199
+ try {
200
+ if (options.isBuildCompatible) return options.isBuildCompatible(buildId);
201
+ } catch {
202
+ return false;
203
+ }
204
+ return buildId === options.buildId || options.compatibleBuildIds?.has(buildId) === true;
205
+ }
206
+ function createUpgradeGate(options) {
207
+ if (!options.protocolVersion || !options.buildId || !options.authorityInstanceId) {
208
+ throw new Error("\u5347\u7EA7\u95E8\u7981\u7684 protocolVersion\u3001buildId \u548C authorityInstanceId \u5FC5\u987B\u6709\u6548");
209
+ }
210
+ if (!validGeneration(options.handoverGeneration)) {
211
+ throw new Error("\u5347\u7EA7\u95E8\u7981\u7684 handoverGeneration \u5FC5\u987B\u662F\u975E\u8D1F\u5B89\u5168\u6574\u6570");
212
+ }
213
+ const contractVersions = uniqueStrings(options.supportedContractVersions);
214
+ if (contractVersions.length === 0) {
215
+ throw new Error("\u5347\u7EA7\u95E8\u7981\u81F3\u5C11\u9700\u8981\u4E00\u4E2A supportedContractVersions");
216
+ }
217
+ let currentState = "active";
218
+ let closeReason = "upgrade gate closed";
219
+ const leases = /* @__PURE__ */ new Set();
220
+ const sessions = /* @__PURE__ */ new Set();
221
+ const sessionByObject = /* @__PURE__ */ new WeakMap();
222
+ const emptyWaiters = /* @__PURE__ */ new Set();
223
+ const notifyEmpty = () => {
224
+ if (leases.size !== 0) return;
225
+ for (const resolve of [...emptyWaiters]) {
226
+ emptyWaiters.delete(resolve);
227
+ resolve();
228
+ }
229
+ };
230
+ const removeLease = (record) => {
231
+ if (!leases.delete(record)) return;
232
+ record.session.leases.delete(record);
233
+ record.removeExternalAbort?.();
234
+ record.removeExternalAbort = void 0;
235
+ notifyEmpty();
236
+ };
237
+ const revokeSession = (record, reason) => {
238
+ if (record.revoked) return;
239
+ record.revoked = true;
240
+ record.reason = reason;
241
+ try {
242
+ record.controller.abort(new UpgradeGateRejectedError(reason));
243
+ } catch {
244
+ record.controller.abort();
245
+ }
246
+ for (const lease of [...record.leases]) revokeLease(lease, reason);
247
+ sessions.delete(record);
248
+ };
249
+ const revokeLease = (record, reason) => {
250
+ if (record.revoked) return;
251
+ record.revoked = true;
252
+ record.reason = reason;
253
+ try {
254
+ record.controller.abort(new UpgradeGateRejectedError(reason));
255
+ } catch {
256
+ record.controller.abort();
257
+ }
258
+ removeLease(record);
259
+ };
260
+ const assertAccepting = () => {
261
+ if (currentState === "active") return;
262
+ throw new UpgradeGateRejectedError(currentState === "draining" ? "draining" : "closed", closeReason);
263
+ };
264
+ const handshake = (input) => {
265
+ if (currentState !== "active") {
266
+ return { accepted: false, reason: currentState === "draining" ? "draining" : "closed" };
267
+ }
268
+ if (typeof input.connectionId !== "string" || input.connectionId.length === 0 || input.protocolVersion !== options.protocolVersion || typeof input.protocolVersion !== "string" || typeof input.buildId !== "string" || typeof input.authorityInstanceId !== "string" || input.authorityInstanceId.length === 0) {
269
+ return { accepted: false, reason: "protocol-mismatch" };
270
+ }
271
+ if (!buildIsCompatible(options, input.buildId)) {
272
+ return { accepted: false, reason: "build-incompatible" };
273
+ }
274
+ if (!validGeneration(input.handoverGeneration)) {
275
+ return { accepted: false, reason: "stale-generation" };
276
+ }
277
+ if (input.handoverGeneration < options.handoverGeneration) {
278
+ return { accepted: false, reason: "stale-generation" };
279
+ }
280
+ if (input.handoverGeneration > options.handoverGeneration) {
281
+ return { accepted: false, reason: "future-generation" };
282
+ }
283
+ const contractVersion = contractVersions.find((version) => input.supportedContractVersions.includes(version));
284
+ if (!contractVersion) return { accepted: false, reason: "contract-mismatch" };
285
+ const sessionRecord = {
286
+ sessionId: makeSessionId(),
287
+ connectionId: input.connectionId,
288
+ contractVersion,
289
+ controller: new AbortController(),
290
+ revoked: false,
291
+ reason: "upgrade session closed",
292
+ leases: /* @__PURE__ */ new Set()
293
+ };
294
+ const session = {
295
+ connectionId: sessionRecord.connectionId,
296
+ sessionId: sessionRecord.sessionId,
297
+ authorityInstanceId: options.authorityInstanceId,
298
+ handoverGeneration: options.handoverGeneration,
299
+ contractVersion,
300
+ get revoked() {
301
+ return sessionRecord.revoked || !sessions.has(sessionRecord);
302
+ },
303
+ signal: sessionRecord.controller.signal,
304
+ assertActive() {
305
+ if (sessionRecord.revoked || !sessions.has(sessionRecord)) {
306
+ throw new UpgradeGateRejectedError(sessionRecord.reason, "\u5347\u7EA7\u63E1\u624B\u4F1A\u8BDD\u5DF2\u5931\u6548");
307
+ }
308
+ if (currentState === "closed") {
309
+ throw new UpgradeGateRejectedError("closed", closeReason);
310
+ }
311
+ },
312
+ admit(input2) {
313
+ return admitForSession(sessionRecord, input2);
314
+ },
315
+ close(reason = "upgrade session closed") {
316
+ revokeSession(sessionRecord, reason);
317
+ }
318
+ };
319
+ sessionRecord.session = session;
320
+ sessions.add(sessionRecord);
321
+ sessionByObject.set(session, sessionRecord);
322
+ return {
323
+ accepted: true,
324
+ mode: options.mode ?? "cold-switch",
325
+ handoverGeneration: options.handoverGeneration,
326
+ contractVersion,
327
+ connectionId: sessionRecord.connectionId,
328
+ sessionId: sessionRecord.sessionId,
329
+ session
330
+ };
331
+ };
332
+ const admitForSession = (sessionRecord, input) => {
333
+ if (sessionRecord.revoked || !sessions.has(sessionRecord)) {
334
+ throw new UpgradeGateRejectedError(sessionRecord.reason, "\u5347\u7EA7\u63E1\u624B\u4F1A\u8BDD\u5DF2\u5931\u6548");
335
+ }
336
+ assertAccepting();
337
+ if (input.signal?.aborted) {
338
+ throw new UpgradeGateRejectedError("caller-aborted", "I/O \u5728\u53D6\u5F97\u63A5\u7BA1\u79DF\u7EA6\u524D\u5DF2\u53D6\u6D88");
339
+ }
340
+ const record = {
341
+ session: sessionRecord,
342
+ operation: input.operation,
343
+ controller: new AbortController(),
344
+ revoked: false,
345
+ reason: "upgrade I/O lease released"
346
+ };
347
+ leases.add(record);
348
+ sessionRecord.leases.add(record);
349
+ if (input.signal) {
350
+ const onAbort = () => revokeLease(record, "caller-aborted");
351
+ input.signal.addEventListener("abort", onAbort, { once: true });
352
+ record.removeExternalAbort = () => input.signal?.removeEventListener("abort", onAbort);
353
+ }
354
+ const lease = {
355
+ connectionId: sessionRecord.connectionId,
356
+ sessionId: sessionRecord.sessionId,
357
+ authorityInstanceId: options.authorityInstanceId,
358
+ handoverGeneration: options.handoverGeneration,
359
+ contractVersion: sessionRecord.contractVersion,
360
+ operation: record.operation,
361
+ get revoked() {
362
+ return record.revoked || !leases.has(record);
363
+ },
364
+ signal: record.controller.signal,
365
+ assertActive() {
366
+ if (record.revoked || !leases.has(record)) {
367
+ throw new UpgradeGateRejectedError(record.reason, "\u5347\u7EA7 I/O \u79DF\u7EA6\u5DF2\u5931\u6548");
368
+ }
369
+ if (currentState === "closed") {
370
+ throw new UpgradeGateRejectedError("closed", closeReason);
371
+ }
372
+ },
373
+ release() {
374
+ if (record.revoked) return;
375
+ record.revoked = true;
376
+ record.reason = "upgrade I/O lease released";
377
+ removeLease(record);
378
+ }
379
+ };
380
+ return lease;
381
+ };
382
+ const admit = (input) => {
383
+ const sessionRecord = sessionByObject.get(input.session);
384
+ if (!sessionRecord) {
385
+ throw new UpgradeGateRejectedError("invalid-session", "I/O \u5FC5\u987B\u4F7F\u7528\u5F53\u524D\u95E8\u7981\u63E1\u624B\u8FD4\u56DE\u7684\u4F1A\u8BDD");
386
+ }
387
+ return admitForSession(sessionRecord, input);
388
+ };
389
+ const beginDrain = (reason = "upgrade handover draining") => {
390
+ if (currentState !== "active") return;
391
+ closeReason = reason;
392
+ currentState = "draining";
393
+ };
394
+ const drain = async (timeoutMs) => {
395
+ beginDrain();
396
+ if (leases.size === 0) return { state: currentState, drained: true, pending: 0 };
397
+ if (timeoutMs !== void 0 && (!Number.isFinite(timeoutMs) || timeoutMs < 0)) {
398
+ throw new Error("\u5347\u7EA7\u6392\u7A7A timeoutMs \u5FC5\u987B\u662F\u975E\u8D1F\u6709\u9650\u6570");
399
+ }
400
+ let timeout;
401
+ let timerResolve;
402
+ const empty = new Promise((resolve) => {
403
+ emptyWaiters.add(resolve);
404
+ timerResolve = resolve;
405
+ });
406
+ const timeoutPromise = timeoutMs === void 0 ? void 0 : new Promise((resolve) => {
407
+ timeout = setTimeout(resolve, timeoutMs);
408
+ });
409
+ if (timeoutPromise) await Promise.race([empty, timeoutPromise]);
410
+ else await empty;
411
+ if (timeout !== void 0) clearTimeout(timeout);
412
+ if (timerResolve) emptyWaiters.delete(timerResolve);
413
+ const pending = leases.size;
414
+ return { state: currentState, drained: pending === 0, pending };
415
+ };
416
+ const close = (reason = "upgrade gate closed") => {
417
+ if (currentState === "closed") return;
418
+ closeReason = reason;
419
+ currentState = "closed";
420
+ for (const session of [...sessions]) revokeSession(session, reason);
421
+ for (const lease of [...leases]) revokeLease(lease, reason);
422
+ notifyEmpty();
423
+ };
424
+ return {
425
+ get state() {
426
+ return currentState;
427
+ },
428
+ mode: options.mode ?? "cold-switch",
429
+ authorityInstanceId: options.authorityInstanceId,
430
+ handoverGeneration: options.handoverGeneration,
431
+ handshake,
432
+ assertAccepting,
433
+ admit,
434
+ beginDrain,
435
+ drain,
436
+ close,
437
+ activeIo: () => leases.size
438
+ };
439
+ }
440
+
441
+ // src/transport/messagePortServiceProvider.ts
442
+ function isCallMessage(input, codec) {
443
+ if (!input || typeof input !== "object") return false;
444
+ const message = input;
445
+ return message.type === codec.type("call") && typeof message.callId === "string" && message.callId.length > 0 && typeof message.connectionId === "string" && typeof message.providerInstanceId === "string" && Boolean(message.reference);
446
+ }
447
+ function isCancelMessage(input, codec) {
448
+ if (!input || typeof input !== "object") return false;
449
+ const message = input;
450
+ return message.type === codec.type("cancel") && typeof message.callId === "string" && typeof message.connectionId === "string" && typeof message.providerInstanceId === "string";
451
+ }
452
+ function errorMessage(error) {
453
+ const candidate = error && typeof error === "object" ? error : void 0;
454
+ return {
455
+ ...typeof candidate?.name === "string" ? { name: candidate.name } : {},
456
+ message: typeof candidate?.message === "string" ? candidate.message : String(error),
457
+ ...typeof candidate?.code === "string" ? { code: candidate.code } : {}
458
+ };
459
+ }
460
+ function post(port, message) {
461
+ try {
462
+ port.postMessage(message);
463
+ } catch {
464
+ }
465
+ }
466
+ function createMessagePortServiceProvider(options) {
467
+ const pending = /* @__PURE__ */ new Map();
468
+ const codec = options.codec ?? createRemoteServiceMessageCodec();
469
+ let activeProviderInstanceIds = new Set(
470
+ options.snapshot.services.map((service) => service.providerInstanceId)
471
+ );
472
+ let disposed = false;
473
+ const sendError = (message, error) => {
474
+ const response = {
475
+ type: codec.type("error"),
476
+ callId: message.callId,
477
+ connectionId: message.connectionId,
478
+ providerInstanceId: message.providerInstanceId,
479
+ error: errorMessage(error)
480
+ };
481
+ post(options.port, codec.encode(response));
482
+ };
483
+ const onMessage = (event) => {
484
+ if (disposed) return;
485
+ const decoded = codec.decode(event.data);
486
+ if (isCancelMessage(decoded, codec)) {
487
+ if (decoded.connectionId !== options.handshake.connectionId) return;
488
+ const call = pending.get(decoded.callId);
489
+ if (call?.providerInstanceId === decoded.providerInstanceId) {
490
+ call.controller.abort(new Error("Remote service request cancelled"));
491
+ }
492
+ return;
493
+ }
494
+ if (!isCallMessage(decoded, codec)) return;
495
+ const message = decoded;
496
+ if (message.connectionId !== options.handshake.connectionId) {
497
+ sendError(message, Object.assign(new Error("Remote service connection mismatch"), { code: "service.connection_mismatch" }));
498
+ return;
499
+ }
500
+ if (!activeProviderInstanceIds.has(message.providerInstanceId)) {
501
+ sendError(message, Object.assign(new Error("Remote service provider mismatch"), { code: "service.provider_mismatch" }));
502
+ return;
503
+ }
504
+ if (pending.has(message.callId)) {
505
+ sendError(message, Object.assign(new Error("Remote service callId is duplicated"), { code: "service.duplicate_call" }));
506
+ return;
507
+ }
508
+ const controller = new AbortController();
509
+ pending.set(message.callId, { controller, providerInstanceId: message.providerInstanceId });
510
+ void (async () => {
511
+ try {
512
+ const result = await options.handleCall({ message, signal: controller.signal });
513
+ if (controller.signal.aborted || disposed) return;
514
+ const response = {
515
+ type: codec.type("result"),
516
+ callId: message.callId,
517
+ connectionId: message.connectionId,
518
+ providerInstanceId: message.providerInstanceId,
519
+ result
520
+ };
521
+ post(options.port, codec.encode(response));
522
+ } catch (error) {
523
+ if (disposed) return;
524
+ sendError(message, error);
525
+ } finally {
526
+ pending.delete(message.callId);
527
+ }
528
+ })();
529
+ };
530
+ options.port.addEventListener("message", onMessage);
531
+ options.port.start();
532
+ post(options.port, codec.encode({
533
+ type: codec.type("handshake"),
534
+ handshake: options.handshake
535
+ }));
536
+ post(options.port, codec.encode({
537
+ type: codec.type("snapshot"),
538
+ snapshot: options.snapshot
539
+ }));
540
+ const dispose = () => {
541
+ if (disposed) return;
542
+ disposed = true;
543
+ options.port.removeEventListener("message", onMessage);
544
+ for (const { controller } of pending.values()) controller.abort(new Error("Remote service provider disposed"));
545
+ pending.clear();
546
+ if (options.closeOnDispose !== false) options.port.close();
547
+ };
548
+ const provider = {
549
+ publishSnapshot(snapshot) {
550
+ if (disposed) return;
551
+ activeProviderInstanceIds = new Set(
552
+ snapshot.services.map((service) => service.providerInstanceId)
553
+ );
554
+ post(options.port, codec.encode({
555
+ type: codec.type("snapshot"),
556
+ snapshot
557
+ }));
558
+ },
559
+ invalidate(reason = "Remote service invalidated") {
560
+ if (disposed) return;
561
+ activeProviderInstanceIds = /* @__PURE__ */ new Set();
562
+ for (const { controller } of pending.values()) controller.abort(new Error(reason));
563
+ post(options.port, codec.encode({
564
+ type: codec.type("invalidate"),
565
+ reason
566
+ }));
567
+ },
568
+ disconnect(reason = "Remote service disconnected") {
569
+ if (disposed) return;
570
+ post(options.port, codec.encode({
571
+ type: codec.type("disconnect"),
572
+ reason
573
+ }));
574
+ dispose();
575
+ },
576
+ dispose
577
+ };
578
+ return provider;
579
+ }
580
+
581
+ export { createMessagePortServiceProvider, createScopedRegistryFacade, createUpgradeGate, defineRuntimeUnitDependencies, defineRuntimeUnitProvidedContracts, runtimeCapabilityContractVersion, verifyPermissionLease };
582
+ //# sourceMappingURL=index.js.map
583
+ //# sourceMappingURL=index.js.map