@qlever-llc/trellis 0.10.11 → 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 (110) hide show
  1. package/esm/errors/TrellisError.d.ts +3 -3
  2. package/esm/errors/TrellisError.js +3 -3
  3. package/esm/server/internal_jobs/job-manager.d.ts.map +1 -1
  4. package/esm/server/internal_jobs/job-manager.js +32 -1
  5. package/esm/server/runtime.d.ts +3 -0
  6. package/esm/server/runtime.d.ts.map +1 -1
  7. package/esm/server/service.d.ts +15 -0
  8. package/esm/server/service.d.ts.map +1 -1
  9. package/esm/server/service.js +8 -0
  10. package/esm/server.d.ts.map +1 -1
  11. package/esm/server.js +54 -6
  12. package/esm/service/deno.d.ts +1 -1
  13. package/esm/service/deno.d.ts.map +1 -1
  14. package/esm/service/mod.d.ts +1 -1
  15. package/esm/service/mod.d.ts.map +1 -1
  16. package/esm/service/node.d.ts +1 -1
  17. package/esm/service/node.d.ts.map +1 -1
  18. package/esm/service/outbox_inbox.d.ts.map +1 -1
  19. package/esm/service/outbox_inbox.js +14 -0
  20. package/esm/telemetry/core.d.ts.map +1 -1
  21. package/esm/telemetry/core.js +1 -1
  22. package/esm/telemetry/env.d.ts.map +1 -1
  23. package/esm/telemetry/env.js +6 -1
  24. package/esm/telemetry/init.d.ts +3 -0
  25. package/esm/telemetry/init.d.ts.map +1 -0
  26. package/esm/telemetry/init.js +7 -0
  27. package/esm/telemetry/metrics.d.ts +34 -0
  28. package/esm/telemetry/metrics.d.ts.map +1 -0
  29. package/esm/telemetry/metrics.js +181 -0
  30. package/esm/telemetry/mod.d.ts +3 -0
  31. package/esm/telemetry/mod.d.ts.map +1 -1
  32. package/esm/telemetry/mod.js +2 -0
  33. package/esm/telemetry/runtime.d.ts +2 -0
  34. package/esm/telemetry/runtime.d.ts.map +1 -0
  35. package/esm/telemetry/runtime.js +134 -0
  36. package/esm/telemetry.d.ts +3 -0
  37. package/esm/telemetry.d.ts.map +1 -0
  38. package/esm/telemetry.js +2 -0
  39. package/esm/transfer.d.ts.map +1 -1
  40. package/esm/transfer.js +27 -16
  41. package/esm/trellis.d.ts.map +1 -1
  42. package/esm/trellis.js +460 -56
  43. package/package.json +7 -5
  44. package/script/errors/TrellisError.d.ts +3 -3
  45. package/script/errors/TrellisError.js +3 -3
  46. package/script/server/internal_jobs/job-manager.d.ts.map +1 -1
  47. package/script/server/internal_jobs/job-manager.js +32 -1
  48. package/script/server/runtime.d.ts +3 -0
  49. package/script/server/runtime.d.ts.map +1 -1
  50. package/script/server/service.d.ts +15 -0
  51. package/script/server/service.d.ts.map +1 -1
  52. package/script/server/service.js +8 -0
  53. package/script/server.d.ts.map +1 -1
  54. package/script/server.js +54 -6
  55. package/script/service/deno.d.ts +1 -1
  56. package/script/service/deno.d.ts.map +1 -1
  57. package/script/service/mod.d.ts +1 -1
  58. package/script/service/mod.d.ts.map +1 -1
  59. package/script/service/node.d.ts +1 -1
  60. package/script/service/node.d.ts.map +1 -1
  61. package/script/service/outbox_inbox.d.ts.map +1 -1
  62. package/script/service/outbox_inbox.js +14 -0
  63. package/script/telemetry/core.d.ts.map +1 -1
  64. package/script/telemetry/core.js +1 -1
  65. package/script/telemetry/env.d.ts.map +1 -1
  66. package/script/telemetry/env.js +6 -1
  67. package/script/telemetry/init.d.ts +3 -0
  68. package/script/telemetry/init.d.ts.map +1 -0
  69. package/script/telemetry/init.js +10 -0
  70. package/script/telemetry/metrics.d.ts +34 -0
  71. package/script/telemetry/metrics.d.ts.map +1 -0
  72. package/script/telemetry/metrics.js +186 -0
  73. package/script/telemetry/mod.d.ts +3 -0
  74. package/script/telemetry/mod.d.ts.map +1 -1
  75. package/script/telemetry/mod.js +7 -1
  76. package/script/telemetry/runtime.d.ts +2 -0
  77. package/script/telemetry/runtime.d.ts.map +1 -0
  78. package/script/telemetry/runtime.js +137 -0
  79. package/script/telemetry.d.ts +3 -0
  80. package/script/telemetry.d.ts.map +1 -0
  81. package/script/telemetry.js +18 -0
  82. package/script/transfer.d.ts.map +1 -1
  83. package/script/transfer.js +28 -17
  84. package/script/trellis.d.ts.map +1 -1
  85. package/script/trellis.js +490 -86
  86. package/src/errors/TrellisError.ts +4 -4
  87. package/src/server/internal_jobs/job-manager.ts +35 -5
  88. package/src/server/runtime.ts +4 -0
  89. package/src/server/service.ts +27 -0
  90. package/src/server.ts +66 -11
  91. package/src/service/deno.ts +1 -0
  92. package/src/service/mod.ts +1 -0
  93. package/src/service/node.ts +1 -0
  94. package/src/service/outbox_inbox.ts +14 -0
  95. package/src/telemetry/core.ts +1 -1
  96. package/src/telemetry/env.ts +5 -1
  97. package/src/telemetry/init.ts +8 -0
  98. package/src/telemetry/metrics.ts +294 -0
  99. package/src/telemetry/mod.ts +7 -0
  100. package/src/telemetry/runtime.ts +218 -0
  101. package/src/telemetry.ts +2 -0
  102. package/src/transfer.ts +69 -30
  103. package/src/trellis.ts +487 -88
  104. package/esm/tracing.d.ts +0 -5
  105. package/esm/tracing.d.ts.map +0 -1
  106. package/esm/tracing.js +0 -8
  107. package/script/tracing.d.ts +0 -5
  108. package/script/tracing.d.ts.map +0 -1
  109. package/script/tracing.js +0 -27
  110. package/src/tracing.ts +0 -28
package/esm/trellis.js CHANGED
@@ -14,7 +14,7 @@ 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";
@@ -205,6 +205,12 @@ export function annotateHandlerBoundaryError(cause, context) {
205
205
  error.withTraceId(context.traceId);
206
206
  return error;
207
207
  }
208
+ function recordRuntimeError(error, attributes) {
209
+ recordTrellisError(error, {
210
+ messagingSystem: "nats",
211
+ ...attributes,
212
+ });
213
+ }
208
214
  const DurableOperationSignalSchema = Type.Object({
209
215
  operationId: Type.String(),
210
216
  sequence: Type.Number(),
@@ -935,14 +941,27 @@ export class Trellis {
935
941
  const eventName = event;
936
942
  const ctx = this.api["events"][eventName];
937
943
  if (!ctx) {
938
- return err(new UnexpectedError({
944
+ const error = new UnexpectedError({
939
945
  cause: __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_unknownApiError).call(this, "event", event.toString()),
940
946
  context: { event: event.toString() },
941
- }));
947
+ });
948
+ recordRuntimeError(error, {
949
+ surface: "event",
950
+ direction: "publisher",
951
+ operation: event,
952
+ phase: "prepare",
953
+ });
954
+ return err(error);
942
955
  }
943
956
  const subject = this.template(ctx.subject, data).take();
944
957
  if (isErr(subject)) {
945
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
+ });
946
965
  return subject;
947
966
  }
948
967
  const header = {
@@ -956,7 +975,14 @@ export class Trellis {
956
975
  const msg = encodeSchema(ctx.event, payload).take();
957
976
  if (isErr(msg)) {
958
977
  logger.error({ err: msg.error }, "Failed to encode event.");
959
- 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);
960
986
  }
961
987
  const headers = natsHeaders();
962
988
  headers.set("Nats-Msg-Id", header.id);
@@ -974,7 +1000,17 @@ export class Trellis {
974
1000
  }));
975
1001
  }
976
1002
  catch (cause) {
977
- 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);
978
1014
  }
979
1015
  }
980
1016
  /**
@@ -993,7 +1029,17 @@ export class Trellis {
993
1029
  return ok(undefined);
994
1030
  }
995
1031
  catch (cause) {
996
- 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);
997
1043
  }
998
1044
  })());
999
1045
  }
@@ -1229,10 +1275,22 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1229
1275
  __classPrivateFieldGet(this, _Trellis_log, "f").trace({ method: String(method) }, `Calling ${method.toString()}.`);
1230
1276
  const msg = encodeRuntimeSchema(ctx.input, input).take();
1231
1277
  if (isErr(msg)) {
1278
+ recordRuntimeError(msg.error, {
1279
+ surface: "rpc",
1280
+ direction: "client",
1281
+ operation: method,
1282
+ phase: "request_encoding",
1283
+ });
1232
1284
  return msg;
1233
1285
  }
1234
1286
  const subject = this.template(ctx.subject, input).take();
1235
1287
  if (isErr(subject)) {
1288
+ recordRuntimeError(subject.error, {
1289
+ surface: "rpc",
1290
+ direction: "client",
1291
+ operation: method,
1292
+ phase: "request_encoding",
1293
+ });
1236
1294
  return subject;
1237
1295
  }
1238
1296
  const span = startClientSpan(method, subject);
@@ -1254,30 +1312,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1254
1312
  });
1255
1313
  const response = msgResult.take();
1256
1314
  if (isErr(response)) {
1315
+ recordRuntimeError(response.error, {
1316
+ surface: "rpc",
1317
+ direction: "client",
1318
+ operation: method,
1319
+ phase: "request_send",
1320
+ });
1257
1321
  return response;
1258
1322
  }
1259
1323
  if (response.headers?.get("status") === "error") {
1260
1324
  const json = safeJson(response).take();
1261
1325
  if (isErr(json)) {
1262
- return err(requestFailedTransportError({
1326
+ const error = requestFailedTransportError({
1263
1327
  code: "trellis.request.invalid_response",
1264
1328
  message: "Trellis returned an invalid response.",
1265
1329
  hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
1266
1330
  method,
1267
1331
  subject,
1268
1332
  cause: json.error.cause,
1269
- }));
1333
+ });
1334
+ recordRuntimeError(error, {
1335
+ surface: "rpc",
1336
+ direction: "client",
1337
+ operation: method,
1338
+ phase: "response_decoding",
1339
+ });
1340
+ return err(error);
1270
1341
  }
1271
1342
  const errorData = parse(TrellisErrorDataSchema, json).take();
1272
1343
  if (isErr(errorData)) {
1273
- return err(requestFailedTransportError({
1344
+ const error = requestFailedTransportError({
1274
1345
  code: "trellis.request.invalid_response",
1275
1346
  message: "Trellis returned an invalid response.",
1276
1347
  hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
1277
1348
  method,
1278
1349
  subject,
1279
1350
  cause: errorData.error,
1280
- }));
1351
+ });
1352
+ recordRuntimeError(error, {
1353
+ surface: "rpc",
1354
+ direction: "client",
1355
+ operation: method,
1356
+ phase: "response_decoding",
1357
+ });
1358
+ return err(error);
1281
1359
  }
1282
1360
  const declaredErrorTypes = Array.isArray(ctx.declaredErrorTypes)
1283
1361
  ? ctx.declaredErrorTypes.filter((value) => typeof value === "string")
@@ -1288,25 +1366,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1288
1366
  const reconstructed = reconstructDeclaredRpcError(declaredErrorTypes, runtimeErrors, errorData, json);
1289
1367
  if (reconstructed) {
1290
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
+ });
1291
1375
  return err(reconstructed);
1292
1376
  }
1293
1377
  const remoteError = new RemoteError({ error: errorData });
1294
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
+ });
1295
1385
  return err(remoteError);
1296
1386
  }
1297
1387
  const json = safeJson(response).take();
1298
1388
  if (isErr(json)) {
1299
- return err(requestFailedTransportError({
1389
+ const error = requestFailedTransportError({
1300
1390
  code: "trellis.request.invalid_response",
1301
1391
  message: "Trellis returned an invalid response.",
1302
1392
  hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
1303
1393
  method,
1304
1394
  subject,
1305
1395
  cause: json.error.cause,
1306
- }));
1396
+ });
1397
+ recordRuntimeError(error, {
1398
+ surface: "rpc",
1399
+ direction: "client",
1400
+ operation: method,
1401
+ phase: "response_decoding",
1402
+ });
1403
+ return err(error);
1307
1404
  }
1308
1405
  const outputResult = parseRuntimeSchema(ctx.output, json).take();
1309
1406
  if (isErr(outputResult)) {
1407
+ recordRuntimeError(outputResult.error, {
1408
+ surface: "rpc",
1409
+ direction: "client",
1410
+ operation: method,
1411
+ phase: "response_decoding",
1412
+ });
1310
1413
  return err(outputResult.error);
1311
1414
  }
1312
1415
  return ok(outputResult);
@@ -1335,6 +1438,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1335
1438
  message: unexpected.message,
1336
1439
  });
1337
1440
  span.recordException(unexpected);
1441
+ recordRuntimeError(unexpected, {
1442
+ surface: "rpc",
1443
+ direction: "client",
1444
+ operation: method,
1445
+ phase: "unexpected",
1446
+ });
1338
1447
  return err(unexpected);
1339
1448
  }
1340
1449
  finally {
@@ -1403,11 +1512,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1403
1512
  }, _Trellis_subscribeFeed = function _Trellis_subscribeFeed(feed, descriptor, input, opts) {
1404
1513
  return AsyncResult.from((async () => {
1405
1514
  const payload = encodeRuntimeSchema(descriptor.input, input).take();
1406
- 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
+ });
1407
1522
  return payload;
1523
+ }
1408
1524
  const subject = this.template(descriptor.subject, input).take();
1409
- 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
+ });
1410
1532
  return subject;
1533
+ }
1411
1534
  const authHeaders = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_createProof).call(this, subject, payload);
1412
1535
  const headers = natsHeaders();
1413
1536
  headers.set("session-key", this.auth.sessionKey);
@@ -1427,13 +1550,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1427
1550
  catch (cause) {
1428
1551
  opts?.signal?.removeEventListener("abort", abort);
1429
1552
  sub.unsubscribe();
1430
- return err(createTransportError({
1553
+ const error = createTransportError({
1431
1554
  code: "trellis.feed.subscribe_failed",
1432
1555
  message: "Trellis could not subscribe to the feed.",
1433
1556
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
1434
1557
  cause,
1435
1558
  context: { feed, subject },
1436
- }));
1559
+ });
1560
+ recordRuntimeError(error, {
1561
+ surface: "feed",
1562
+ direction: "client",
1563
+ operation: feed,
1564
+ phase: "request_send",
1565
+ });
1566
+ return err(error);
1437
1567
  }
1438
1568
  let timeoutId;
1439
1569
  let abortHandler;
@@ -1459,7 +1589,7 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1459
1589
  if (firstFrame === "timeout" || firstFrame === "aborted") {
1460
1590
  opts?.signal?.removeEventListener("abort", abort);
1461
1591
  sub.unsubscribe();
1462
- return err(createTransportError({
1592
+ const error = createTransportError({
1463
1593
  code: firstFrame === "timeout"
1464
1594
  ? "trellis.feed.subscribe_timeout"
1465
1595
  : "trellis.feed.subscribe_aborted",
@@ -1470,28 +1600,49 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1470
1600
  ? "Check that the target service is running and has the current deployment digest, then retry."
1471
1601
  : "Retry the subscription if the feed is still needed.",
1472
1602
  context: { feed, subject },
1473
- }));
1603
+ });
1604
+ recordRuntimeError(error, {
1605
+ surface: "feed",
1606
+ direction: "client",
1607
+ operation: feed,
1608
+ phase: "handshake",
1609
+ });
1610
+ return err(error);
1474
1611
  }
1475
1612
  if (firstFrame.done) {
1476
1613
  opts?.signal?.removeEventListener("abort", abort);
1477
1614
  sub.unsubscribe();
1478
- return err(createTransportError({
1615
+ const error = createTransportError({
1479
1616
  code: "trellis.feed.subscribe_closed",
1480
1617
  message: "Trellis closed the feed before acknowledging it.",
1481
1618
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
1482
1619
  context: { feed, subject },
1483
- }));
1620
+ });
1621
+ recordRuntimeError(error, {
1622
+ surface: "feed",
1623
+ direction: "client",
1624
+ operation: feed,
1625
+ phase: "handshake",
1626
+ });
1627
+ return err(error);
1484
1628
  }
1485
1629
  const firstMessage = firstFrame.value;
1486
1630
  if (firstMessage.headers?.get("status") === "error") {
1487
1631
  opts?.signal?.removeEventListener("abort", abort);
1488
1632
  sub.unsubscribe();
1489
- return err(createTransportError({
1633
+ const error = createTransportError({
1490
1634
  code: "trellis.feed.failed",
1491
1635
  message: "Trellis rejected the feed subscription.",
1492
1636
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health and permissions.",
1493
1637
  context: { feed, subject, frame: firstMessage.string() },
1494
- }));
1638
+ });
1639
+ recordRuntimeError(error, {
1640
+ surface: "feed",
1641
+ direction: "client",
1642
+ operation: feed,
1643
+ phase: "remote_error",
1644
+ });
1645
+ return err(error);
1495
1646
  }
1496
1647
  const firstEvent = firstMessage.headers?.get("feed-status") === "ready"
1497
1648
  ? undefined
@@ -1501,19 +1652,40 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1501
1652
  try {
1502
1653
  const parseFeedFrame = (msg) => {
1503
1654
  if (msg.headers?.get("status") === "error") {
1504
- throw createTransportError({
1655
+ const error = createTransportError({
1505
1656
  code: "trellis.feed.failed",
1506
1657
  message: "Trellis stopped the feed.",
1507
1658
  hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
1508
1659
  context: { feed, subject, frame: msg.string() },
1509
1660
  });
1661
+ recordRuntimeError(error, {
1662
+ surface: "feed",
1663
+ direction: "client",
1664
+ operation: feed,
1665
+ phase: "remote_error",
1666
+ });
1667
+ throw error;
1510
1668
  }
1511
1669
  const json = safeJson(msg).take();
1512
- 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
+ });
1513
1677
  throw json.error;
1678
+ }
1514
1679
  const parsed = parseRuntimeSchema(eventSchema, json).take();
1515
- 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
+ });
1516
1687
  throw parsed.error;
1688
+ }
1517
1689
  return parsed;
1518
1690
  };
1519
1691
  if (firstEvent)
@@ -1541,13 +1713,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1541
1713
  await this.nats.flush();
1542
1714
  }
1543
1715
  catch (cause) {
1544
- throw createTransportError({
1716
+ const error = createTransportError({
1545
1717
  code: "trellis.feed.listen_failed",
1546
1718
  message: "Trellis could not listen for feed requests.",
1547
1719
  hint: "Check the service deployment digest and runtime permissions, then restart the service.",
1548
1720
  cause,
1549
1721
  context: { feed, subject },
1550
1722
  });
1723
+ recordRuntimeError(error, {
1724
+ surface: "feed",
1725
+ direction: "server",
1726
+ operation: feed,
1727
+ phase: "listen",
1728
+ });
1729
+ throw error;
1551
1730
  }
1552
1731
  const task = AsyncResult.try(async () => {
1553
1732
  for await (const msg of sub) {
@@ -1568,6 +1747,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1568
1747
  contractDigest: this.contractDigest,
1569
1748
  traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
1570
1749
  });
1750
+ recordRuntimeError(error, {
1751
+ surface: "feed",
1752
+ direction: "server",
1753
+ operation: feed,
1754
+ phase: "handler_throw",
1755
+ });
1571
1756
  __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_respondWithError).call(this, msg, error);
1572
1757
  }
1573
1758
  })();
@@ -1576,11 +1761,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1576
1761
  __classPrivateFieldGet(this, _Trellis_tasks, "f").add(`feed:${feed}`, task);
1577
1762
  }, _Trellis_processFeedMessage = async function _Trellis_processFeedMessage(feed, descriptor, msg, handler) {
1578
1763
  const json = safeJson(msg).take();
1579
- 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
+ });
1580
1771
  return json;
1772
+ }
1581
1773
  const parsed = parseRuntimeSchema(descriptor.input, json).take();
1582
- 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
+ });
1583
1781
  return parsed;
1782
+ }
1584
1783
  const caller = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_authenticateFeedRequest).call(this, {
1585
1784
  feed,
1586
1785
  subject: msg.subject,
@@ -1589,12 +1788,26 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1589
1788
  requiredCapabilities: descriptor.subscribeCapabilities,
1590
1789
  });
1591
1790
  const callerValue = caller.take();
1592
- if (isErr(callerValue))
1791
+ if (isErr(callerValue)) {
1792
+ recordRuntimeError(callerValue.error, {
1793
+ surface: "feed",
1794
+ direction: "server",
1795
+ operation: feed,
1796
+ phase: "auth",
1797
+ });
1593
1798
  return callerValue;
1799
+ }
1594
1800
  if (!msg.reply) {
1595
- return err(new UnexpectedError({
1801
+ const error = new UnexpectedError({
1596
1802
  context: { feed, reason: "missing_reply" },
1597
- }));
1803
+ });
1804
+ recordRuntimeError(error, {
1805
+ surface: "feed",
1806
+ direction: "server",
1807
+ operation: feed,
1808
+ phase: "handshake",
1809
+ });
1810
+ return err(error);
1598
1811
  }
1599
1812
  const readyHeaders = natsHeaders();
1600
1813
  readyHeaders.set("feed-status", "ready");
@@ -1608,15 +1821,44 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1608
1821
  signal: controller.signal,
1609
1822
  emit: (event) => AsyncResult.from((async () => {
1610
1823
  const payload = encodeRuntimeSchema(descriptor.event, event).take();
1611
- 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
+ });
1612
1831
  return payload;
1832
+ }
1613
1833
  if (!msg.reply) {
1614
- return err(new UnexpectedError({
1834
+ const error = new UnexpectedError({
1615
1835
  context: { feed, reason: "missing_reply" },
1616
- }));
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);
1617
1861
  }
1618
- this.nats.publish(msg.reply, payload);
1619
- await this.nats.flush();
1620
1862
  return ok(undefined);
1621
1863
  })()),
1622
1864
  });
@@ -1624,14 +1866,21 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1624
1866
  ? handlerResult.take()
1625
1867
  : handlerResult;
1626
1868
  if (isErr(handlerOutcome)) {
1627
- return err(annotateHandlerBoundaryError(handlerOutcome.error, {
1869
+ const error = annotateHandlerBoundaryError(handlerOutcome.error, {
1628
1870
  feed,
1629
1871
  requestId: msg.headers?.get("request-id"),
1630
1872
  service: this.name,
1631
1873
  contractId: this.contractId,
1632
1874
  contractDigest: this.contractDigest,
1633
1875
  traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
1634
- }));
1876
+ });
1877
+ recordRuntimeError(error, {
1878
+ surface: "feed",
1879
+ direction: "server",
1880
+ operation: feed,
1881
+ phase: "handler_result",
1882
+ });
1883
+ return err(error);
1635
1884
  }
1636
1885
  return ok(undefined);
1637
1886
  }
@@ -1700,6 +1949,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1700
1949
  code: SpanStatusCode.ERROR,
1701
1950
  message: "Failed to parse JSON",
1702
1951
  });
1952
+ recordRuntimeError(jsonData.error, {
1953
+ surface: "rpc",
1954
+ direction: "server",
1955
+ operation: String(method),
1956
+ phase: "parse",
1957
+ });
1703
1958
  return jsonData;
1704
1959
  }
1705
1960
  const parsedInput = parseRuntimeSchema(ctx.input, jsonData).take();
@@ -1708,6 +1963,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1708
1963
  code: SpanStatusCode.ERROR,
1709
1964
  message: "Input validation failed",
1710
1965
  });
1966
+ recordRuntimeError(parsedInput.error, {
1967
+ surface: "rpc",
1968
+ direction: "server",
1969
+ operation: String(method),
1970
+ phase: "input_validation",
1971
+ });
1711
1972
  return parsedInput;
1712
1973
  }
1713
1974
  let caller;
@@ -1733,7 +1994,14 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1733
1994
  code: SpanStatusCode.ERROR,
1734
1995
  message: "Missing session-key",
1735
1996
  });
1736
- 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);
1737
2005
  }
1738
2006
  if (!proof) {
1739
2007
  __classPrivateFieldGet(this, _Trellis_log, "f").warn({ method }, "Missing proof in request");
@@ -1741,11 +2009,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1741
2009
  code: SpanStatusCode.ERROR,
1742
2010
  message: "Missing proof",
1743
2011
  });
1744
- 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);
1745
2020
  }
1746
2021
  const iat = Number(iatHeader);
1747
2022
  if (!Number.isSafeInteger(iat) || !requestId) {
1748
- 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);
1749
2031
  }
1750
2032
  // Verify proof signature locally using the raw request bytes we received.
1751
2033
  const payloadBytes = msg.data ?? new Uint8Array();
@@ -1764,10 +2046,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1764
2046
  code: SpanStatusCode.ERROR,
1765
2047
  message: "Invalid signature",
1766
2048
  });
1767
- return err(new AuthError({
2049
+ const error = new AuthError({
1768
2050
  reason: "invalid_signature",
1769
2051
  context: { sessionKey },
1770
- }));
2052
+ });
2053
+ recordRuntimeError(error, {
2054
+ surface: "rpc",
2055
+ direction: "server",
2056
+ operation: String(method),
2057
+ phase: "auth",
2058
+ });
2059
+ return err(error);
1771
2060
  }
1772
2061
  let auth;
1773
2062
  for (let attempt = 0; attempt < DEFAULT_AUTH_VALIDATE_SESSION_RETRY_ATTEMPTS; attempt++) {
@@ -1793,9 +2082,16 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1793
2082
  await sleep(DEFAULT_AUTH_VALIDATE_SESSION_RETRY_MS * (attempt + 1));
1794
2083
  }
1795
2084
  if (!auth) {
1796
- return err(new UnexpectedError({
2085
+ const error = new UnexpectedError({
1797
2086
  context: { reason: "missing_auth_validate_result" },
1798
- }));
2087
+ });
2088
+ recordRuntimeError(error, {
2089
+ surface: "rpc",
2090
+ direction: "server",
2091
+ operation: String(method),
2092
+ phase: "auth",
2093
+ });
2094
+ return err(error);
1799
2095
  }
1800
2096
  if (auth instanceof Error) {
1801
2097
  __classPrivateFieldGet(this, _Trellis_log, "f").warn({
@@ -1811,22 +2107,42 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1811
2107
  message: "Auth.Requests.Validate failed",
1812
2108
  });
1813
2109
  if (auth instanceof BaseError) {
2110
+ recordRuntimeError(auth, {
2111
+ surface: "rpc",
2112
+ direction: "server",
2113
+ operation: String(method),
2114
+ phase: "auth",
2115
+ });
1814
2116
  return err(auth);
1815
2117
  }
1816
- 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);
1817
2126
  }
1818
2127
  if (!auth.allowed) {
1819
2128
  span.setStatus({
1820
2129
  code: SpanStatusCode.ERROR,
1821
2130
  message: "Insufficient permissions",
1822
2131
  });
1823
- return err(new AuthError({
2132
+ const error = new AuthError({
1824
2133
  reason: "insufficient_permissions",
1825
2134
  context: {
1826
2135
  requiredCapabilities: ctx.callerCapabilities,
1827
2136
  userCapabilities: auth.caller.capabilities,
1828
2137
  },
1829
- }));
2138
+ });
2139
+ recordRuntimeError(error, {
2140
+ surface: "rpc",
2141
+ direction: "server",
2142
+ operation: String(method),
2143
+ phase: "auth",
2144
+ });
2145
+ return err(error);
1830
2146
  }
1831
2147
  if (typeof msg.reply !== "string" ||
1832
2148
  !msg.reply.startsWith(`${auth.inboxPrefix}.`)) {
@@ -1834,10 +2150,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1834
2150
  code: SpanStatusCode.ERROR,
1835
2151
  message: "Reply subject mismatch",
1836
2152
  });
1837
- return err(new AuthError({
2153
+ const error = new AuthError({
1838
2154
  reason: "reply_subject_mismatch",
1839
2155
  context: { expected: auth.inboxPrefix, actual: msg.reply },
1840
- }));
2156
+ });
2157
+ recordRuntimeError(error, {
2158
+ surface: "rpc",
2159
+ direction: "server",
2160
+ operation: String(method),
2161
+ phase: "auth",
2162
+ });
2163
+ return err(error);
1841
2164
  }
1842
2165
  caller = auth.caller;
1843
2166
  }
@@ -1885,6 +2208,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1885
2208
  message: error.message,
1886
2209
  });
1887
2210
  span.recordException(error);
2211
+ recordRuntimeError(error, {
2212
+ surface: "rpc",
2213
+ direction: "server",
2214
+ operation: String(method),
2215
+ phase: "handler_throw",
2216
+ });
1888
2217
  return err(error);
1889
2218
  }
1890
2219
  const handlerResult = handlerResultWrapped.take();
@@ -1910,6 +2239,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1910
2239
  code: SpanStatusCode.ERROR,
1911
2240
  message: error.message,
1912
2241
  });
2242
+ recordRuntimeError(error, {
2243
+ surface: "rpc",
2244
+ direction: "server",
2245
+ operation: String(method),
2246
+ phase: "handler_result",
2247
+ });
1913
2248
  return err(error);
1914
2249
  }
1915
2250
  const encoded = encodeSchema(ctx.output, handlerOutcome).take();
@@ -1918,6 +2253,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1918
2253
  code: SpanStatusCode.ERROR,
1919
2254
  message: "Output encoding failed",
1920
2255
  });
2256
+ recordRuntimeError(encoded.error, {
2257
+ surface: "rpc",
2258
+ direction: "server",
2259
+ operation: String(method),
2260
+ phase: "output_encoding",
2261
+ });
1921
2262
  return encoded;
1922
2263
  }
1923
2264
  span.setStatus({ code: SpanStatusCode.OK });
@@ -1994,6 +2335,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
1994
2335
  const m = parsedEvent.take();
1995
2336
  if (isErr(m)) {
1996
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
+ });
1997
2344
  continue;
1998
2345
  }
1999
2346
  const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
@@ -2005,6 +2352,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2005
2352
  });
2006
2353
  const handlerValue = handlerResult.take();
2007
2354
  if (isErr(handlerValue)) {
2355
+ recordRuntimeError(handlerValue.error, {
2356
+ surface: "event",
2357
+ direction: "consumer",
2358
+ operation: String(event),
2359
+ phase: "handler_result",
2360
+ });
2008
2361
  __classPrivateFieldGet(this, _Trellis_log, "f").error({
2009
2362
  error: handlerValue.error.toSerializable(),
2010
2363
  event,
@@ -2204,6 +2557,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2204
2557
  const eventPayload = parsedEvent.take();
2205
2558
  if (isErr(eventPayload)) {
2206
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
+ });
2207
2566
  msg.term();
2208
2567
  failed = true;
2209
2568
  break;
@@ -2218,6 +2577,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2218
2577
  });
2219
2578
  const handlerValue = handlerResult.take();
2220
2579
  if (isErr(handlerValue)) {
2580
+ recordRuntimeError(handlerValue.error, {
2581
+ surface: "event",
2582
+ direction: "consumer",
2583
+ operation: String(registration.event),
2584
+ phase: "handler_result",
2585
+ });
2221
2586
  __classPrivateFieldGet(this, _Trellis_log, "f").error({
2222
2587
  error: handlerValue.error.toSerializable(),
2223
2588
  event: registration.event,
@@ -2315,6 +2680,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2315
2680
  code: SpanStatusCode.ERROR,
2316
2681
  message: response.error.message,
2317
2682
  });
2683
+ recordRuntimeError(response.error, {
2684
+ surface: "operation",
2685
+ direction: "client",
2686
+ operation: "requestJson",
2687
+ phase: "request_send",
2688
+ });
2318
2689
  return response;
2319
2690
  }
2320
2691
  const json = safeJson(response).take();
@@ -2330,6 +2701,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2330
2701
  code: SpanStatusCode.ERROR,
2331
2702
  message: error.message,
2332
2703
  });
2704
+ recordRuntimeError(error, {
2705
+ surface: "operation",
2706
+ direction: "client",
2707
+ operation: "requestJson",
2708
+ phase: "response_decoding",
2709
+ });
2333
2710
  return err(error);
2334
2711
  }
2335
2712
  span.setStatus({ code: SpanStatusCode.OK });
@@ -2342,6 +2719,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2342
2719
  message: error.message,
2343
2720
  });
2344
2721
  span.recordException(error);
2722
+ recordRuntimeError(error, {
2723
+ surface: "operation",
2724
+ direction: "client",
2725
+ operation: "requestJson",
2726
+ phase: "unexpected",
2727
+ });
2345
2728
  return err(error);
2346
2729
  }
2347
2730
  finally {
@@ -2369,35 +2752,56 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
2369
2752
  }
2370
2753
  catch (cause) {
2371
2754
  sub.unsubscribe();
2372
- return err(createTransportError({
2755
+ const error = createTransportError({
2373
2756
  code: "trellis.watch.failed",
2374
2757
  message: "Trellis could not start the operation watch.",
2375
2758
  hint: "Retry watching the operation. If it keeps failing, reconnect to Trellis and try again.",
2376
2759
  cause,
2377
2760
  context: { subject },
2378
- }));
2761
+ });
2762
+ recordRuntimeError(error, {
2763
+ surface: "operation",
2764
+ direction: "client",
2765
+ operation: "watchJson",
2766
+ phase: "request_send",
2767
+ });
2768
+ return err(error);
2379
2769
  }
2380
2770
  return ok((async function* () {
2381
2771
  try {
2382
2772
  for await (const msg of sub) {
2383
2773
  if (msg.headers?.get("status") === "error") {
2384
- yield err(createTransportError({
2774
+ const error = createTransportError({
2385
2775
  code: "trellis.watch.failed",
2386
2776
  message: "Trellis stopped the operation watch.",
2387
2777
  hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
2388
2778
  context: { subject, frame: msg.string() },
2389
- }));
2779
+ });
2780
+ recordRuntimeError(error, {
2781
+ surface: "operation",
2782
+ direction: "client",
2783
+ operation: "watchJson",
2784
+ phase: "remote_error",
2785
+ });
2786
+ yield err(error);
2390
2787
  continue;
2391
2788
  }
2392
2789
  const json = safeJson(msg).take();
2393
2790
  if (isErr(json)) {
2394
- yield err(createTransportError({
2791
+ const error = createTransportError({
2395
2792
  code: "trellis.watch.invalid_response",
2396
2793
  message: "Trellis returned an invalid watch update.",
2397
2794
  hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
2398
2795
  cause: json.error.cause,
2399
2796
  context: { subject },
2400
- }));
2797
+ });
2798
+ recordRuntimeError(error, {
2799
+ surface: "operation",
2800
+ direction: "client",
2801
+ operation: "watchJson",
2802
+ phase: "response_decoding",
2803
+ });
2804
+ yield err(error);
2401
2805
  continue;
2402
2806
  }
2403
2807
  yield ok(json);