service-bridge 1.0.10-dev.23 → 1.0.10-dev.26

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.
Files changed (2) hide show
  1. package/dist/index.js +70 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -90,7 +90,27 @@ class ServiceBridgeError extends Error {
90
90
  this.operation = opts.operation;
91
91
  this.severity = opts.severity;
92
92
  this.retryable = opts.severity === "retriable";
93
- this.cause = opts.cause;
93
+ Object.defineProperty(this, "cause", {
94
+ value: opts.cause,
95
+ writable: true,
96
+ enumerable: false,
97
+ configurable: true
98
+ });
99
+ }
100
+ toString() {
101
+ return `ServiceBridgeError [${this.severity}]: ${this.message} (${this.operation})`;
102
+ }
103
+ [Symbol.for("nodejs.util.inspect.custom")]() {
104
+ const lines = [
105
+ `ServiceBridgeError [${this.severity}]`,
106
+ ` operation: ${this.operation}`,
107
+ ` message: ${this.message}`
108
+ ];
109
+ if (this.code !== undefined) {
110
+ lines.push(` code: ${this.code}`);
111
+ }
112
+ return lines.join(`
113
+ `);
94
114
  }
95
115
  }
96
116
  function containsValue(values, value) {
@@ -214,7 +234,14 @@ function normalizeServiceError(err, operation, component = "control-plane") {
214
234
  }
215
235
  const grpcErr = err;
216
236
  const code = typeof grpcErr?.code === "number" ? grpcErr.code : undefined;
217
- const message = typeof grpcErr?.message === "string" && grpcErr.message.length > 0 ? grpcErr.message : err instanceof Error ? err.message : String(err);
237
+ const details = typeof grpcErr?.details === "string" && grpcErr.details.trim() ? grpcErr.details.trim() : undefined;
238
+ const rawMsg = (() => {
239
+ const m = typeof grpcErr?.message === "string" && grpcErr.message ? grpcErr.message : err instanceof Error ? err.message : String(err);
240
+ return m.replace(/^\d+\s+[A-Z_]+:\s*/, "").trim() || m;
241
+ })();
242
+ const base = friendlyGrpcMessage(code, details ?? rawMsg);
243
+ const appendDetails = details && (code === grpc.status.INTERNAL || code === grpc.status.UNKNOWN || code === undefined);
244
+ const message = appendDetails ? `${base}: ${details}` : base;
218
245
  return new ServiceBridgeError({
219
246
  message,
220
247
  code,
@@ -225,6 +252,8 @@ function normalizeServiceError(err, operation, component = "control-plane") {
225
252
  });
226
253
  }
227
254
  function friendlyGrpcMessage(code, rawMessage) {
255
+ if (code === undefined)
256
+ return rawMessage || "unknown error";
228
257
  switch (code) {
229
258
  case grpc.status.UNAVAILABLE:
230
259
  return "control plane unavailable";
@@ -246,12 +275,14 @@ function friendlyGrpcMessage(code, rawMessage) {
246
275
  return "request cancelled";
247
276
  case grpc.status.UNIMPLEMENTED:
248
277
  return "operation not supported by control plane";
278
+ case grpc.status.UNKNOWN:
279
+ return "unknown error from control plane";
249
280
  default:
250
281
  return rawMessage || "unknown error";
251
282
  }
252
283
  }
253
284
  function friendlySDKMessage(operation, err) {
254
- const base = friendlyGrpcMessage(err.code, err.message);
285
+ const base = err.message;
255
286
  switch (operation) {
256
287
  case "open-worker-session":
257
288
  case "worker-session":
@@ -268,7 +299,7 @@ function friendlySDKMessage(operation, err) {
268
299
  return `offline queue flush failed: ${base}`;
269
300
  case "flush-on-ready":
270
301
  case "flush-on-restore":
271
- return `log flush failed: ${base}`;
302
+ return `offline queue flush failed: ${base}`;
272
303
  case "report-call-start":
273
304
  case "report-call":
274
305
  return `call report failed: ${base}`;
@@ -884,12 +915,15 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
884
915
  onlineRestoreTimer = null;
885
916
  if (!stopped) {
886
917
  isOnline = true;
918
+ const queueLen = offlineQueue.length;
919
+ console.info(`[servicebridge] reconnected to runtime${queueLen > 0 ? ` — flushing ${queueLen} queued operation(s)` : ""}`);
887
920
  flushQueue().catch((err) => {
888
921
  if (isConnectionError(err))
889
922
  scheduleOnlineRestore();
890
923
  else
891
924
  reportSDKError("flush-on-restore", err);
892
925
  });
926
+ scheduleNextHeartbeat(100);
893
927
  }
894
928
  }, 2000);
895
929
  }
@@ -924,6 +958,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
924
958
  attempt
925
959
  }).catch((err) => {
926
960
  if (isConnectionError(err)) {
961
+ if (isOnline) {
962
+ console.warn("[servicebridge] lost connection to runtime — entering offline mode");
963
+ }
927
964
  isOnline = false;
928
965
  scheduleOnlineRestore();
929
966
  enqueueOffline({
@@ -972,6 +1009,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
972
1009
  error
973
1010
  }).catch((err) => {
974
1011
  if (isConnectionError(err)) {
1012
+ if (isOnline) {
1013
+ console.warn("[servicebridge] lost connection to runtime — entering offline mode");
1014
+ }
975
1015
  isOnline = false;
976
1016
  scheduleOnlineRestore();
977
1017
  enqueueOffline({
@@ -993,6 +1033,10 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
993
1033
  });
994
1034
  }
995
1035
  async function flushQueue() {
1036
+ if (offlineQueue.length === 0)
1037
+ return;
1038
+ const snapshot = offlineQueue.slice();
1039
+ let flushed = 0;
996
1040
  while (offlineQueue.length > 0 && isOnline) {
997
1041
  const op = offlineQueue[0];
998
1042
  try {
@@ -1036,8 +1080,12 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1036
1080
  await sendReportCall(op);
1037
1081
  }
1038
1082
  offlineQueue.shift();
1083
+ flushed++;
1039
1084
  } catch (err) {
1040
1085
  if (isConnectionError(err)) {
1086
+ if (isOnline) {
1087
+ console.warn("[servicebridge] lost connection to runtime — entering offline mode");
1088
+ }
1041
1089
  isOnline = false;
1042
1090
  scheduleOnlineRestore();
1043
1091
  break;
@@ -1046,6 +1094,18 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1046
1094
  break;
1047
1095
  }
1048
1096
  }
1097
+ if (flushed > 0) {
1098
+ const remaining = offlineQueue.length;
1099
+ const sentOps = snapshot.slice(0, flushed);
1100
+ console.info(`[servicebridge] flushed ${flushed}/${snapshot.length} queued operation(s) to runtime` + ` (${formatQueueSummary(sentOps)})` + (remaining > 0 ? ` — ${remaining} still queued` : ""));
1101
+ }
1102
+ }
1103
+ function formatQueueSummary(ops) {
1104
+ const counts = {};
1105
+ for (const op of ops) {
1106
+ counts[op.type] = (counts[op.type] ?? 0) + 1;
1107
+ }
1108
+ return Object.entries(counts).map(([type, n]) => `${n} ${type}`).join(", ");
1049
1109
  }
1050
1110
  function requirePeerCN(call, allowedCallers, isDeliver) {
1051
1111
  const peerCN = extractPeerCN(call);
@@ -1192,6 +1252,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1192
1252
  }
1193
1253
  } catch (err) {
1194
1254
  if (isConnectionError(err)) {
1255
+ if (isOnline) {
1256
+ console.warn("[servicebridge] lost connection to runtime — entering offline mode");
1257
+ }
1195
1258
  isOnline = false;
1196
1259
  scheduleOnlineRestore();
1197
1260
  } else if (isRegistryResyncRequiredError(err)) {
@@ -1716,7 +1779,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1716
1779
  closeWorkerSession();
1717
1780
  serveState = null;
1718
1781
  shutdownWorkerServerGracefully();
1719
- throw normalizeServiceError(err, "serve");
1782
+ const fatal = normalizeServiceError(err, "serve");
1783
+ console.error(`[servicebridge] startup failed: ${fatal.message}`);
1784
+ throw fatal;
1720
1785
  }
1721
1786
  },
1722
1787
  stop() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "service-bridge",
3
- "version": "1.0.10-dev.23",
3
+ "version": "1.0.10-dev.26",
4
4
  "type": "module",
5
5
  "description": "ServiceBridge SDK for Node.js — production-ready RPC, durable events, workflows, jobs, and distributed tracing. One Go runtime + PostgreSQL replaces Istio, RabbitMQ, Temporal, and Jaeger.",
6
6
  "keywords": [