@cadenza.io/service 2.17.74 → 2.17.76

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
@@ -1161,39 +1161,12 @@ var ServiceRegistry = class _ServiceRegistry {
1161
1161
  if (trackedInstance?.isFrontend) {
1162
1162
  return true;
1163
1163
  }
1164
- const trackedTransport = this.getRouteableTransport(
1165
- trackedInstance,
1166
- this.useSocket ? "socket" : "rest"
1167
- );
1168
1164
  if (this.deputies.has(serviceName) || this.remoteIntents.has(serviceName) || this.remoteSignals.has(serviceName)) {
1169
- const communicationTypes = Array.from(
1170
- new Set(
1171
- this.deputies.get(serviceName)?.map((d) => d.communicationType) ?? []
1172
- )
1165
+ const connected = this.ensureDependeeClientForInstance(
1166
+ trackedInstance,
1167
+ emit
1173
1168
  );
1174
- if (!communicationTypes.includes("signal") && this.remoteSignals.has(serviceName)) {
1175
- communicationTypes.push("signal");
1176
- }
1177
- if (trackedTransport) {
1178
- const clientCreated = this.hasTransportClientCreated(
1179
- trackedInstance,
1180
- trackedTransport.uuid
1181
- );
1182
- if (!clientCreated) {
1183
- emit("meta.service_registry.dependee_registered", {
1184
- serviceName,
1185
- serviceInstanceId: uuid8,
1186
- serviceTransportId: trackedTransport.uuid,
1187
- serviceOrigin: trackedTransport.origin,
1188
- transportProtocols: trackedTransport.protocols,
1189
- communicationTypes
1190
- });
1191
- this.markTransportClientCreated(
1192
- trackedInstance,
1193
- trackedTransport.uuid
1194
- );
1195
- }
1196
- } else {
1169
+ if (!connected) {
1197
1170
  emit("meta.service_registry.routeable_transport_missing", {
1198
1171
  serviceName,
1199
1172
  serviceInstanceId: uuid8,
@@ -1259,25 +1232,7 @@ var ServiceRegistry = class _ServiceRegistry {
1259
1232
  if (!hasRemoteInterest) {
1260
1233
  return true;
1261
1234
  }
1262
- if (!this.hasTransportClientCreated(ownerInstance, transport.uuid)) {
1263
- const communicationTypes = Array.from(
1264
- new Set(
1265
- this.deputies.get(ownerInstance.serviceName)?.map((descriptor) => descriptor.communicationType) ?? []
1266
- )
1267
- );
1268
- if (!communicationTypes.includes("signal") && this.remoteSignals.has(ownerInstance.serviceName)) {
1269
- communicationTypes.push("signal");
1270
- }
1271
- emit("meta.service_registry.dependee_registered", {
1272
- serviceName: ownerInstance.serviceName,
1273
- serviceInstanceId: ownerInstance.uuid,
1274
- serviceTransportId: transport.uuid,
1275
- serviceOrigin: transport.origin,
1276
- transportProtocols: transport.protocols,
1277
- communicationTypes
1278
- });
1279
- this.markTransportClientCreated(ownerInstance, transport.uuid);
1280
- }
1235
+ this.ensureDependeeClientForInstance(ownerInstance, emit);
1281
1236
  return true;
1282
1237
  },
1283
1238
  "Handles service transport updates independently from instance rows."
@@ -1358,7 +1313,7 @@ var ServiceRegistry = class _ServiceRegistry {
1358
1313
  ).emits("meta.service_registry.registered_global_signals").doOn("global.meta.cadenza_db.gathered_sync_data");
1359
1314
  this.handleGlobalIntentRegistrationTask = CadenzaService.createMetaTask(
1360
1315
  "Handle global intent registration",
1361
- (ctx) => {
1316
+ (ctx, emit) => {
1362
1317
  const intentToTaskMaps = this.normalizeIntentMaps(ctx);
1363
1318
  const sorted = intentToTaskMaps.sort((a, b) => {
1364
1319
  if (a.deleted && !b.deleted) return -1;
@@ -1374,6 +1329,7 @@ var ServiceRegistry = class _ServiceRegistry {
1374
1329
  name: map.intentName
1375
1330
  });
1376
1331
  this.registerRemoteIntentDeputy(map);
1332
+ this.ensureDependeeClientsForService(map.serviceName, emit);
1377
1333
  }
1378
1334
  return true;
1379
1335
  },
@@ -2799,6 +2755,49 @@ var ServiceRegistry = class _ServiceRegistry {
2799
2755
  }
2800
2756
  return buildTransportClientKey(transport);
2801
2757
  }
2758
+ resolveCommunicationTypesForService(serviceName) {
2759
+ const communicationTypes = Array.from(
2760
+ new Set(
2761
+ this.deputies.get(serviceName)?.map((descriptor) => descriptor.communicationType) ?? []
2762
+ )
2763
+ );
2764
+ if (!communicationTypes.includes("signal") && this.remoteSignals.has(serviceName)) {
2765
+ communicationTypes.push("signal");
2766
+ }
2767
+ return communicationTypes;
2768
+ }
2769
+ ensureDependeeClientForInstance(instance, emit) {
2770
+ if (!instance || instance.uuid === this.serviceInstanceId || instance.isFrontend || !instance.isActive || instance.isNonResponsive || instance.isBlocked) {
2771
+ return false;
2772
+ }
2773
+ if (!this.deputies.has(instance.serviceName) && !this.remoteIntents.has(instance.serviceName) && !this.remoteSignals.has(instance.serviceName)) {
2774
+ return false;
2775
+ }
2776
+ const transport = this.getRouteableTransport(
2777
+ instance,
2778
+ this.useSocket ? "socket" : "rest"
2779
+ );
2780
+ if (!transport || this.hasTransportClientCreated(instance, transport.uuid)) {
2781
+ return false;
2782
+ }
2783
+ emit("meta.service_registry.dependee_registered", {
2784
+ serviceName: instance.serviceName,
2785
+ serviceInstanceId: instance.uuid,
2786
+ serviceTransportId: transport.uuid,
2787
+ serviceOrigin: transport.origin,
2788
+ transportProtocols: transport.protocols,
2789
+ communicationTypes: this.resolveCommunicationTypesForService(
2790
+ instance.serviceName
2791
+ )
2792
+ });
2793
+ this.markTransportClientCreated(instance, transport.uuid);
2794
+ return true;
2795
+ }
2796
+ ensureDependeeClientsForService(serviceName, emit) {
2797
+ for (const instance of this.instances.get(serviceName) ?? []) {
2798
+ this.ensureDependeeClientForInstance(instance, emit);
2799
+ }
2800
+ }
2802
2801
  hasTransportClientCreated(instance, transportId) {
2803
2802
  return (instance.clientCreatedTransportIds ?? []).includes(transportId);
2804
2803
  }
@@ -3612,11 +3611,12 @@ var RestController = class _RestController {
3612
3611
  * @returns {Promise<any>} A promise that resolves to the parsed response data if the request is successful.
3613
3612
  * @throws {Error} Throws an error if the request fails due to issues such as timeout or other unexpected errors.
3614
3613
  */
3615
- this.fetchDataWithTimeout = async function(url, requestInit, timeoutMs) {
3614
+ this.fetchDataWithTimeout = async (url, requestInit, timeoutMs) => {
3616
3615
  const signal = AbortSignal.timeout(timeoutMs);
3617
3616
  try {
3618
3617
  const response = await fetch(url, { ...requestInit, signal });
3619
- return await response.json();
3618
+ const parsedResponse = await this.parseFetchResponse(response);
3619
+ return parsedResponse.data;
3620
3620
  } catch (error) {
3621
3621
  if (error?.name === "AbortError") {
3622
3622
  CadenzaService.log(
@@ -3682,7 +3682,11 @@ var RestController = class _RestController {
3682
3682
  }
3683
3683
  console.log("Service inserted...");
3684
3684
  const app = express();
3685
- app.use(bodyParser.json());
3685
+ app.use(
3686
+ bodyParser.json({
3687
+ limit: this.resolveJsonBodyLimit()
3688
+ })
3689
+ );
3686
3690
  switch (ctx.__securityProfile) {
3687
3691
  case "low":
3688
3692
  app.use(helmet());
@@ -3855,6 +3859,32 @@ var RestController = class _RestController {
3855
3859
  )
3856
3860
  );
3857
3861
  });
3862
+ app.use(
3863
+ (error, _req, res, next) => {
3864
+ if (!error) {
3865
+ next();
3866
+ return;
3867
+ }
3868
+ const statusCode = typeof error.statusCode === "number" ? error.statusCode : typeof error.status === "number" ? error.status : error.type === "entity.too.large" ? 413 : 500;
3869
+ const message = error.type === "entity.too.large" ? `Request payload exceeded REST body limit ${this.resolveJsonBodyLimit()}.` : this.getErrorMessage(error);
3870
+ CadenzaService.log(
3871
+ "REST request failed before route completion.",
3872
+ {
3873
+ error: message,
3874
+ type: error.type,
3875
+ statusCode,
3876
+ path: _req?.path,
3877
+ method: _req?.method
3878
+ },
3879
+ statusCode >= 500 ? "error" : "warning"
3880
+ );
3881
+ res.status(statusCode).json({
3882
+ __status: "error",
3883
+ errored: true,
3884
+ __error: message
3885
+ });
3886
+ }
3887
+ );
3858
3888
  return true;
3859
3889
  },
3860
3890
  "Starts REST server and initiates meta-handling"
@@ -4393,6 +4423,42 @@ var RestController = class _RestController {
4393
4423
  return String(error);
4394
4424
  }
4395
4425
  }
4426
+ resolveJsonBodyLimit() {
4427
+ const configuredLimit = process.env.CADENZA_REST_BODY_LIMIT?.trim();
4428
+ return configuredLimit && configuredLimit.length > 0 ? configuredLimit : "10mb";
4429
+ }
4430
+ async parseFetchResponse(response) {
4431
+ const contentType = response.headers.get("content-type") ?? "";
4432
+ const rawText = await response.text();
4433
+ const headers = Object.fromEntries(response.headers.entries());
4434
+ if (rawText.length === 0) {
4435
+ return {
4436
+ ok: response.ok,
4437
+ status: response.status,
4438
+ statusText: response.statusText,
4439
+ headers,
4440
+ data: {}
4441
+ };
4442
+ }
4443
+ if (!contentType.toLowerCase().includes("application/json")) {
4444
+ throw new Error(
4445
+ `Expected JSON response from ${response.url ?? "remote service"} but received ${contentType || "unknown content type"} (HTTP ${response.status}). Body preview: ${rawText.slice(0, 200)}`
4446
+ );
4447
+ }
4448
+ try {
4449
+ return {
4450
+ ok: response.ok,
4451
+ status: response.status,
4452
+ statusText: response.statusText,
4453
+ headers,
4454
+ data: JSON.parse(rawText)
4455
+ };
4456
+ } catch (error) {
4457
+ throw new Error(
4458
+ `Failed to parse JSON response from ${response.url ?? "remote service"} (HTTP ${response.status}). Body preview: ${rawText.slice(0, 200)}. Parse error: ${this.getErrorMessage(error)}`
4459
+ );
4460
+ }
4461
+ }
4396
4462
  resolveDelegationTimeoutMs(ctx) {
4397
4463
  const syncing = ctx?.__syncing === true || ctx?.__metadata?.__syncing === true || Array.isArray(ctx?.joinedContexts) && ctx.joinedContexts.some(
4398
4464
  (joinedCtx) => joinedCtx?.__syncing === true || joinedCtx?.__metadata?.__syncing === true