@cadenza.io/service 2.17.4 → 2.17.6

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.d.mts CHANGED
@@ -282,6 +282,10 @@ declare class ServiceRegistry {
282
282
  getInquiryResponderDescriptor(task: Task): InquiryResponderDescriptor;
283
283
  private getInstance;
284
284
  private getLocalInstance;
285
+ private summarizeTransportForDebug;
286
+ private summarizeInstanceForRuntimeStatusFallback;
287
+ private summarizeRuntimeStatusInquiryReports;
288
+ private createRuntimeStatusFallbackError;
285
289
  resolveLocalStatusCheck(ctx?: AnyObject): {
286
290
  __status: string;
287
291
  __error: string;
@@ -316,6 +320,7 @@ declare class ServiceRegistry {
316
320
  private markTransportClientCreated;
317
321
  private registerDependee;
318
322
  private unregisterDependee;
323
+ private reconcileBootstrapPlaceholderInstance;
319
324
  private getHeartbeatMisses;
320
325
  private shouldRequireReadinessFromCommunicationTypes;
321
326
  private resolveRuntimeStatusSnapshot;
@@ -23162,6 +23167,7 @@ interface ServiceInstanceDescriptor {
23162
23167
  health: AnyObject;
23163
23168
  isFrontend: boolean;
23164
23169
  isDatabase?: boolean;
23170
+ isBootstrapPlaceholder?: boolean;
23165
23171
  transports: ServiceTransportDescriptor[];
23166
23172
  clientCreatedTransportIds?: string[];
23167
23173
  }
package/dist/index.d.ts CHANGED
@@ -282,6 +282,10 @@ declare class ServiceRegistry {
282
282
  getInquiryResponderDescriptor(task: Task): InquiryResponderDescriptor;
283
283
  private getInstance;
284
284
  private getLocalInstance;
285
+ private summarizeTransportForDebug;
286
+ private summarizeInstanceForRuntimeStatusFallback;
287
+ private summarizeRuntimeStatusInquiryReports;
288
+ private createRuntimeStatusFallbackError;
285
289
  resolveLocalStatusCheck(ctx?: AnyObject): {
286
290
  __status: string;
287
291
  __error: string;
@@ -316,6 +320,7 @@ declare class ServiceRegistry {
316
320
  private markTransportClientCreated;
317
321
  private registerDependee;
318
322
  private unregisterDependee;
323
+ private reconcileBootstrapPlaceholderInstance;
319
324
  private getHeartbeatMisses;
320
325
  private shouldRequireReadinessFromCommunicationTypes;
321
326
  private resolveRuntimeStatusSnapshot;
@@ -23162,6 +23167,7 @@ interface ServiceInstanceDescriptor {
23162
23167
  health: AnyObject;
23163
23168
  isFrontend: boolean;
23164
23169
  isDatabase?: boolean;
23170
+ isBootstrapPlaceholder?: boolean;
23165
23171
  transports: ServiceTransportDescriptor[];
23166
23172
  clientCreatedTransportIds?: string[];
23167
23173
  }
package/dist/index.js CHANGED
@@ -531,6 +531,9 @@ function normalizeServiceInstanceDescriptor(value) {
531
531
  health: raw.health ?? {},
532
532
  isFrontend: Boolean(raw.isFrontend ?? raw.is_frontend ?? false),
533
533
  isDatabase: Boolean(raw.isDatabase ?? raw.is_database ?? false),
534
+ isBootstrapPlaceholder: Boolean(
535
+ raw.isBootstrapPlaceholder ?? raw.is_bootstrap_placeholder ?? false
536
+ ),
534
537
  transports,
535
538
  clientCreatedTransportIds: Array.isArray(raw.clientCreatedTransportIds) ? raw.clientCreatedTransportIds.map((entry) => normalizeString2(entry)).filter((entry) => entry.length > 0) : void 0
536
539
  };
@@ -944,6 +947,7 @@ var ServiceRegistry = class _ServiceRegistry {
944
947
  health: ctx.health ?? ctx.__health ?? {},
945
948
  numberOfRunningGraphs: ctx.numberOfRunningGraphs ?? ctx.__numberOfRunningGraphs ?? 0,
946
949
  isPrimary: false,
950
+ isBootstrapPlaceholder: !!ctx.isBootstrapPlaceholder,
947
951
  transports: ctx.transports ?? []
948
952
  } : void 0)
949
953
  );
@@ -998,6 +1002,9 @@ var ServiceRegistry = class _ServiceRegistry {
998
1002
  trackedInstance.acceptingWork = snapshot.acceptingWork;
999
1003
  trackedInstance.reportedAt = trackedInstance.reportedAt ?? (/* @__PURE__ */ new Date()).toISOString();
1000
1004
  }
1005
+ if (!serviceInstance.isBootstrapPlaceholder) {
1006
+ this.reconcileBootstrapPlaceholderInstance(serviceName, uuid5, emit);
1007
+ }
1001
1008
  if (this.serviceName === serviceName) {
1002
1009
  return false;
1003
1010
  }
@@ -1844,12 +1851,14 @@ var ServiceRegistry = class _ServiceRegistry {
1844
1851
  this.useSocket ? "socket" : "rest"
1845
1852
  ) : void 0;
1846
1853
  const message = error instanceof Error ? error.message : String(error);
1854
+ const diagnostics = error && typeof error === "object" && "runtimeStatusFallback" in error && error.runtimeStatusFallback && typeof error.runtimeStatusFallback === "object" ? error.runtimeStatusFallback : void 0;
1847
1855
  CadenzaService.log(
1848
1856
  "Runtime status fallback inquiry failed.",
1849
1857
  {
1850
1858
  serviceName,
1851
1859
  serviceInstanceId,
1852
- error: message
1860
+ error: message,
1861
+ diagnostics
1853
1862
  },
1854
1863
  "warning"
1855
1864
  );
@@ -2339,6 +2348,67 @@ var ServiceRegistry = class _ServiceRegistry {
2339
2348
  }
2340
2349
  return this.getInstance(this.serviceName, this.serviceInstanceId);
2341
2350
  }
2351
+ summarizeTransportForDebug(transport) {
2352
+ if (!transport) {
2353
+ return void 0;
2354
+ }
2355
+ return {
2356
+ uuid: transport.uuid,
2357
+ role: transport.role,
2358
+ origin: transport.origin,
2359
+ protocols: transport.protocols,
2360
+ clientCreated: transport.clientCreated
2361
+ };
2362
+ }
2363
+ summarizeInstanceForRuntimeStatusFallback(instance) {
2364
+ return {
2365
+ exists: Boolean(instance),
2366
+ runtimeState: instance?.runtimeState,
2367
+ acceptingWork: instance?.acceptingWork,
2368
+ reportedAt: instance?.reportedAt ?? null,
2369
+ isDatabase: instance?.isDatabase,
2370
+ isFrontend: instance?.isFrontend,
2371
+ isBootstrapPlaceholder: instance?.isBootstrapPlaceholder,
2372
+ transports: (instance?.transports ?? []).map((transport) => ({
2373
+ uuid: transport.uuid,
2374
+ role: transport.role,
2375
+ origin: transport.origin,
2376
+ protocols: transport.protocols,
2377
+ clientCreated: transport.clientCreated
2378
+ }))
2379
+ };
2380
+ }
2381
+ summarizeRuntimeStatusInquiryReports(inquiryResult) {
2382
+ const reports = Array.isArray(inquiryResult.runtimeStatusReports) ? inquiryResult.runtimeStatusReports : [];
2383
+ return reports.map((candidate) => {
2384
+ const normalized = this.normalizeRuntimeStatusReport(candidate);
2385
+ if (normalized) {
2386
+ return {
2387
+ serviceName: normalized.serviceName,
2388
+ serviceInstanceId: normalized.serviceInstanceId,
2389
+ transportId: normalized.transportId,
2390
+ state: normalized.state,
2391
+ acceptingWork: normalized.acceptingWork,
2392
+ reportedAt: normalized.reportedAt
2393
+ };
2394
+ }
2395
+ const raw = candidate && typeof candidate === "object" ? candidate : {};
2396
+ const rawState = raw.state === "healthy" || raw.state === "degraded" || raw.state === "overloaded" || raw.state === "unavailable" ? raw.state : void 0;
2397
+ return {
2398
+ serviceName: typeof raw.serviceName === "string" ? raw.serviceName : typeof raw.__serviceName === "string" ? raw.__serviceName : void 0,
2399
+ serviceInstanceId: typeof raw.serviceInstanceId === "string" ? raw.serviceInstanceId : typeof raw.__serviceInstanceId === "string" ? raw.__serviceInstanceId : void 0,
2400
+ transportId: typeof raw.transportId === "string" ? raw.transportId : typeof raw.serviceTransportId === "string" ? raw.serviceTransportId : void 0,
2401
+ state: rawState,
2402
+ acceptingWork: typeof raw.acceptingWork === "boolean" ? raw.acceptingWork : void 0,
2403
+ reportedAt: typeof raw.reportedAt === "string" ? raw.reportedAt : void 0
2404
+ };
2405
+ });
2406
+ }
2407
+ createRuntimeStatusFallbackError(message, diagnostics) {
2408
+ return Object.assign(new Error(message), {
2409
+ runtimeStatusFallback: diagnostics
2410
+ });
2411
+ }
2342
2412
  resolveLocalStatusCheck(ctx = {}) {
2343
2413
  if (!this.serviceName) {
2344
2414
  return {
@@ -2465,6 +2535,39 @@ var ServiceRegistry = class _ServiceRegistry {
2465
2535
  this.missedHeartbeatsByInstance.delete(serviceInstanceId);
2466
2536
  this.runtimeStatusFallbackInFlightByInstance.delete(serviceInstanceId);
2467
2537
  }
2538
+ reconcileBootstrapPlaceholderInstance(serviceName, resolvedInstanceId, emit) {
2539
+ const instances = this.instances.get(serviceName);
2540
+ if (!instances?.length) {
2541
+ return;
2542
+ }
2543
+ const placeholders = instances.filter(
2544
+ (instance) => instance.uuid !== resolvedInstanceId && instance.isBootstrapPlaceholder
2545
+ );
2546
+ if (!placeholders.length) {
2547
+ return;
2548
+ }
2549
+ for (const placeholder of placeholders) {
2550
+ const wasDependee = this.dependeeByInstance.has(placeholder.uuid);
2551
+ const requiredForReadiness = this.readinessDependeeByInstance.has(
2552
+ placeholder.uuid
2553
+ );
2554
+ for (const transport of placeholder.transports) {
2555
+ const transportKey = buildTransportClientKey(transport);
2556
+ emit(`meta.socket_shutdown_requested:${transportKey}`, {});
2557
+ emit(`meta.fetch.destroy_requested:${transportKey}`, {});
2558
+ }
2559
+ this.unregisterDependee(placeholder.uuid, serviceName);
2560
+ if (wasDependee) {
2561
+ this.registerDependee(serviceName, resolvedInstanceId, {
2562
+ requiredForReadiness
2563
+ });
2564
+ }
2565
+ }
2566
+ this.instances.set(
2567
+ serviceName,
2568
+ instances.filter((instance) => !instance.isBootstrapPlaceholder)
2569
+ );
2570
+ }
2468
2571
  getHeartbeatMisses(serviceInstanceId, now = Date.now()) {
2469
2572
  const observedMisses = this.missedHeartbeatsByInstance.get(serviceInstanceId) ?? 0;
2470
2573
  const lastHeartbeatAt = this.lastHeartbeatAtByInstance.get(serviceInstanceId) ?? 0;
@@ -2658,32 +2761,48 @@ var ServiceRegistry = class _ServiceRegistry {
2658
2761
  }
2659
2762
  async resolveRuntimeStatusFallbackInquiry(serviceName, serviceInstanceId, options = {}) {
2660
2763
  const instance = this.getInstance(serviceName, serviceInstanceId);
2661
- if (instance) {
2662
- const directReport = await this.requestRuntimeStatusViaRest(
2663
- instance,
2664
- serviceName,
2665
- serviceInstanceId
2666
- );
2667
- if (directReport) {
2668
- if (!this.applyRuntimeStatusReport(directReport)) {
2669
- throw new Error(
2670
- `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`
2671
- );
2672
- }
2673
- this.lastHeartbeatAtByInstance.set(serviceInstanceId, Date.now());
2674
- this.missedHeartbeatsByInstance.set(serviceInstanceId, 0);
2675
- return {
2676
- report: directReport,
2677
- inquiryMeta: {
2678
- inquiry: META_RUNTIME_STATUS_INTENT,
2679
- responded: 1,
2680
- failed: 0,
2681
- timedOut: 0,
2682
- pending: 0,
2683
- directStatusCheck: true
2764
+ const directStatusCheck = instance ? await this.requestRuntimeStatusViaRest(
2765
+ instance,
2766
+ serviceName,
2767
+ serviceInstanceId
2768
+ ) : {
2769
+ report: null,
2770
+ diagnostic: {
2771
+ attempted: false,
2772
+ outcome: "instance_missing"
2773
+ }
2774
+ };
2775
+ if (directStatusCheck.report) {
2776
+ if (!this.applyRuntimeStatusReport(directStatusCheck.report)) {
2777
+ throw this.createRuntimeStatusFallbackError(
2778
+ `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`,
2779
+ {
2780
+ target: {
2781
+ serviceName,
2782
+ serviceInstanceId
2783
+ },
2784
+ instance: this.summarizeInstanceForRuntimeStatusFallback(instance),
2785
+ directStatusCheck: directStatusCheck.diagnostic,
2786
+ inquiry: {
2787
+ meta: {},
2788
+ reportTargets: []
2789
+ }
2684
2790
  }
2685
- };
2791
+ );
2686
2792
  }
2793
+ this.lastHeartbeatAtByInstance.set(serviceInstanceId, Date.now());
2794
+ this.missedHeartbeatsByInstance.set(serviceInstanceId, 0);
2795
+ return {
2796
+ report: directStatusCheck.report,
2797
+ inquiryMeta: {
2798
+ inquiry: META_RUNTIME_STATUS_INTENT,
2799
+ responded: 1,
2800
+ failed: 0,
2801
+ timedOut: 0,
2802
+ pending: 0,
2803
+ directStatusCheck: true
2804
+ }
2805
+ };
2687
2806
  }
2688
2807
  const inquiryResult = await CadenzaService.inquire(
2689
2808
  META_RUNTIME_STATUS_INTENT,
@@ -2705,13 +2824,37 @@ var ServiceRegistry = class _ServiceRegistry {
2705
2824
  serviceInstanceId
2706
2825
  );
2707
2826
  if (!report) {
2708
- throw new Error(
2709
- `No runtime status report for ${serviceName}/${serviceInstanceId}`
2827
+ throw this.createRuntimeStatusFallbackError(
2828
+ `No runtime status report for ${serviceName}/${serviceInstanceId}`,
2829
+ {
2830
+ target: {
2831
+ serviceName,
2832
+ serviceInstanceId
2833
+ },
2834
+ instance: this.summarizeInstanceForRuntimeStatusFallback(instance),
2835
+ directStatusCheck: directStatusCheck.diagnostic,
2836
+ inquiry: {
2837
+ meta: inquiryResult.__inquiryMeta && typeof inquiryResult.__inquiryMeta === "object" ? inquiryResult.__inquiryMeta : {},
2838
+ reportTargets: this.summarizeRuntimeStatusInquiryReports(inquiryResult)
2839
+ }
2840
+ }
2710
2841
  );
2711
2842
  }
2712
2843
  if (!this.applyRuntimeStatusReport(report)) {
2713
- throw new Error(
2714
- `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`
2844
+ throw this.createRuntimeStatusFallbackError(
2845
+ `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`,
2846
+ {
2847
+ target: {
2848
+ serviceName,
2849
+ serviceInstanceId
2850
+ },
2851
+ instance: this.summarizeInstanceForRuntimeStatusFallback(instance),
2852
+ directStatusCheck: directStatusCheck.diagnostic,
2853
+ inquiry: {
2854
+ meta: inquiryResult.__inquiryMeta && typeof inquiryResult.__inquiryMeta === "object" ? inquiryResult.__inquiryMeta : {},
2855
+ reportTargets: this.summarizeRuntimeStatusInquiryReports(inquiryResult)
2856
+ }
2857
+ }
2715
2858
  );
2716
2859
  }
2717
2860
  this.lastHeartbeatAtByInstance.set(serviceInstanceId, Date.now());
@@ -2723,11 +2866,23 @@ var ServiceRegistry = class _ServiceRegistry {
2723
2866
  }
2724
2867
  async requestRuntimeStatusViaRest(instance, serviceName, serviceInstanceId) {
2725
2868
  if (typeof globalThis.fetch !== "function") {
2726
- return null;
2869
+ return {
2870
+ report: null,
2871
+ diagnostic: {
2872
+ attempted: false,
2873
+ outcome: "fetch_unavailable"
2874
+ }
2875
+ };
2727
2876
  }
2728
2877
  const transport = this.getRouteableTransport(instance, "rest");
2729
2878
  if (!transport) {
2730
- return null;
2879
+ return {
2880
+ report: null,
2881
+ diagnostic: {
2882
+ attempted: false,
2883
+ outcome: "no_rest_transport"
2884
+ }
2885
+ };
2731
2886
  }
2732
2887
  const controller = typeof AbortController === "function" ? new AbortController() : null;
2733
2888
  const timeoutId = controller ? setTimeout(() => controller.abort(), this.runtimeStatusFallbackTimeoutMs) : null;
@@ -2737,7 +2892,16 @@ var ServiceRegistry = class _ServiceRegistry {
2737
2892
  signal: controller?.signal
2738
2893
  });
2739
2894
  if ("ok" in response && response.ok === false) {
2740
- return null;
2895
+ return {
2896
+ report: null,
2897
+ diagnostic: {
2898
+ attempted: true,
2899
+ outcome: "http_error",
2900
+ transport: this.summarizeTransportForDebug(transport),
2901
+ responseStatus: typeof response.status === "number" ? response.status : void 0,
2902
+ responseStatusText: typeof response.statusText === "string" ? response.statusText : void 0
2903
+ }
2904
+ };
2741
2905
  }
2742
2906
  const payload = typeof response.json === "function" ? await response.json() : response;
2743
2907
  const report = this.normalizeRuntimeStatusReport({
@@ -2747,14 +2911,46 @@ var ServiceRegistry = class _ServiceRegistry {
2747
2911
  transportProtocols: payload?.transportProtocols ?? transport.protocols
2748
2912
  });
2749
2913
  if (!report) {
2750
- return null;
2914
+ return {
2915
+ report: null,
2916
+ diagnostic: {
2917
+ attempted: true,
2918
+ outcome: "invalid_report",
2919
+ transport: this.summarizeTransportForDebug(transport),
2920
+ payloadKeys: payload && typeof payload === "object" ? Object.keys(payload).sort() : []
2921
+ }
2922
+ };
2751
2923
  }
2752
2924
  if (report.serviceName !== serviceName || report.serviceInstanceId !== serviceInstanceId) {
2753
- return null;
2925
+ return {
2926
+ report: null,
2927
+ diagnostic: {
2928
+ attempted: true,
2929
+ outcome: "identity_mismatch",
2930
+ transport: this.summarizeTransportForDebug(transport),
2931
+ payloadServiceName: report.serviceName,
2932
+ payloadServiceInstanceId: report.serviceInstanceId
2933
+ }
2934
+ };
2754
2935
  }
2755
- return report;
2756
- } catch {
2757
- return null;
2936
+ return {
2937
+ report,
2938
+ diagnostic: {
2939
+ attempted: true,
2940
+ outcome: "matched",
2941
+ transport: this.summarizeTransportForDebug(transport)
2942
+ }
2943
+ };
2944
+ } catch (error) {
2945
+ return {
2946
+ report: null,
2947
+ diagnostic: {
2948
+ attempted: true,
2949
+ outcome: "fetch_error",
2950
+ transport: this.summarizeTransportForDebug(transport),
2951
+ error: error instanceof Error ? error.message : String(error)
2952
+ }
2953
+ };
2758
2954
  } finally {
2759
2955
  if (timeoutId) {
2760
2956
  clearTimeout(timeoutId);
@@ -9460,6 +9656,7 @@ var CadenzaService = class {
9460
9656
  isBlocked: false,
9461
9657
  health: {},
9462
9658
  isFrontend: false,
9659
+ isBootstrapPlaceholder: true,
9463
9660
  transports: resolvedBootstrapEndpoint ? [
9464
9661
  this.createBootstrapTransport(
9465
9662
  "cadenza-db",
@@ -9487,6 +9684,7 @@ var CadenzaService = class {
9487
9684
  isBlocked: false,
9488
9685
  health: {},
9489
9686
  isFrontend: false,
9687
+ isBootstrapPlaceholder: true,
9490
9688
  transports: relatedTransport ? [
9491
9689
  {
9492
9690
  uuid: `${service[0]}-${relatedTransport.role}`,