@xmoxmo/bncr 0.6.6 → 0.6.8

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 CHANGED
@@ -1,6 +1,10 @@
1
1
  // src/bootstrap/channel-plugin-runtime.ts
2
2
  function createDynamicChannelPlugin(args) {
3
3
  const { loaded, getCurrentBridge: getCurrentBridge2 } = args;
4
+ const resolveBridgeForStart = args.resolveBridgeForStart || getCurrentBridge2;
5
+ const isBridgeForStartCurrent = args.isBridgeForStartCurrent || (() => true);
6
+ const resolveBridgeForStop = args.resolveBridgeForStop || getCurrentBridge2;
7
+ const isBridgeForStopCurrent = args.isBridgeForStopCurrent || (() => true);
4
8
  const base = loaded.createBncrChannelPlugin(() => getCurrentBridge2());
5
9
  const plugin2 = { ...base };
6
10
  const outbound = base.outbound;
@@ -40,12 +44,25 @@ function createDynamicChannelPlugin(args) {
40
44
  };
41
45
  plugin2.gateway = {
42
46
  ...baseGateway,
43
- startAccount: (ctx) => getCurrentBridge2().channelStartAccount(
44
- ctx
45
- ),
46
- stopAccount: (ctx) => getCurrentBridge2().channelStopAccount(
47
- ctx
48
- )
47
+ startAccount: async (ctx) => {
48
+ const bridge = await resolveBridgeForStart();
49
+ if (!isBridgeForStartCurrent()) return;
50
+ const task = bridge.channelStartAccount(
51
+ ctx
52
+ );
53
+ if (isBridgeForStartCurrent()) {
54
+ try {
55
+ args.onBridgeStartObserved?.();
56
+ } catch {
57
+ }
58
+ }
59
+ return task;
60
+ },
61
+ stopAccount: async (ctx) => {
62
+ const bridge = await resolveBridgeForStop();
63
+ if (!bridge || !isBridgeForStopCurrent()) return;
64
+ return bridge.channelStopAccount(ctx);
65
+ }
49
66
  };
50
67
  return plugin2;
51
68
  }
@@ -155,16 +172,305 @@ function emitBncrLogLine(level, line, options, isDebugEnabled) {
155
172
  emitConsole(resolveConsoleMethod(level), normalizeBncrLogLine(line));
156
173
  }
157
174
 
175
+ // src/bootstrap/register-lifecycle.ts
176
+ function normalizeString(value, fallback = "unknown") {
177
+ const text = typeof value === "string" ? value.trim() : "";
178
+ return text || fallback;
179
+ }
180
+ function createRegisterLifecycleState() {
181
+ return {
182
+ nextGeneration: 1,
183
+ staleCallbackSuppressions: 0
184
+ };
185
+ }
186
+ function sameLifecycleRegistration(left, right) {
187
+ if (!left || !right) return false;
188
+ return left.apiInstanceId === right.apiInstanceId && left.registryFingerprint === right.registryFingerprint;
189
+ }
190
+ function sameBridgeGeneration(left, right) {
191
+ if (!left || !right) return false;
192
+ return left.moduleEpoch === right.moduleEpoch && left.bridgeFactoryId === right.bridgeFactoryId && left.pluginVersion === right.pluginVersion && left.registrationMode === right.registrationMode && left.pluginRoot === right.pluginRoot && left.pluginFile === right.pluginFile;
193
+ }
194
+ function getBridgeGenerationKey(generation) {
195
+ return [
196
+ generation.moduleEpoch,
197
+ generation.bridgeFactoryId,
198
+ generation.pluginVersion,
199
+ generation.registrationMode,
200
+ generation.pluginRoot,
201
+ generation.pluginFile
202
+ ].join("\0");
203
+ }
204
+ function createLifecycleOwner(input, generation, phase) {
205
+ return {
206
+ key: {
207
+ generation,
208
+ apiInstanceId: normalizeString(input.apiInstanceId, "unknown-api"),
209
+ registryFingerprint: normalizeString(input.registryFingerprint, "unknown-registry")
210
+ },
211
+ bridgeGeneration: {
212
+ moduleEpoch: normalizeString(input.bridgeGeneration.moduleEpoch),
213
+ bridgeFactoryId: normalizeString(input.bridgeGeneration.bridgeFactoryId),
214
+ pluginVersion: normalizeString(input.bridgeGeneration.pluginVersion),
215
+ registrationMode: normalizeString(input.bridgeGeneration.registrationMode),
216
+ pluginRoot: normalizeString(input.bridgeGeneration.pluginRoot),
217
+ pluginFile: normalizeString(input.bridgeGeneration.pluginFile)
218
+ },
219
+ phase,
220
+ startedAt: phase === "active" ? input.now : null,
221
+ stopRequestedAt: null,
222
+ stoppedAt: null,
223
+ failure: null
224
+ };
225
+ }
226
+ function planLifecycleAdoption(state, input) {
227
+ const owner = createLifecycleOwner(input, state.nextGeneration, "active");
228
+ const active = state.active;
229
+ if (!active) {
230
+ return {
231
+ kind: "initialize",
232
+ owner,
233
+ reason: "no-active-lifecycle"
234
+ };
235
+ }
236
+ if (active.phase === "failed") {
237
+ return {
238
+ kind: "reject",
239
+ owner,
240
+ reason: "predecessor-failed"
241
+ };
242
+ }
243
+ if (state.pending) {
244
+ const sameRegistration = sameLifecycleRegistration(state.pending.key, owner.key);
245
+ const sameGeneration = sameBridgeGeneration(
246
+ state.pending.bridgeGeneration,
247
+ owner.bridgeGeneration
248
+ );
249
+ if (sameRegistration && sameGeneration) {
250
+ return {
251
+ kind: "duplicate",
252
+ owner,
253
+ reason: "pending-successor"
254
+ };
255
+ }
256
+ if (sameRegistration) {
257
+ return {
258
+ kind: "reject",
259
+ owner,
260
+ reason: "registration-generation-conflict"
261
+ };
262
+ }
263
+ return {
264
+ kind: "reject",
265
+ owner,
266
+ reason: "pending-successor-conflict"
267
+ };
268
+ }
269
+ if (active.phase === "active" || active.phase === "registering") {
270
+ if (sameLifecycleRegistration(active.key, owner.key) && !sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration)) {
271
+ return {
272
+ kind: "reject",
273
+ owner,
274
+ reason: "registration-generation-conflict"
275
+ };
276
+ }
277
+ return {
278
+ kind: "duplicate",
279
+ owner,
280
+ reason: "active-owner"
281
+ };
282
+ }
283
+ if (active.phase === "stopping") {
284
+ const sameRegistration = sameLifecycleRegistration(active.key, owner.key);
285
+ const sameGeneration = sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration);
286
+ if (sameRegistration && sameGeneration) {
287
+ return {
288
+ kind: "duplicate",
289
+ owner,
290
+ reason: "lifecycle-stopping"
291
+ };
292
+ }
293
+ if (sameRegistration) {
294
+ return {
295
+ kind: "reject",
296
+ owner,
297
+ reason: "registration-generation-conflict"
298
+ };
299
+ }
300
+ return {
301
+ kind: "defer",
302
+ owner,
303
+ predecessorGeneration: active.key.generation,
304
+ mode: sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration) ? "reuse" : "replace",
305
+ reason: "predecessor-stopping"
306
+ };
307
+ }
308
+ if (sameLifecycleRegistration(active.key, owner.key)) {
309
+ if (!sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration)) {
310
+ return {
311
+ kind: "reject",
312
+ owner,
313
+ reason: "registration-generation-conflict"
314
+ };
315
+ }
316
+ return {
317
+ kind: "duplicate",
318
+ owner,
319
+ reason: "same-lifecycle-stopped"
320
+ };
321
+ }
322
+ if (sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration)) {
323
+ return {
324
+ kind: "takeover",
325
+ owner,
326
+ mode: "reuse",
327
+ reason: "bridge-generation-reused"
328
+ };
329
+ }
330
+ return {
331
+ kind: "takeover",
332
+ owner,
333
+ mode: "replace",
334
+ reason: "bridge-generation-changed"
335
+ };
336
+ }
337
+ function commitLifecycleAdoption(state, decision) {
338
+ if (decision.kind === "duplicate" || decision.kind === "reject") {
339
+ return state.active;
340
+ }
341
+ state.nextGeneration = Math.max(state.nextGeneration + 1, decision.owner.key.generation + 1);
342
+ if (decision.kind === "defer") {
343
+ state.pending = decision.owner;
344
+ state.pendingMode = decision.mode;
345
+ return state.active;
346
+ }
347
+ state.active = decision.owner;
348
+ state.pending = void 0;
349
+ state.pendingMode = void 0;
350
+ state.retirement = void 0;
351
+ state.resolveRetirement = void 0;
352
+ return state.active;
353
+ }
354
+ function beginLifecycleRetirement(state, registryFingerprint, now, expectedGeneration) {
355
+ const active = state.active;
356
+ if (!active || active.key.registryFingerprint !== registryFingerprint || expectedGeneration !== void 0 && active.key.generation !== expectedGeneration || active.phase !== "active" && active.phase !== "registering") {
357
+ state.staleCallbackSuppressions += 1;
358
+ return null;
359
+ }
360
+ let resolveRetirement;
361
+ state.retirement = new Promise((resolve) => {
362
+ resolveRetirement = resolve;
363
+ });
364
+ state.resolveRetirement = resolveRetirement;
365
+ active.phase = "stopping";
366
+ active.stopRequestedAt = now;
367
+ return state.retirement;
368
+ }
369
+ function settleLifecycleRetirement(state, registryFingerprint, outcome, now, expectedGeneration) {
370
+ const active = state.active;
371
+ if (!active || active.key.registryFingerprint !== registryFingerprint || expectedGeneration !== void 0 && active.key.generation !== expectedGeneration || active.phase !== "stopping") {
372
+ state.staleCallbackSuppressions += 1;
373
+ return false;
374
+ }
375
+ const resolveRetirement = state.resolveRetirement;
376
+ state.resolveRetirement = void 0;
377
+ active.phase = outcome.ok ? "stopped" : "failed";
378
+ active.stoppedAt = outcome.ok ? now : null;
379
+ active.failure = outcome.ok ? null : outcome.error;
380
+ resolveRetirement?.(outcome);
381
+ return true;
382
+ }
383
+ function activatePendingLifecycle(state, registryFingerprint, now, expectedGeneration) {
384
+ const pending = state.pending;
385
+ if (!pending || pending.key.registryFingerprint !== registryFingerprint || expectedGeneration !== void 0 && pending.key.generation !== expectedGeneration) {
386
+ return null;
387
+ }
388
+ if (state.active?.phase !== "stopped") {
389
+ return null;
390
+ }
391
+ pending.phase = "active";
392
+ pending.startedAt = now;
393
+ state.active = pending;
394
+ state.pending = void 0;
395
+ state.pendingMode = void 0;
396
+ state.retirement = void 0;
397
+ state.resolveRetirement = void 0;
398
+ return state.active;
399
+ }
400
+ function reactivateStoppedLifecycle(state, registryFingerprint, now, expectedGeneration) {
401
+ const active = state.active;
402
+ if (state.pending || !active || active.key.registryFingerprint !== registryFingerprint || expectedGeneration !== void 0 && active.key.generation !== expectedGeneration || active.phase !== "stopped") {
403
+ return null;
404
+ }
405
+ active.phase = "active";
406
+ active.startedAt = now;
407
+ active.stopRequestedAt = null;
408
+ active.stoppedAt = null;
409
+ active.failure = null;
410
+ return active;
411
+ }
412
+ function failLifecycle(state, registryFingerprint, error, expectedGeneration) {
413
+ const active = state.active;
414
+ if (!active || active.key.registryFingerprint !== registryFingerprint || expectedGeneration !== void 0 && active.key.generation !== expectedGeneration) {
415
+ state.staleCallbackSuppressions += 1;
416
+ return false;
417
+ }
418
+ if (active.phase !== "active" && active.phase !== "registering") {
419
+ state.staleCallbackSuppressions += 1;
420
+ return false;
421
+ }
422
+ active.phase = "failed";
423
+ active.stoppedAt = null;
424
+ active.failure = error instanceof Error ? error.message : String(error);
425
+ return true;
426
+ }
427
+ function getPendingLifecycle(state) {
428
+ return state.pending;
429
+ }
430
+ function isLifecycleRegistryActive(state, registryFingerprint, expectedGeneration) {
431
+ return Boolean(
432
+ state.active?.key.registryFingerprint === registryFingerprint && (expectedGeneration === void 0 || state.active.key.generation === expectedGeneration) && (state.active.phase === "active" || state.active.phase === "registering")
433
+ );
434
+ }
435
+ function isLifecycleRegistryStoppable(state, registryFingerprint, expectedGeneration) {
436
+ const active = state.active;
437
+ return Boolean(
438
+ active?.key.registryFingerprint === registryFingerprint && (expectedGeneration === void 0 || active.key.generation === expectedGeneration) && (active.phase === "registering" || active.phase === "active" || active.phase === "stopping")
439
+ );
440
+ }
441
+
158
442
  // src/bootstrap/register-runtime-gateway.ts
159
443
  function createBncrGatewayMethodRegistry(runtime2) {
160
- const dispatchGatewayMethod = (name, opts) => {
444
+ const dispatchGatewayMethod = (name, opts, registryFingerprint) => {
161
445
  const gatewayRuntime = runtime2.getGatewayRuntime();
446
+ const declaration = registryFingerprint ? gatewayRuntime.registryDeclarations?.get(registryFingerprint) : void 0;
447
+ const lifecycleFailed = gatewayRuntime.lifecycle?.active?.phase === "failed";
448
+ const declarationFailed = declaration?.registration === "failed";
449
+ const dispatchAllowed = declaration ? declaration.state !== "retired" && !declarationFailed && !lifecycleFailed : !gatewayRuntime.lifecycle?.active;
450
+ if (registryFingerprint && !dispatchAllowed) {
451
+ throw new Error(`bncr lifecycle registry is inactive for ${name}`);
452
+ }
162
453
  const bridge = gatewayRuntime.currentBridge;
163
454
  if (!bridge) {
164
455
  throw new Error(`bncr gateway runtime unavailable for ${name}`);
165
456
  }
457
+ const dispatcherOpts = name === "bncr.diagnostics" && registryFingerprint && runtime2.getRegistryRuntimeObservation && typeof opts.respond === "function" ? {
458
+ ...opts,
459
+ respond: (...response) => {
460
+ const [ok, payload, error, meta] = response;
461
+ let observation = null;
462
+ try {
463
+ observation = runtime2.getRegistryRuntimeObservation?.(registryFingerprint) ?? null;
464
+ } catch {
465
+ observation = null;
466
+ }
467
+ const nextPayload = ok && payload && typeof payload === "object" && !Array.isArray(payload) ? { ...payload, lifecycleObservation: observation } : payload;
468
+ opts.respond(ok, nextPayload, error, meta);
469
+ }
470
+ } : opts;
166
471
  try {
167
- return runtime2.gatewayMethodDispatchers[name](bridge, opts);
472
+ const dispatcher = gatewayRuntime.gatewayMethodDispatchers?.[name] || runtime2.gatewayMethodDispatchers[name];
473
+ return dispatcher(bridge, dispatcherOpts);
168
474
  } catch (error) {
169
475
  const state = runtime2.getBridgeRegisterStateCarrier(bridge);
170
476
  const detail = error instanceof Error ? { name: error.name, message: error.message, stack: error.stack || null } : { name: "NonError", message: String(error), stack: null };
@@ -189,7 +495,14 @@ function createBncrGatewayMethodRegistry(runtime2) {
189
495
  const mirrorGatewayMethodForMockApi = (api, name) => {
190
496
  if (!Array.isArray(api?.methods)) return;
191
497
  if (api.methods.some((item) => item?.name === name)) return;
192
- api.methods.push({ name, handler: (opts) => dispatchGatewayMethod(name, opts) });
498
+ api.methods.push({
499
+ name,
500
+ handler: (opts) => dispatchGatewayMethod(
501
+ name,
502
+ opts,
503
+ runtime2.getRegisterMeta(api).registryFingerprint || runtime2.getRegistryFingerprint(api)
504
+ )
505
+ });
193
506
  };
194
507
  const ensureGatewayMethodRegistered2 = (api, name, debugLog) => {
195
508
  const meta = runtime2.getRegisterMeta(api);
@@ -212,7 +525,7 @@ function createBncrGatewayMethodRegistry(runtime2) {
212
525
  }
213
526
  api.registerGatewayMethod(
214
527
  name,
215
- (opts) => dispatchGatewayMethod(name, opts)
528
+ (opts) => dispatchGatewayMethod(name, opts, registryFingerprint)
216
529
  );
217
530
  mirrorGatewayMethodForMockApi(api, name);
218
531
  registryMethods.add(name);
@@ -225,14 +538,131 @@ function createBncrGatewayMethodRegistry(runtime2) {
225
538
  };
226
539
  }
227
540
 
228
- // src/bootstrap/register-runtime-helpers.ts
229
- function getProcessOwnerApiInstanceId(args) {
230
- return args.serviceOwnerApiInstanceId || args.channelOwnerApiInstanceId || void 0;
541
+ // src/bootstrap/register-runtime-observation.ts
542
+ var REGISTRY_RUNTIME_OBSERVATION_TIMEOUT_MS = 3e4;
543
+ var REGISTRY_RUNTIME_OBSERVATION_POLL_MS = 5e3;
544
+ function finiteTimestamp(value) {
545
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
231
546
  }
232
- function sameBridgeOwner(left, right) {
233
- if (!left || !right) return false;
234
- return left.moduleEpoch === right.moduleEpoch && left.bridgeFactoryId === right.bridgeFactoryId && left.apiInstanceId === right.apiInstanceId && left.registryFingerprint === right.registryFingerprint;
547
+ function nonNegativeInteger(value) {
548
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
549
+ }
550
+ function createRegistryRuntimeObservation(now) {
551
+ return {
552
+ phase: "awaiting",
553
+ startedAt: now,
554
+ deadlineAt: now + REGISTRY_RUNTIME_OBSERVATION_TIMEOUT_MS,
555
+ pollingStartedAt: null,
556
+ readyAt: null,
557
+ retiredAt: null,
558
+ lastProbeAt: null,
559
+ probeCount: 0,
560
+ serviceObservedAt: null,
561
+ channelObservedAt: null
562
+ };
563
+ }
564
+ function normalizeRegistryRuntimeObservation(value) {
565
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
566
+ const source = value;
567
+ const phase = source.phase;
568
+ if (phase !== "awaiting" && phase !== "polling" && phase !== "ready" && phase !== "retired") {
569
+ return void 0;
570
+ }
571
+ const startedAt = finiteTimestamp(source.startedAt);
572
+ const deadlineAt = finiteTimestamp(source.deadlineAt);
573
+ if (startedAt === null || deadlineAt === null) return void 0;
574
+ return {
575
+ phase,
576
+ startedAt,
577
+ deadlineAt,
578
+ pollingStartedAt: finiteTimestamp(source.pollingStartedAt),
579
+ readyAt: finiteTimestamp(source.readyAt),
580
+ retiredAt: finiteTimestamp(source.retiredAt),
581
+ lastProbeAt: finiteTimestamp(source.lastProbeAt),
582
+ probeCount: nonNegativeInteger(source.probeCount),
583
+ serviceObservedAt: finiteTimestamp(source.serviceObservedAt),
584
+ channelObservedAt: finiteTimestamp(source.channelObservedAt)
585
+ };
586
+ }
587
+ function retireObservation(observation, now) {
588
+ return {
589
+ observation: {
590
+ ...observation,
591
+ phase: "retired",
592
+ retiredAt: observation.retiredAt ?? now,
593
+ lastProbeAt: now,
594
+ probeCount: observation.probeCount + 1
595
+ },
596
+ action: "retired",
597
+ transitionedToPolling: false,
598
+ transitionedToReady: false
599
+ };
600
+ }
601
+ function evaluateRegistryRuntimeObservation(args) {
602
+ const { observation, now, lifecycleActive, probe } = args;
603
+ if (observation.phase === "retired") {
604
+ return {
605
+ observation,
606
+ action: "retired",
607
+ transitionedToPolling: false,
608
+ transitionedToReady: false
609
+ };
610
+ }
611
+ if (!lifecycleActive) {
612
+ return retireObservation(observation, now);
613
+ }
614
+ if (observation.phase === "ready") {
615
+ return {
616
+ observation,
617
+ action: "ready",
618
+ transitionedToPolling: false,
619
+ transitionedToReady: false
620
+ };
621
+ }
622
+ const serviceRunning = probe.serviceRunning === true;
623
+ const channelRunning = probe.channelRunning === true || (probe.activeChannelAccounts ?? 0) > 0;
624
+ const serviceObservedAt = observation.serviceObservedAt ?? (serviceRunning ? now : null);
625
+ const channelObservedAt = observation.channelObservedAt ?? (channelRunning ? now : null);
626
+ const observed = {
627
+ ...observation,
628
+ lastProbeAt: now,
629
+ probeCount: observation.probeCount + 1,
630
+ serviceObservedAt,
631
+ channelObservedAt
632
+ };
633
+ if (serviceRunning && channelRunning) {
634
+ return {
635
+ observation: {
636
+ ...observed,
637
+ phase: "ready",
638
+ readyAt: observed.readyAt ?? now
639
+ },
640
+ action: "ready",
641
+ transitionedToPolling: false,
642
+ transitionedToReady: true
643
+ };
644
+ }
645
+ if (now >= observation.deadlineAt) {
646
+ return {
647
+ observation: {
648
+ ...observed,
649
+ phase: "polling",
650
+ pollingStartedAt: observed.pollingStartedAt ?? now
651
+ },
652
+ action: "poll",
653
+ transitionedToPolling: observation.phase === "awaiting",
654
+ transitionedToReady: false
655
+ };
656
+ }
657
+ return {
658
+ observation: observed,
659
+ action: "wait",
660
+ transitionedToPolling: false,
661
+ transitionedToReady: false
662
+ };
235
663
  }
664
+
665
+ // src/bootstrap/register-runtime-helpers.ts
236
666
  function snapshotBridgeRegisterState(bridge) {
237
667
  if (!bridge) return null;
238
668
  return {
@@ -264,32 +694,6 @@ function hydrateBridgeRegisterState(bridge, snapshot) {
264
694
  bridge.registerTraceRecent = snapshot.registerTraceRecent.map((trace) => ({ ...trace }));
265
695
  return bridge;
266
696
  }
267
- function shouldAdoptProcessOwner(args) {
268
- const existingOwnerApiInstanceId = getProcessOwnerApiInstanceId({
269
- serviceOwnerApiInstanceId: args.serviceOwnerApiInstanceId,
270
- channelOwnerApiInstanceId: args.channelOwnerApiInstanceId
271
- });
272
- const hasSingletonOwner = Boolean(args.serviceRegistered) || Boolean(args.channelRegistered);
273
- if (!hasSingletonOwner) {
274
- return {
275
- adoptOwner: true,
276
- existingOwnerApiInstanceId,
277
- reason: "no-singleton-owner"
278
- };
279
- }
280
- if (existingOwnerApiInstanceId && existingOwnerApiInstanceId === args.apiInstanceId) {
281
- return {
282
- adoptOwner: true,
283
- existingOwnerApiInstanceId,
284
- reason: "same-owner-api"
285
- };
286
- }
287
- return {
288
- adoptOwner: false,
289
- existingOwnerApiInstanceId,
290
- reason: "singleton-owned-by-other-api"
291
- };
292
- }
293
697
 
294
698
  // src/bootstrap/register-runtime-singleton.ts
295
699
  function isBridgeOwner(value) {
@@ -308,54 +712,46 @@ function createBncrBridgeSingletonManager(runtime2) {
308
712
  getBridgeOwnedCarrier(bridge)[runtime2.bridgeOwnerSymbol] = owner;
309
713
  return bridge;
310
714
  };
311
- const getBridgeSingleton2 = (api) => {
715
+ const adoptBridgeSingleton = (api, owner, mode) => {
312
716
  const loaded = runtime2.loadBncrRuntimeSync();
313
717
  const g = globalThis;
314
- const owner = runtime2.getBridgeOwner(api, loaded);
315
718
  const previousOwnerRaw = g.__bncrBridge ? getBridgeOwnedCarrier(g.__bncrBridge)[runtime2.bridgeOwnerSymbol] : void 0;
316
719
  const previousOwner = isBridgeOwner(previousOwnerRaw) ? previousOwnerRaw : void 0;
317
720
  let created = false;
318
721
  let rebuilt = false;
319
- if (g.__bncrBridge) {
320
- const mustRebuild = !sameBridgeOwner(previousOwner, owner) && (previousOwner?.moduleEpoch !== owner.moduleEpoch || previousOwner?.bridgeFactoryId !== owner.bridgeFactoryId || previousOwner?.registrationMode !== owner.registrationMode || previousOwner?.apiInstanceId !== owner.apiInstanceId || previousOwner?.registryFingerprint !== owner.registryFingerprint);
321
- if (mustRebuild) {
322
- const registerState = snapshotBridgeRegisterState(
323
- getBridgeRegisterStateCarrier(g.__bncrBridge)
324
- );
325
- try {
326
- g.__bncrBridge.stopService?.();
327
- } catch {
328
- }
329
- const rebuiltBridge = assignBridgeOwner(
330
- loaded.createBncrBridge(api, {
331
- pluginRoot: runtime2.pluginRoot,
332
- pluginFile: runtime2.pluginFile
333
- }),
334
- owner
335
- );
336
- hydrateBridgeRegisterState(getBridgeRegisterStateCarrier(rebuiltBridge), registerState);
337
- g.__bncrBridge = rebuiltBridge;
338
- created = true;
339
- rebuilt = true;
340
- } else {
341
- g.__bncrBridge.bindApi?.(api);
342
- assignBridgeOwner(g.__bncrBridge, owner);
343
- }
344
- } else {
345
- g.__bncrBridge = assignBridgeOwner(
722
+ let bridge;
723
+ if (!g.__bncrBridge || mode === "create" || mode === "replace") {
724
+ const registerState = mode === "replace" && g.__bncrBridge ? snapshotBridgeRegisterState(getBridgeRegisterStateCarrier(g.__bncrBridge)) : null;
725
+ bridge = assignBridgeOwner(
346
726
  loaded.createBncrBridge(api, {
347
727
  pluginRoot: runtime2.pluginRoot,
348
728
  pluginFile: runtime2.pluginFile
349
729
  }),
350
730
  owner
351
731
  );
732
+ hydrateBridgeRegisterState(getBridgeRegisterStateCarrier(bridge), registerState);
733
+ g.__bncrBridge = bridge;
352
734
  created = true;
735
+ rebuilt = mode === "replace" && Boolean(previousOwner);
736
+ } else {
737
+ bridge = g.__bncrBridge;
738
+ bridge.bindApi?.(api);
739
+ assignBridgeOwner(bridge, owner);
353
740
  }
354
- g.__bncrBridge?.bindRuntimePaths?.({
741
+ bridge.bindRuntimePaths?.({
355
742
  pluginRoot: runtime2.pluginRoot,
356
743
  pluginFile: runtime2.pluginFile
357
744
  });
358
- return { bridge: g.__bncrBridge, runtime: loaded, created, rebuilt, owner, previousOwner };
745
+ return { bridge, runtime: loaded, created, rebuilt, owner, previousOwner };
746
+ };
747
+ const getBridgeSingleton = (api) => {
748
+ const loaded = runtime2.loadBncrRuntimeSync();
749
+ const owner = runtime2.getBridgeOwner(api, loaded);
750
+ const g = globalThis;
751
+ const previous = getBridgeOwnerFromBridge2(g.__bncrBridge);
752
+ const sameGeneration = previous?.moduleEpoch === owner.moduleEpoch && previous?.bridgeFactoryId === owner.bridgeFactoryId && previous?.pluginVersion === owner.pluginVersion && previous?.registrationMode === owner.registrationMode && previous?.pluginRoot === owner.pluginRoot && previous?.pluginFile === owner.pluginFile;
753
+ const mode = !g.__bncrBridge ? "create" : sameGeneration ? "reuse" : "replace";
754
+ return adoptBridgeSingleton(api, owner, mode);
359
755
  };
360
756
  const getExistingBridgeSingleton2 = () => {
361
757
  const g = globalThis;
@@ -372,8 +768,9 @@ function createBncrBridgeSingletonManager(runtime2) {
372
768
  };
373
769
  return {
374
770
  assignBridgeOwner,
771
+ adoptBridgeSingleton,
375
772
  getBridgeRegisterStateCarrier,
376
- getBridgeSingleton: getBridgeSingleton2,
773
+ getBridgeSingleton,
377
774
  getExistingBridgeSingleton: getExistingBridgeSingleton2,
378
775
  getBridgeOwnerFromBridge: getBridgeOwnerFromBridge2
379
776
  };
@@ -597,6 +994,8 @@ var registerMetaSymbol = /* @__PURE__ */ Symbol.for("bncr.register.meta");
597
994
  var globalRegisterTraceSymbol = /* @__PURE__ */ Symbol.for("bncr.global.register.trace");
598
995
  var bridgeOwnerSymbol = /* @__PURE__ */ Symbol.for("bncr.bridge.owner");
599
996
  var gatewayRuntimeSymbol = /* @__PURE__ */ Symbol.for("bncr.gateway.runtime");
997
+ var maxRegistryDeclarations = 6;
998
+ var maxRegisterTraceEntries = 64;
600
999
  var moduleEpoch = `${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
601
1000
  var identityIds = /* @__PURE__ */ new WeakMap();
602
1001
  var identitySeq = 0;
@@ -617,6 +1016,61 @@ var getProcessStore = () => {
617
1016
  const p = process;
618
1017
  return p;
619
1018
  };
1019
+ var rememberRegisterTraceValue = (values, value) => {
1020
+ values.delete(value);
1021
+ values.add(value);
1022
+ while (values.size > maxRegisterTraceEntries) {
1023
+ const oldest = values.values().next().value;
1024
+ if (oldest === void 0) break;
1025
+ values.delete(oldest);
1026
+ }
1027
+ };
1028
+ var normalizeRegistryDeclaration = (value) => {
1029
+ const legacy = value || {};
1030
+ const service = legacy.service === "declared" || legacy.service === true ? "declared" : "missing";
1031
+ const channel = legacy.channel === "declared" || legacy.channel === true ? "declared" : "missing";
1032
+ const registration = legacy.registration === "pending" || legacy.registration === "complete" || legacy.registration === "failed" ? legacy.registration : service === "declared" && channel === "declared" ? "complete" : "failed";
1033
+ const state = legacy.state === "active" || legacy.state === "retired" ? legacy.state : "shadow";
1034
+ const lifecycleGeneration = typeof legacy.lifecycleGeneration === "number" ? legacy.lifecycleGeneration : typeof legacy.generation === "number" ? legacy.generation : 0;
1035
+ return {
1036
+ bridgeGenerationKey: typeof legacy.bridgeGenerationKey === "string" ? legacy.bridgeGenerationKey : "",
1037
+ lifecycleGeneration,
1038
+ service,
1039
+ channel,
1040
+ registration,
1041
+ state,
1042
+ runtimeObservation: normalizeRegistryRuntimeObservation(legacy.runtimeObservation)
1043
+ };
1044
+ };
1045
+ var normalizeRegistryDeclarationInPlace = (value) => {
1046
+ const normalized = normalizeRegistryDeclaration(value);
1047
+ if (!value || typeof value !== "object" || Array.isArray(value)) return normalized;
1048
+ const target = value;
1049
+ delete target.generation;
1050
+ Object.assign(target, normalized);
1051
+ return value;
1052
+ };
1053
+ var pruneRegistryLedgers = (runtime2) => {
1054
+ const protectedRegistries = new Set(
1055
+ [
1056
+ runtime2.lifecycle.active?.key.registryFingerprint,
1057
+ runtime2.lifecycle.pending?.key.registryFingerprint
1058
+ ].filter((value) => Boolean(value))
1059
+ );
1060
+ for (const ledger of [runtime2.registryDeclarations, runtime2.registeredMethodsByRegistry]) {
1061
+ for (const registryFingerprint of ledger.keys()) {
1062
+ if (ledger.size <= maxRegistryDeclarations) break;
1063
+ if (!protectedRegistries.has(registryFingerprint)) {
1064
+ const timer = runtime2.runtimeObservationTimers.get(registryFingerprint);
1065
+ if (timer) {
1066
+ clearTimeout(timer.timer);
1067
+ runtime2.runtimeObservationTimers.delete(registryFingerprint);
1068
+ }
1069
+ ledger.delete(registryFingerprint);
1070
+ }
1071
+ }
1072
+ }
1073
+ };
620
1074
  function createBncrRegisterRuntime() {
621
1075
  const gatewayMethodDispatchers = {
622
1076
  "bncr.connect": (bridge, opts) => bridge.handleConnect(opts),
@@ -664,11 +1118,32 @@ function createBncrRegisterRuntime() {
664
1118
  if (!p[gatewayRuntimeSymbol]) {
665
1119
  p[gatewayRuntimeSymbol] = {
666
1120
  registeredMethodsByRegistry: /* @__PURE__ */ new Map(),
667
- serviceRegistered: false,
668
- channelRegistered: false
1121
+ registryDeclarations: /* @__PURE__ */ new Map(),
1122
+ runtimeObservationTimers: /* @__PURE__ */ new Map(),
1123
+ lifecycle: createRegisterLifecycleState()
669
1124
  };
670
1125
  }
671
- return p[gatewayRuntimeSymbol];
1126
+ const runtime2 = p[gatewayRuntimeSymbol];
1127
+ if (!(runtime2.registeredMethodsByRegistry instanceof Map)) {
1128
+ runtime2.registeredMethodsByRegistry = /* @__PURE__ */ new Map();
1129
+ }
1130
+ if (!(runtime2.registryDeclarations instanceof Map)) {
1131
+ runtime2.registryDeclarations = /* @__PURE__ */ new Map();
1132
+ } else {
1133
+ for (const [registryFingerprint, declaration] of runtime2.registryDeclarations) {
1134
+ const normalized = normalizeRegistryDeclarationInPlace(declaration);
1135
+ if (normalized !== declaration) {
1136
+ runtime2.registryDeclarations.set(registryFingerprint, normalized);
1137
+ }
1138
+ }
1139
+ }
1140
+ if (!(runtime2.runtimeObservationTimers instanceof Map)) {
1141
+ runtime2.runtimeObservationTimers = /* @__PURE__ */ new Map();
1142
+ }
1143
+ if (!runtime2.lifecycle) {
1144
+ runtime2.lifecycle = createRegisterLifecycleState();
1145
+ }
1146
+ return runtime2;
672
1147
  };
673
1148
  const getBridgeRegisterStateCarrier2 = (bridge) => bridge;
674
1149
  const gatewayMethodRegistry = createBncrGatewayMethodRegistry({
@@ -676,7 +1151,8 @@ function createBncrRegisterRuntime() {
676
1151
  getRegistryFingerprint,
677
1152
  getGatewayRuntime: getGatewayRuntime2,
678
1153
  gatewayMethodDispatchers,
679
- getBridgeRegisterStateCarrier: getBridgeRegisterStateCarrier2
1154
+ getBridgeRegisterStateCarrier: getBridgeRegisterStateCarrier2,
1155
+ getRegistryRuntimeObservation: (registryFingerprint) => getRegistryRuntimeObservation(registryFingerprint)
680
1156
  });
681
1157
  const getBridgeOwner = (api, loaded) => {
682
1158
  const meta = getRegisterMeta2(api);
@@ -685,7 +1161,10 @@ function createBncrRegisterRuntime() {
685
1161
  bridgeFactoryId: getIdentityId(loaded.createBncrBridge, "bridgeFactory"),
686
1162
  apiInstanceId: meta.apiInstanceId || "unknown",
687
1163
  registryFingerprint: meta.registryFingerprint || "unknown",
688
- registrationMode: meta.registrationMode
1164
+ registrationMode: meta.registrationMode,
1165
+ pluginVersion,
1166
+ pluginRoot,
1167
+ pluginFile
689
1168
  };
690
1169
  };
691
1170
  const bridgeSingletonManager = createBncrBridgeSingletonManager({
@@ -695,22 +1174,588 @@ function createBncrRegisterRuntime() {
695
1174
  loadBncrRuntimeSync,
696
1175
  getBridgeOwner
697
1176
  });
1177
+ const toBridgeOwner = (owner) => ({
1178
+ moduleEpoch: owner.bridgeGeneration.moduleEpoch,
1179
+ bridgeFactoryId: owner.bridgeGeneration.bridgeFactoryId,
1180
+ apiInstanceId: owner.key.apiInstanceId,
1181
+ registryFingerprint: owner.key.registryFingerprint,
1182
+ registrationMode: owner.bridgeGeneration.registrationMode,
1183
+ pluginVersion: owner.bridgeGeneration.pluginVersion,
1184
+ pluginRoot: owner.bridgeGeneration.pluginRoot,
1185
+ pluginFile: owner.bridgeGeneration.pluginFile
1186
+ });
698
1187
  const getCurrentBridge2 = () => {
699
1188
  const bridge = getGatewayRuntime2().currentBridge;
700
1189
  if (!bridge) throw new Error("bncr current bridge unavailable");
701
1190
  return bridge;
702
1191
  };
1192
+ const clearRegistryRuntimeObservationTimer = (registryFingerprint) => {
1193
+ const runtime2 = getGatewayRuntime2();
1194
+ const entry = runtime2.runtimeObservationTimers.get(registryFingerprint);
1195
+ if (entry) {
1196
+ clearTimeout(entry.timer);
1197
+ runtime2.runtimeObservationTimers.delete(registryFingerprint);
1198
+ }
1199
+ };
1200
+ const readRegistryRuntimeProbe = () => {
1201
+ const bridge = getGatewayRuntime2().currentBridge;
1202
+ const probe = bridge?.getRuntimeObservation?.();
1203
+ return probe && typeof probe === "object" ? probe : {};
1204
+ };
1205
+ const isRegistryRuntimeLifecycleActive = (registryFingerprint, lifecycleGeneration) => {
1206
+ const active = getGatewayRuntime2().lifecycle.active;
1207
+ return Boolean(
1208
+ active && active.key.registryFingerprint === registryFingerprint && active.key.generation === lifecycleGeneration && (active.phase === "active" || active.phase === "registering")
1209
+ );
1210
+ };
1211
+ const probeRegistryRuntimeObservation2 = (registryFingerprint, bridgeGenerationKey, lifecycleGeneration, now = Date.now()) => {
1212
+ try {
1213
+ const runtime2 = getGatewayRuntime2();
1214
+ const declaration = runtime2.registryDeclarations.get(registryFingerprint);
1215
+ if (!declaration || declaration.bridgeGenerationKey !== bridgeGenerationKey || declaration.lifecycleGeneration !== lifecycleGeneration || !declaration.runtimeObservation) {
1216
+ return null;
1217
+ }
1218
+ const decision = evaluateRegistryRuntimeObservation({
1219
+ observation: declaration.runtimeObservation,
1220
+ now,
1221
+ lifecycleActive: declaration.registration === "complete" && declaration.state === "active" && isRegistryRuntimeLifecycleActive(registryFingerprint, lifecycleGeneration),
1222
+ probe: readRegistryRuntimeProbe()
1223
+ });
1224
+ declaration.runtimeObservation = decision.observation;
1225
+ if (decision.transitionedToPolling) {
1226
+ emitBncrLogLine(
1227
+ "warn",
1228
+ `[bncr] runtime observation timeout registry=${registryFingerprint} generation=${lifecycleGeneration} service=${decision.observation.serviceObservedAt !== null} channel=${decision.observation.channelObservedAt !== null} action=poll intervalMs=${REGISTRY_RUNTIME_OBSERVATION_POLL_MS}`
1229
+ );
1230
+ }
1231
+ if (decision.transitionedToReady) {
1232
+ emitBncrLogLine(
1233
+ "info",
1234
+ `[bncr] runtime observation ready registry=${registryFingerprint} generation=${lifecycleGeneration} probes=${decision.observation.probeCount}`
1235
+ );
1236
+ }
1237
+ if (decision.action === "wait" || decision.action === "poll") {
1238
+ clearRegistryRuntimeObservationTimer(registryFingerprint);
1239
+ const delay = decision.action === "poll" ? REGISTRY_RUNTIME_OBSERVATION_POLL_MS : Math.max(0, decision.observation.deadlineAt - now);
1240
+ const timer = setTimeout(() => {
1241
+ const current = runtime2.runtimeObservationTimers.get(registryFingerprint);
1242
+ if (current?.timer !== timer) return;
1243
+ runtime2.runtimeObservationTimers.delete(registryFingerprint);
1244
+ probeRegistryRuntimeObservation2(
1245
+ registryFingerprint,
1246
+ bridgeGenerationKey,
1247
+ lifecycleGeneration,
1248
+ Date.now()
1249
+ );
1250
+ }, delay);
1251
+ timer.unref?.();
1252
+ runtime2.runtimeObservationTimers.set(registryFingerprint, {
1253
+ timer,
1254
+ bridgeGenerationKey,
1255
+ lifecycleGeneration
1256
+ });
1257
+ } else {
1258
+ clearRegistryRuntimeObservationTimer(registryFingerprint);
1259
+ }
1260
+ return { ...decision.observation };
1261
+ } catch (error) {
1262
+ try {
1263
+ emitBncrLogLine(
1264
+ "warn",
1265
+ `[bncr] runtime observation probe failed registry=${registryFingerprint} generation=${lifecycleGeneration} error=${error instanceof Error ? error.message : String(error)}`
1266
+ );
1267
+ } catch {
1268
+ }
1269
+ try {
1270
+ const runtime2 = getGatewayRuntime2();
1271
+ const declaration = runtime2.registryDeclarations.get(registryFingerprint);
1272
+ if (declaration?.bridgeGenerationKey === bridgeGenerationKey && declaration.lifecycleGeneration === lifecycleGeneration && declaration.registration === "complete" && declaration.state === "active") {
1273
+ clearRegistryRuntimeObservationTimer(registryFingerprint);
1274
+ const timer = setTimeout(() => {
1275
+ const current = runtime2.runtimeObservationTimers.get(registryFingerprint);
1276
+ if (current?.timer !== timer) return;
1277
+ runtime2.runtimeObservationTimers.delete(registryFingerprint);
1278
+ probeRegistryRuntimeObservation2(
1279
+ registryFingerprint,
1280
+ bridgeGenerationKey,
1281
+ lifecycleGeneration,
1282
+ Date.now()
1283
+ );
1284
+ }, REGISTRY_RUNTIME_OBSERVATION_POLL_MS);
1285
+ timer.unref?.();
1286
+ runtime2.runtimeObservationTimers.set(registryFingerprint, {
1287
+ timer,
1288
+ bridgeGenerationKey,
1289
+ lifecycleGeneration
1290
+ });
1291
+ }
1292
+ } catch {
1293
+ }
1294
+ return null;
1295
+ }
1296
+ };
1297
+ const startRegistryRuntimeObservation = (registryFingerprint, bridgeGenerationKey, lifecycleGeneration, now = Date.now(), reset = false) => {
1298
+ const runtime2 = getGatewayRuntime2();
1299
+ const declaration = runtime2.registryDeclarations.get(registryFingerprint);
1300
+ if (!declaration || declaration.bridgeGenerationKey !== bridgeGenerationKey || declaration.lifecycleGeneration !== lifecycleGeneration || declaration.registration !== "complete" || declaration.state !== "active") {
1301
+ return null;
1302
+ }
1303
+ if (!reset && declaration.runtimeObservation) {
1304
+ return { ...declaration.runtimeObservation };
1305
+ }
1306
+ clearRegistryRuntimeObservationTimer(registryFingerprint);
1307
+ declaration.runtimeObservation = createRegistryRuntimeObservation(now);
1308
+ return probeRegistryRuntimeObservation2(
1309
+ registryFingerprint,
1310
+ bridgeGenerationKey,
1311
+ lifecycleGeneration,
1312
+ now
1313
+ );
1314
+ };
1315
+ const retireRegistryRuntimeObservation = (registryFingerprint, now = Date.now()) => {
1316
+ clearRegistryRuntimeObservationTimer(registryFingerprint);
1317
+ const declaration = getGatewayRuntime2().registryDeclarations.get(registryFingerprint);
1318
+ if (!declaration?.runtimeObservation) return;
1319
+ declaration.runtimeObservation = {
1320
+ ...declaration.runtimeObservation,
1321
+ phase: "retired",
1322
+ retiredAt: declaration.runtimeObservation.retiredAt ?? now
1323
+ };
1324
+ };
1325
+ const getRegistryRuntimeObservation = (registryFingerprint) => {
1326
+ const runtime2 = getGatewayRuntime2();
1327
+ const declaration = runtime2.registryDeclarations.get(registryFingerprint);
1328
+ if (declaration?.state === "active" && declaration.runtimeObservation) {
1329
+ return { ...declaration.runtimeObservation };
1330
+ }
1331
+ const activeRegistry = runtime2.lifecycle.active?.key.registryFingerprint;
1332
+ if (activeRegistry && activeRegistry !== registryFingerprint) {
1333
+ const activeObservation = runtime2.registryDeclarations.get(activeRegistry)?.runtimeObservation;
1334
+ if (activeObservation) return { ...activeObservation };
1335
+ }
1336
+ return declaration?.runtimeObservation ? { ...declaration.runtimeObservation } : null;
1337
+ };
1338
+ const planLifecycle2 = (api, now = Date.now()) => {
1339
+ const loaded = loadBncrRuntimeSync();
1340
+ const owner = getBridgeOwner(api, loaded);
1341
+ const runtime2 = getGatewayRuntime2();
1342
+ const input = {
1343
+ apiInstanceId: owner.apiInstanceId,
1344
+ registryFingerprint: owner.registryFingerprint,
1345
+ bridgeGeneration: {
1346
+ moduleEpoch: owner.moduleEpoch,
1347
+ bridgeFactoryId: owner.bridgeFactoryId,
1348
+ pluginVersion: owner.pluginVersion || "unknown",
1349
+ registrationMode: owner.registrationMode || "unknown",
1350
+ pluginRoot: owner.pluginRoot || pluginRoot,
1351
+ pluginFile: owner.pluginFile || pluginFile
1352
+ },
1353
+ now
1354
+ };
1355
+ const decision = planLifecycleAdoption(runtime2.lifecycle, input);
1356
+ const declaration = runtime2.registryDeclarations.get(owner.registryFingerprint);
1357
+ if (!declaration) return decision;
1358
+ const bridgeGenerationKey = getBridgeGenerationKey(input.bridgeGeneration);
1359
+ if (!declaration.bridgeGenerationKey) {
1360
+ declaration.bridgeGenerationKey = bridgeGenerationKey;
1361
+ } else if (declaration.bridgeGenerationKey !== bridgeGenerationKey) {
1362
+ return {
1363
+ kind: "reject",
1364
+ owner: decision.owner,
1365
+ reason: "registration-generation-conflict"
1366
+ };
1367
+ }
1368
+ if (declaration.registration === "failed") {
1369
+ return {
1370
+ kind: "reject",
1371
+ owner: decision.owner,
1372
+ reason: "registration-failed"
1373
+ };
1374
+ }
1375
+ if (declaration.registration === "pending") {
1376
+ return {
1377
+ kind: "reject",
1378
+ owner: decision.owner,
1379
+ reason: "registration-in-progress"
1380
+ };
1381
+ }
1382
+ return decision;
1383
+ };
1384
+ const commitLifecycle2 = (decision) => {
1385
+ const runtime2 = getGatewayRuntime2();
1386
+ if (decision.kind === "initialize" || decision.kind === "takeover") {
1387
+ runtime2.gatewayMethodDispatchers = gatewayMethodDispatchers;
1388
+ }
1389
+ const owner = commitLifecycleAdoption(runtime2.lifecycle, decision);
1390
+ const requestedRegistry = decision.owner.key.registryFingerprint;
1391
+ const requestedGeneration = decision.owner.key.generation;
1392
+ const requestedBridgeGenerationKey = getBridgeGenerationKey(decision.owner.bridgeGeneration);
1393
+ if (decision.kind === "initialize" || decision.kind === "takeover") {
1394
+ for (const [registryFingerprint, declaration2] of runtime2.registryDeclarations) {
1395
+ if (registryFingerprint !== owner?.key.registryFingerprint) {
1396
+ declaration2.state = "retired";
1397
+ retireRegistryRuntimeObservation(registryFingerprint);
1398
+ }
1399
+ }
1400
+ const declaration = getRegistryDeclaration2(
1401
+ requestedRegistry,
1402
+ requestedBridgeGenerationKey,
1403
+ requestedGeneration
1404
+ );
1405
+ if (declaration.bridgeGenerationKey && declaration.bridgeGenerationKey !== requestedBridgeGenerationKey) {
1406
+ declaration.service = "missing";
1407
+ declaration.channel = "missing";
1408
+ }
1409
+ declaration.bridgeGenerationKey = requestedBridgeGenerationKey;
1410
+ declaration.lifecycleGeneration = requestedGeneration;
1411
+ declaration.registration = "pending";
1412
+ declaration.state = "active";
1413
+ declaration.runtimeObservation = void 0;
1414
+ } else if (decision.kind === "defer") {
1415
+ const declaration = getRegistryDeclaration2(
1416
+ requestedRegistry,
1417
+ requestedBridgeGenerationKey,
1418
+ requestedGeneration
1419
+ );
1420
+ if (declaration.bridgeGenerationKey && declaration.bridgeGenerationKey !== requestedBridgeGenerationKey) {
1421
+ declaration.service = "missing";
1422
+ declaration.channel = "missing";
1423
+ }
1424
+ declaration.bridgeGenerationKey = requestedBridgeGenerationKey;
1425
+ declaration.lifecycleGeneration = requestedGeneration;
1426
+ declaration.registration = "pending";
1427
+ declaration.state = "shadow";
1428
+ declaration.runtimeObservation = void 0;
1429
+ } else if (decision.kind === "duplicate") {
1430
+ const existing = runtime2.registryDeclarations.get(requestedRegistry);
1431
+ if (!existing) {
1432
+ const declaration = getRegistryDeclaration2(
1433
+ requestedRegistry,
1434
+ requestedBridgeGenerationKey,
1435
+ requestedGeneration
1436
+ );
1437
+ declaration.registration = "pending";
1438
+ declaration.state = "shadow";
1439
+ } else if (!existing.bridgeGenerationKey || existing.bridgeGenerationKey === requestedBridgeGenerationKey) {
1440
+ existing.bridgeGenerationKey ||= requestedBridgeGenerationKey;
1441
+ const activeRegistry = runtime2.lifecycle.active?.key.registryFingerprint;
1442
+ const pendingRegistry = runtime2.lifecycle.pending?.key.registryFingerprint;
1443
+ if (requestedRegistry === activeRegistry && runtime2.lifecycle.active?.phase !== "stopped") {
1444
+ existing.state = "active";
1445
+ } else if (requestedRegistry === pendingRegistry) {
1446
+ existing.state = "shadow";
1447
+ }
1448
+ }
1449
+ }
1450
+ pruneRegistryLedgers(runtime2);
1451
+ return owner;
1452
+ };
1453
+ const adoptLifecycleBridge2 = (api, owner, mode) => {
1454
+ try {
1455
+ const adopted = bridgeSingletonManager.adoptBridgeSingleton(api, toBridgeOwner(owner), mode);
1456
+ getGatewayRuntime2().currentBridge = adopted.bridge;
1457
+ return adopted;
1458
+ } catch (error) {
1459
+ const lifecycle = getGatewayRuntime2().lifecycle;
1460
+ if (lifecycle.active?.key.generation === owner.key.generation && lifecycle.active.key.registryFingerprint === owner.key.registryFingerprint) {
1461
+ failLifecycle(lifecycle, owner.key.registryFingerprint, error, owner.key.generation);
1462
+ const declaration = getRegistryDeclaration2(
1463
+ owner.key.registryFingerprint,
1464
+ getBridgeGenerationKey(owner.bridgeGeneration),
1465
+ owner.key.generation
1466
+ );
1467
+ declaration.registration = "failed";
1468
+ declaration.state = "retired";
1469
+ retireRegistryRuntimeObservation(owner.key.registryFingerprint);
1470
+ }
1471
+ throw error;
1472
+ }
1473
+ };
1474
+ const beginRetirement2 = (registryFingerprint, expectedGeneration, now = Date.now()) => {
1475
+ const retirement = beginLifecycleRetirement(
1476
+ getGatewayRuntime2().lifecycle,
1477
+ registryFingerprint,
1478
+ now,
1479
+ expectedGeneration
1480
+ );
1481
+ if (retirement) {
1482
+ retireRegistryRuntimeObservation(registryFingerprint, now);
1483
+ }
1484
+ return retirement;
1485
+ };
1486
+ const settleRetirement2 = (registryFingerprint, outcome, expectedGeneration, now = Date.now()) => {
1487
+ const settled = settleLifecycleRetirement(
1488
+ getGatewayRuntime2().lifecycle,
1489
+ registryFingerprint,
1490
+ outcome,
1491
+ now,
1492
+ expectedGeneration
1493
+ );
1494
+ if (settled) {
1495
+ const lifecycle = getGatewayRuntime2().lifecycle;
1496
+ const declaration = getRegistryDeclaration2(registryFingerprint);
1497
+ if (!outcome.ok) {
1498
+ declaration.state = "retired";
1499
+ retireRegistryRuntimeObservation(registryFingerprint, now);
1500
+ const pendingRegistry = lifecycle.pending?.key.registryFingerprint;
1501
+ if (pendingRegistry && pendingRegistry !== registryFingerprint) {
1502
+ getRegistryDeclaration2(pendingRegistry).state = "retired";
1503
+ retireRegistryRuntimeObservation(pendingRegistry, now);
1504
+ }
1505
+ } else {
1506
+ declaration.state = lifecycle.pending?.key.registryFingerprint === registryFingerprint ? "shadow" : "retired";
1507
+ retireRegistryRuntimeObservation(registryFingerprint, now);
1508
+ }
1509
+ }
1510
+ return settled;
1511
+ };
1512
+ const getLifecycleBridgeForStart2 = async (api, registryFingerprint, expectedGeneration) => {
1513
+ const runtime2 = getGatewayRuntime2();
1514
+ const state = runtime2.lifecycle;
1515
+ if (runtime2.registryDeclarations.get(registryFingerprint)?.registration === "failed") {
1516
+ state.staleCallbackSuppressions += 1;
1517
+ return null;
1518
+ }
1519
+ if (isLifecycleRegistryActive(state, registryFingerprint, expectedGeneration)) {
1520
+ getRegistryDeclaration2(registryFingerprint).state = "active";
1521
+ startRegistryRuntimeObservation(
1522
+ registryFingerprint,
1523
+ getBridgeGenerationKey(state.active.bridgeGeneration),
1524
+ expectedGeneration
1525
+ );
1526
+ return runtime2.currentBridge || null;
1527
+ }
1528
+ const pending = getPendingLifecycle(state);
1529
+ if (pending?.key.registryFingerprint === registryFingerprint && pending.key.generation === expectedGeneration) {
1530
+ const pendingMode = state.pendingMode || "reuse";
1531
+ const retirement = state.retirement;
1532
+ const outcome = retirement ? await retirement : { ok: true };
1533
+ if (!outcome.ok) {
1534
+ throw new Error(`bncr lifecycle predecessor stop failed: ${outcome.error}`);
1535
+ }
1536
+ const activated = activatePendingLifecycle(
1537
+ state,
1538
+ registryFingerprint,
1539
+ Date.now(),
1540
+ expectedGeneration
1541
+ );
1542
+ if (activated) {
1543
+ getRegistryDeclaration2(registryFingerprint).state = "active";
1544
+ const bridge = adoptLifecycleBridge2(api, activated, pendingMode).bridge;
1545
+ runtime2.gatewayMethodDispatchers = gatewayMethodDispatchers;
1546
+ startRegistryRuntimeObservation(
1547
+ registryFingerprint,
1548
+ getBridgeGenerationKey(activated.bridgeGeneration),
1549
+ expectedGeneration,
1550
+ Date.now(),
1551
+ true
1552
+ );
1553
+ return bridge;
1554
+ }
1555
+ }
1556
+ const reactivated = reactivateStoppedLifecycle(
1557
+ state,
1558
+ registryFingerprint,
1559
+ Date.now(),
1560
+ expectedGeneration
1561
+ );
1562
+ if (reactivated) {
1563
+ getRegistryDeclaration2(registryFingerprint).state = "active";
1564
+ const bridge = adoptLifecycleBridge2(api, reactivated, "reuse").bridge;
1565
+ runtime2.gatewayMethodDispatchers = gatewayMethodDispatchers;
1566
+ startRegistryRuntimeObservation(
1567
+ registryFingerprint,
1568
+ getBridgeGenerationKey(reactivated.bridgeGeneration),
1569
+ expectedGeneration,
1570
+ Date.now(),
1571
+ true
1572
+ );
1573
+ return bridge;
1574
+ }
1575
+ if (isLifecycleRegistryActive(state, registryFingerprint, expectedGeneration)) {
1576
+ getRegistryDeclaration2(registryFingerprint).state = "active";
1577
+ startRegistryRuntimeObservation(
1578
+ registryFingerprint,
1579
+ getBridgeGenerationKey(state.active.bridgeGeneration),
1580
+ expectedGeneration
1581
+ );
1582
+ return runtime2.currentBridge || null;
1583
+ }
1584
+ state.staleCallbackSuppressions += 1;
1585
+ return null;
1586
+ };
1587
+ const failLifecycleStart2 = (registryFingerprint, error, expectedGeneration) => {
1588
+ const failed = failLifecycle(
1589
+ getGatewayRuntime2().lifecycle,
1590
+ registryFingerprint,
1591
+ error,
1592
+ expectedGeneration
1593
+ );
1594
+ if (failed) {
1595
+ getRegistryDeclaration2(registryFingerprint).state = "retired";
1596
+ retireRegistryRuntimeObservation(registryFingerprint);
1597
+ }
1598
+ return failed;
1599
+ };
1600
+ const recoverLifecycleRegistration2 = (decision, error) => {
1601
+ const runtime2 = getGatewayRuntime2();
1602
+ const state = runtime2.lifecycle;
1603
+ const owner = decision.owner;
1604
+ const ownsActive = Boolean(
1605
+ state.active && state.active.key.registryFingerprint === owner.key.registryFingerprint && state.active.key.generation === owner.key.generation
1606
+ );
1607
+ const ownsPending = Boolean(
1608
+ state.pending && state.pending.key.registryFingerprint === owner.key.registryFingerprint && state.pending.key.generation === owner.key.generation
1609
+ );
1610
+ let outcome = "none";
1611
+ if (ownsActive && (decision.kind === "initialize" || decision.kind === "takeover")) {
1612
+ if (state.active) {
1613
+ state.active.phase = "stopped";
1614
+ state.active.stopRequestedAt = null;
1615
+ state.active.stoppedAt = Date.now();
1616
+ state.active.failure = null;
1617
+ }
1618
+ state.pending = void 0;
1619
+ state.pendingMode = void 0;
1620
+ state.retirement = void 0;
1621
+ state.resolveRetirement = void 0;
1622
+ outcome = "stopped";
1623
+ } else if (ownsPending && decision.kind === "defer") {
1624
+ state.pending = void 0;
1625
+ state.pendingMode = void 0;
1626
+ outcome = "pending-dropped";
1627
+ } else if (ownsActive) {
1628
+ failLifecycle(state, owner.key.registryFingerprint, error, owner.key.generation);
1629
+ outcome = "failed";
1630
+ }
1631
+ const declaration = runtime2.registryDeclarations.get(owner.key.registryFingerprint);
1632
+ const ownsDeclaration = Boolean(
1633
+ declaration && declaration.bridgeGenerationKey === getBridgeGenerationKey(owner.bridgeGeneration) && declaration.lifecycleGeneration === owner.key.generation
1634
+ );
1635
+ if (ownsDeclaration && declaration) {
1636
+ declaration.registration = "failed";
1637
+ declaration.state = "retired";
1638
+ retireRegistryRuntimeObservation(owner.key.registryFingerprint);
1639
+ }
1640
+ return outcome;
1641
+ };
1642
+ const canStopLifecycleRegistry2 = (registryFingerprint, expectedGeneration) => {
1643
+ const state = getGatewayRuntime2().lifecycle;
1644
+ const allowed = isLifecycleRegistryStoppable(state, registryFingerprint, expectedGeneration);
1645
+ if (!allowed) {
1646
+ state.staleCallbackSuppressions += 1;
1647
+ }
1648
+ return allowed;
1649
+ };
1650
+ const canStartLifecycleRegistry2 = (registryFingerprint, expectedGeneration) => {
1651
+ const state = getGatewayRuntime2().lifecycle;
1652
+ const allowed = getGatewayRuntime2().registryDeclarations.get(registryFingerprint)?.registration !== "failed" && isLifecycleRegistryActive(state, registryFingerprint, expectedGeneration);
1653
+ if (!allowed) {
1654
+ state.staleCallbackSuppressions += 1;
1655
+ }
1656
+ return allowed;
1657
+ };
1658
+ const isLifecycleRegistryCurrent2 = (registryFingerprint, expectedGeneration) => isLifecycleRegistryActive(
1659
+ getGatewayRuntime2().lifecycle,
1660
+ registryFingerprint,
1661
+ expectedGeneration
1662
+ );
1663
+ const getRegistryDeclaration2 = (registryFingerprint, bridgeGenerationKey = "", lifecycleGeneration = 0) => {
1664
+ const runtime2 = getGatewayRuntime2();
1665
+ let declaration = runtime2.registryDeclarations.get(registryFingerprint);
1666
+ if (!declaration) {
1667
+ declaration = {
1668
+ bridgeGenerationKey,
1669
+ lifecycleGeneration,
1670
+ service: "missing",
1671
+ channel: "missing",
1672
+ registration: "pending",
1673
+ state: "shadow"
1674
+ };
1675
+ runtime2.registryDeclarations.set(registryFingerprint, declaration);
1676
+ } else {
1677
+ declaration = normalizeRegistryDeclarationInPlace(declaration);
1678
+ if (!declaration.bridgeGenerationKey) {
1679
+ declaration.bridgeGenerationKey = bridgeGenerationKey;
1680
+ }
1681
+ if (!declaration.lifecycleGeneration) {
1682
+ declaration.lifecycleGeneration = lifecycleGeneration;
1683
+ }
1684
+ }
1685
+ return declaration;
1686
+ };
1687
+ const markRegistryServiceDeclared2 = (registryFingerprint, bridgeGenerationKey, lifecycleGeneration) => {
1688
+ const declaration = getRegistryDeclaration2(
1689
+ registryFingerprint,
1690
+ bridgeGenerationKey,
1691
+ lifecycleGeneration
1692
+ );
1693
+ if (declaration.bridgeGenerationKey !== bridgeGenerationKey) {
1694
+ throw new Error(`bncr service declaration scope mismatch: ${registryFingerprint}`);
1695
+ }
1696
+ declaration.lifecycleGeneration = lifecycleGeneration;
1697
+ declaration.service = "declared";
1698
+ return declaration;
1699
+ };
1700
+ const markRegistryChannelDeclared2 = (registryFingerprint, bridgeGenerationKey, lifecycleGeneration) => {
1701
+ const declaration = getRegistryDeclaration2(
1702
+ registryFingerprint,
1703
+ bridgeGenerationKey,
1704
+ lifecycleGeneration
1705
+ );
1706
+ if (declaration.bridgeGenerationKey !== bridgeGenerationKey) {
1707
+ throw new Error(`bncr channel declaration scope mismatch: ${registryFingerprint}`);
1708
+ }
1709
+ declaration.lifecycleGeneration = lifecycleGeneration;
1710
+ declaration.channel = "declared";
1711
+ return declaration;
1712
+ };
1713
+ const markRegistryRegistrationComplete2 = (registryFingerprint, bridgeGenerationKey, lifecycleGeneration) => {
1714
+ const declaration = getRegistryDeclaration2(
1715
+ registryFingerprint,
1716
+ bridgeGenerationKey,
1717
+ lifecycleGeneration
1718
+ );
1719
+ if (declaration.bridgeGenerationKey !== bridgeGenerationKey) {
1720
+ throw new Error(`bncr register scope mismatch: ${registryFingerprint}`);
1721
+ }
1722
+ declaration.lifecycleGeneration ||= lifecycleGeneration;
1723
+ declaration.registration = "complete";
1724
+ if (declaration.state === "active") {
1725
+ startRegistryRuntimeObservation(
1726
+ registryFingerprint,
1727
+ bridgeGenerationKey,
1728
+ declaration.lifecycleGeneration
1729
+ );
1730
+ }
1731
+ return declaration;
1732
+ };
703
1733
  return {
704
1734
  getRegisterMeta: getRegisterMeta2,
705
1735
  getGlobalRegisterTrace: getGlobalRegisterTrace2,
1736
+ noteRegisterTraceValue: rememberRegisterTraceValue,
1737
+ getBridgeGenerationKey,
706
1738
  getGatewayRuntime: getGatewayRuntime2,
707
- shouldAdoptProcessOwner: (apiInstanceId, gatewayRuntime) => shouldAdoptProcessOwner({
708
- apiInstanceId,
709
- serviceRegistered: gatewayRuntime.serviceRegistered,
710
- channelRegistered: gatewayRuntime.channelRegistered,
711
- serviceOwnerApiInstanceId: gatewayRuntime.serviceOwnerApiInstanceId,
712
- channelOwnerApiInstanceId: gatewayRuntime.channelOwnerApiInstanceId
713
- }),
1739
+ planLifecycle: planLifecycle2,
1740
+ commitLifecycle: commitLifecycle2,
1741
+ adoptLifecycleBridge: adoptLifecycleBridge2,
1742
+ beginRetirement: beginRetirement2,
1743
+ settleRetirement: settleRetirement2,
1744
+ getLifecycleBridgeForStart: getLifecycleBridgeForStart2,
1745
+ failLifecycleStart: failLifecycleStart2,
1746
+ recoverLifecycleRegistration: recoverLifecycleRegistration2,
1747
+ canStopLifecycleRegistry: canStopLifecycleRegistry2,
1748
+ canStartLifecycleRegistry: canStartLifecycleRegistry2,
1749
+ isLifecycleRegistryCurrent: isLifecycleRegistryCurrent2,
1750
+ getRegistryDeclaration: getRegistryDeclaration2,
1751
+ getRegistryRuntimeObservation,
1752
+ startRegistryRuntimeObservation,
1753
+ probeRegistryRuntimeObservation: probeRegistryRuntimeObservation2,
1754
+ retireRegistryRuntimeObservation,
1755
+ markRegistryServiceDeclared: markRegistryServiceDeclared2,
1756
+ markRegistryChannelDeclared: markRegistryChannelDeclared2,
1757
+ markRegistryRegistrationComplete: markRegistryRegistrationComplete2,
1758
+ pruneRegistryLedgers: () => pruneRegistryLedgers(getGatewayRuntime2()),
714
1759
  ensureGatewayMethodRegistered: gatewayMethodRegistry.ensureGatewayMethodRegistered,
715
1760
  getBridgeSingleton: bridgeSingletonManager.getBridgeSingleton,
716
1761
  getBridgeOwnerFromBridge: bridgeSingletonManager.getBridgeOwnerFromBridge,
@@ -787,15 +1832,32 @@ var BncrConfigSchema = {
787
1832
  var pluginVersion2 = pluginVersion;
788
1833
  var registerRuntime = createBncrRegisterRuntime();
789
1834
  var {
1835
+ adoptLifecycleBridge,
1836
+ beginRetirement,
1837
+ canStartLifecycleRegistry,
1838
+ canStopLifecycleRegistry,
1839
+ commitLifecycle,
790
1840
  ensureGatewayMethodRegistered,
791
- getBridgeSingleton,
792
1841
  getBridgeOwnerFromBridge,
1842
+ getBridgeGenerationKey: getBridgeGenerationKey2,
793
1843
  getCurrentBridge,
794
1844
  getExistingBridgeSingleton,
795
1845
  getGatewayRuntime,
796
1846
  getGlobalRegisterTrace,
1847
+ getLifecycleBridgeForStart,
797
1848
  getRegisterMeta,
798
- shouldAdoptProcessOwner: shouldAdoptProcessOwner2
1849
+ getRegistryDeclaration,
1850
+ isLifecycleRegistryCurrent,
1851
+ markRegistryChannelDeclared,
1852
+ markRegistryRegistrationComplete,
1853
+ markRegistryServiceDeclared,
1854
+ noteRegisterTraceValue,
1855
+ planLifecycle,
1856
+ probeRegistryRuntimeObservation,
1857
+ pruneRegistryLedgers: pruneRegistryLedgers2,
1858
+ recoverLifecycleRegistration,
1859
+ settleRetirement,
1860
+ failLifecycleStart
799
1861
  } = registerRuntime;
800
1862
  var plugin = {
801
1863
  id: "bncr",
@@ -814,136 +1876,254 @@ var plugin = {
814
1876
  const registryFingerprint = meta.registryFingerprint || "unknown";
815
1877
  const sameApiAsPrevious = previousApiInstanceId === apiInstanceId;
816
1878
  const sameRegistryAsPrevious = previousRegistryFingerprint === registryFingerprint;
817
- const firstSeenApi = !globalTrace.seenApiInstanceIds.has(apiInstanceId);
818
- const firstSeenRegistry = !globalTrace.seenRegistryFingerprints.has(registryFingerprint);
819
- const gatewayRuntime = getGatewayRuntime();
820
- const ownerDecision = shouldAdoptProcessOwner2(apiInstanceId, gatewayRuntime);
821
- let bridge;
822
- let runtime2;
823
- let created = false;
824
- let rebuilt = false;
825
- let owner;
826
- let previousOwner;
827
- if (ownerDecision.adoptOwner) {
828
- const adopted = getBridgeSingleton(api);
829
- bridge = adopted.bridge;
830
- runtime2 = adopted.runtime;
831
- created = adopted.created;
832
- rebuilt = adopted.rebuilt;
833
- owner = adopted.owner;
834
- previousOwner = adopted.previousOwner;
835
- gatewayRuntime.currentBridge = bridge;
836
- if (rebuilt) {
837
- gatewayRuntime.serviceRegistered = false;
838
- gatewayRuntime.channelRegistered = false;
839
- gatewayRuntime.serviceOwnerApiInstanceId = void 0;
840
- gatewayRuntime.channelOwnerApiInstanceId = void 0;
1879
+ const apiSeenRecently = globalTrace.seenApiInstanceIds.has(apiInstanceId);
1880
+ const registrySeenRecently = globalTrace.seenRegistryFingerprints.has(registryFingerprint);
1881
+ let ownerDecision;
1882
+ try {
1883
+ const gatewayRuntime = getGatewayRuntime();
1884
+ ownerDecision = planLifecycle(api);
1885
+ const lifecycleOwner = commitLifecycle(ownerDecision);
1886
+ let bridge;
1887
+ const runtime2 = loadBncrRuntimeSync();
1888
+ let created = false;
1889
+ let rebuilt = false;
1890
+ let owner;
1891
+ let previousOwner;
1892
+ const shouldDeclareLifecycle = ownerDecision.kind === "initialize" || ownerDecision.kind === "takeover" || ownerDecision.kind === "defer";
1893
+ if (ownerDecision.kind === "initialize") {
1894
+ if (lifecycleOwner) {
1895
+ const adopted = adoptLifecycleBridge(api, lifecycleOwner, "create");
1896
+ bridge = adopted.bridge;
1897
+ created = adopted.created;
1898
+ rebuilt = adopted.rebuilt;
1899
+ owner = adopted.owner;
1900
+ previousOwner = adopted.previousOwner;
1901
+ }
1902
+ } else if (ownerDecision.kind === "takeover") {
1903
+ if (lifecycleOwner) {
1904
+ const adopted = adoptLifecycleBridge(api, lifecycleOwner, ownerDecision.mode);
1905
+ bridge = adopted.bridge;
1906
+ created = adopted.created;
1907
+ rebuilt = adopted.rebuilt;
1908
+ owner = adopted.owner;
1909
+ previousOwner = adopted.previousOwner;
1910
+ }
1911
+ } else if (ownerDecision.kind === "defer") {
1912
+ bridge = gatewayRuntime.currentBridge || getExistingBridgeSingleton();
1913
+ previousOwner = getBridgeOwnerFromBridge(bridge);
1914
+ owner = previousOwner;
1915
+ } else {
1916
+ bridge = gatewayRuntime.currentBridge || getExistingBridgeSingleton();
1917
+ previousOwner = getBridgeOwnerFromBridge(bridge);
1918
+ owner = previousOwner;
1919
+ if (bridge && !gatewayRuntime.currentBridge) {
1920
+ gatewayRuntime.currentBridge = bridge;
1921
+ }
841
1922
  }
842
- } else {
843
- runtime2 = loadBncrRuntimeSync();
844
- bridge = gatewayRuntime.currentBridge || getExistingBridgeSingleton();
845
- previousOwner = getBridgeOwnerFromBridge(bridge);
846
- owner = previousOwner;
847
1923
  if (bridge && !gatewayRuntime.currentBridge) {
848
1924
  gatewayRuntime.currentBridge = bridge;
849
1925
  }
850
- }
851
- globalTrace.seenApiInstanceIds.add(apiInstanceId);
852
- globalTrace.seenRegistryFingerprints.add(registryFingerprint);
853
- globalTrace.lastApiInstanceId = apiInstanceId;
854
- globalTrace.lastRegistryFingerprint = registryFingerprint;
855
- bridge?.noteRegister?.({
856
- source: "@xmoxmo/bncr",
857
- pluginVersion: pluginVersion2,
858
- apiRebound: ownerDecision.adoptOwner ? !created && !rebuilt : false,
859
- apiInstanceId: meta.apiInstanceId,
860
- registryFingerprint: meta.registryFingerprint
861
- });
862
- const debugLog = (...args) => {
863
- const rendered = args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ").trim();
864
- if (!rendered) return;
865
- emitBncrLogLine(
866
- "info",
867
- `[bncr] debug ${rendered}`,
868
- { debugOnly: true },
869
- () => Boolean(bridge?.isDebugEnabled?.())
1926
+ noteRegisterTraceValue(globalTrace.seenApiInstanceIds, apiInstanceId);
1927
+ noteRegisterTraceValue(globalTrace.seenRegistryFingerprints, registryFingerprint);
1928
+ globalTrace.lastApiInstanceId = apiInstanceId;
1929
+ globalTrace.lastRegistryFingerprint = registryFingerprint;
1930
+ bridge?.noteRegister?.({
1931
+ source: "@xmoxmo/bncr",
1932
+ pluginVersion: pluginVersion2,
1933
+ apiRebound: ownerDecision.kind === "takeover" && !created && !rebuilt,
1934
+ apiInstanceId: meta.apiInstanceId,
1935
+ registryFingerprint: meta.registryFingerprint
1936
+ });
1937
+ const debugLog = (...args) => {
1938
+ const rendered = args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ").trim();
1939
+ if (!rendered) return;
1940
+ emitBncrLogLine(
1941
+ "info",
1942
+ `[bncr] debug ${rendered}`,
1943
+ { debugOnly: true },
1944
+ () => Boolean(bridge?.isDebugEnabled?.())
1945
+ );
1946
+ };
1947
+ debugLog(
1948
+ `register begin bridge=${bridge?.getBridgeId?.() || "unknown"} created=${created} rebuilt=${rebuilt} ownerApi=${owner?.apiInstanceId || "none"} ownerRegistry=${owner?.registryFingerprint || "none"} previousOwnerApi=${previousOwner?.apiInstanceId || "none"} previousOwnerRegistry=${previousOwner?.registryFingerprint || "none"}`
870
1949
  );
871
- };
872
- debugLog(
873
- `register begin bridge=${bridge?.getBridgeId?.() || "unknown"} created=${created} rebuilt=${rebuilt} ownerApi=${owner?.apiInstanceId || "none"} ownerRegistry=${owner?.registryFingerprint || "none"} previousOwnerApi=${previousOwner?.apiInstanceId || "none"} previousOwnerRegistry=${previousOwner?.registryFingerprint || "none"}`
874
- );
875
- debugLog(
876
- `register classify mode=${meta.registrationMode || "unknown"} api=${apiInstanceId} registry=${registryFingerprint} sameApiAsPrevious=${sameApiAsPrevious} sameRegistryAsPrevious=${sameRegistryAsPrevious} firstSeenApi=${firstSeenApi} firstSeenRegistry=${firstSeenRegistry}`
877
- );
878
- debugLog(
879
- `register owner adopt=${ownerDecision.adoptOwner} reason=${ownerDecision.reason} existingOwnerApi=${ownerDecision.existingOwnerApiInstanceId || "none"}`
880
- );
881
- if (!ownerDecision.adoptOwner) {
882
1950
  debugLog(
883
- `bridge rebuild suppressed due to existing singleton owner api ${ownerDecision.existingOwnerApiInstanceId || "unknown"}`
1951
+ `register classify mode=${meta.registrationMode || "unknown"} api=${apiInstanceId} registry=${registryFingerprint} sameApiAsPrevious=${sameApiAsPrevious} sameRegistryAsPrevious=${sameRegistryAsPrevious} apiSeenRecently=${apiSeenRecently} registrySeenRecently=${registrySeenRecently}`
884
1952
  );
885
- } else {
886
- if (!created && !rebuilt) debugLog("bridge api rebound");
887
- if (rebuilt) debugLog("bridge rebuilt due to owner/runtime change");
888
- }
889
- const resolveDebug = async () => {
890
- try {
891
- const cfg = getOpenClawRuntimeConfig(api);
892
- return Boolean(cfg?.channels?.bncr?.debug?.verbose);
893
- } catch {
894
- return false;
1953
+ debugLog(
1954
+ `register lifecycle decision=${ownerDecision.kind} reason=${ownerDecision.reason} generation=${ownerDecision.owner.key.generation} existingOwnerApi=${previousOwner?.apiInstanceId || "none"}`
1955
+ );
1956
+ if (!shouldDeclareLifecycle) {
1957
+ debugLog(
1958
+ `service/channel declaration suppressed for ${ownerDecision.kind} registry=${registryFingerprint}`
1959
+ );
895
1960
  }
896
- };
897
- if (!gatewayRuntime.serviceRegistered) {
898
- const serviceStopHandler = async () => {
899
- await getCurrentBridge().stopService?.();
1961
+ const resolveDebug = async () => {
1962
+ try {
1963
+ const cfg = getOpenClawRuntimeConfig(api);
1964
+ return Boolean(cfg?.channels?.bncr?.debug?.verbose);
1965
+ } catch {
1966
+ return false;
1967
+ }
900
1968
  };
901
- api.registerService({
902
- id: "bncr-bridge-service",
903
- start: async (ctx) => {
904
- const debug = await resolveDebug();
905
- await getCurrentBridge().startService(ctx, debug);
906
- },
907
- stop: serviceStopHandler
908
- });
909
- gatewayRuntime.serviceRegistered = true;
910
- gatewayRuntime.serviceOwnerApiInstanceId = apiInstanceId;
911
- meta.service = true;
912
- debugLog(`register service ok ownerApi=${apiInstanceId}`);
913
- } else {
914
- meta.service = true;
915
- debugLog(
916
- `register service skip (process singleton already registered by api ${gatewayRuntime.serviceOwnerApiInstanceId || "unknown"})`
1969
+ const registryGeneration = ownerDecision.owner.key.generation;
1970
+ const registryBridgeGenerationKey = getBridgeGenerationKey2(
1971
+ ownerDecision.owner.bridgeGeneration
917
1972
  );
918
- }
919
- if (!gatewayRuntime.channelRegistered) {
920
- api.registerChannel({
921
- plugin: createDynamicChannelPlugin({ loaded: runtime2, getCurrentBridge })
922
- });
923
- gatewayRuntime.channelRegistered = true;
924
- gatewayRuntime.channelOwnerApiInstanceId = apiInstanceId;
925
- meta.channel = true;
926
- debugLog(`register channel ok ownerApi=${apiInstanceId}`);
927
- } else {
928
- meta.channel = true;
929
- debugLog(
930
- `register channel skip (process singleton already registered by api ${gatewayRuntime.channelOwnerApiInstanceId || "unknown"})`
1973
+ const registryDeclaration = ownerDecision.kind === "reject" ? gatewayRuntime.registryDeclarations.get(registryFingerprint) : getRegistryDeclaration(
1974
+ registryFingerprint,
1975
+ registryBridgeGenerationKey,
1976
+ registryGeneration
931
1977
  );
1978
+ const serviceDeclaredForGeneration = registryDeclaration?.service === "declared";
1979
+ const channelDeclaredForGeneration = registryDeclaration?.channel === "declared";
1980
+ if (shouldDeclareLifecycle && !serviceDeclaredForGeneration) {
1981
+ const serviceStopHandler = async () => {
1982
+ const retirement = beginRetirement(registryFingerprint, registryGeneration);
1983
+ if (!retirement) {
1984
+ debugLog(`service stop suppressed for stale registry=${registryFingerprint}`);
1985
+ return;
1986
+ }
1987
+ try {
1988
+ await getCurrentBridge().stopService?.();
1989
+ settleRetirement(registryFingerprint, { ok: true }, registryGeneration);
1990
+ } catch (error) {
1991
+ const detail = error instanceof Error ? error.message : String(error);
1992
+ settleRetirement(registryFingerprint, { ok: false, error: detail }, registryGeneration);
1993
+ throw error;
1994
+ }
1995
+ };
1996
+ api.registerService({
1997
+ id: "bncr-bridge-service",
1998
+ start: async (ctx) => {
1999
+ const lifecycleBridge = await getLifecycleBridgeForStart(
2000
+ api,
2001
+ registryFingerprint,
2002
+ registryGeneration
2003
+ );
2004
+ if (!lifecycleBridge) {
2005
+ debugLog(`service start suppressed for stale registry=${registryFingerprint}`);
2006
+ return;
2007
+ }
2008
+ const debug = await resolveDebug();
2009
+ if (!canStartLifecycleRegistry(registryFingerprint, registryGeneration)) {
2010
+ debugLog(
2011
+ `service start suppressed after await for stale registry=${registryFingerprint}`
2012
+ );
2013
+ return;
2014
+ }
2015
+ try {
2016
+ const started = await lifecycleBridge.startService(
2017
+ ctx,
2018
+ debug,
2019
+ () => isLifecycleRegistryCurrent(registryFingerprint, registryGeneration)
2020
+ );
2021
+ if (started === false || !canStartLifecycleRegistry(registryFingerprint, registryGeneration)) {
2022
+ debugLog(
2023
+ `service start suppressed after await for stale registry=${registryFingerprint}`
2024
+ );
2025
+ return;
2026
+ }
2027
+ probeRegistryRuntimeObservation(
2028
+ registryFingerprint,
2029
+ registryBridgeGenerationKey,
2030
+ registryGeneration
2031
+ );
2032
+ } catch (error) {
2033
+ failLifecycleStart(registryFingerprint, error, registryGeneration);
2034
+ throw error;
2035
+ }
2036
+ },
2037
+ stop: serviceStopHandler
2038
+ });
2039
+ markRegistryServiceDeclared(
2040
+ registryFingerprint,
2041
+ registryBridgeGenerationKey,
2042
+ registryGeneration
2043
+ );
2044
+ meta.service = true;
2045
+ debugLog(`register service ok ownerApi=${apiInstanceId}`);
2046
+ } else {
2047
+ meta.service = registryDeclaration?.service === "declared";
2048
+ debugLog(
2049
+ `register service skip registry=${registryFingerprint} declared=${registryDeclaration?.service || "missing"}`
2050
+ );
2051
+ }
2052
+ if (shouldDeclareLifecycle && !channelDeclaredForGeneration) {
2053
+ api.registerChannel({
2054
+ plugin: createDynamicChannelPlugin({
2055
+ loaded: runtime2,
2056
+ getCurrentBridge,
2057
+ resolveBridgeForStart: async () => {
2058
+ const lifecycleBridge = await getLifecycleBridgeForStart(
2059
+ api,
2060
+ registryFingerprint,
2061
+ registryGeneration
2062
+ );
2063
+ if (!lifecycleBridge) {
2064
+ throw new Error(`bncr lifecycle registry is inactive: ${registryFingerprint}`);
2065
+ }
2066
+ return lifecycleBridge;
2067
+ },
2068
+ isBridgeForStartCurrent: () => canStartLifecycleRegistry(registryFingerprint, registryGeneration),
2069
+ onBridgeStartObserved: () => probeRegistryRuntimeObservation(
2070
+ registryFingerprint,
2071
+ registryBridgeGenerationKey,
2072
+ registryGeneration
2073
+ ),
2074
+ resolveBridgeForStop: () => {
2075
+ if (!canStopLifecycleRegistry(registryFingerprint, registryGeneration)) {
2076
+ debugLog(`channel stop suppressed for stale registry=${registryFingerprint}`);
2077
+ return null;
2078
+ }
2079
+ return getCurrentBridge();
2080
+ },
2081
+ isBridgeForStopCurrent: () => canStopLifecycleRegistry(registryFingerprint, registryGeneration)
2082
+ })
2083
+ });
2084
+ markRegistryChannelDeclared(
2085
+ registryFingerprint,
2086
+ registryBridgeGenerationKey,
2087
+ registryGeneration
2088
+ );
2089
+ meta.channel = true;
2090
+ debugLog(`register channel ok ownerApi=${apiInstanceId}`);
2091
+ } else {
2092
+ meta.channel = registryDeclaration?.channel === "declared";
2093
+ debugLog(
2094
+ `register channel skip registry=${registryFingerprint} declared=${registryDeclaration?.channel || "missing"}`
2095
+ );
2096
+ }
2097
+ ensureGatewayMethodRegistered(api, "bncr.connect", debugLog);
2098
+ ensureGatewayMethodRegistered(api, "bncr.inbound", debugLog);
2099
+ ensureGatewayMethodRegistered(api, "bncr.activity", debugLog);
2100
+ ensureGatewayMethodRegistered(api, "bncr.ack", debugLog);
2101
+ ensureGatewayMethodRegistered(api, "bncr.diagnostics", debugLog);
2102
+ ensureGatewayMethodRegistered(api, "bncr.deadLetter.inspect", debugLog);
2103
+ ensureGatewayMethodRegistered(api, "bncr.deadLetter.prune", debugLog);
2104
+ ensureGatewayMethodRegistered(api, "bncr.rpc.response", debugLog);
2105
+ ensureGatewayMethodRegistered(api, "bncr.file.init", debugLog);
2106
+ ensureGatewayMethodRegistered(api, "bncr.file.chunk", debugLog);
2107
+ ensureGatewayMethodRegistered(api, "bncr.file.complete", debugLog);
2108
+ ensureGatewayMethodRegistered(api, "bncr.file.abort", debugLog);
2109
+ ensureGatewayMethodRegistered(api, "bncr.file.ack", debugLog);
2110
+ if (ownerDecision.kind !== "reject") {
2111
+ markRegistryRegistrationComplete(
2112
+ registryFingerprint,
2113
+ registryBridgeGenerationKey,
2114
+ registryGeneration
2115
+ );
2116
+ }
2117
+ pruneRegistryLedgers2();
2118
+ debugLog("register done");
2119
+ } catch (error) {
2120
+ const recovered = ownerDecision ? recoverLifecycleRegistration(ownerDecision, error) : "plan-not-committed";
2121
+ emitBncrLogLine(
2122
+ "error",
2123
+ `[bncr] register failed recovered=${recovered} error=${error instanceof Error ? error.message : String(error)}`
2124
+ );
2125
+ throw error;
932
2126
  }
933
- ensureGatewayMethodRegistered(api, "bncr.connect", debugLog);
934
- ensureGatewayMethodRegistered(api, "bncr.inbound", debugLog);
935
- ensureGatewayMethodRegistered(api, "bncr.activity", debugLog);
936
- ensureGatewayMethodRegistered(api, "bncr.ack", debugLog);
937
- ensureGatewayMethodRegistered(api, "bncr.diagnostics", debugLog);
938
- ensureGatewayMethodRegistered(api, "bncr.deadLetter.inspect", debugLog);
939
- ensureGatewayMethodRegistered(api, "bncr.deadLetter.prune", debugLog);
940
- ensureGatewayMethodRegistered(api, "bncr.rpc.response", debugLog);
941
- ensureGatewayMethodRegistered(api, "bncr.file.init", debugLog);
942
- ensureGatewayMethodRegistered(api, "bncr.file.chunk", debugLog);
943
- ensureGatewayMethodRegistered(api, "bncr.file.complete", debugLog);
944
- ensureGatewayMethodRegistered(api, "bncr.file.abort", debugLog);
945
- ensureGatewayMethodRegistered(api, "bncr.file.ack", debugLog);
946
- debugLog("register done");
947
2127
  }
948
2128
  };
949
2129
  var index_default = plugin;