@xmoxmo/bncr 0.6.7 → 0.6.9
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 +1386 -206
- package/index.ts +291 -131
- package/package.json +1 -1
- package/scripts/check-pack.mjs +2 -0
- package/scripts/selfcheck.mjs +2 -0
- package/src/bootstrap/channel-plugin-runtime.ts +28 -7
- package/src/bootstrap/register-lifecycle.ts +492 -0
- package/src/bootstrap/register-runtime-gateway.ts +64 -3
- package/src/bootstrap/register-runtime-helpers.ts +8 -44
- package/src/bootstrap/register-runtime-observation.ts +170 -0
- package/src/bootstrap/register-runtime-singleton.ts +44 -40
- package/src/bootstrap/register-runtime.ts +918 -15
- package/src/channel.ts +32 -2
- package/src/messaging/outbound/session-route.ts +4 -6
- package/src/plugin/bridge-lifecycle.ts +13 -2
- package/src/plugin/channel-plugin-bridge-group.ts +2 -8
- package/src/plugin/channel-plugin-surface-group.ts +2 -3
- package/src/plugin/messaging.ts +9 -32
- package/src/plugin/scene-registry.ts +14 -3
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) =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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({
|
|
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-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
233
|
-
|
|
234
|
-
|
|
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
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
741
|
+
bridge.bindRuntimePaths?.({
|
|
355
742
|
pluginRoot: runtime2.pluginRoot,
|
|
356
743
|
pluginFile: runtime2.pluginFile
|
|
357
744
|
});
|
|
358
|
-
return { bridge
|
|
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
|
|
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
|
-
|
|
668
|
-
|
|
1121
|
+
registryDeclarations: /* @__PURE__ */ new Map(),
|
|
1122
|
+
runtimeObservationTimers: /* @__PURE__ */ new Map(),
|
|
1123
|
+
lifecycle: createRegisterLifecycleState()
|
|
669
1124
|
};
|
|
670
1125
|
}
|
|
671
|
-
|
|
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
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
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
|
-
|
|
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
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
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
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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
|
-
`
|
|
1951
|
+
`register classify mode=${meta.registrationMode || "unknown"} api=${apiInstanceId} registry=${registryFingerprint} sameApiAsPrevious=${sameApiAsPrevious} sameRegistryAsPrevious=${sameRegistryAsPrevious} apiSeenRecently=${apiSeenRecently} registrySeenRecently=${registrySeenRecently}`
|
|
884
1952
|
);
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
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
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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
|
-
|
|
902
|
-
|
|
903
|
-
|
|
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
|
-
|
|
920
|
-
|
|
921
|
-
|
|
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;
|