@qlever-llc/trellis 0.10.10 → 0.10.12

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 (131) hide show
  1. package/esm/client.d.ts +2 -0
  2. package/esm/client.d.ts.map +1 -1
  3. package/esm/client.js +2 -0
  4. package/esm/client_connect.d.ts +3 -2
  5. package/esm/client_connect.d.ts.map +1 -1
  6. package/esm/client_connect.js +4 -1
  7. package/esm/errors/TrellisError.d.ts +3 -3
  8. package/esm/errors/TrellisError.js +3 -3
  9. package/esm/server/health.d.ts.map +1 -1
  10. package/esm/server/health.js +34 -3
  11. package/esm/server/internal_jobs/job-manager.d.ts.map +1 -1
  12. package/esm/server/internal_jobs/job-manager.js +32 -1
  13. package/esm/server/runtime.d.ts +3 -0
  14. package/esm/server/runtime.d.ts.map +1 -1
  15. package/esm/server/service.d.ts +15 -0
  16. package/esm/server/service.d.ts.map +1 -1
  17. package/esm/server/service.js +41 -3
  18. package/esm/server.d.ts.map +1 -1
  19. package/esm/server.js +99 -10
  20. package/esm/service/deno.d.ts +1 -1
  21. package/esm/service/deno.d.ts.map +1 -1
  22. package/esm/service/mod.d.ts +1 -1
  23. package/esm/service/mod.d.ts.map +1 -1
  24. package/esm/service/node.d.ts +1 -1
  25. package/esm/service/node.d.ts.map +1 -1
  26. package/esm/service/outbox_inbox.d.ts.map +1 -1
  27. package/esm/service/outbox_inbox.js +14 -0
  28. package/esm/telemetry/core.d.ts.map +1 -1
  29. package/esm/telemetry/core.js +1 -1
  30. package/esm/telemetry/env.d.ts.map +1 -1
  31. package/esm/telemetry/env.js +6 -1
  32. package/esm/telemetry/init.d.ts +3 -0
  33. package/esm/telemetry/init.d.ts.map +1 -0
  34. package/esm/telemetry/init.js +7 -0
  35. package/esm/telemetry/metrics.d.ts +34 -0
  36. package/esm/telemetry/metrics.d.ts.map +1 -0
  37. package/esm/telemetry/metrics.js +181 -0
  38. package/esm/telemetry/mod.d.ts +3 -0
  39. package/esm/telemetry/mod.d.ts.map +1 -1
  40. package/esm/telemetry/mod.js +2 -0
  41. package/esm/telemetry/runtime.d.ts +2 -0
  42. package/esm/telemetry/runtime.d.ts.map +1 -0
  43. package/esm/telemetry/runtime.js +134 -0
  44. package/esm/telemetry.d.ts +3 -0
  45. package/esm/telemetry.d.ts.map +1 -0
  46. package/esm/telemetry.js +2 -0
  47. package/esm/transfer.d.ts.map +1 -1
  48. package/esm/transfer.js +27 -16
  49. package/esm/trellis.d.ts +28 -4
  50. package/esm/trellis.d.ts.map +1 -1
  51. package/esm/trellis.js +575 -80
  52. package/package.json +7 -5
  53. package/script/client.d.ts +2 -0
  54. package/script/client.d.ts.map +1 -1
  55. package/script/client.js +2 -0
  56. package/script/client_connect.d.ts +3 -2
  57. package/script/client_connect.d.ts.map +1 -1
  58. package/script/client_connect.js +4 -1
  59. package/script/errors/TrellisError.d.ts +3 -3
  60. package/script/errors/TrellisError.js +3 -3
  61. package/script/server/health.d.ts.map +1 -1
  62. package/script/server/health.js +34 -3
  63. package/script/server/internal_jobs/job-manager.d.ts.map +1 -1
  64. package/script/server/internal_jobs/job-manager.js +32 -1
  65. package/script/server/runtime.d.ts +3 -0
  66. package/script/server/runtime.d.ts.map +1 -1
  67. package/script/server/service.d.ts +15 -0
  68. package/script/server/service.d.ts.map +1 -1
  69. package/script/server/service.js +40 -2
  70. package/script/server.d.ts.map +1 -1
  71. package/script/server.js +98 -9
  72. package/script/service/deno.d.ts +1 -1
  73. package/script/service/deno.d.ts.map +1 -1
  74. package/script/service/mod.d.ts +1 -1
  75. package/script/service/mod.d.ts.map +1 -1
  76. package/script/service/node.d.ts +1 -1
  77. package/script/service/node.d.ts.map +1 -1
  78. package/script/service/outbox_inbox.d.ts.map +1 -1
  79. package/script/service/outbox_inbox.js +14 -0
  80. package/script/telemetry/core.d.ts.map +1 -1
  81. package/script/telemetry/core.js +1 -1
  82. package/script/telemetry/env.d.ts.map +1 -1
  83. package/script/telemetry/env.js +6 -1
  84. package/script/telemetry/init.d.ts +3 -0
  85. package/script/telemetry/init.d.ts.map +1 -0
  86. package/script/telemetry/init.js +10 -0
  87. package/script/telemetry/metrics.d.ts +34 -0
  88. package/script/telemetry/metrics.d.ts.map +1 -0
  89. package/script/telemetry/metrics.js +186 -0
  90. package/script/telemetry/mod.d.ts +3 -0
  91. package/script/telemetry/mod.d.ts.map +1 -1
  92. package/script/telemetry/mod.js +7 -1
  93. package/script/telemetry/runtime.d.ts +2 -0
  94. package/script/telemetry/runtime.d.ts.map +1 -0
  95. package/script/telemetry/runtime.js +137 -0
  96. package/script/telemetry.d.ts +3 -0
  97. package/script/telemetry.d.ts.map +1 -0
  98. package/script/telemetry.js +18 -0
  99. package/script/transfer.d.ts.map +1 -1
  100. package/script/transfer.js +28 -17
  101. package/script/trellis.d.ts +28 -4
  102. package/script/trellis.d.ts.map +1 -1
  103. package/script/trellis.js +606 -110
  104. package/src/client.ts +4 -0
  105. package/src/client_connect.ts +11 -9
  106. package/src/errors/TrellisError.ts +4 -4
  107. package/src/server/health.ts +41 -3
  108. package/src/server/internal_jobs/job-manager.ts +35 -5
  109. package/src/server/runtime.ts +4 -0
  110. package/src/server/service.ts +75 -3
  111. package/src/server.ts +124 -14
  112. package/src/service/deno.ts +1 -0
  113. package/src/service/mod.ts +1 -0
  114. package/src/service/node.ts +1 -0
  115. package/src/service/outbox_inbox.ts +14 -0
  116. package/src/telemetry/core.ts +1 -1
  117. package/src/telemetry/env.ts +5 -1
  118. package/src/telemetry/init.ts +8 -0
  119. package/src/telemetry/metrics.ts +294 -0
  120. package/src/telemetry/mod.ts +7 -0
  121. package/src/telemetry/runtime.ts +218 -0
  122. package/src/telemetry.ts +2 -0
  123. package/src/transfer.ts +69 -30
  124. package/src/trellis.ts +652 -141
  125. package/esm/tracing.d.ts +0 -5
  126. package/esm/tracing.d.ts.map +0 -1
  127. package/esm/tracing.js +0 -8
  128. package/script/tracing.d.ts +0 -5
  129. package/script/tracing.d.ts.map +0 -1
  130. package/script/tracing.js +0 -27
  131. package/src/tracing.ts +0 -28
package/esm/trellis.js CHANGED
@@ -9,12 +9,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _Trellis_instances, _Trellis_log, _Trellis_tasks, _Trellis_hasExplicitApi, _Trellis_noResponderMaxRetries, _Trellis_noResponderRetryMs, _Trellis_onSessionNotFound, _Trellis_operationStore, _Trellis_eventConsumers, _Trellis_durableEventLoops, _Trellis_createStateFacade, _Trellis_createRpcFacade, _Trellis_createRpcHandleFacade, _Trellis_createEventFacade, _Trellis_createEventPublishFacade, _Trellis_createFeedFacade, _Trellis_createHandlerTrellis, _Trellis_createOperationFacade, _Trellis_unknownApiError, _Trellis_requestBuiltRpcUnknown, _Trellis_requestBuiltRpc, _Trellis_handleBrowserAuthRequired, _Trellis_authenticateFeedRequest, _Trellis_subscribeFeed, _Trellis_handleFeed, _Trellis_processFeedMessage, _Trellis_handleRPC, _Trellis_processRPCMessage, _Trellis_respondWithPayload, _Trellis_respondWithError, _Trellis_startEphemeralEvent, _Trellis_resolveEventConsumerGroup, _Trellis_registerDurableEventHandler, _Trellis_startDurableEventConsumer, _Trellis_durableEventConsumerGroupReady, _Trellis_runDurableEventConsumer, _Trellis_handleDurableEvent, _Trellis_handleDurableEventConsumer, _Trellis_parseEventMessage, _Trellis_escapeSubjectToken, _Trellis_currentIat, _Trellis_createProof, _Trellis_requestMessageWithRetry, _Trellis_requestJson, _Trellis_watchJson;
12
+ var _Trellis_instances, _Trellis_log, _Trellis_tasks, _Trellis_hasExplicitApi, _Trellis_noResponderMaxRetries, _Trellis_noResponderRetryMs, _Trellis_onSessionNotFound, _Trellis_operationStore, _Trellis_eventConsumers, _Trellis_durableEventLoops, _Trellis_createStateFacade, _Trellis_createRpcFacade, _Trellis_createRpcHandleFacade, _Trellis_createEventFacade, _Trellis_createEventPublishFacade, _Trellis_createFeedFacade, _Trellis_createHandlerTrellis, _Trellis_createOperationFacade, _Trellis_unknownApiError, _Trellis_requestBuiltRpcUnknown, _Trellis_requestBuiltRpc, _Trellis_handleBrowserAuthRequired, _Trellis_authenticateFeedRequest, _Trellis_subscribeFeed, _Trellis_handleFeed, _Trellis_processFeedMessage, _Trellis_handleRPC, _Trellis_processRPCMessage, _Trellis_respondWithPayload, _Trellis_respondWithError, _Trellis_startEphemeralEvent, _Trellis_invokeEventHandler, _Trellis_resolveEventConsumerGroup, _Trellis_registerDurableEventHandler, _Trellis_startDurableEventConsumer, _Trellis_durableEventConsumerGroupReady, _Trellis_runDurableEventConsumer, _Trellis_handleDurableEvent, _Trellis_handleDurableEventConsumer, _Trellis_parseEventMessage, _Trellis_escapeSubjectToken, _Trellis_currentIat, _Trellis_createProof, _Trellis_requestMessageWithRetry, _Trellis_requestJson, _Trellis_watchJson;
13
13
  import { jetstream, jetstreamManager, } from "@nats-io/jetstream";
14
14
  import { createInbox, headers as natsHeaders, } from "@nats-io/nats-core";
15
15
  import { CONTRACT_JOBS_METADATA, CONTRACT_KV_METADATA, CONTRACT_STATE_METADATA, } from "./contract_support/mod.js";
16
16
  import { AsyncResult, BaseError, err, isErr, ok, Result, } from "@qlever-llc/result";
17
- import { createNatsHeaderCarrier, extractTraceContext, injectTraceContext, SpanStatusCode, startClientSpan, startServerSpan, withSpanAsync, } from "./tracing.js";
17
+ import { createNatsHeaderCarrier, extractTraceContext, injectTraceContext, recordTrellisError, SpanStatusCode, startClientSpan, startServerSpan, withSpanAsync, } from "./telemetry/mod.js";
18
18
  import { Type } from "typebox";
19
19
  import { Pointer } from "typebox/value";
20
20
  import { ulid } from "ulid";
@@ -190,6 +190,27 @@ export function isOperationDeferred(value) {
190
190
  export function isResultLike(value) {
191
191
  return value instanceof Result;
192
192
  }
193
+ function compactHandlerErrorContext(context) {
194
+ return Object.fromEntries(Object.entries(context).filter(([key, value]) => key !== "traceId" && value !== undefined));
195
+ }
196
+ function sanitizeHandlerErrorContext(error) {
197
+ delete error.getContext().subject;
198
+ }
199
+ export function annotateHandlerBoundaryError(cause, context) {
200
+ const error = cause instanceof BaseError && !(cause instanceof RemoteError)
201
+ ? cause
202
+ : new UnexpectedError({ cause });
203
+ sanitizeHandlerErrorContext(error);
204
+ error.withContext(compactHandlerErrorContext(context));
205
+ error.withTraceId(context.traceId);
206
+ return error;
207
+ }
208
+ function recordRuntimeError(error, attributes) {
209
+ recordTrellisError(error, {
210
+ messagingSystem: "nats",
211
+ ...attributes,
212
+ });
213
+ }
193
214
  const DurableOperationSignalSchema = Type.Object({
194
215
  operationId: Type.String(),
195
216
  sequence: Type.Number(),
@@ -220,8 +241,11 @@ const DurableOperationSnapshotSchema = Type.Object({
220
241
  })),
221
242
  output: Type.Optional(Type.Any()),
222
243
  error: Type.Optional(Type.Object({
244
+ id: Type.Optional(Type.String()),
223
245
  type: Type.String(),
224
246
  message: Type.String(),
247
+ context: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
248
+ traceId: Type.Optional(Type.String()),
225
249
  })),
226
250
  });
227
251
  export const DurableOperationRecordSchema = Type.Object({
@@ -727,6 +751,18 @@ export class Trellis {
727
751
  writable: true,
728
752
  value: void 0
729
753
  });
754
+ Object.defineProperty(this, "contractId", {
755
+ enumerable: true,
756
+ configurable: true,
757
+ writable: true,
758
+ value: void 0
759
+ });
760
+ Object.defineProperty(this, "contractDigest", {
761
+ enumerable: true,
762
+ configurable: true,
763
+ writable: true,
764
+ value: void 0
765
+ });
730
766
  Object.defineProperty(this, "nats", {
731
767
  enumerable: true,
732
768
  configurable: true,
@@ -769,6 +805,8 @@ export class Trellis {
769
805
  __classPrivateFieldSet(this, _Trellis_log, (opts?.log ?? logger).child({ lib: "trellis" }), "f");
770
806
  this.timeout = opts?.timeout ?? 3000;
771
807
  this.stream = opts?.stream ?? "trellis";
808
+ this.contractId = opts?.contractId;
809
+ this.contractDigest = opts?.contractDigest;
772
810
  __classPrivateFieldSet(this, _Trellis_hasExplicitApi, api !== undefined, "f");
773
811
  __classPrivateFieldSet(this, _Trellis_noResponderMaxRetries, opts?.noResponderRetry?.maxAttempts ??
774
812
  DEFAULT_NO_RESPONDER_MAX_RETRIES, "f");
@@ -903,14 +941,27 @@ export class Trellis {
903
941
  const eventName = event;
904
942
  const ctx = this.api["events"][eventName];
905
943
  if (!ctx) {
906
- return err(new UnexpectedError({
944
+ const error = new UnexpectedError({
907
945
  cause: __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_unknownApiError).call(this, "event", event.toString()),
908
946
  context: { event: event.toString() },
909
- }));
947
+ });
948
+ recordRuntimeError(error, {
949
+ surface: "event",
950
+ direction: "publisher",
951
+ operation: event,
952
+ phase: "prepare",
953
+ });
954
+ return err(error);
910
955
  }
911
956
  const subject = this.template(ctx.subject, data).take();
912
957
  if (isErr(subject)) {
913
958
  logger.error({ err: subject.error }, "Failed to template event.");
959
+ recordRuntimeError(subject.error, {
960
+ surface: "event",
961
+ direction: "publisher",
962
+ operation: event,
963
+ phase: "request_encoding",
964
+ });
914
965
  return subject;
915
966
  }
916
967
  const header = {
@@ -924,7 +975,14 @@ export class Trellis {
924
975
  const msg = encodeSchema(ctx.event, payload).take();
925
976
  if (isErr(msg)) {
926
977
  logger.error({ err: msg.error }, "Failed to encode event.");
927
- return err(new UnexpectedError({ cause: msg.error }));
978
+ const error = new UnexpectedError({ cause: msg.error });
979
+ recordRuntimeError(error, {
980
+ surface: "event",
981
+ direction: "publisher",
982
+ operation: event,
983
+ phase: "request_encoding",
984
+ });
985
+ return err(error);
928
986
  }
929
987
  const headers = natsHeaders();
930
988
  headers.set("Nats-Msg-Id", header.id);
@@ -942,7 +1000,17 @@ export class Trellis {
942
1000
  }));
943
1001
  }
944
1002
  catch (cause) {
945
- return err(new UnexpectedError({ cause, context: { event: event.toString() } }));
1003
+ const error = new UnexpectedError({
1004
+ cause,
1005
+ context: { event: event.toString() },
1006
+ });
1007
+ recordRuntimeError(error, {
1008
+ surface: "event",
1009
+ direction: "publisher",
1010
+ operation: event,
1011
+ phase: "prepare",
1012
+ });
1013
+ return err(error);
946
1014
  }
947
1015
  }
948
1016
  /**
@@ -961,7 +1029,17 @@ export class Trellis {
961
1029
  return ok(undefined);
962
1030
  }
963
1031
  catch (cause) {
964
- return err(new UnexpectedError({ cause, context: { event: event.event } }));
1032
+ const error = new UnexpectedError({
1033
+ cause,
1034
+ context: { event: event.event },
1035
+ });
1036
+ recordRuntimeError(error, {
1037
+ surface: "event",
1038
+ direction: "publisher",
1039
+ operation: event.event,
1040
+ phase: "publish",
1041
+ });
1042
+ return err(error);
965
1043
  }
966
1044
  })());
967
1045
  }
@@ -1197,10 +1275,22 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1197
1275
  __classPrivateFieldGet(this, _Trellis_log, "f").trace({ method: String(method) }, `Calling ${method.toString()}.`);
1198
1276
  const msg = encodeRuntimeSchema(ctx.input, input).take();
1199
1277
  if (isErr(msg)) {
1278
+ recordRuntimeError(msg.error, {
1279
+ surface: "rpc",
1280
+ direction: "client",
1281
+ operation: method,
1282
+ phase: "request_encoding",
1283
+ });
1200
1284
  return msg;
1201
1285
  }
1202
1286
  const subject = this.template(ctx.subject, input).take();
1203
1287
  if (isErr(subject)) {
1288
+ recordRuntimeError(subject.error, {
1289
+ surface: "rpc",
1290
+ direction: "client",
1291
+ operation: method,
1292
+ phase: "request_encoding",
1293
+ });
1204
1294
  return subject;
1205
1295
  }
1206
1296
  const span = startClientSpan(method, subject);
@@ -1222,30 +1312,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1222
1312
  });
1223
1313
  const response = msgResult.take();
1224
1314
  if (isErr(response)) {
1315
+ recordRuntimeError(response.error, {
1316
+ surface: "rpc",
1317
+ direction: "client",
1318
+ operation: method,
1319
+ phase: "request_send",
1320
+ });
1225
1321
  return response;
1226
1322
  }
1227
1323
  if (response.headers?.get("status") === "error") {
1228
1324
  const json = safeJson(response).take();
1229
1325
  if (isErr(json)) {
1230
- return err(requestFailedTransportError({
1326
+ const error = requestFailedTransportError({
1231
1327
  code: "trellis.request.invalid_response",
1232
1328
  message: "Trellis returned an invalid response.",
1233
1329
  hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
1234
1330
  method,
1235
1331
  subject,
1236
1332
  cause: json.error.cause,
1237
- }));
1333
+ });
1334
+ recordRuntimeError(error, {
1335
+ surface: "rpc",
1336
+ direction: "client",
1337
+ operation: method,
1338
+ phase: "response_decoding",
1339
+ });
1340
+ return err(error);
1238
1341
  }
1239
1342
  const errorData = parse(TrellisErrorDataSchema, json).take();
1240
1343
  if (isErr(errorData)) {
1241
- return err(requestFailedTransportError({
1344
+ const error = requestFailedTransportError({
1242
1345
  code: "trellis.request.invalid_response",
1243
1346
  message: "Trellis returned an invalid response.",
1244
1347
  hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
1245
1348
  method,
1246
1349
  subject,
1247
1350
  cause: errorData.error,
1248
- }));
1351
+ });
1352
+ recordRuntimeError(error, {
1353
+ surface: "rpc",
1354
+ direction: "client",
1355
+ operation: method,
1356
+ phase: "response_decoding",
1357
+ });
1358
+ return err(error);
1249
1359
  }
1250
1360
  const declaredErrorTypes = Array.isArray(ctx.declaredErrorTypes)
1251
1361
  ? ctx.declaredErrorTypes.filter((value) => typeof value === "string")
@@ -1256,25 +1366,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1256
1366
  const reconstructed = reconstructDeclaredRpcError(declaredErrorTypes, runtimeErrors, errorData, json);
1257
1367
  if (reconstructed) {
1258
1368
  await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_handleBrowserAuthRequired).call(this, reconstructed);
1369
+ recordRuntimeError(new RemoteError({ error: errorData }), {
1370
+ surface: "rpc",
1371
+ direction: "client",
1372
+ operation: method,
1373
+ phase: "remote_error",
1374
+ });
1259
1375
  return err(reconstructed);
1260
1376
  }
1261
1377
  const remoteError = new RemoteError({ error: errorData });
1262
1378
  await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_handleBrowserAuthRequired).call(this, remoteError);
1379
+ recordRuntimeError(remoteError, {
1380
+ surface: "rpc",
1381
+ direction: "client",
1382
+ operation: method,
1383
+ phase: "remote_error",
1384
+ });
1263
1385
  return err(remoteError);
1264
1386
  }
1265
1387
  const json = safeJson(response).take();
1266
1388
  if (isErr(json)) {
1267
- return err(requestFailedTransportError({
1389
+ const error = requestFailedTransportError({
1268
1390
  code: "trellis.request.invalid_response",
1269
1391
  message: "Trellis returned an invalid response.",
1270
1392
  hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
1271
1393
  method,
1272
1394
  subject,
1273
1395
  cause: json.error.cause,
1274
- }));
1396
+ });
1397
+ recordRuntimeError(error, {
1398
+ surface: "rpc",
1399
+ direction: "client",
1400
+ operation: method,
1401
+ phase: "response_decoding",
1402
+ });
1403
+ return err(error);
1275
1404
  }
1276
1405
  const outputResult = parseRuntimeSchema(ctx.output, json).take();
1277
1406
  if (isErr(outputResult)) {
1407
+ recordRuntimeError(outputResult.error, {
1408
+ surface: "rpc",
1409
+ direction: "client",
1410
+ operation: method,
1411
+ phase: "response_decoding",
1412
+ });
1278
1413
  return err(outputResult.error);
1279
1414
  }
1280
1415
  return ok(outputResult);
@@ -1303,6 +1438,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1303
1438
  message: unexpected.message,
1304
1439
  });
1305
1440
  span.recordException(unexpected);
1441
+ recordRuntimeError(unexpected, {
1442
+ surface: "rpc",
1443
+ direction: "client",
1444
+ operation: method,
1445
+ phase: "unexpected",
1446
+ });
1306
1447
  return err(unexpected);
1307
1448
  }
1308
1449
  finally {
@@ -1371,11 +1512,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1371
1512
  }, _Trellis_subscribeFeed = function _Trellis_subscribeFeed(feed, descriptor, input, opts) {
1372
1513
  return AsyncResult.from((async () => {
1373
1514
  const payload = encodeRuntimeSchema(descriptor.input, input).take();
1374
- if (isErr(payload))
1515
+ if (isErr(payload)) {
1516
+ recordRuntimeError(payload.error, {
1517
+ surface: "feed",
1518
+ direction: "client",
1519
+ operation: feed,
1520
+ phase: "request_encoding",
1521
+ });
1375
1522
  return payload;
1523
+ }
1376
1524
  const subject = this.template(descriptor.subject, input).take();
1377
- if (isErr(subject))
1525
+ if (isErr(subject)) {
1526
+ recordRuntimeError(subject.error, {
1527
+ surface: "feed",
1528
+ direction: "client",
1529
+ operation: feed,
1530
+ phase: "request_template",
1531
+ });
1378
1532
  return subject;
1533
+ }
1379
1534
  const authHeaders = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_createProof).call(this, subject, payload);
1380
1535
  const headers = natsHeaders();
1381
1536
  headers.set("session-key", this.auth.sessionKey);
@@ -1395,13 +1550,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1395
1550
  catch (cause) {
1396
1551
  opts?.signal?.removeEventListener("abort", abort);
1397
1552
  sub.unsubscribe();
1398
- return err(createTransportError({
1553
+ const error = createTransportError({
1399
1554
  code: "trellis.feed.subscribe_failed",
1400
1555
  message: "Trellis could not subscribe to the feed.",
1401
1556
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
1402
1557
  cause,
1403
1558
  context: { feed, subject },
1404
- }));
1559
+ });
1560
+ recordRuntimeError(error, {
1561
+ surface: "feed",
1562
+ direction: "client",
1563
+ operation: feed,
1564
+ phase: "request_send",
1565
+ });
1566
+ return err(error);
1405
1567
  }
1406
1568
  let timeoutId;
1407
1569
  let abortHandler;
@@ -1427,7 +1589,7 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1427
1589
  if (firstFrame === "timeout" || firstFrame === "aborted") {
1428
1590
  opts?.signal?.removeEventListener("abort", abort);
1429
1591
  sub.unsubscribe();
1430
- return err(createTransportError({
1592
+ const error = createTransportError({
1431
1593
  code: firstFrame === "timeout"
1432
1594
  ? "trellis.feed.subscribe_timeout"
1433
1595
  : "trellis.feed.subscribe_aborted",
@@ -1438,28 +1600,49 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1438
1600
  ? "Check that the target service is running and has the current deployment digest, then retry."
1439
1601
  : "Retry the subscription if the feed is still needed.",
1440
1602
  context: { feed, subject },
1441
- }));
1603
+ });
1604
+ recordRuntimeError(error, {
1605
+ surface: "feed",
1606
+ direction: "client",
1607
+ operation: feed,
1608
+ phase: "handshake",
1609
+ });
1610
+ return err(error);
1442
1611
  }
1443
1612
  if (firstFrame.done) {
1444
1613
  opts?.signal?.removeEventListener("abort", abort);
1445
1614
  sub.unsubscribe();
1446
- return err(createTransportError({
1615
+ const error = createTransportError({
1447
1616
  code: "trellis.feed.subscribe_closed",
1448
1617
  message: "Trellis closed the feed before acknowledging it.",
1449
1618
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
1450
1619
  context: { feed, subject },
1451
- }));
1620
+ });
1621
+ recordRuntimeError(error, {
1622
+ surface: "feed",
1623
+ direction: "client",
1624
+ operation: feed,
1625
+ phase: "handshake",
1626
+ });
1627
+ return err(error);
1452
1628
  }
1453
1629
  const firstMessage = firstFrame.value;
1454
1630
  if (firstMessage.headers?.get("status") === "error") {
1455
1631
  opts?.signal?.removeEventListener("abort", abort);
1456
1632
  sub.unsubscribe();
1457
- return err(createTransportError({
1633
+ const error = createTransportError({
1458
1634
  code: "trellis.feed.failed",
1459
1635
  message: "Trellis rejected the feed subscription.",
1460
1636
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health and permissions.",
1461
1637
  context: { feed, subject, frame: firstMessage.string() },
1462
- }));
1638
+ });
1639
+ recordRuntimeError(error, {
1640
+ surface: "feed",
1641
+ direction: "client",
1642
+ operation: feed,
1643
+ phase: "remote_error",
1644
+ });
1645
+ return err(error);
1463
1646
  }
1464
1647
  const firstEvent = firstMessage.headers?.get("feed-status") === "ready"
1465
1648
  ? undefined
@@ -1469,19 +1652,40 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1469
1652
  try {
1470
1653
  const parseFeedFrame = (msg) => {
1471
1654
  if (msg.headers?.get("status") === "error") {
1472
- throw createTransportError({
1655
+ const error = createTransportError({
1473
1656
  code: "trellis.feed.failed",
1474
1657
  message: "Trellis stopped the feed.",
1475
1658
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
1476
1659
  context: { feed, subject, frame: msg.string() },
1477
1660
  });
1661
+ recordRuntimeError(error, {
1662
+ surface: "feed",
1663
+ direction: "client",
1664
+ operation: feed,
1665
+ phase: "remote_error",
1666
+ });
1667
+ throw error;
1478
1668
  }
1479
1669
  const json = safeJson(msg).take();
1480
- if (isErr(json))
1670
+ if (isErr(json)) {
1671
+ recordRuntimeError(json.error, {
1672
+ surface: "feed",
1673
+ direction: "client",
1674
+ operation: feed,
1675
+ phase: "event_decoding",
1676
+ });
1481
1677
  throw json.error;
1678
+ }
1482
1679
  const parsed = parseRuntimeSchema(eventSchema, json).take();
1483
- if (isErr(parsed))
1680
+ if (isErr(parsed)) {
1681
+ recordRuntimeError(parsed.error, {
1682
+ surface: "feed",
1683
+ direction: "client",
1684
+ operation: feed,
1685
+ phase: "event_validation",
1686
+ });
1484
1687
  throw parsed.error;
1688
+ }
1485
1689
  return parsed;
1486
1690
  };
1487
1691
  if (firstEvent)
@@ -1509,13 +1713,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1509
1713
  await this.nats.flush();
1510
1714
  }
1511
1715
  catch (cause) {
1512
- throw createTransportError({
1716
+ const error = createTransportError({
1513
1717
  code: "trellis.feed.listen_failed",
1514
1718
  message: "Trellis could not listen for feed requests.",
1515
1719
  hint: "Check the service deployment digest and runtime permissions, then restart the service.",
1516
1720
  cause,
1517
1721
  context: { feed, subject },
1518
1722
  });
1723
+ recordRuntimeError(error, {
1724
+ surface: "feed",
1725
+ direction: "server",
1726
+ operation: feed,
1727
+ phase: "listen",
1728
+ });
1729
+ throw error;
1519
1730
  }
1520
1731
  const task = AsyncResult.try(async () => {
1521
1732
  for await (const msg of sub) {
@@ -1528,9 +1739,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1528
1739
  }
1529
1740
  }
1530
1741
  catch (cause) {
1531
- const error = cause instanceof BaseError
1532
- ? cause
1533
- : new UnexpectedError({ cause });
1742
+ const error = annotateHandlerBoundaryError(cause, {
1743
+ feed,
1744
+ requestId: msg.headers?.get("request-id"),
1745
+ service: this.name,
1746
+ contractId: this.contractId,
1747
+ contractDigest: this.contractDigest,
1748
+ traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
1749
+ });
1750
+ recordRuntimeError(error, {
1751
+ surface: "feed",
1752
+ direction: "server",
1753
+ operation: feed,
1754
+ phase: "handler_throw",
1755
+ });
1534
1756
  __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_respondWithError).call(this, msg, error);
1535
1757
  }
1536
1758
  })();
@@ -1539,11 +1761,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1539
1761
  __classPrivateFieldGet(this, _Trellis_tasks, "f").add(`feed:${feed}`, task);
1540
1762
  }, _Trellis_processFeedMessage = async function _Trellis_processFeedMessage(feed, descriptor, msg, handler) {
1541
1763
  const json = safeJson(msg).take();
1542
- if (isErr(json))
1764
+ if (isErr(json)) {
1765
+ recordRuntimeError(json.error, {
1766
+ surface: "feed",
1767
+ direction: "server",
1768
+ operation: feed,
1769
+ phase: "request_decoding",
1770
+ });
1543
1771
  return json;
1772
+ }
1544
1773
  const parsed = parseRuntimeSchema(descriptor.input, json).take();
1545
- if (isErr(parsed))
1774
+ if (isErr(parsed)) {
1775
+ recordRuntimeError(parsed.error, {
1776
+ surface: "feed",
1777
+ direction: "server",
1778
+ operation: feed,
1779
+ phase: "input_validation",
1780
+ });
1546
1781
  return parsed;
1782
+ }
1547
1783
  const caller = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_authenticateFeedRequest).call(this, {
1548
1784
  feed,
1549
1785
  subject: msg.subject,
@@ -1552,12 +1788,26 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1552
1788
  requiredCapabilities: descriptor.subscribeCapabilities,
1553
1789
  });
1554
1790
  const callerValue = caller.take();
1555
- if (isErr(callerValue))
1791
+ if (isErr(callerValue)) {
1792
+ recordRuntimeError(callerValue.error, {
1793
+ surface: "feed",
1794
+ direction: "server",
1795
+ operation: feed,
1796
+ phase: "auth",
1797
+ });
1556
1798
  return callerValue;
1799
+ }
1557
1800
  if (!msg.reply) {
1558
- return err(new UnexpectedError({
1801
+ const error = new UnexpectedError({
1559
1802
  context: { feed, reason: "missing_reply" },
1560
- }));
1803
+ });
1804
+ recordRuntimeError(error, {
1805
+ surface: "feed",
1806
+ direction: "server",
1807
+ operation: feed,
1808
+ phase: "handshake",
1809
+ });
1810
+ return err(error);
1561
1811
  }
1562
1812
  const readyHeaders = natsHeaders();
1563
1813
  readyHeaders.set("feed-status", "ready");
@@ -1565,24 +1815,73 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1565
1815
  await this.nats.flush();
1566
1816
  const controller = new AbortController();
1567
1817
  try {
1568
- await handler({
1818
+ const handlerResult = await handler({
1569
1819
  input: parsed,
1570
1820
  caller: callerValue,
1571
1821
  signal: controller.signal,
1572
1822
  emit: (event) => AsyncResult.from((async () => {
1573
1823
  const payload = encodeRuntimeSchema(descriptor.event, event).take();
1574
- if (isErr(payload))
1824
+ if (isErr(payload)) {
1825
+ recordRuntimeError(payload.error, {
1826
+ surface: "feed",
1827
+ direction: "server",
1828
+ operation: feed,
1829
+ phase: "event_encoding",
1830
+ });
1575
1831
  return payload;
1832
+ }
1576
1833
  if (!msg.reply) {
1577
- return err(new UnexpectedError({
1834
+ const error = new UnexpectedError({
1578
1835
  context: { feed, reason: "missing_reply" },
1579
- }));
1836
+ });
1837
+ recordRuntimeError(error, {
1838
+ surface: "feed",
1839
+ direction: "server",
1840
+ operation: feed,
1841
+ phase: "event_publish",
1842
+ });
1843
+ return err(error);
1844
+ }
1845
+ try {
1846
+ this.nats.publish(msg.reply, payload);
1847
+ await this.nats.flush();
1848
+ }
1849
+ catch (cause) {
1850
+ const error = new UnexpectedError({
1851
+ cause,
1852
+ context: { feed },
1853
+ });
1854
+ recordRuntimeError(error, {
1855
+ surface: "feed",
1856
+ direction: "server",
1857
+ operation: feed,
1858
+ phase: "event_publish",
1859
+ });
1860
+ return err(error);
1580
1861
  }
1581
- this.nats.publish(msg.reply, payload);
1582
- await this.nats.flush();
1583
1862
  return ok(undefined);
1584
1863
  })()),
1585
1864
  });
1865
+ const handlerOutcome = isResultLike(handlerResult)
1866
+ ? handlerResult.take()
1867
+ : handlerResult;
1868
+ if (isErr(handlerOutcome)) {
1869
+ const error = annotateHandlerBoundaryError(handlerOutcome.error, {
1870
+ feed,
1871
+ requestId: msg.headers?.get("request-id"),
1872
+ service: this.name,
1873
+ contractId: this.contractId,
1874
+ contractDigest: this.contractDigest,
1875
+ traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
1876
+ });
1877
+ recordRuntimeError(error, {
1878
+ surface: "feed",
1879
+ direction: "server",
1880
+ operation: feed,
1881
+ phase: "handler_result",
1882
+ });
1883
+ return err(error);
1884
+ }
1586
1885
  return ok(undefined);
1587
1886
  }
1588
1887
  finally {
@@ -1650,6 +1949,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1650
1949
  code: SpanStatusCode.ERROR,
1651
1950
  message: "Failed to parse JSON",
1652
1951
  });
1952
+ recordRuntimeError(jsonData.error, {
1953
+ surface: "rpc",
1954
+ direction: "server",
1955
+ operation: String(method),
1956
+ phase: "parse",
1957
+ });
1653
1958
  return jsonData;
1654
1959
  }
1655
1960
  const parsedInput = parseRuntimeSchema(ctx.input, jsonData).take();
@@ -1658,6 +1963,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1658
1963
  code: SpanStatusCode.ERROR,
1659
1964
  message: "Input validation failed",
1660
1965
  });
1966
+ recordRuntimeError(parsedInput.error, {
1967
+ surface: "rpc",
1968
+ direction: "server",
1969
+ operation: String(method),
1970
+ phase: "input_validation",
1971
+ });
1661
1972
  return parsedInput;
1662
1973
  }
1663
1974
  let caller;
@@ -1683,7 +1994,14 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1683
1994
  code: SpanStatusCode.ERROR,
1684
1995
  message: "Missing session-key",
1685
1996
  });
1686
- return err(new AuthError({ reason: "missing_session_key" }));
1997
+ const error = new AuthError({ reason: "missing_session_key" });
1998
+ recordRuntimeError(error, {
1999
+ surface: "rpc",
2000
+ direction: "server",
2001
+ operation: String(method),
2002
+ phase: "auth",
2003
+ });
2004
+ return err(error);
1687
2005
  }
1688
2006
  if (!proof) {
1689
2007
  __classPrivateFieldGet(this, _Trellis_log, "f").warn({ method }, "Missing proof in request");
@@ -1691,11 +2009,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1691
2009
  code: SpanStatusCode.ERROR,
1692
2010
  message: "Missing proof",
1693
2011
  });
1694
- return err(new AuthError({ reason: "missing_proof" }));
2012
+ const error = new AuthError({ reason: "missing_proof" });
2013
+ recordRuntimeError(error, {
2014
+ surface: "rpc",
2015
+ direction: "server",
2016
+ operation: String(method),
2017
+ phase: "auth",
2018
+ });
2019
+ return err(error);
1695
2020
  }
1696
2021
  const iat = Number(iatHeader);
1697
2022
  if (!Number.isSafeInteger(iat) || !requestId) {
1698
- return err(new AuthError({ reason: "invalid_signature" }));
2023
+ const error = new AuthError({ reason: "invalid_signature" });
2024
+ recordRuntimeError(error, {
2025
+ surface: "rpc",
2026
+ direction: "server",
2027
+ operation: String(method),
2028
+ phase: "auth",
2029
+ });
2030
+ return err(error);
1699
2031
  }
1700
2032
  // Verify proof signature locally using the raw request bytes we received.
1701
2033
  const payloadBytes = msg.data ?? new Uint8Array();
@@ -1714,10 +2046,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1714
2046
  code: SpanStatusCode.ERROR,
1715
2047
  message: "Invalid signature",
1716
2048
  });
1717
- return err(new AuthError({
2049
+ const error = new AuthError({
1718
2050
  reason: "invalid_signature",
1719
2051
  context: { sessionKey },
1720
- }));
2052
+ });
2053
+ recordRuntimeError(error, {
2054
+ surface: "rpc",
2055
+ direction: "server",
2056
+ operation: String(method),
2057
+ phase: "auth",
2058
+ });
2059
+ return err(error);
1721
2060
  }
1722
2061
  let auth;
1723
2062
  for (let attempt = 0; attempt < DEFAULT_AUTH_VALIDATE_SESSION_RETRY_ATTEMPTS; attempt++) {
@@ -1743,9 +2082,16 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1743
2082
  await sleep(DEFAULT_AUTH_VALIDATE_SESSION_RETRY_MS * (attempt + 1));
1744
2083
  }
1745
2084
  if (!auth) {
1746
- return err(new UnexpectedError({
2085
+ const error = new UnexpectedError({
1747
2086
  context: { reason: "missing_auth_validate_result" },
1748
- }));
2087
+ });
2088
+ recordRuntimeError(error, {
2089
+ surface: "rpc",
2090
+ direction: "server",
2091
+ operation: String(method),
2092
+ phase: "auth",
2093
+ });
2094
+ return err(error);
1749
2095
  }
1750
2096
  if (auth instanceof Error) {
1751
2097
  __classPrivateFieldGet(this, _Trellis_log, "f").warn({
@@ -1761,22 +2107,42 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1761
2107
  message: "Auth.Requests.Validate failed",
1762
2108
  });
1763
2109
  if (auth instanceof BaseError) {
2110
+ recordRuntimeError(auth, {
2111
+ surface: "rpc",
2112
+ direction: "server",
2113
+ operation: String(method),
2114
+ phase: "auth",
2115
+ });
1764
2116
  return err(auth);
1765
2117
  }
1766
- return err(new UnexpectedError({ cause: auth }));
2118
+ const error = new UnexpectedError({ cause: auth });
2119
+ recordRuntimeError(error, {
2120
+ surface: "rpc",
2121
+ direction: "server",
2122
+ operation: String(method),
2123
+ phase: "auth",
2124
+ });
2125
+ return err(error);
1767
2126
  }
1768
2127
  if (!auth.allowed) {
1769
2128
  span.setStatus({
1770
2129
  code: SpanStatusCode.ERROR,
1771
2130
  message: "Insufficient permissions",
1772
2131
  });
1773
- return err(new AuthError({
2132
+ const error = new AuthError({
1774
2133
  reason: "insufficient_permissions",
1775
2134
  context: {
1776
2135
  requiredCapabilities: ctx.callerCapabilities,
1777
2136
  userCapabilities: auth.caller.capabilities,
1778
2137
  },
1779
- }));
2138
+ });
2139
+ recordRuntimeError(error, {
2140
+ surface: "rpc",
2141
+ direction: "server",
2142
+ operation: String(method),
2143
+ phase: "auth",
2144
+ });
2145
+ return err(error);
1780
2146
  }
1781
2147
  if (typeof msg.reply !== "string" ||
1782
2148
  !msg.reply.startsWith(`${auth.inboxPrefix}.`)) {
@@ -1784,10 +2150,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1784
2150
  code: SpanStatusCode.ERROR,
1785
2151
  message: "Reply subject mismatch",
1786
2152
  });
1787
- return err(new AuthError({
2153
+ const error = new AuthError({
1788
2154
  reason: "reply_subject_mismatch",
1789
2155
  context: { expected: auth.inboxPrefix, actual: msg.reply },
1790
- }));
2156
+ });
2157
+ recordRuntimeError(error, {
2158
+ surface: "rpc",
2159
+ direction: "server",
2160
+ operation: String(method),
2161
+ phase: "auth",
2162
+ });
2163
+ return err(error);
1791
2164
  }
1792
2165
  caller = auth.caller;
1793
2166
  }
@@ -1815,7 +2188,14 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1815
2188
  client: handlerTrellis,
1816
2189
  })));
1817
2190
  if (handlerResultWrapped.isErr()) {
1818
- const error = handlerResultWrapped.error.withContext({ method });
2191
+ const error = annotateHandlerBoundaryError(handlerResultWrapped.error, {
2192
+ method: String(method),
2193
+ requestId: msg.headers?.get("request-id"),
2194
+ service: this.name,
2195
+ contractId: this.contractId,
2196
+ contractDigest: this.contractDigest,
2197
+ traceId: activeTraceId(span) ?? incomingTraceId,
2198
+ });
1819
2199
  __classPrivateFieldGet(this, _Trellis_log, "f").error({
1820
2200
  method,
1821
2201
  error: error.message,
@@ -1828,16 +2208,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1828
2208
  message: error.message,
1829
2209
  });
1830
2210
  span.recordException(error);
2211
+ recordRuntimeError(error, {
2212
+ surface: "rpc",
2213
+ direction: "server",
2214
+ operation: String(method),
2215
+ phase: "handler_throw",
2216
+ });
1831
2217
  return err(error);
1832
2218
  }
1833
2219
  const handlerResult = handlerResultWrapped.take();
1834
2220
  const handlerOutcome = handlerResult.take();
1835
2221
  if (isErr(handlerOutcome)) {
1836
- const handlerError = handlerOutcome.error;
1837
- const error = handlerError instanceof BaseError &&
1838
- !(handlerError instanceof RemoteError)
1839
- ? handlerError
1840
- : new UnexpectedError({ cause: handlerError });
2222
+ const error = annotateHandlerBoundaryError(handlerOutcome.error, {
2223
+ method: String(method),
2224
+ requestId: msg.headers?.get("request-id"),
2225
+ service: this.name,
2226
+ contractId: this.contractId,
2227
+ contractDigest: this.contractDigest,
2228
+ traceId: activeTraceId(span) ?? incomingTraceId,
2229
+ });
1841
2230
  __classPrivateFieldGet(this, _Trellis_log, "f").error({
1842
2231
  method,
1843
2232
  error: error.message,
@@ -1850,6 +2239,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1850
2239
  code: SpanStatusCode.ERROR,
1851
2240
  message: error.message,
1852
2241
  });
2242
+ recordRuntimeError(error, {
2243
+ surface: "rpc",
2244
+ direction: "server",
2245
+ operation: String(method),
2246
+ phase: "handler_result",
2247
+ });
1853
2248
  return err(error);
1854
2249
  }
1855
2250
  const encoded = encodeSchema(ctx.output, handlerOutcome).take();
@@ -1858,6 +2253,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1858
2253
  code: SpanStatusCode.ERROR,
1859
2254
  message: "Output encoding failed",
1860
2255
  });
2256
+ recordRuntimeError(encoded.error, {
2257
+ surface: "rpc",
2258
+ direction: "server",
2259
+ operation: String(method),
2260
+ phase: "output_encoding",
2261
+ });
1861
2262
  return encoded;
1862
2263
  }
1863
2264
  span.setStatus({ code: SpanStatusCode.OK });
@@ -1934,17 +2335,31 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1934
2335
  const m = parsedEvent.take();
1935
2336
  if (isErr(m)) {
1936
2337
  __classPrivateFieldGet(this, _Trellis_log, "f").error({ error: m.error }, "Event validation failed");
2338
+ recordRuntimeError(m.error, {
2339
+ surface: "event",
2340
+ direction: "consumer",
2341
+ operation: String(event),
2342
+ phase: "input_validation",
2343
+ });
1937
2344
  continue;
1938
2345
  }
1939
- const handlerResult = await AsyncResult.lift(fn(m, createEventListenerContext({
2346
+ const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
2347
+ event,
1940
2348
  payload: m,
1941
- subject: msg.subject,
1942
2349
  mode: "ephemeral",
1943
2350
  message: msg,
1944
- })));
1945
- if (handlerResult.isErr()) {
2351
+ fn,
2352
+ });
2353
+ const handlerValue = handlerResult.take();
2354
+ if (isErr(handlerValue)) {
2355
+ recordRuntimeError(handlerValue.error, {
2356
+ surface: "event",
2357
+ direction: "consumer",
2358
+ operation: String(event),
2359
+ phase: "handler_result",
2360
+ });
1946
2361
  __classPrivateFieldGet(this, _Trellis_log, "f").error({
1947
- error: handlerResult.error.toSerializable(),
2362
+ error: handlerValue.error.toSerializable(),
1948
2363
  event,
1949
2364
  subject: msg.subject,
1950
2365
  }, "Event handler failed");
@@ -1953,6 +2368,31 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1953
2368
  });
1954
2369
  __classPrivateFieldGet(this, _Trellis_tasks, "f").add(`event:${event}:${ulid()}`, task);
1955
2370
  return ok(undefined);
2371
+ }, _Trellis_invokeEventHandler = async function _Trellis_invokeEventHandler(args) {
2372
+ const annotation = {
2373
+ event: String(args.event),
2374
+ service: this.name,
2375
+ contractId: this.contractId,
2376
+ contractDigest: this.contractDigest,
2377
+ traceId: traceIdFromTraceparent(args.message.headers?.get("traceparent")),
2378
+ };
2379
+ try {
2380
+ const result = await Promise.resolve(args.fn(args.payload, createEventListenerContext({
2381
+ payload: args.payload,
2382
+ subject: args.message.subject,
2383
+ mode: args.mode,
2384
+ ...(args.group ? { group: args.group } : {}),
2385
+ message: args.message,
2386
+ })));
2387
+ const outcome = isResultLike(result) ? result.take() : result;
2388
+ if (isErr(outcome)) {
2389
+ return err(annotateHandlerBoundaryError(outcome.error, annotation));
2390
+ }
2391
+ return ok(undefined);
2392
+ }
2393
+ catch (cause) {
2394
+ return err(annotateHandlerBoundaryError(cause, annotation));
2395
+ }
1956
2396
  }, _Trellis_resolveEventConsumerGroup = function _Trellis_resolveEventConsumerGroup(event, opts) {
1957
2397
  const metadata = __classPrivateFieldGet(this, _Trellis_eventConsumers, "f").metadata;
1958
2398
  const bindings = __classPrivateFieldGet(this, _Trellis_eventConsumers, "f").bindings ?? {};
@@ -2078,15 +2518,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2078
2518
  msg.term();
2079
2519
  continue;
2080
2520
  }
2081
- const handlerResult = await AsyncResult.lift(fn(m, createEventListenerContext({
2521
+ const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
2522
+ event,
2082
2523
  payload: m,
2083
- subject: msg.subject,
2084
2524
  mode: "durable",
2085
2525
  message: msg,
2086
- })));
2087
- if (handlerResult.isErr()) {
2526
+ fn,
2527
+ });
2528
+ const handlerValue = handlerResult.take();
2529
+ if (isErr(handlerValue)) {
2088
2530
  __classPrivateFieldGet(this, _Trellis_log, "f").error({
2089
- error: handlerResult.error.toSerializable(),
2531
+ error: handlerValue.error.toSerializable(),
2090
2532
  event,
2091
2533
  subject: msg.subject,
2092
2534
  }, "Event handler failed");
@@ -2115,20 +2557,34 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2115
2557
  const eventPayload = parsedEvent.take();
2116
2558
  if (isErr(eventPayload)) {
2117
2559
  __classPrivateFieldGet(this, _Trellis_log, "f").error({ error: eventPayload.error }, "Event validation failed");
2560
+ recordRuntimeError(eventPayload.error, {
2561
+ surface: "event",
2562
+ direction: "consumer",
2563
+ operation: String(registration.event),
2564
+ phase: "input_validation",
2565
+ });
2118
2566
  msg.term();
2119
2567
  failed = true;
2120
2568
  break;
2121
2569
  }
2122
- const handlerResult = await AsyncResult.lift(registration.fn(eventPayload, createEventListenerContext({
2570
+ const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
2571
+ event: registration.event,
2123
2572
  payload: eventPayload,
2124
- subject: msg.subject,
2125
2573
  mode: "durable",
2126
2574
  group,
2127
2575
  message: msg,
2128
- })));
2129
- if (handlerResult.isErr()) {
2576
+ fn: registration.fn,
2577
+ });
2578
+ const handlerValue = handlerResult.take();
2579
+ if (isErr(handlerValue)) {
2580
+ recordRuntimeError(handlerValue.error, {
2581
+ surface: "event",
2582
+ direction: "consumer",
2583
+ operation: String(registration.event),
2584
+ phase: "handler_result",
2585
+ });
2130
2586
  __classPrivateFieldGet(this, _Trellis_log, "f").error({
2131
- error: handlerResult.error.toSerializable(),
2587
+ error: handlerValue.error.toSerializable(),
2132
2588
  event: registration.event,
2133
2589
  subject: msg.subject,
2134
2590
  }, "Event handler failed");
@@ -2224,6 +2680,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2224
2680
  code: SpanStatusCode.ERROR,
2225
2681
  message: response.error.message,
2226
2682
  });
2683
+ recordRuntimeError(response.error, {
2684
+ surface: "operation",
2685
+ direction: "client",
2686
+ operation: "requestJson",
2687
+ phase: "request_send",
2688
+ });
2227
2689
  return response;
2228
2690
  }
2229
2691
  const json = safeJson(response).take();
@@ -2239,6 +2701,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2239
2701
  code: SpanStatusCode.ERROR,
2240
2702
  message: error.message,
2241
2703
  });
2704
+ recordRuntimeError(error, {
2705
+ surface: "operation",
2706
+ direction: "client",
2707
+ operation: "requestJson",
2708
+ phase: "response_decoding",
2709
+ });
2242
2710
  return err(error);
2243
2711
  }
2244
2712
  span.setStatus({ code: SpanStatusCode.OK });
@@ -2251,6 +2719,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2251
2719
  message: error.message,
2252
2720
  });
2253
2721
  span.recordException(error);
2722
+ recordRuntimeError(error, {
2723
+ surface: "operation",
2724
+ direction: "client",
2725
+ operation: "requestJson",
2726
+ phase: "unexpected",
2727
+ });
2254
2728
  return err(error);
2255
2729
  }
2256
2730
  finally {
@@ -2278,35 +2752,56 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2278
2752
  }
2279
2753
  catch (cause) {
2280
2754
  sub.unsubscribe();
2281
- return err(createTransportError({
2755
+ const error = createTransportError({
2282
2756
  code: "trellis.watch.failed",
2283
2757
  message: "Trellis could not start the operation watch.",
2284
2758
  hint: "Retry watching the operation. If it keeps failing, reconnect to Trellis and try again.",
2285
2759
  cause,
2286
2760
  context: { subject },
2287
- }));
2761
+ });
2762
+ recordRuntimeError(error, {
2763
+ surface: "operation",
2764
+ direction: "client",
2765
+ operation: "watchJson",
2766
+ phase: "request_send",
2767
+ });
2768
+ return err(error);
2288
2769
  }
2289
2770
  return ok((async function* () {
2290
2771
  try {
2291
2772
  for await (const msg of sub) {
2292
2773
  if (msg.headers?.get("status") === "error") {
2293
- yield err(createTransportError({
2774
+ const error = createTransportError({
2294
2775
  code: "trellis.watch.failed",
2295
2776
  message: "Trellis stopped the operation watch.",
2296
2777
  hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
2297
2778
  context: { subject, frame: msg.string() },
2298
- }));
2779
+ });
2780
+ recordRuntimeError(error, {
2781
+ surface: "operation",
2782
+ direction: "client",
2783
+ operation: "watchJson",
2784
+ phase: "remote_error",
2785
+ });
2786
+ yield err(error);
2299
2787
  continue;
2300
2788
  }
2301
2789
  const json = safeJson(msg).take();
2302
2790
  if (isErr(json)) {
2303
- yield err(createTransportError({
2791
+ const error = createTransportError({
2304
2792
  code: "trellis.watch.invalid_response",
2305
2793
  message: "Trellis returned an invalid watch update.",
2306
2794
  hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
2307
2795
  cause: json.error.cause,
2308
2796
  context: { subject },
2309
- }));
2797
+ });
2798
+ recordRuntimeError(error, {
2799
+ surface: "operation",
2800
+ direction: "client",
2801
+ operation: "watchJson",
2802
+ phase: "response_decoding",
2803
+ });
2804
+ yield err(error);
2310
2805
  continue;
2311
2806
  }
2312
2807
  yield ok(json);