@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.mjs CHANGED
@@ -480,6 +480,9 @@ function normalizeServiceInstanceDescriptor(value) {
480
480
  health: raw.health ?? {},
481
481
  isFrontend: Boolean(raw.isFrontend ?? raw.is_frontend ?? false),
482
482
  isDatabase: Boolean(raw.isDatabase ?? raw.is_database ?? false),
483
+ isBootstrapPlaceholder: Boolean(
484
+ raw.isBootstrapPlaceholder ?? raw.is_bootstrap_placeholder ?? false
485
+ ),
483
486
  transports,
484
487
  clientCreatedTransportIds: Array.isArray(raw.clientCreatedTransportIds) ? raw.clientCreatedTransportIds.map((entry) => normalizeString2(entry)).filter((entry) => entry.length > 0) : void 0
485
488
  };
@@ -893,6 +896,7 @@ var ServiceRegistry = class _ServiceRegistry {
893
896
  health: ctx.health ?? ctx.__health ?? {},
894
897
  numberOfRunningGraphs: ctx.numberOfRunningGraphs ?? ctx.__numberOfRunningGraphs ?? 0,
895
898
  isPrimary: false,
899
+ isBootstrapPlaceholder: !!ctx.isBootstrapPlaceholder,
896
900
  transports: ctx.transports ?? []
897
901
  } : void 0)
898
902
  );
@@ -947,6 +951,9 @@ var ServiceRegistry = class _ServiceRegistry {
947
951
  trackedInstance.acceptingWork = snapshot.acceptingWork;
948
952
  trackedInstance.reportedAt = trackedInstance.reportedAt ?? (/* @__PURE__ */ new Date()).toISOString();
949
953
  }
954
+ if (!serviceInstance.isBootstrapPlaceholder) {
955
+ this.reconcileBootstrapPlaceholderInstance(serviceName, uuid5, emit);
956
+ }
950
957
  if (this.serviceName === serviceName) {
951
958
  return false;
952
959
  }
@@ -1793,12 +1800,14 @@ var ServiceRegistry = class _ServiceRegistry {
1793
1800
  this.useSocket ? "socket" : "rest"
1794
1801
  ) : void 0;
1795
1802
  const message = error instanceof Error ? error.message : String(error);
1803
+ const diagnostics = error && typeof error === "object" && "runtimeStatusFallback" in error && error.runtimeStatusFallback && typeof error.runtimeStatusFallback === "object" ? error.runtimeStatusFallback : void 0;
1796
1804
  CadenzaService.log(
1797
1805
  "Runtime status fallback inquiry failed.",
1798
1806
  {
1799
1807
  serviceName,
1800
1808
  serviceInstanceId,
1801
- error: message
1809
+ error: message,
1810
+ diagnostics
1802
1811
  },
1803
1812
  "warning"
1804
1813
  );
@@ -2288,6 +2297,67 @@ var ServiceRegistry = class _ServiceRegistry {
2288
2297
  }
2289
2298
  return this.getInstance(this.serviceName, this.serviceInstanceId);
2290
2299
  }
2300
+ summarizeTransportForDebug(transport) {
2301
+ if (!transport) {
2302
+ return void 0;
2303
+ }
2304
+ return {
2305
+ uuid: transport.uuid,
2306
+ role: transport.role,
2307
+ origin: transport.origin,
2308
+ protocols: transport.protocols,
2309
+ clientCreated: transport.clientCreated
2310
+ };
2311
+ }
2312
+ summarizeInstanceForRuntimeStatusFallback(instance) {
2313
+ return {
2314
+ exists: Boolean(instance),
2315
+ runtimeState: instance?.runtimeState,
2316
+ acceptingWork: instance?.acceptingWork,
2317
+ reportedAt: instance?.reportedAt ?? null,
2318
+ isDatabase: instance?.isDatabase,
2319
+ isFrontend: instance?.isFrontend,
2320
+ isBootstrapPlaceholder: instance?.isBootstrapPlaceholder,
2321
+ transports: (instance?.transports ?? []).map((transport) => ({
2322
+ uuid: transport.uuid,
2323
+ role: transport.role,
2324
+ origin: transport.origin,
2325
+ protocols: transport.protocols,
2326
+ clientCreated: transport.clientCreated
2327
+ }))
2328
+ };
2329
+ }
2330
+ summarizeRuntimeStatusInquiryReports(inquiryResult) {
2331
+ const reports = Array.isArray(inquiryResult.runtimeStatusReports) ? inquiryResult.runtimeStatusReports : [];
2332
+ return reports.map((candidate) => {
2333
+ const normalized = this.normalizeRuntimeStatusReport(candidate);
2334
+ if (normalized) {
2335
+ return {
2336
+ serviceName: normalized.serviceName,
2337
+ serviceInstanceId: normalized.serviceInstanceId,
2338
+ transportId: normalized.transportId,
2339
+ state: normalized.state,
2340
+ acceptingWork: normalized.acceptingWork,
2341
+ reportedAt: normalized.reportedAt
2342
+ };
2343
+ }
2344
+ const raw = candidate && typeof candidate === "object" ? candidate : {};
2345
+ const rawState = raw.state === "healthy" || raw.state === "degraded" || raw.state === "overloaded" || raw.state === "unavailable" ? raw.state : void 0;
2346
+ return {
2347
+ serviceName: typeof raw.serviceName === "string" ? raw.serviceName : typeof raw.__serviceName === "string" ? raw.__serviceName : void 0,
2348
+ serviceInstanceId: typeof raw.serviceInstanceId === "string" ? raw.serviceInstanceId : typeof raw.__serviceInstanceId === "string" ? raw.__serviceInstanceId : void 0,
2349
+ transportId: typeof raw.transportId === "string" ? raw.transportId : typeof raw.serviceTransportId === "string" ? raw.serviceTransportId : void 0,
2350
+ state: rawState,
2351
+ acceptingWork: typeof raw.acceptingWork === "boolean" ? raw.acceptingWork : void 0,
2352
+ reportedAt: typeof raw.reportedAt === "string" ? raw.reportedAt : void 0
2353
+ };
2354
+ });
2355
+ }
2356
+ createRuntimeStatusFallbackError(message, diagnostics) {
2357
+ return Object.assign(new Error(message), {
2358
+ runtimeStatusFallback: diagnostics
2359
+ });
2360
+ }
2291
2361
  resolveLocalStatusCheck(ctx = {}) {
2292
2362
  if (!this.serviceName) {
2293
2363
  return {
@@ -2414,6 +2484,39 @@ var ServiceRegistry = class _ServiceRegistry {
2414
2484
  this.missedHeartbeatsByInstance.delete(serviceInstanceId);
2415
2485
  this.runtimeStatusFallbackInFlightByInstance.delete(serviceInstanceId);
2416
2486
  }
2487
+ reconcileBootstrapPlaceholderInstance(serviceName, resolvedInstanceId, emit) {
2488
+ const instances = this.instances.get(serviceName);
2489
+ if (!instances?.length) {
2490
+ return;
2491
+ }
2492
+ const placeholders = instances.filter(
2493
+ (instance) => instance.uuid !== resolvedInstanceId && instance.isBootstrapPlaceholder
2494
+ );
2495
+ if (!placeholders.length) {
2496
+ return;
2497
+ }
2498
+ for (const placeholder of placeholders) {
2499
+ const wasDependee = this.dependeeByInstance.has(placeholder.uuid);
2500
+ const requiredForReadiness = this.readinessDependeeByInstance.has(
2501
+ placeholder.uuid
2502
+ );
2503
+ for (const transport of placeholder.transports) {
2504
+ const transportKey = buildTransportClientKey(transport);
2505
+ emit(`meta.socket_shutdown_requested:${transportKey}`, {});
2506
+ emit(`meta.fetch.destroy_requested:${transportKey}`, {});
2507
+ }
2508
+ this.unregisterDependee(placeholder.uuid, serviceName);
2509
+ if (wasDependee) {
2510
+ this.registerDependee(serviceName, resolvedInstanceId, {
2511
+ requiredForReadiness
2512
+ });
2513
+ }
2514
+ }
2515
+ this.instances.set(
2516
+ serviceName,
2517
+ instances.filter((instance) => !instance.isBootstrapPlaceholder)
2518
+ );
2519
+ }
2417
2520
  getHeartbeatMisses(serviceInstanceId, now = Date.now()) {
2418
2521
  const observedMisses = this.missedHeartbeatsByInstance.get(serviceInstanceId) ?? 0;
2419
2522
  const lastHeartbeatAt = this.lastHeartbeatAtByInstance.get(serviceInstanceId) ?? 0;
@@ -2607,32 +2710,48 @@ var ServiceRegistry = class _ServiceRegistry {
2607
2710
  }
2608
2711
  async resolveRuntimeStatusFallbackInquiry(serviceName, serviceInstanceId, options = {}) {
2609
2712
  const instance = this.getInstance(serviceName, serviceInstanceId);
2610
- if (instance) {
2611
- const directReport = await this.requestRuntimeStatusViaRest(
2612
- instance,
2613
- serviceName,
2614
- serviceInstanceId
2615
- );
2616
- if (directReport) {
2617
- if (!this.applyRuntimeStatusReport(directReport)) {
2618
- throw new Error(
2619
- `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`
2620
- );
2621
- }
2622
- this.lastHeartbeatAtByInstance.set(serviceInstanceId, Date.now());
2623
- this.missedHeartbeatsByInstance.set(serviceInstanceId, 0);
2624
- return {
2625
- report: directReport,
2626
- inquiryMeta: {
2627
- inquiry: META_RUNTIME_STATUS_INTENT,
2628
- responded: 1,
2629
- failed: 0,
2630
- timedOut: 0,
2631
- pending: 0,
2632
- directStatusCheck: true
2713
+ const directStatusCheck = instance ? await this.requestRuntimeStatusViaRest(
2714
+ instance,
2715
+ serviceName,
2716
+ serviceInstanceId
2717
+ ) : {
2718
+ report: null,
2719
+ diagnostic: {
2720
+ attempted: false,
2721
+ outcome: "instance_missing"
2722
+ }
2723
+ };
2724
+ if (directStatusCheck.report) {
2725
+ if (!this.applyRuntimeStatusReport(directStatusCheck.report)) {
2726
+ throw this.createRuntimeStatusFallbackError(
2727
+ `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`,
2728
+ {
2729
+ target: {
2730
+ serviceName,
2731
+ serviceInstanceId
2732
+ },
2733
+ instance: this.summarizeInstanceForRuntimeStatusFallback(instance),
2734
+ directStatusCheck: directStatusCheck.diagnostic,
2735
+ inquiry: {
2736
+ meta: {},
2737
+ reportTargets: []
2738
+ }
2633
2739
  }
2634
- };
2740
+ );
2635
2741
  }
2742
+ this.lastHeartbeatAtByInstance.set(serviceInstanceId, Date.now());
2743
+ this.missedHeartbeatsByInstance.set(serviceInstanceId, 0);
2744
+ return {
2745
+ report: directStatusCheck.report,
2746
+ inquiryMeta: {
2747
+ inquiry: META_RUNTIME_STATUS_INTENT,
2748
+ responded: 1,
2749
+ failed: 0,
2750
+ timedOut: 0,
2751
+ pending: 0,
2752
+ directStatusCheck: true
2753
+ }
2754
+ };
2636
2755
  }
2637
2756
  const inquiryResult = await CadenzaService.inquire(
2638
2757
  META_RUNTIME_STATUS_INTENT,
@@ -2654,13 +2773,37 @@ var ServiceRegistry = class _ServiceRegistry {
2654
2773
  serviceInstanceId
2655
2774
  );
2656
2775
  if (!report) {
2657
- throw new Error(
2658
- `No runtime status report for ${serviceName}/${serviceInstanceId}`
2776
+ throw this.createRuntimeStatusFallbackError(
2777
+ `No runtime status report for ${serviceName}/${serviceInstanceId}`,
2778
+ {
2779
+ target: {
2780
+ serviceName,
2781
+ serviceInstanceId
2782
+ },
2783
+ instance: this.summarizeInstanceForRuntimeStatusFallback(instance),
2784
+ directStatusCheck: directStatusCheck.diagnostic,
2785
+ inquiry: {
2786
+ meta: inquiryResult.__inquiryMeta && typeof inquiryResult.__inquiryMeta === "object" ? inquiryResult.__inquiryMeta : {},
2787
+ reportTargets: this.summarizeRuntimeStatusInquiryReports(inquiryResult)
2788
+ }
2789
+ }
2659
2790
  );
2660
2791
  }
2661
2792
  if (!this.applyRuntimeStatusReport(report)) {
2662
- throw new Error(
2663
- `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`
2793
+ throw this.createRuntimeStatusFallbackError(
2794
+ `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`,
2795
+ {
2796
+ target: {
2797
+ serviceName,
2798
+ serviceInstanceId
2799
+ },
2800
+ instance: this.summarizeInstanceForRuntimeStatusFallback(instance),
2801
+ directStatusCheck: directStatusCheck.diagnostic,
2802
+ inquiry: {
2803
+ meta: inquiryResult.__inquiryMeta && typeof inquiryResult.__inquiryMeta === "object" ? inquiryResult.__inquiryMeta : {},
2804
+ reportTargets: this.summarizeRuntimeStatusInquiryReports(inquiryResult)
2805
+ }
2806
+ }
2664
2807
  );
2665
2808
  }
2666
2809
  this.lastHeartbeatAtByInstance.set(serviceInstanceId, Date.now());
@@ -2672,11 +2815,23 @@ var ServiceRegistry = class _ServiceRegistry {
2672
2815
  }
2673
2816
  async requestRuntimeStatusViaRest(instance, serviceName, serviceInstanceId) {
2674
2817
  if (typeof globalThis.fetch !== "function") {
2675
- return null;
2818
+ return {
2819
+ report: null,
2820
+ diagnostic: {
2821
+ attempted: false,
2822
+ outcome: "fetch_unavailable"
2823
+ }
2824
+ };
2676
2825
  }
2677
2826
  const transport = this.getRouteableTransport(instance, "rest");
2678
2827
  if (!transport) {
2679
- return null;
2828
+ return {
2829
+ report: null,
2830
+ diagnostic: {
2831
+ attempted: false,
2832
+ outcome: "no_rest_transport"
2833
+ }
2834
+ };
2680
2835
  }
2681
2836
  const controller = typeof AbortController === "function" ? new AbortController() : null;
2682
2837
  const timeoutId = controller ? setTimeout(() => controller.abort(), this.runtimeStatusFallbackTimeoutMs) : null;
@@ -2686,7 +2841,16 @@ var ServiceRegistry = class _ServiceRegistry {
2686
2841
  signal: controller?.signal
2687
2842
  });
2688
2843
  if ("ok" in response && response.ok === false) {
2689
- return null;
2844
+ return {
2845
+ report: null,
2846
+ diagnostic: {
2847
+ attempted: true,
2848
+ outcome: "http_error",
2849
+ transport: this.summarizeTransportForDebug(transport),
2850
+ responseStatus: typeof response.status === "number" ? response.status : void 0,
2851
+ responseStatusText: typeof response.statusText === "string" ? response.statusText : void 0
2852
+ }
2853
+ };
2690
2854
  }
2691
2855
  const payload = typeof response.json === "function" ? await response.json() : response;
2692
2856
  const report = this.normalizeRuntimeStatusReport({
@@ -2696,14 +2860,46 @@ var ServiceRegistry = class _ServiceRegistry {
2696
2860
  transportProtocols: payload?.transportProtocols ?? transport.protocols
2697
2861
  });
2698
2862
  if (!report) {
2699
- return null;
2863
+ return {
2864
+ report: null,
2865
+ diagnostic: {
2866
+ attempted: true,
2867
+ outcome: "invalid_report",
2868
+ transport: this.summarizeTransportForDebug(transport),
2869
+ payloadKeys: payload && typeof payload === "object" ? Object.keys(payload).sort() : []
2870
+ }
2871
+ };
2700
2872
  }
2701
2873
  if (report.serviceName !== serviceName || report.serviceInstanceId !== serviceInstanceId) {
2702
- return null;
2874
+ return {
2875
+ report: null,
2876
+ diagnostic: {
2877
+ attempted: true,
2878
+ outcome: "identity_mismatch",
2879
+ transport: this.summarizeTransportForDebug(transport),
2880
+ payloadServiceName: report.serviceName,
2881
+ payloadServiceInstanceId: report.serviceInstanceId
2882
+ }
2883
+ };
2703
2884
  }
2704
- return report;
2705
- } catch {
2706
- return null;
2885
+ return {
2886
+ report,
2887
+ diagnostic: {
2888
+ attempted: true,
2889
+ outcome: "matched",
2890
+ transport: this.summarizeTransportForDebug(transport)
2891
+ }
2892
+ };
2893
+ } catch (error) {
2894
+ return {
2895
+ report: null,
2896
+ diagnostic: {
2897
+ attempted: true,
2898
+ outcome: "fetch_error",
2899
+ transport: this.summarizeTransportForDebug(transport),
2900
+ error: error instanceof Error ? error.message : String(error)
2901
+ }
2902
+ };
2707
2903
  } finally {
2708
2904
  if (timeoutId) {
2709
2905
  clearTimeout(timeoutId);
@@ -9409,6 +9605,7 @@ var CadenzaService = class {
9409
9605
  isBlocked: false,
9410
9606
  health: {},
9411
9607
  isFrontend: false,
9608
+ isBootstrapPlaceholder: true,
9412
9609
  transports: resolvedBootstrapEndpoint ? [
9413
9610
  this.createBootstrapTransport(
9414
9611
  "cadenza-db",
@@ -9436,6 +9633,7 @@ var CadenzaService = class {
9436
9633
  isBlocked: false,
9437
9634
  health: {},
9438
9635
  isFrontend: false,
9636
+ isBootstrapPlaceholder: true,
9439
9637
  transports: relatedTransport ? [
9440
9638
  {
9441
9639
  uuid: `${service[0]}-${relatedTransport.role}`,