@cadenza.io/service 2.17.2 → 2.17.3

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
@@ -280,6 +280,8 @@ declare class ServiceRegistry {
280
280
  getInquiryResponderDescriptor(task: Task): InquiryResponderDescriptor;
281
281
  private getInstance;
282
282
  private getLocalInstance;
283
+ private resolveTransportProtocolOrder;
284
+ private selectTransportForInstance;
283
285
  private getRoutingTransportRole;
284
286
  private getTransportById;
285
287
  private getRouteableTransport;
@@ -296,6 +298,7 @@ declare class ServiceRegistry {
296
298
  private buildLocalRuntimeStatusReport;
297
299
  private selectRuntimeStatusReportForTarget;
298
300
  private resolveRuntimeStatusFallbackInquiry;
301
+ private requestRuntimeStatusViaRest;
299
302
  private evaluateDependencyReadinessDetail;
300
303
  private buildLocalReadinessReport;
301
304
  /**
package/dist/index.d.ts CHANGED
@@ -280,6 +280,8 @@ declare class ServiceRegistry {
280
280
  getInquiryResponderDescriptor(task: Task): InquiryResponderDescriptor;
281
281
  private getInstance;
282
282
  private getLocalInstance;
283
+ private resolveTransportProtocolOrder;
284
+ private selectTransportForInstance;
283
285
  private getRoutingTransportRole;
284
286
  private getTransportById;
285
287
  private getRouteableTransport;
@@ -296,6 +298,7 @@ declare class ServiceRegistry {
296
298
  private buildLocalRuntimeStatusReport;
297
299
  private selectRuntimeStatusReportForTarget;
298
300
  private resolveRuntimeStatusFallbackInquiry;
301
+ private requestRuntimeStatusViaRest;
299
302
  private evaluateDependencyReadinessDetail;
300
303
  private buildLocalReadinessReport;
301
304
  /**
package/dist/index.js CHANGED
@@ -460,7 +460,14 @@ function selectTransportForRole(transports, role, protocol) {
460
460
  const filtered = transports.filter(
461
461
  (transport) => !transport.deleted && transport.role === role && (!protocol || transportSupportsProtocol(transport, protocol))
462
462
  );
463
- return filtered[0];
463
+ return filtered.sort((left, right) => {
464
+ const leftIsBootstrap = left.uuid.endsWith("-bootstrap") ? 1 : 0;
465
+ const rightIsBootstrap = right.uuid.endsWith("-bootstrap") ? 1 : 0;
466
+ if (leftIsBootstrap !== rightIsBootstrap) {
467
+ return leftIsBootstrap - rightIsBootstrap;
468
+ }
469
+ return left.origin.localeCompare(right.origin);
470
+ })[0];
464
471
  }
465
472
  function buildTransportClientKey(transport) {
466
473
  return transport.uuid;
@@ -1478,20 +1485,25 @@ var ServiceRegistry = class _ServiceRegistry {
1478
1485
  this.getBalancedInstance = CadenzaService.createMetaTask(
1479
1486
  "Get balanced instance",
1480
1487
  (context, emit) => {
1481
- const { __serviceName, __triedInstances, __retries, __broadcast } = context;
1488
+ const {
1489
+ __serviceName,
1490
+ __triedInstances,
1491
+ __retries,
1492
+ __broadcast,
1493
+ targetServiceInstanceId
1494
+ } = context;
1482
1495
  let retries = __retries ?? 0;
1483
1496
  let triedInstances = __triedInstances ?? [];
1484
1497
  const preferredRole = this.getRoutingTransportRole();
1485
1498
  const instances = this.instances.get(__serviceName)?.filter((instance) => {
1499
+ if (targetServiceInstanceId && instance.uuid !== targetServiceInstanceId) {
1500
+ return false;
1501
+ }
1486
1502
  if (!instance.isActive || instance.isNonResponsive || instance.isBlocked) {
1487
1503
  return false;
1488
1504
  }
1489
1505
  return Boolean(
1490
- this.getRouteableTransport(
1491
- instance,
1492
- this.useSocket ? "socket" : "rest",
1493
- preferredRole
1494
- ) ?? this.getRouteableTransport(instance, "rest", preferredRole)
1506
+ this.selectTransportForInstance(instance, context, preferredRole)
1495
1507
  );
1496
1508
  }).sort((a, b) => {
1497
1509
  const leftStatus = this.resolveRuntimeStatusSnapshot(
@@ -1523,13 +1535,17 @@ var ServiceRegistry = class _ServiceRegistry {
1523
1535
  }
1524
1536
  if (__broadcast || instances[0].isFrontend) {
1525
1537
  for (const instance of instances) {
1526
- const selectedTransport2 = this.getRouteableTransport(instance, "socket", preferredRole) ?? this.getRouteableTransport(instance, "rest", preferredRole);
1538
+ const selectedTransport2 = this.selectTransportForInstance(
1539
+ instance,
1540
+ context,
1541
+ preferredRole
1542
+ );
1527
1543
  if (!selectedTransport2) {
1528
1544
  continue;
1529
1545
  }
1530
1546
  const transportKey = buildTransportClientKey(selectedTransport2);
1531
1547
  emit(
1532
- `${this.useSocket && transportSupportsProtocol(selectedTransport2, "socket") ? "meta.service_registry.selected_instance_for_socket" : "meta.service_registry.selected_instance_for_fetch"}:${transportKey}`,
1548
+ `${this.resolveTransportProtocolOrder(context)[0] === "socket" && transportSupportsProtocol(selectedTransport2, "socket") ? "meta.service_registry.selected_instance_for_socket" : "meta.service_registry.selected_instance_for_fetch"}:${transportKey}`,
1533
1549
  {
1534
1550
  ...context,
1535
1551
  __instance: instance.uuid,
@@ -1560,7 +1576,11 @@ var ServiceRegistry = class _ServiceRegistry {
1560
1576
  if (retries > 0) {
1561
1577
  selected = instancesToTry[Math.floor(Math.random() * instancesToTry.length)];
1562
1578
  }
1563
- const selectedTransport = this.getRouteableTransport(selected, "socket", preferredRole) ?? this.getRouteableTransport(selected, "rest", preferredRole);
1579
+ const selectedTransport = this.selectTransportForInstance(
1580
+ selected,
1581
+ context,
1582
+ preferredRole
1583
+ );
1564
1584
  if (!selectedTransport) {
1565
1585
  context.errored = true;
1566
1586
  context.__error = `No routeable ${preferredRole} transport available for ${selected.serviceName}/${selected.uuid}.`;
@@ -1578,7 +1598,7 @@ var ServiceRegistry = class _ServiceRegistry {
1578
1598
  context.__triedInstances = triedInstances;
1579
1599
  context.__triedInstances.push(selected.uuid);
1580
1600
  context.__retries = retries;
1581
- if (this.useSocket && transportSupportsProtocol(selectedTransport, "socket")) {
1601
+ if (this.resolveTransportProtocolOrder(context)[0] === "socket" && transportSupportsProtocol(selectedTransport, "socket")) {
1582
1602
  emit(
1583
1603
  `meta.service_registry.selected_instance_for_socket:${context.__fetchId}`,
1584
1604
  context
@@ -2359,6 +2379,21 @@ var ServiceRegistry = class _ServiceRegistry {
2359
2379
  }
2360
2380
  return this.getInstance(this.serviceName, this.serviceInstanceId);
2361
2381
  }
2382
+ resolveTransportProtocolOrder(ctx) {
2383
+ const explicit = ctx.__preferredTransportProtocol === "rest" || ctx.__preferredTransportProtocol === "socket" ? ctx.__preferredTransportProtocol : void 0;
2384
+ const preferred = explicit ?? (this.useSocket ? "socket" : "rest");
2385
+ const fallback = preferred === "socket" ? "rest" : "socket";
2386
+ return [preferred, fallback];
2387
+ }
2388
+ selectTransportForInstance(instance, ctx, role = this.getRoutingTransportRole()) {
2389
+ for (const protocol of this.resolveTransportProtocolOrder(ctx)) {
2390
+ const transport = this.getRouteableTransport(instance, protocol, role);
2391
+ if (transport) {
2392
+ return transport;
2393
+ }
2394
+ }
2395
+ return void 0;
2396
+ }
2362
2397
  getRoutingTransportRole() {
2363
2398
  return this.isFrontend ? "public" : "internal";
2364
2399
  }
@@ -2618,12 +2653,41 @@ var ServiceRegistry = class _ServiceRegistry {
2618
2653
  return null;
2619
2654
  }
2620
2655
  async resolveRuntimeStatusFallbackInquiry(serviceName, serviceInstanceId, options = {}) {
2656
+ const instance = this.getInstance(serviceName, serviceInstanceId);
2657
+ if (instance) {
2658
+ const directReport = await this.requestRuntimeStatusViaRest(
2659
+ instance,
2660
+ serviceName,
2661
+ serviceInstanceId
2662
+ );
2663
+ if (directReport) {
2664
+ if (!this.applyRuntimeStatusReport(directReport)) {
2665
+ throw new Error(
2666
+ `No tracked instance for runtime fallback ${serviceName}/${serviceInstanceId}`
2667
+ );
2668
+ }
2669
+ this.lastHeartbeatAtByInstance.set(serviceInstanceId, Date.now());
2670
+ this.missedHeartbeatsByInstance.set(serviceInstanceId, 0);
2671
+ return {
2672
+ report: directReport,
2673
+ inquiryMeta: {
2674
+ inquiry: META_RUNTIME_STATUS_INTENT,
2675
+ responded: 1,
2676
+ failed: 0,
2677
+ timedOut: 0,
2678
+ pending: 0,
2679
+ directStatusCheck: true
2680
+ }
2681
+ };
2682
+ }
2683
+ }
2621
2684
  const inquiryResult = await CadenzaService.inquire(
2622
2685
  META_RUNTIME_STATUS_INTENT,
2623
2686
  {
2624
2687
  targetServiceName: serviceName,
2625
2688
  targetServiceInstanceId: serviceInstanceId,
2626
- detailLevel: options.detailLevel ?? "minimal"
2689
+ detailLevel: options.detailLevel ?? "minimal",
2690
+ __preferredTransportProtocol: "rest"
2627
2691
  },
2628
2692
  {
2629
2693
  overallTimeoutMs: options.overallTimeoutMs ?? this.runtimeStatusFallbackTimeoutMs,
@@ -2653,6 +2717,46 @@ var ServiceRegistry = class _ServiceRegistry {
2653
2717
  inquiryMeta: inquiryResult.__inquiryMeta ?? {}
2654
2718
  };
2655
2719
  }
2720
+ async requestRuntimeStatusViaRest(instance, serviceName, serviceInstanceId) {
2721
+ if (typeof globalThis.fetch !== "function") {
2722
+ return null;
2723
+ }
2724
+ const transport = this.getRouteableTransport(instance, "rest");
2725
+ if (!transport) {
2726
+ return null;
2727
+ }
2728
+ const controller = typeof AbortController === "function" ? new AbortController() : null;
2729
+ const timeoutId = controller ? setTimeout(() => controller.abort(), this.runtimeStatusFallbackTimeoutMs) : null;
2730
+ try {
2731
+ const response = await globalThis.fetch(`${transport.origin}/status`, {
2732
+ method: "GET",
2733
+ signal: controller?.signal
2734
+ });
2735
+ if ("ok" in response && response.ok === false) {
2736
+ return null;
2737
+ }
2738
+ const payload = typeof response.json === "function" ? await response.json() : response;
2739
+ const report = this.normalizeRuntimeStatusReport({
2740
+ ...payload,
2741
+ serviceTransportId: payload?.serviceTransportId ?? transport.uuid,
2742
+ serviceOrigin: payload?.serviceOrigin ?? transport.origin,
2743
+ transportProtocols: payload?.transportProtocols ?? transport.protocols
2744
+ });
2745
+ if (!report) {
2746
+ return null;
2747
+ }
2748
+ if (report.serviceName !== serviceName || report.serviceInstanceId !== serviceInstanceId) {
2749
+ return null;
2750
+ }
2751
+ return report;
2752
+ } catch {
2753
+ return null;
2754
+ } finally {
2755
+ if (timeoutId) {
2756
+ clearTimeout(timeoutId);
2757
+ }
2758
+ }
2759
+ }
2656
2760
  evaluateDependencyReadinessDetail(serviceName, serviceInstanceId, now = Date.now()) {
2657
2761
  const instance = this.getInstance(serviceName, serviceInstanceId);
2658
2762
  const missedHeartbeats = this.getHeartbeatMisses(serviceInstanceId, now);