@qlever-llc/trellis 0.10.9 → 0.10.11

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 (220) 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/generated-sdk/auth/api.d.ts.map +1 -1
  8. package/esm/generated-sdk/auth/client.d.ts.map +1 -1
  9. package/esm/generated-sdk/auth/contract.d.ts.map +1 -1
  10. package/esm/generated-sdk/auth/contract.js +9590 -7
  11. package/esm/generated-sdk/auth/mod.d.ts +3 -3
  12. package/esm/generated-sdk/auth/mod.d.ts.map +1 -1
  13. package/esm/generated-sdk/auth/mod.js +1 -1
  14. package/esm/generated-sdk/auth/owned_api.d.ts.map +1 -1
  15. package/esm/generated-sdk/auth/owned_api.js +305 -62
  16. package/esm/generated-sdk/auth/schemas.d.ts.map +1 -1
  17. package/esm/generated-sdk/auth/schemas.js +6492 -139
  18. package/esm/generated-sdk/auth/types.d.ts +254 -254
  19. package/esm/generated-sdk/auth/types.d.ts.map +1 -1
  20. package/esm/generated-sdk/health/api.d.ts.map +1 -1
  21. package/esm/generated-sdk/health/client.d.ts.map +1 -1
  22. package/esm/generated-sdk/health/contract.d.ts.map +1 -1
  23. package/esm/generated-sdk/health/contract.js +117 -7
  24. package/esm/generated-sdk/health/mod.d.ts +2 -2
  25. package/esm/generated-sdk/health/mod.d.ts.map +1 -1
  26. package/esm/generated-sdk/health/mod.js +1 -1
  27. package/esm/generated-sdk/health/owned_api.d.ts.map +1 -1
  28. package/esm/generated-sdk/health/schemas.d.ts.map +1 -1
  29. package/esm/generated-sdk/health/schemas.js +79 -1
  30. package/esm/generated-sdk/health/types.d.ts +4 -4
  31. package/esm/generated-sdk/health/types.d.ts.map +1 -1
  32. package/esm/generated-sdk/jobs/api.d.ts.map +1 -1
  33. package/esm/generated-sdk/jobs/api.js +12 -4
  34. package/esm/generated-sdk/jobs/client.d.ts.map +1 -1
  35. package/esm/generated-sdk/jobs/contract.d.ts.map +1 -1
  36. package/esm/generated-sdk/jobs/contract.js +1080 -7
  37. package/esm/generated-sdk/jobs/mod.d.ts +2 -2
  38. package/esm/generated-sdk/jobs/mod.d.ts.map +1 -1
  39. package/esm/generated-sdk/jobs/mod.js +1 -1
  40. package/esm/generated-sdk/jobs/owned_api.d.ts.map +1 -1
  41. package/esm/generated-sdk/jobs/owned_api.js +26 -6
  42. package/esm/generated-sdk/jobs/schemas.d.ts.map +1 -1
  43. package/esm/generated-sdk/jobs/schemas.js +768 -18
  44. package/esm/generated-sdk/jobs/types.d.ts +14 -14
  45. package/esm/generated-sdk/jobs/types.d.ts.map +1 -1
  46. package/esm/generated-sdk/state/api.d.ts.map +1 -1
  47. package/esm/generated-sdk/state/api.js +3 -1
  48. package/esm/generated-sdk/state/client.d.ts.map +1 -1
  49. package/esm/generated-sdk/state/contract.d.ts.map +1 -1
  50. package/esm/generated-sdk/state/contract.js +638 -7
  51. package/esm/generated-sdk/state/mod.d.ts +2 -2
  52. package/esm/generated-sdk/state/mod.d.ts.map +1 -1
  53. package/esm/generated-sdk/state/mod.js +1 -1
  54. package/esm/generated-sdk/state/owned_api.d.ts.map +1 -1
  55. package/esm/generated-sdk/state/owned_api.js +36 -8
  56. package/esm/generated-sdk/state/schemas.d.ts.map +1 -1
  57. package/esm/generated-sdk/state/schemas.js +441 -14
  58. package/esm/generated-sdk/state/types.d.ts +15 -15
  59. package/esm/generated-sdk/state/types.d.ts.map +1 -1
  60. package/esm/generated-sdk/trellis-core/api.d.ts.map +1 -1
  61. package/esm/generated-sdk/trellis-core/client.d.ts.map +1 -1
  62. package/esm/generated-sdk/trellis-core/contract.d.ts.map +1 -1
  63. package/esm/generated-sdk/trellis-core/contract.js +744 -7
  64. package/esm/generated-sdk/trellis-core/mod.d.ts +2 -2
  65. package/esm/generated-sdk/trellis-core/mod.d.ts.map +1 -1
  66. package/esm/generated-sdk/trellis-core/mod.js +1 -1
  67. package/esm/generated-sdk/trellis-core/owned_api.d.ts.map +1 -1
  68. package/esm/generated-sdk/trellis-core/owned_api.js +1 -1
  69. package/esm/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
  70. package/esm/generated-sdk/trellis-core/schemas.js +626 -8
  71. package/esm/generated-sdk/trellis-core/types.d.ts +14 -14
  72. package/esm/generated-sdk/trellis-core/types.d.ts.map +1 -1
  73. package/esm/server/health.d.ts.map +1 -1
  74. package/esm/server/health.js +34 -3
  75. package/esm/server/service.d.ts +35 -13
  76. package/esm/server/service.d.ts.map +1 -1
  77. package/esm/server/service.js +33 -3
  78. package/esm/server.d.ts.map +1 -1
  79. package/esm/server.js +49 -8
  80. package/esm/service/deno.d.ts +1 -1
  81. package/esm/service/deno.d.ts.map +1 -1
  82. package/esm/service/mod.d.ts +1 -1
  83. package/esm/service/mod.d.ts.map +1 -1
  84. package/esm/service/node.d.ts +1 -1
  85. package/esm/service/node.d.ts.map +1 -1
  86. package/esm/trellis.d.ts +28 -4
  87. package/esm/trellis.d.ts.map +1 -1
  88. package/esm/trellis.js +117 -26
  89. package/package.json +2 -2
  90. package/script/client.d.ts +2 -0
  91. package/script/client.d.ts.map +1 -1
  92. package/script/client.js +2 -0
  93. package/script/client_connect.d.ts +3 -2
  94. package/script/client_connect.d.ts.map +1 -1
  95. package/script/client_connect.js +4 -1
  96. package/script/generated-sdk/auth/api.d.ts.map +1 -1
  97. package/script/generated-sdk/auth/client.d.ts.map +1 -1
  98. package/script/generated-sdk/auth/contract.d.ts.map +1 -1
  99. package/script/generated-sdk/auth/contract.js +9590 -7
  100. package/script/generated-sdk/auth/mod.d.ts +3 -3
  101. package/script/generated-sdk/auth/mod.d.ts.map +1 -1
  102. package/script/generated-sdk/auth/mod.js +2 -2
  103. package/script/generated-sdk/auth/owned_api.d.ts.map +1 -1
  104. package/script/generated-sdk/auth/owned_api.js +304 -61
  105. package/script/generated-sdk/auth/schemas.d.ts.map +1 -1
  106. package/script/generated-sdk/auth/schemas.js +6492 -139
  107. package/script/generated-sdk/auth/types.d.ts +254 -254
  108. package/script/generated-sdk/auth/types.d.ts.map +1 -1
  109. package/script/generated-sdk/health/api.d.ts.map +1 -1
  110. package/script/generated-sdk/health/client.d.ts.map +1 -1
  111. package/script/generated-sdk/health/contract.d.ts.map +1 -1
  112. package/script/generated-sdk/health/contract.js +117 -7
  113. package/script/generated-sdk/health/mod.d.ts +2 -2
  114. package/script/generated-sdk/health/mod.d.ts.map +1 -1
  115. package/script/generated-sdk/health/mod.js +2 -2
  116. package/script/generated-sdk/health/owned_api.d.ts.map +1 -1
  117. package/script/generated-sdk/health/schemas.d.ts.map +1 -1
  118. package/script/generated-sdk/health/schemas.js +79 -1
  119. package/script/generated-sdk/health/types.d.ts +4 -4
  120. package/script/generated-sdk/health/types.d.ts.map +1 -1
  121. package/script/generated-sdk/jobs/api.d.ts.map +1 -1
  122. package/script/generated-sdk/jobs/api.js +12 -4
  123. package/script/generated-sdk/jobs/client.d.ts.map +1 -1
  124. package/script/generated-sdk/jobs/contract.d.ts.map +1 -1
  125. package/script/generated-sdk/jobs/contract.js +1080 -7
  126. package/script/generated-sdk/jobs/mod.d.ts +2 -2
  127. package/script/generated-sdk/jobs/mod.d.ts.map +1 -1
  128. package/script/generated-sdk/jobs/mod.js +2 -2
  129. package/script/generated-sdk/jobs/owned_api.d.ts.map +1 -1
  130. package/script/generated-sdk/jobs/owned_api.js +25 -5
  131. package/script/generated-sdk/jobs/schemas.d.ts.map +1 -1
  132. package/script/generated-sdk/jobs/schemas.js +768 -18
  133. package/script/generated-sdk/jobs/types.d.ts +14 -14
  134. package/script/generated-sdk/jobs/types.d.ts.map +1 -1
  135. package/script/generated-sdk/state/api.d.ts.map +1 -1
  136. package/script/generated-sdk/state/api.js +3 -1
  137. package/script/generated-sdk/state/client.d.ts.map +1 -1
  138. package/script/generated-sdk/state/contract.d.ts.map +1 -1
  139. package/script/generated-sdk/state/contract.js +638 -7
  140. package/script/generated-sdk/state/mod.d.ts +2 -2
  141. package/script/generated-sdk/state/mod.d.ts.map +1 -1
  142. package/script/generated-sdk/state/mod.js +2 -2
  143. package/script/generated-sdk/state/owned_api.d.ts.map +1 -1
  144. package/script/generated-sdk/state/owned_api.js +35 -7
  145. package/script/generated-sdk/state/schemas.d.ts.map +1 -1
  146. package/script/generated-sdk/state/schemas.js +441 -14
  147. package/script/generated-sdk/state/types.d.ts +15 -15
  148. package/script/generated-sdk/state/types.d.ts.map +1 -1
  149. package/script/generated-sdk/trellis-core/api.d.ts.map +1 -1
  150. package/script/generated-sdk/trellis-core/client.d.ts.map +1 -1
  151. package/script/generated-sdk/trellis-core/contract.d.ts.map +1 -1
  152. package/script/generated-sdk/trellis-core/contract.js +744 -7
  153. package/script/generated-sdk/trellis-core/mod.d.ts +2 -2
  154. package/script/generated-sdk/trellis-core/mod.d.ts.map +1 -1
  155. package/script/generated-sdk/trellis-core/mod.js +2 -2
  156. package/script/generated-sdk/trellis-core/owned_api.d.ts.map +1 -1
  157. package/script/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
  158. package/script/generated-sdk/trellis-core/schemas.js +626 -8
  159. package/script/generated-sdk/trellis-core/types.d.ts +14 -14
  160. package/script/generated-sdk/trellis-core/types.d.ts.map +1 -1
  161. package/script/server/health.d.ts.map +1 -1
  162. package/script/server/health.js +34 -3
  163. package/script/server/service.d.ts +35 -13
  164. package/script/server/service.d.ts.map +1 -1
  165. package/script/server/service.js +32 -2
  166. package/script/server.d.ts.map +1 -1
  167. package/script/server.js +48 -7
  168. package/script/service/deno.d.ts +1 -1
  169. package/script/service/deno.d.ts.map +1 -1
  170. package/script/service/mod.d.ts +1 -1
  171. package/script/service/mod.d.ts.map +1 -1
  172. package/script/service/node.d.ts +1 -1
  173. package/script/service/node.d.ts.map +1 -1
  174. package/script/trellis.d.ts +28 -4
  175. package/script/trellis.d.ts.map +1 -1
  176. package/script/trellis.js +118 -26
  177. package/src/client.ts +4 -0
  178. package/src/client_connect.ts +11 -9
  179. package/src/sdk/_generated/auth/api.ts +10 -21
  180. package/src/sdk/_generated/auth/client.ts +1178 -160
  181. package/src/sdk/_generated/auth/contract.ts +9637 -13
  182. package/src/sdk/_generated/auth/mod.ts +22 -3
  183. package/src/sdk/_generated/auth/owned_api.ts +897 -264
  184. package/src/sdk/_generated/auth/schemas.ts +6630 -278
  185. package/src/sdk/_generated/auth/types.ts +2950 -359
  186. package/src/sdk/_generated/core/api.ts +10 -21
  187. package/src/sdk/_generated/core/client.ts +105 -12
  188. package/src/sdk/_generated/core/contract.ts +774 -13
  189. package/src/sdk/_generated/core/mod.ts +17 -2
  190. package/src/sdk/_generated/core/owned_api.ts +40 -24
  191. package/src/sdk/_generated/core/schemas.ts +626 -9
  192. package/src/sdk/_generated/core/types.ts +233 -18
  193. package/src/sdk/_generated/health/api.ts +10 -21
  194. package/src/sdk/_generated/health/client.ts +104 -8
  195. package/src/sdk/_generated/health/contract.ts +144 -13
  196. package/src/sdk/_generated/health/mod.ts +17 -2
  197. package/src/sdk/_generated/health/owned_api.ts +4 -9
  198. package/src/sdk/_generated/health/schemas.ts +79 -2
  199. package/src/sdk/_generated/health/types.ts +31 -4
  200. package/src/sdk/_generated/jobs/api.ts +20 -18
  201. package/src/sdk/_generated/jobs/client.ts +199 -28
  202. package/src/sdk/_generated/jobs/contract.ts +1119 -13
  203. package/src/sdk/_generated/jobs/mod.ts +17 -2
  204. package/src/sdk/_generated/jobs/owned_api.ts +64 -27
  205. package/src/sdk/_generated/jobs/schemas.ts +776 -20
  206. package/src/sdk/_generated/jobs/types.ts +407 -34
  207. package/src/sdk/_generated/state/api.ts +11 -18
  208. package/src/sdk/_generated/state/client.ts +169 -22
  209. package/src/sdk/_generated/state/contract.ts +670 -13
  210. package/src/sdk/_generated/state/mod.ts +17 -2
  211. package/src/sdk/_generated/state/owned_api.ts +65 -25
  212. package/src/sdk/_generated/state/schemas.ts +441 -15
  213. package/src/sdk/_generated/state/types.ts +229 -26
  214. package/src/server/health.ts +41 -3
  215. package/src/server/service.ts +140 -18
  216. package/src/server.ts +62 -7
  217. package/src/service/deno.ts +4 -0
  218. package/src/service/mod.ts +4 -0
  219. package/src/service/node.ts +4 -0
  220. package/src/trellis.ts +167 -55
package/src/server.ts CHANGED
@@ -27,6 +27,7 @@ import type { LoggerLike } from "./globals.js";
27
27
  import { serverLogger } from "./server_logger.js";
28
28
  import {
29
29
  type AcceptedOperation,
30
+ annotateHandlerBoundaryError,
30
31
  type AnyTrellisAPI,
31
32
  type AuthRequestsValidateResponse,
32
33
  base64urlDecode,
@@ -199,6 +200,26 @@ function asOptionalStringRecordPointerValue(
199
200
  return ok(Object.fromEntries(entries) as Record<string, string>);
200
201
  }
201
202
 
203
+ function traceIdFromTraceparent(
204
+ traceparent: string | undefined,
205
+ ): string | undefined {
206
+ const [version, traceId, parentId, flags, extra] = traceparent?.split("-") ??
207
+ [];
208
+ if (
209
+ extra !== undefined ||
210
+ !/^[0-9a-f]{2}$/u.test(version ?? "") ||
211
+ version === "ff" ||
212
+ !/^[0-9a-f]{32}$/u.test(traceId ?? "") ||
213
+ traceId === "00000000000000000000000000000000" ||
214
+ !/^[0-9a-f]{16}$/u.test(parentId ?? "") ||
215
+ parentId === "0000000000000000" ||
216
+ !/^[0-9a-f]{2}$/u.test(flags ?? "")
217
+ ) {
218
+ return undefined;
219
+ }
220
+ return traceId;
221
+ }
222
+
202
223
  export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
203
224
  #version?: string;
204
225
  #log: LoggerLike;
@@ -243,7 +264,7 @@ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
243
264
  }),
244
265
  fail: (operationId, error) =>
245
266
  this.#applyOperationUpdate(operationId, "failed", {
246
- patch: { error: { type: error.name, message: error.message } },
267
+ patch: { error: error.toSerializable() },
247
268
  event: { type: "failed" },
248
269
  }),
249
270
  cancel: (operationId) =>
@@ -506,7 +527,7 @@ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
506
527
  }),
507
528
  fail: (error: BaseError) =>
508
529
  this.#applyControlledOperationUpdate(runtime, ctx, "failed", {
509
- patch: { error: { type: error.name, message: error.message } },
530
+ patch: { error: error.toSerializable() },
510
531
  event: { type: "failed" },
511
532
  }),
512
533
  cancel: () => {
@@ -1210,7 +1231,10 @@ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
1210
1231
  runtime.waiters.clear();
1211
1232
  };
1212
1233
 
1213
- const makeOperation = (runtime: RuntimeOperationRecord) => {
1234
+ const makeOperation = (
1235
+ runtime: RuntimeOperationRecord,
1236
+ context: { requestId?: string; traceId?: string },
1237
+ ) => {
1214
1238
  const ensureActive = () => {
1215
1239
  if (runtime.terminal) {
1216
1240
  return err(
@@ -1291,11 +1315,19 @@ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
1291
1315
  AsyncResult.from((async () => {
1292
1316
  const active = ensureActive();
1293
1317
  if (active) return active;
1318
+ const annotatedError = annotateHandlerBoundaryError(error, {
1319
+ operation: String(operation),
1320
+ requestId: context.requestId,
1321
+ service: this.name,
1322
+ contractId: this.contractId,
1323
+ contractDigest: this.contractDigest,
1324
+ traceId: context.traceId,
1325
+ });
1294
1326
  const snapshot = buildRuntimeOperationSnapshot(
1295
1327
  runtime,
1296
1328
  "failed",
1297
1329
  {
1298
- error: { type: error.name, message: error.message },
1330
+ error: annotatedError.toSerializable(),
1299
1331
  completedAt: now(),
1300
1332
  },
1301
1333
  );
@@ -1636,7 +1668,13 @@ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
1636
1668
  msg.respond(JSON.stringify(accepted));
1637
1669
 
1638
1670
  void (async () => {
1639
- const op = makeOperation(runtime);
1671
+ const operationContext = {
1672
+ requestId: msg.headers?.get("request-id"),
1673
+ traceId: traceIdFromTraceparent(
1674
+ msg.headers?.get("traceparent"),
1675
+ ),
1676
+ };
1677
+ const op = makeOperation(runtime, operationContext);
1640
1678
  try {
1641
1679
  const handlerResult: unknown = await handler(
1642
1680
  transferSession
@@ -1656,7 +1694,17 @@ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
1656
1694
  ? handlerResult.take()
1657
1695
  : handlerResult;
1658
1696
  if (isErr(handlerOutcome)) {
1659
- await op.fail(handlerOutcome.error);
1697
+ await op.fail(annotateHandlerBoundaryError(
1698
+ handlerOutcome.error,
1699
+ {
1700
+ operation: String(operation),
1701
+ requestId: operationContext.requestId,
1702
+ service: this.name,
1703
+ contractId: this.contractId,
1704
+ contractDigest: this.contractDigest,
1705
+ traceId: operationContext.traceId,
1706
+ },
1707
+ ));
1660
1708
  return;
1661
1709
  }
1662
1710
 
@@ -1676,7 +1724,14 @@ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
1676
1724
  await op.complete(handlerOutcome);
1677
1725
  }
1678
1726
  } catch (cause) {
1679
- await op.fail(new UnexpectedError({ cause }));
1727
+ await op.fail(annotateHandlerBoundaryError(cause, {
1728
+ operation: String(operation),
1729
+ requestId: operationContext.requestId,
1730
+ service: this.name,
1731
+ contractId: this.contractId,
1732
+ contractDigest: this.contractDigest,
1733
+ traceId: operationContext.traceId,
1734
+ }));
1680
1735
  }
1681
1736
  })();
1682
1737
  }
@@ -1,6 +1,9 @@
1
1
  import "../_dnt.polyfills.js";
2
2
  export {
3
3
  type BoundTrellisService,
4
+ type FeedHandler,
5
+ type HealthCheckHandler,
6
+ type HealthInfoHandler,
4
7
  type JobArgs,
5
8
  type JobHandler,
6
9
  type JobQueue,
@@ -10,6 +13,7 @@ export {
10
13
  type OperationRegistration,
11
14
  type RpcHandler,
12
15
  type ServiceContract,
16
+ type ServiceEventHandler,
13
17
  type Trellis,
14
18
  TrellisService,
15
19
  type TrellisServiceConnectOpts,
@@ -37,6 +37,9 @@ export {
37
37
  } from "../server/health_schemas.js";
38
38
  export {
39
39
  type BoundTrellisService,
40
+ type FeedHandler,
41
+ type HealthCheckHandler,
42
+ type HealthInfoHandler,
40
43
  type JobArgs,
41
44
  type JobHandler,
42
45
  type JobQueue,
@@ -46,6 +49,7 @@ export {
46
49
  type OperationRegistration,
47
50
  type RpcHandler,
48
51
  type ServiceContract,
52
+ type ServiceEventHandler,
49
53
  StoreHandle,
50
54
  type Trellis,
51
55
  TrellisService,
@@ -1,6 +1,9 @@
1
1
  import "../_dnt.polyfills.js";
2
2
  export {
3
3
  type BoundTrellisService,
4
+ type FeedHandler,
5
+ type HealthCheckHandler,
6
+ type HealthInfoHandler,
4
7
  type JobArgs,
5
8
  type JobHandler,
6
9
  type JobQueue,
@@ -10,6 +13,7 @@ export {
10
13
  type OperationRegistration,
11
14
  type RpcHandler,
12
15
  type ServiceContract,
16
+ type ServiceEventHandler,
13
17
  type Trellis,
14
18
  TrellisService,
15
19
  type TrellisServiceConnectOpts,
package/src/trellis.ts CHANGED
@@ -732,6 +732,55 @@ export function isResultLike(
732
732
  ): value is Result<unknown, BaseError> {
733
733
  return value instanceof Result;
734
734
  }
735
+
736
+ type SerializableRuntimeError = {
737
+ id?: string;
738
+ type: string;
739
+ message: string;
740
+ context?: Record<string, unknown>;
741
+ traceId?: string;
742
+ } & Record<string, unknown>;
743
+
744
+ export type HandlerErrorAnnotationContext = {
745
+ method?: string;
746
+ event?: string;
747
+ feed?: string;
748
+ operation?: string;
749
+ jobType?: string;
750
+ requestId?: string;
751
+ service?: string;
752
+ contractId?: string;
753
+ contractDigest?: string;
754
+ traceId?: string;
755
+ };
756
+
757
+ function compactHandlerErrorContext(
758
+ context: HandlerErrorAnnotationContext,
759
+ ): Record<string, unknown> {
760
+ return Object.fromEntries(
761
+ Object.entries(context).filter(([key, value]) =>
762
+ key !== "traceId" && value !== undefined
763
+ ),
764
+ );
765
+ }
766
+
767
+ function sanitizeHandlerErrorContext(error: BaseError): void {
768
+ delete error.getContext().subject;
769
+ }
770
+
771
+ export function annotateHandlerBoundaryError(
772
+ cause: unknown,
773
+ context: HandlerErrorAnnotationContext,
774
+ ): BaseError {
775
+ const error = cause instanceof BaseError && !(cause instanceof RemoteError)
776
+ ? cause
777
+ : new UnexpectedError({ cause });
778
+ sanitizeHandlerErrorContext(error);
779
+ error.withContext(compactHandlerErrorContext(context));
780
+ error.withTraceId(context.traceId);
781
+ return error;
782
+ }
783
+
735
784
  export type RuntimeOperationDesc = {
736
785
  subject: string;
737
786
  input: unknown;
@@ -788,10 +837,7 @@ export type RuntimeOperationSnapshot = {
788
837
  progress?: unknown;
789
838
  transfer?: RuntimeOperationTransferProgress;
790
839
  output?: unknown;
791
- error?: {
792
- type: string;
793
- message: string;
794
- };
840
+ error?: SerializableRuntimeError;
795
841
  };
796
842
 
797
843
  export type RuntimeOperationRecord = {
@@ -848,8 +894,11 @@ const DurableOperationSnapshotSchema = Type.Object({
848
894
  })),
849
895
  output: Type.Optional(Type.Any()),
850
896
  error: Type.Optional(Type.Object({
897
+ id: Type.Optional(Type.String()),
851
898
  type: Type.String(),
852
899
  message: Type.String(),
900
+ context: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
901
+ traceId: Type.Optional(Type.String()),
853
902
  })),
854
903
  });
855
904
 
@@ -990,6 +1039,8 @@ export type TrellisOpts<TA extends AnyTrellisAPI> = {
990
1039
  state?: RuntimeStateStores;
991
1040
  connection?: TrellisConnection;
992
1041
  onSessionNotFound?: () => MaybePromise<void>;
1042
+ contractId?: string;
1043
+ contractDigest?: string;
993
1044
  };
994
1045
 
995
1046
  export type RequestOpts = {
@@ -1960,6 +2011,8 @@ export class Trellis<
1960
2011
  readonly handle: { readonly rpc: ActiveRpcHandleFacade<TA, TRequests> };
1961
2012
  /** Framework-neutral lifecycle handle for this Trellis runtime connection. */
1962
2013
  readonly connection: TrellisConnection;
2014
+ readonly contractId?: string;
2015
+ readonly contractDigest?: string;
1963
2016
 
1964
2017
  protected nats: NatsConnection;
1965
2018
  protected js: JetStreamClient;
@@ -1991,6 +2044,8 @@ export class Trellis<
1991
2044
  this.#log = (opts?.log ?? logger).child({ lib: "trellis" });
1992
2045
  this.timeout = opts?.timeout ?? 3000;
1993
2046
  this.stream = opts?.stream ?? "trellis";
2047
+ this.contractId = opts?.contractId;
2048
+ this.contractDigest = opts?.contractDigest;
1994
2049
  this.#hasExplicitApi = api !== undefined;
1995
2050
  this.#noResponderMaxRetries = opts?.noResponderRetry?.maxAttempts ??
1996
2051
  DEFAULT_NO_RESPONDER_MAX_RETRIES;
@@ -2860,9 +2915,14 @@ export class Trellis<
2860
2915
  this.#respondWithError(msg, value.error);
2861
2916
  }
2862
2917
  } catch (cause) {
2863
- const error = cause instanceof BaseError
2864
- ? cause
2865
- : new UnexpectedError({ cause });
2918
+ const error = annotateHandlerBoundaryError(cause, {
2919
+ feed,
2920
+ requestId: msg.headers?.get("request-id"),
2921
+ service: this.name,
2922
+ contractId: this.contractId,
2923
+ contractDigest: this.contractDigest,
2924
+ traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
2925
+ });
2866
2926
  this.#respondWithError(msg, error);
2867
2927
  }
2868
2928
  })();
@@ -2907,7 +2967,7 @@ export class Trellis<
2907
2967
 
2908
2968
  const controller = new AbortController();
2909
2969
  try {
2910
- await handler({
2970
+ const handlerResult = await handler({
2911
2971
  input: parsed as TInput,
2912
2972
  caller: callerValue,
2913
2973
  signal: controller.signal,
@@ -2927,6 +2987,19 @@ export class Trellis<
2927
2987
  return ok(undefined);
2928
2988
  })()),
2929
2989
  });
2990
+ const handlerOutcome = isResultLike(handlerResult)
2991
+ ? handlerResult.take()
2992
+ : handlerResult;
2993
+ if (isErr(handlerOutcome)) {
2994
+ return err(annotateHandlerBoundaryError(handlerOutcome.error, {
2995
+ feed,
2996
+ requestId: msg.headers?.get("request-id"),
2997
+ service: this.name,
2998
+ contractId: this.contractId,
2999
+ contractDigest: this.contractDigest,
3000
+ traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
3001
+ }));
3002
+ }
2930
3003
  return ok(undefined);
2931
3004
  } finally {
2932
3005
  controller.abort();
@@ -3350,7 +3423,17 @@ export class Trellis<
3350
3423
  );
3351
3424
 
3352
3425
  if (handlerResultWrapped.isErr()) {
3353
- const error = handlerResultWrapped.error.withContext({ method });
3426
+ const error = annotateHandlerBoundaryError(
3427
+ handlerResultWrapped.error,
3428
+ {
3429
+ method: String(method),
3430
+ requestId: msg.headers?.get("request-id"),
3431
+ service: this.name,
3432
+ contractId: this.contractId,
3433
+ contractDigest: this.contractDigest,
3434
+ traceId: activeTraceId(span) ?? incomingTraceId,
3435
+ },
3436
+ );
3354
3437
  this.#log.error(
3355
3438
  {
3356
3439
  method,
@@ -3374,12 +3457,14 @@ export class Trellis<
3374
3457
  };
3375
3458
  const handlerOutcome = handlerResult.take();
3376
3459
  if (isErr(handlerOutcome)) {
3377
- const handlerError = handlerOutcome.error;
3378
-
3379
- const error = handlerError instanceof BaseError &&
3380
- !(handlerError instanceof RemoteError)
3381
- ? handlerError
3382
- : new UnexpectedError({ cause: handlerError });
3460
+ const error = annotateHandlerBoundaryError(handlerOutcome.error, {
3461
+ method: String(method),
3462
+ requestId: msg.headers?.get("request-id"),
3463
+ service: this.name,
3464
+ contractId: this.contractId,
3465
+ contractDigest: this.contractDigest,
3466
+ traceId: activeTraceId(span) ?? incomingTraceId,
3467
+ });
3383
3468
 
3384
3469
  this.#log.error(
3385
3470
  {
@@ -3713,21 +3798,18 @@ export class Trellis<
3713
3798
  continue;
3714
3799
  }
3715
3800
 
3716
- const handlerResult = await AsyncResult.lift(
3717
- fn(
3718
- m as EventOf<TA, EventsOf<TA>>,
3719
- createEventListenerContext({
3720
- payload: m,
3721
- subject: msg.subject,
3722
- mode: "ephemeral",
3723
- message: msg,
3724
- }),
3725
- ),
3726
- );
3727
- if (handlerResult.isErr()) {
3801
+ const handlerResult = await this.#invokeEventHandler({
3802
+ event,
3803
+ payload: m,
3804
+ mode: "ephemeral",
3805
+ message: msg,
3806
+ fn,
3807
+ });
3808
+ const handlerValue = handlerResult.take();
3809
+ if (isErr(handlerValue)) {
3728
3810
  this.#log.error(
3729
3811
  {
3730
- error: handlerResult.error.toSerializable(),
3812
+ error: handlerValue.error.toSerializable(),
3731
3813
  event,
3732
3814
  subject: msg.subject,
3733
3815
  },
@@ -3741,6 +3823,42 @@ export class Trellis<
3741
3823
  return ok(undefined);
3742
3824
  }
3743
3825
 
3826
+ async #invokeEventHandler(args: {
3827
+ event: EventsOf<TA>;
3828
+ payload: unknown;
3829
+ mode: "durable" | "ephemeral";
3830
+ group?: string;
3831
+ message: Pick<Msg, "headers" | "subject"> & object;
3832
+ fn: EventCallback<EventOf<TA, EventsOf<TA>>>;
3833
+ }): Promise<Result<void, BaseError>> {
3834
+ const annotation = {
3835
+ event: String(args.event),
3836
+ service: this.name,
3837
+ contractId: this.contractId,
3838
+ contractDigest: this.contractDigest,
3839
+ traceId: traceIdFromTraceparent(args.message.headers?.get("traceparent")),
3840
+ };
3841
+ try {
3842
+ const result = await Promise.resolve(args.fn(
3843
+ args.payload as EventOf<TA, EventsOf<TA>>,
3844
+ createEventListenerContext({
3845
+ payload: args.payload,
3846
+ subject: args.message.subject,
3847
+ mode: args.mode,
3848
+ ...(args.group ? { group: args.group } : {}),
3849
+ message: args.message,
3850
+ }),
3851
+ ));
3852
+ const outcome = isResultLike(result) ? result.take() : result;
3853
+ if (isErr(outcome)) {
3854
+ return err(annotateHandlerBoundaryError(outcome.error, annotation));
3855
+ }
3856
+ return ok(undefined);
3857
+ } catch (cause) {
3858
+ return err(annotateHandlerBoundaryError(cause, annotation));
3859
+ }
3860
+ }
3861
+
3744
3862
  #resolveEventConsumerGroup(
3745
3863
  event: EventsOf<TA>,
3746
3864
  opts: EventOpts | undefined,
@@ -3944,21 +4062,18 @@ export class Trellis<
3944
4062
  continue;
3945
4063
  }
3946
4064
 
3947
- const handlerResult = await AsyncResult.lift(
3948
- fn(
3949
- m as EventOf<TA, EventsOf<TA>>,
3950
- createEventListenerContext({
3951
- payload: m,
3952
- subject: msg.subject,
3953
- mode: "durable",
3954
- message: msg,
3955
- }),
3956
- ),
3957
- );
3958
- if (handlerResult.isErr()) {
4065
+ const handlerResult = await this.#invokeEventHandler({
4066
+ event,
4067
+ payload: m,
4068
+ mode: "durable",
4069
+ message: msg,
4070
+ fn,
4071
+ });
4072
+ const handlerValue = handlerResult.take();
4073
+ if (isErr(handlerValue)) {
3959
4074
  this.#log.error(
3960
4075
  {
3961
- error: handlerResult.error.toSerializable(),
4076
+ error: handlerValue.error.toSerializable(),
3962
4077
  event,
3963
4078
  subject: msg.subject,
3964
4079
  },
@@ -4014,22 +4129,19 @@ export class Trellis<
4014
4129
  break;
4015
4130
  }
4016
4131
 
4017
- const handlerResult = await AsyncResult.lift(
4018
- registration.fn(
4019
- eventPayload as EventOf<TA, EventsOf<TA>>,
4020
- createEventListenerContext({
4021
- payload: eventPayload,
4022
- subject: msg.subject,
4023
- mode: "durable",
4024
- group,
4025
- message: msg,
4026
- }),
4027
- ),
4028
- );
4029
- if (handlerResult.isErr()) {
4132
+ const handlerResult = await this.#invokeEventHandler({
4133
+ event: registration.event,
4134
+ payload: eventPayload,
4135
+ mode: "durable",
4136
+ group,
4137
+ message: msg,
4138
+ fn: registration.fn,
4139
+ });
4140
+ const handlerValue = handlerResult.take();
4141
+ if (isErr(handlerValue)) {
4030
4142
  this.#log.error(
4031
4143
  {
4032
- error: handlerResult.error.toSerializable(),
4144
+ error: handlerValue.error.toSerializable(),
4033
4145
  event: registration.event,
4034
4146
  subject: msg.subject,
4035
4147
  },