@xmoxmo/bncr 0.6.7 → 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.
@@ -0,0 +1,492 @@
1
+ export type LifecyclePhase = 'registering' | 'active' | 'stopping' | 'stopped' | 'failed';
2
+
3
+ export type LifecycleKey = {
4
+ generation: number;
5
+ apiInstanceId: string;
6
+ registryFingerprint: string;
7
+ };
8
+
9
+ export type BridgeGeneration = {
10
+ moduleEpoch: string;
11
+ bridgeFactoryId: string;
12
+ pluginVersion: string;
13
+ registrationMode: string;
14
+ pluginRoot: string;
15
+ pluginFile: string;
16
+ };
17
+
18
+ export type LifecycleOwner = {
19
+ key: LifecycleKey;
20
+ bridgeGeneration: BridgeGeneration;
21
+ phase: LifecyclePhase;
22
+ startedAt: number | null;
23
+ stopRequestedAt: number | null;
24
+ stoppedAt: number | null;
25
+ failure: string | null;
26
+ };
27
+
28
+ export type LifecycleRetirementOutcome =
29
+ | { ok: true }
30
+ | {
31
+ ok: false;
32
+ error: string;
33
+ };
34
+
35
+ export type LifecycleAdoptionDecision =
36
+ | {
37
+ kind: 'initialize';
38
+ owner: LifecycleOwner;
39
+ reason: 'no-active-lifecycle';
40
+ }
41
+ | {
42
+ kind: 'duplicate';
43
+ owner: LifecycleOwner;
44
+ reason:
45
+ | 'active-owner'
46
+ | 'pending-successor'
47
+ | 'lifecycle-stopping'
48
+ | 'same-lifecycle-stopped';
49
+ }
50
+ | {
51
+ kind: 'takeover';
52
+ owner: LifecycleOwner;
53
+ mode: 'reuse' | 'replace';
54
+ reason: 'bridge-generation-reused' | 'bridge-generation-changed';
55
+ }
56
+ | {
57
+ kind: 'defer';
58
+ owner: LifecycleOwner;
59
+ predecessorGeneration: number;
60
+ mode: 'reuse' | 'replace';
61
+ reason: 'predecessor-stopping';
62
+ }
63
+ | {
64
+ kind: 'reject';
65
+ owner: LifecycleOwner;
66
+ reason:
67
+ | 'predecessor-failed'
68
+ | 'pending-successor-conflict'
69
+ | 'registration-generation-conflict'
70
+ | 'registration-failed'
71
+ | 'registration-in-progress';
72
+ };
73
+
74
+ export type RegisterLifecycleState = {
75
+ nextGeneration: number;
76
+ active?: LifecycleOwner;
77
+ pending?: LifecycleOwner;
78
+ pendingMode?: 'reuse' | 'replace';
79
+ retirement?: Promise<LifecycleRetirementOutcome>;
80
+ resolveRetirement?: (outcome: LifecycleRetirementOutcome) => void;
81
+ staleCallbackSuppressions: number;
82
+ };
83
+
84
+ export type LifecyclePlanInput = {
85
+ apiInstanceId: string;
86
+ registryFingerprint: string;
87
+ bridgeGeneration: BridgeGeneration;
88
+ now: number;
89
+ };
90
+
91
+ function normalizeString(value: unknown, fallback = 'unknown') {
92
+ const text = typeof value === 'string' ? value.trim() : '';
93
+ return text || fallback;
94
+ }
95
+
96
+ export function createRegisterLifecycleState(): RegisterLifecycleState {
97
+ return {
98
+ nextGeneration: 1,
99
+ staleCallbackSuppressions: 0,
100
+ };
101
+ }
102
+
103
+ export function sameLifecycleRegistration(left?: LifecycleKey, right?: LifecycleKey) {
104
+ if (!left || !right) return false;
105
+ return (
106
+ left.apiInstanceId === right.apiInstanceId &&
107
+ left.registryFingerprint === right.registryFingerprint
108
+ );
109
+ }
110
+
111
+ export function sameBridgeGeneration(left?: BridgeGeneration, right?: BridgeGeneration) {
112
+ if (!left || !right) return false;
113
+ return (
114
+ left.moduleEpoch === right.moduleEpoch &&
115
+ left.bridgeFactoryId === right.bridgeFactoryId &&
116
+ left.pluginVersion === right.pluginVersion &&
117
+ left.registrationMode === right.registrationMode &&
118
+ left.pluginRoot === right.pluginRoot &&
119
+ left.pluginFile === right.pluginFile
120
+ );
121
+ }
122
+
123
+ export function getBridgeGenerationKey(generation: BridgeGeneration) {
124
+ return [
125
+ generation.moduleEpoch,
126
+ generation.bridgeFactoryId,
127
+ generation.pluginVersion,
128
+ generation.registrationMode,
129
+ generation.pluginRoot,
130
+ generation.pluginFile,
131
+ ].join('\u0000');
132
+ }
133
+
134
+ export function createLifecycleOwner(
135
+ input: LifecyclePlanInput,
136
+ generation: number,
137
+ phase: LifecyclePhase,
138
+ ): LifecycleOwner {
139
+ return {
140
+ key: {
141
+ generation,
142
+ apiInstanceId: normalizeString(input.apiInstanceId, 'unknown-api'),
143
+ registryFingerprint: normalizeString(input.registryFingerprint, 'unknown-registry'),
144
+ },
145
+ bridgeGeneration: {
146
+ moduleEpoch: normalizeString(input.bridgeGeneration.moduleEpoch),
147
+ bridgeFactoryId: normalizeString(input.bridgeGeneration.bridgeFactoryId),
148
+ pluginVersion: normalizeString(input.bridgeGeneration.pluginVersion),
149
+ registrationMode: normalizeString(input.bridgeGeneration.registrationMode),
150
+ pluginRoot: normalizeString(input.bridgeGeneration.pluginRoot),
151
+ pluginFile: normalizeString(input.bridgeGeneration.pluginFile),
152
+ },
153
+ phase,
154
+ startedAt: phase === 'active' ? input.now : null,
155
+ stopRequestedAt: null,
156
+ stoppedAt: null,
157
+ failure: null,
158
+ };
159
+ }
160
+
161
+ export function planLifecycleAdoption(
162
+ state: RegisterLifecycleState,
163
+ input: LifecyclePlanInput,
164
+ ): LifecycleAdoptionDecision {
165
+ const owner = createLifecycleOwner(input, state.nextGeneration, 'active');
166
+ const active = state.active;
167
+
168
+ if (!active) {
169
+ return {
170
+ kind: 'initialize',
171
+ owner,
172
+ reason: 'no-active-lifecycle',
173
+ };
174
+ }
175
+
176
+ if (active.phase === 'failed') {
177
+ return {
178
+ kind: 'reject',
179
+ owner,
180
+ reason: 'predecessor-failed',
181
+ };
182
+ }
183
+
184
+ if (state.pending) {
185
+ const sameRegistration = sameLifecycleRegistration(state.pending.key, owner.key);
186
+ const sameGeneration = sameBridgeGeneration(
187
+ state.pending.bridgeGeneration,
188
+ owner.bridgeGeneration,
189
+ );
190
+ if (sameRegistration && sameGeneration) {
191
+ return {
192
+ kind: 'duplicate',
193
+ owner,
194
+ reason: 'pending-successor',
195
+ };
196
+ }
197
+ if (sameRegistration) {
198
+ return {
199
+ kind: 'reject',
200
+ owner,
201
+ reason: 'registration-generation-conflict',
202
+ };
203
+ }
204
+ return {
205
+ kind: 'reject',
206
+ owner,
207
+ reason: 'pending-successor-conflict',
208
+ };
209
+ }
210
+
211
+ if (active.phase === 'active' || active.phase === 'registering') {
212
+ if (
213
+ sameLifecycleRegistration(active.key, owner.key) &&
214
+ !sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration)
215
+ ) {
216
+ return {
217
+ kind: 'reject',
218
+ owner,
219
+ reason: 'registration-generation-conflict',
220
+ };
221
+ }
222
+ return {
223
+ kind: 'duplicate',
224
+ owner,
225
+ reason: 'active-owner',
226
+ };
227
+ }
228
+
229
+ if (active.phase === 'stopping') {
230
+ const sameRegistration = sameLifecycleRegistration(active.key, owner.key);
231
+ const sameGeneration = sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration);
232
+ if (sameRegistration && sameGeneration) {
233
+ return {
234
+ kind: 'duplicate',
235
+ owner,
236
+ reason: 'lifecycle-stopping',
237
+ };
238
+ }
239
+ if (sameRegistration) {
240
+ return {
241
+ kind: 'reject',
242
+ owner,
243
+ reason: 'registration-generation-conflict',
244
+ };
245
+ }
246
+ return {
247
+ kind: 'defer',
248
+ owner,
249
+ predecessorGeneration: active.key.generation,
250
+ mode: sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration)
251
+ ? 'reuse'
252
+ : 'replace',
253
+ reason: 'predecessor-stopping',
254
+ };
255
+ }
256
+
257
+ if (sameLifecycleRegistration(active.key, owner.key)) {
258
+ if (!sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration)) {
259
+ return {
260
+ kind: 'reject',
261
+ owner,
262
+ reason: 'registration-generation-conflict',
263
+ };
264
+ }
265
+ return {
266
+ kind: 'duplicate',
267
+ owner,
268
+ reason: 'same-lifecycle-stopped',
269
+ };
270
+ }
271
+
272
+ if (sameBridgeGeneration(active.bridgeGeneration, owner.bridgeGeneration)) {
273
+ return {
274
+ kind: 'takeover',
275
+ owner,
276
+ mode: 'reuse',
277
+ reason: 'bridge-generation-reused',
278
+ };
279
+ }
280
+
281
+ return {
282
+ kind: 'takeover',
283
+ owner,
284
+ mode: 'replace',
285
+ reason: 'bridge-generation-changed',
286
+ };
287
+ }
288
+
289
+ export function commitLifecycleAdoption(
290
+ state: RegisterLifecycleState,
291
+ decision: LifecycleAdoptionDecision,
292
+ ) {
293
+ if (decision.kind === 'duplicate' || decision.kind === 'reject') {
294
+ return state.active;
295
+ }
296
+
297
+ state.nextGeneration = Math.max(state.nextGeneration + 1, decision.owner.key.generation + 1);
298
+
299
+ if (decision.kind === 'defer') {
300
+ state.pending = decision.owner;
301
+ state.pendingMode = decision.mode;
302
+ return state.active;
303
+ }
304
+
305
+ state.active = decision.owner;
306
+ state.pending = undefined;
307
+ state.pendingMode = undefined;
308
+ state.retirement = undefined;
309
+ state.resolveRetirement = undefined;
310
+ return state.active;
311
+ }
312
+
313
+ export function beginLifecycleRetirement(
314
+ state: RegisterLifecycleState,
315
+ registryFingerprint: string,
316
+ now: number,
317
+ expectedGeneration?: number,
318
+ ) {
319
+ const active = state.active;
320
+ if (
321
+ !active ||
322
+ active.key.registryFingerprint !== registryFingerprint ||
323
+ (expectedGeneration !== undefined && active.key.generation !== expectedGeneration) ||
324
+ (active.phase !== 'active' && active.phase !== 'registering')
325
+ ) {
326
+ state.staleCallbackSuppressions += 1;
327
+ return null;
328
+ }
329
+
330
+ let resolveRetirement!: (outcome: LifecycleRetirementOutcome) => void;
331
+ state.retirement = new Promise<LifecycleRetirementOutcome>((resolve) => {
332
+ resolveRetirement = resolve;
333
+ });
334
+ state.resolveRetirement = resolveRetirement;
335
+ active.phase = 'stopping';
336
+ active.stopRequestedAt = now;
337
+ return state.retirement;
338
+ }
339
+
340
+ export function settleLifecycleRetirement(
341
+ state: RegisterLifecycleState,
342
+ registryFingerprint: string,
343
+ outcome: LifecycleRetirementOutcome,
344
+ now: number,
345
+ expectedGeneration?: number,
346
+ ) {
347
+ const active = state.active;
348
+ if (
349
+ !active ||
350
+ active.key.registryFingerprint !== registryFingerprint ||
351
+ (expectedGeneration !== undefined && active.key.generation !== expectedGeneration) ||
352
+ active.phase !== 'stopping'
353
+ ) {
354
+ state.staleCallbackSuppressions += 1;
355
+ return false;
356
+ }
357
+
358
+ const resolveRetirement = state.resolveRetirement;
359
+ state.resolveRetirement = undefined;
360
+ active.phase = outcome.ok ? 'stopped' : 'failed';
361
+ active.stoppedAt = outcome.ok ? now : null;
362
+ active.failure = outcome.ok ? null : outcome.error;
363
+ resolveRetirement?.(outcome);
364
+ return true;
365
+ }
366
+
367
+ export function activatePendingLifecycle(
368
+ state: RegisterLifecycleState,
369
+ registryFingerprint: string,
370
+ now: number,
371
+ expectedGeneration?: number,
372
+ ) {
373
+ const pending = state.pending;
374
+ if (
375
+ !pending ||
376
+ pending.key.registryFingerprint !== registryFingerprint ||
377
+ (expectedGeneration !== undefined && pending.key.generation !== expectedGeneration)
378
+ ) {
379
+ return null;
380
+ }
381
+ if (state.active?.phase !== 'stopped') {
382
+ return null;
383
+ }
384
+
385
+ pending.phase = 'active';
386
+ pending.startedAt = now;
387
+ state.active = pending;
388
+ state.pending = undefined;
389
+ state.pendingMode = undefined;
390
+ state.retirement = undefined;
391
+ state.resolveRetirement = undefined;
392
+ return state.active;
393
+ }
394
+
395
+ export function reactivateStoppedLifecycle(
396
+ state: RegisterLifecycleState,
397
+ registryFingerprint: string,
398
+ now: number,
399
+ expectedGeneration?: number,
400
+ ) {
401
+ const active = state.active;
402
+ if (
403
+ state.pending ||
404
+ !active ||
405
+ active.key.registryFingerprint !== registryFingerprint ||
406
+ (expectedGeneration !== undefined && active.key.generation !== expectedGeneration) ||
407
+ active.phase !== 'stopped'
408
+ ) {
409
+ return null;
410
+ }
411
+ active.phase = 'active';
412
+ active.startedAt = now;
413
+ active.stopRequestedAt = null;
414
+ active.stoppedAt = null;
415
+ active.failure = null;
416
+ return active;
417
+ }
418
+
419
+ export function failLifecycle(
420
+ state: RegisterLifecycleState,
421
+ registryFingerprint: string,
422
+ error: unknown,
423
+ expectedGeneration?: number,
424
+ ) {
425
+ const active = state.active;
426
+ if (
427
+ !active ||
428
+ active.key.registryFingerprint !== registryFingerprint ||
429
+ (expectedGeneration !== undefined && active.key.generation !== expectedGeneration)
430
+ ) {
431
+ state.staleCallbackSuppressions += 1;
432
+ return false;
433
+ }
434
+ if (active.phase !== 'active' && active.phase !== 'registering') {
435
+ state.staleCallbackSuppressions += 1;
436
+ return false;
437
+ }
438
+ active.phase = 'failed';
439
+ active.stoppedAt = null;
440
+ active.failure = error instanceof Error ? error.message : String(error);
441
+ return true;
442
+ }
443
+
444
+ export function getActiveLifecycle(state: RegisterLifecycleState) {
445
+ return state.active;
446
+ }
447
+
448
+ export function getPendingLifecycle(state: RegisterLifecycleState) {
449
+ return state.pending;
450
+ }
451
+
452
+ export function isLifecycleRegistryActive(
453
+ state: RegisterLifecycleState,
454
+ registryFingerprint: string,
455
+ expectedGeneration?: number,
456
+ ) {
457
+ return Boolean(
458
+ state.active?.key.registryFingerprint === registryFingerprint &&
459
+ (expectedGeneration === undefined || state.active.key.generation === expectedGeneration) &&
460
+ (state.active.phase === 'active' || state.active.phase === 'registering'),
461
+ );
462
+ }
463
+
464
+ export function isLifecycleRegistryStoppable(
465
+ state: RegisterLifecycleState,
466
+ registryFingerprint: string,
467
+ expectedGeneration?: number,
468
+ ) {
469
+ const active = state.active;
470
+ return Boolean(
471
+ active?.key.registryFingerprint === registryFingerprint &&
472
+ (expectedGeneration === undefined || active.key.generation === expectedGeneration) &&
473
+ (active.phase === 'registering' || active.phase === 'active' || active.phase === 'stopping'),
474
+ );
475
+ }
476
+
477
+ export function dumpLifecycleOwner(owner?: LifecycleOwner) {
478
+ if (!owner) return null;
479
+ return {
480
+ generation: owner.key.generation,
481
+ apiInstanceId: owner.key.apiInstanceId,
482
+ registryFingerprint: owner.key.registryFingerprint,
483
+ phase: owner.phase,
484
+ moduleEpoch: owner.bridgeGeneration.moduleEpoch,
485
+ bridgeFactoryId: owner.bridgeGeneration.bridgeFactoryId,
486
+ pluginVersion: owner.bridgeGeneration.pluginVersion,
487
+ startedAt: owner.startedAt,
488
+ stopRequestedAt: owner.stopRequestedAt,
489
+ stoppedAt: owner.stoppedAt,
490
+ failure: owner.failure,
491
+ };
492
+ }
@@ -1,4 +1,5 @@
1
1
  import { emitBncrLogLine } from '../core/logging.ts';
2
+ import type { RegisterLifecycleState } from './register-lifecycle.ts';
2
3
  import type { BridgeRegisterStateCarrier } from './register-runtime-helpers.ts';
3
4
  import type { ChannelModule } from './runtime-loader.ts';
4
5
 
@@ -39,24 +40,76 @@ export function createBncrGatewayMethodRegistry(runtime: {
39
40
  getGatewayRuntime: () => {
40
41
  currentBridge?: BridgeSingleton;
41
42
  registeredMethodsByRegistry: Map<string, Set<BncrGatewayMethodName>>;
43
+ gatewayMethodDispatchers?: Partial<
44
+ Record<
45
+ BncrGatewayMethodName,
46
+ (bridge: BridgeSingleton, opts: BridgeGatewayHandlerContext) => BridgeGatewayHandlerResult
47
+ >
48
+ >;
49
+ registryDeclarations?: Map<
50
+ string,
51
+ {
52
+ state: 'shadow' | 'active' | 'retired';
53
+ registration?: 'pending' | 'complete' | 'failed';
54
+ }
55
+ >;
56
+ lifecycle?: RegisterLifecycleState;
42
57
  };
43
58
  gatewayMethodDispatchers: Record<
44
59
  BncrGatewayMethodName,
45
60
  (bridge: BridgeSingleton, opts: BridgeGatewayHandlerContext) => BridgeGatewayHandlerResult
46
61
  >;
47
62
  getBridgeRegisterStateCarrier: (bridge: BridgeSingleton) => BridgeRegisterStateCarrier;
63
+ getRegistryRuntimeObservation?: (registryFingerprint: string) => unknown;
48
64
  }) {
49
65
  const dispatchGatewayMethod = (
50
66
  name: BncrGatewayMethodName,
51
67
  opts: BridgeGatewayHandlerContext,
68
+ registryFingerprint?: string,
52
69
  ) => {
53
70
  const gatewayRuntime = runtime.getGatewayRuntime();
71
+ const declaration = registryFingerprint
72
+ ? gatewayRuntime.registryDeclarations?.get(registryFingerprint)
73
+ : undefined;
74
+ const lifecycleFailed = gatewayRuntime.lifecycle?.active?.phase === 'failed';
75
+ const declarationFailed = declaration?.registration === 'failed';
76
+ const dispatchAllowed = declaration
77
+ ? declaration.state !== 'retired' && !declarationFailed && !lifecycleFailed
78
+ : !gatewayRuntime.lifecycle?.active;
79
+ if (registryFingerprint && !dispatchAllowed) {
80
+ throw new Error(`bncr lifecycle registry is inactive for ${name}`);
81
+ }
54
82
  const bridge = gatewayRuntime.currentBridge;
55
83
  if (!bridge) {
56
84
  throw new Error(`bncr gateway runtime unavailable for ${name}`);
57
85
  }
86
+ const dispatcherOpts =
87
+ name === 'bncr.diagnostics' &&
88
+ registryFingerprint &&
89
+ runtime.getRegistryRuntimeObservation &&
90
+ typeof opts.respond === 'function'
91
+ ? {
92
+ ...opts,
93
+ respond: (...response: Parameters<typeof opts.respond>) => {
94
+ const [ok, payload, error, meta] = response;
95
+ let observation: unknown = null;
96
+ try {
97
+ observation = runtime.getRegistryRuntimeObservation?.(registryFingerprint) ?? null;
98
+ } catch {
99
+ observation = null;
100
+ }
101
+ const nextPayload =
102
+ ok && payload && typeof payload === 'object' && !Array.isArray(payload)
103
+ ? { ...payload, lifecycleObservation: observation }
104
+ : payload;
105
+ opts.respond(ok, nextPayload, error, meta);
106
+ },
107
+ }
108
+ : opts;
58
109
  try {
59
- return runtime.gatewayMethodDispatchers[name](bridge, opts);
110
+ const dispatcher =
111
+ gatewayRuntime.gatewayMethodDispatchers?.[name] || runtime.gatewayMethodDispatchers[name];
112
+ return dispatcher(bridge, dispatcherOpts);
60
113
  } catch (error) {
61
114
  const state = runtime.getBridgeRegisterStateCarrier(bridge) as BridgeStateReader;
62
115
  const detail =
@@ -88,7 +141,15 @@ export function createBncrGatewayMethodRegistry(runtime: {
88
141
  ) => {
89
142
  if (!Array.isArray(api?.methods)) return;
90
143
  if (api.methods.some((item) => item?.name === name)) return;
91
- api.methods.push({ name, handler: (opts) => dispatchGatewayMethod(name, opts) });
144
+ api.methods.push({
145
+ name,
146
+ handler: (opts) =>
147
+ dispatchGatewayMethod(
148
+ name,
149
+ opts,
150
+ runtime.getRegisterMeta(api).registryFingerprint || runtime.getRegistryFingerprint(api),
151
+ ),
152
+ });
92
153
  };
93
154
 
94
155
  const ensureGatewayMethodRegistered = (
@@ -115,7 +176,7 @@ export function createBncrGatewayMethodRegistry(runtime: {
115
176
  return;
116
177
  }
117
178
  api.registerGatewayMethod(name, (opts: BridgeGatewayHandlerContext) =>
118
- dispatchGatewayMethod(name, opts),
179
+ dispatchGatewayMethod(name, opts, registryFingerprint),
119
180
  );
120
181
  mirrorGatewayMethodForMockApi(api, name);
121
182
  registryMethods.add(name);
@@ -4,6 +4,9 @@ export type BridgeOwner = {
4
4
  apiInstanceId: string;
5
5
  registryFingerprint: string;
6
6
  registrationMode?: string;
7
+ pluginVersion?: string;
8
+ pluginRoot?: string;
9
+ pluginFile?: string;
7
10
  };
8
11
 
9
12
  export type BridgeRegisterStateSnapshot = {
@@ -35,20 +38,17 @@ export type BridgeRegisterStateCarrier = {
35
38
  gatewayPid?: number;
36
39
  };
37
40
 
38
- export function getProcessOwnerApiInstanceId(args: {
39
- serviceOwnerApiInstanceId?: string;
40
- channelOwnerApiInstanceId?: string;
41
- }) {
42
- return args.serviceOwnerApiInstanceId || args.channelOwnerApiInstanceId || undefined;
43
- }
44
-
45
41
  export function sameBridgeOwner(left?: BridgeOwner, right?: BridgeOwner) {
46
42
  if (!left || !right) return false;
47
43
  return (
48
44
  left.moduleEpoch === right.moduleEpoch &&
49
45
  left.bridgeFactoryId === right.bridgeFactoryId &&
50
46
  left.apiInstanceId === right.apiInstanceId &&
51
- left.registryFingerprint === right.registryFingerprint
47
+ left.registryFingerprint === right.registryFingerprint &&
48
+ left.registrationMode === right.registrationMode &&
49
+ left.pluginVersion === right.pluginVersion &&
50
+ left.pluginRoot === right.pluginRoot &&
51
+ left.pluginFile === right.pluginFile
52
52
  );
53
53
  }
54
54
 
@@ -102,39 +102,3 @@ export function hydrateBridgeRegisterState<T extends BridgeRegisterStateCarrier>
102
102
  bridge.registerTraceRecent = snapshot.registerTraceRecent.map((trace) => ({ ...trace }));
103
103
  return bridge;
104
104
  }
105
-
106
- export function shouldAdoptProcessOwner(args: {
107
- apiInstanceId: string;
108
- serviceRegistered?: boolean;
109
- channelRegistered?: boolean;
110
- serviceOwnerApiInstanceId?: string;
111
- channelOwnerApiInstanceId?: string;
112
- }) {
113
- const existingOwnerApiInstanceId = getProcessOwnerApiInstanceId({
114
- serviceOwnerApiInstanceId: args.serviceOwnerApiInstanceId,
115
- channelOwnerApiInstanceId: args.channelOwnerApiInstanceId,
116
- });
117
- const hasSingletonOwner = Boolean(args.serviceRegistered) || Boolean(args.channelRegistered);
118
-
119
- if (!hasSingletonOwner) {
120
- return {
121
- adoptOwner: true,
122
- existingOwnerApiInstanceId,
123
- reason: 'no-singleton-owner',
124
- };
125
- }
126
-
127
- if (existingOwnerApiInstanceId && existingOwnerApiInstanceId === args.apiInstanceId) {
128
- return {
129
- adoptOwner: true,
130
- existingOwnerApiInstanceId,
131
- reason: 'same-owner-api',
132
- };
133
- }
134
-
135
- return {
136
- adoptOwner: false,
137
- existingOwnerApiInstanceId,
138
- reason: 'singleton-owned-by-other-api',
139
- };
140
- }