@qlever-llc/trellis 0.10.22 → 0.11.0-rc.1

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 (406) hide show
  1. package/esm/auth/mod.d.ts +1 -1
  2. package/esm/auth/mod.d.ts.map +1 -1
  3. package/esm/auth/mod.js +1 -1
  4. package/esm/auth/protocol.d.ts +55 -0
  5. package/esm/auth/protocol.d.ts.map +1 -1
  6. package/esm/browser.d.ts +3 -3
  7. package/esm/browser.d.ts.map +1 -1
  8. package/esm/browser.js +1 -1
  9. package/esm/client_connect.d.ts +12 -3
  10. package/esm/client_connect.d.ts.map +1 -1
  11. package/esm/client_connect.js +112 -7
  12. package/esm/codec.d.ts +7 -6
  13. package/esm/codec.d.ts.map +1 -1
  14. package/esm/codec.js +125 -6
  15. package/esm/contract_support/canonical.d.ts +3 -0
  16. package/esm/contract_support/canonical.d.ts.map +1 -1
  17. package/esm/contract_support/canonical.js +3 -0
  18. package/esm/contract_support/mod.d.ts +125 -10
  19. package/esm/contract_support/mod.d.ts.map +1 -1
  20. package/esm/contract_support/mod.js +159 -5
  21. package/esm/contract_support/protocol.d.ts +92 -7
  22. package/esm/contract_support/protocol.d.ts.map +1 -1
  23. package/esm/contract_support/protocol.js +41 -7
  24. package/esm/contract_support/runtime.d.ts +4 -1
  25. package/esm/contract_support/runtime.d.ts.map +1 -1
  26. package/esm/errors/SchemaValidationError.d.ts +49 -0
  27. package/esm/errors/SchemaValidationError.d.ts.map +1 -0
  28. package/esm/errors/SchemaValidationError.js +67 -0
  29. package/esm/errors/index.d.ts +38 -9
  30. package/esm/errors/index.d.ts.map +1 -1
  31. package/esm/errors/index.js +16 -0
  32. package/esm/generated-sdk/auth/api.d.ts +33 -605
  33. package/esm/generated-sdk/auth/api.d.ts.map +1 -1
  34. package/esm/generated-sdk/auth/api.js +6 -1
  35. package/esm/generated-sdk/auth/client.d.ts +129 -134
  36. package/esm/generated-sdk/auth/client.d.ts.map +1 -1
  37. package/esm/generated-sdk/auth/contract.d.ts +2 -601
  38. package/esm/generated-sdk/auth/contract.d.ts.map +1 -1
  39. package/esm/generated-sdk/auth/contract.js +310 -93
  40. package/esm/generated-sdk/auth/mod.d.ts +1 -1
  41. package/esm/generated-sdk/auth/mod.d.ts.map +1 -1
  42. package/esm/generated-sdk/auth/owned_api.d.ts +88 -86
  43. package/esm/generated-sdk/auth/owned_api.d.ts.map +1 -1
  44. package/esm/generated-sdk/auth/owned_api.js +6 -0
  45. package/esm/generated-sdk/auth/schemas.d.ts +284 -136
  46. package/esm/generated-sdk/auth/schemas.d.ts.map +1 -1
  47. package/esm/generated-sdk/auth/schemas.js +231 -92
  48. package/esm/generated-sdk/auth/types.d.ts +565 -89
  49. package/esm/generated-sdk/auth/types.d.ts.map +1 -1
  50. package/esm/generated-sdk/auth/types.js +1 -1
  51. package/esm/generated-sdk/health/api.d.ts +29 -18
  52. package/esm/generated-sdk/health/api.d.ts.map +1 -1
  53. package/esm/generated-sdk/health/client.d.ts +7 -21
  54. package/esm/generated-sdk/health/client.d.ts.map +1 -1
  55. package/esm/generated-sdk/health/contract.d.ts +2 -15
  56. package/esm/generated-sdk/health/contract.d.ts.map +1 -1
  57. package/esm/generated-sdk/health/contract.js +2 -10
  58. package/esm/generated-sdk/health/mod.d.ts +1 -1
  59. package/esm/generated-sdk/health/mod.d.ts.map +1 -1
  60. package/esm/generated-sdk/health/owned_api.d.ts +2 -2
  61. package/esm/generated-sdk/health/schemas.d.ts +1 -14
  62. package/esm/generated-sdk/health/schemas.d.ts.map +1 -1
  63. package/esm/generated-sdk/health/schemas.js +1 -9
  64. package/esm/generated-sdk/health/types.d.ts +10 -5
  65. package/esm/generated-sdk/health/types.d.ts.map +1 -1
  66. package/esm/generated-sdk/health/types.js +1 -2
  67. package/esm/generated-sdk/jobs/api.d.ts +29 -109
  68. package/esm/generated-sdk/jobs/api.d.ts.map +1 -1
  69. package/esm/generated-sdk/jobs/client.d.ts +21 -33
  70. package/esm/generated-sdk/jobs/client.d.ts.map +1 -1
  71. package/esm/generated-sdk/jobs/contract.d.ts +2 -106
  72. package/esm/generated-sdk/jobs/contract.d.ts.map +1 -1
  73. package/esm/generated-sdk/jobs/contract.js +268 -1
  74. package/esm/generated-sdk/jobs/mod.d.ts +1 -1
  75. package/esm/generated-sdk/jobs/mod.d.ts.map +1 -1
  76. package/esm/generated-sdk/jobs/owned_api.d.ts +22 -9
  77. package/esm/generated-sdk/jobs/owned_api.d.ts.map +1 -1
  78. package/esm/generated-sdk/jobs/owned_api.js +20 -1
  79. package/esm/generated-sdk/jobs/schemas.d.ts +437 -0
  80. package/esm/generated-sdk/jobs/schemas.d.ts.map +1 -1
  81. package/esm/generated-sdk/jobs/schemas.js +211 -0
  82. package/esm/generated-sdk/jobs/types.d.ts +195 -12
  83. package/esm/generated-sdk/jobs/types.d.ts.map +1 -1
  84. package/esm/generated-sdk/jobs/types.js +1 -1
  85. package/esm/generated-sdk/state/api.d.ts +29 -68
  86. package/esm/generated-sdk/state/api.d.ts.map +1 -1
  87. package/esm/generated-sdk/state/client.d.ts +17 -31
  88. package/esm/generated-sdk/state/client.d.ts.map +1 -1
  89. package/esm/generated-sdk/state/contract.d.ts +1 -64
  90. package/esm/generated-sdk/state/contract.d.ts.map +1 -1
  91. package/esm/generated-sdk/state/mod.d.ts +1 -1
  92. package/esm/generated-sdk/state/mod.d.ts.map +1 -1
  93. package/esm/generated-sdk/state/owned_api.d.ts +7 -7
  94. package/esm/generated-sdk/state/types.d.ts +52 -9
  95. package/esm/generated-sdk/state/types.d.ts.map +1 -1
  96. package/esm/generated-sdk/trellis-core/api.d.ts +33 -45
  97. package/esm/generated-sdk/trellis-core/api.d.ts.map +1 -1
  98. package/esm/generated-sdk/trellis-core/api.js +6 -1
  99. package/esm/generated-sdk/trellis-core/client.d.ts +31 -20
  100. package/esm/generated-sdk/trellis-core/client.d.ts.map +1 -1
  101. package/esm/generated-sdk/trellis-core/contract.d.ts +2 -41
  102. package/esm/generated-sdk/trellis-core/contract.d.ts.map +1 -1
  103. package/esm/generated-sdk/trellis-core/contract.js +91 -1
  104. package/esm/generated-sdk/trellis-core/mod.d.ts +1 -1
  105. package/esm/generated-sdk/trellis-core/mod.d.ts.map +1 -1
  106. package/esm/generated-sdk/trellis-core/owned_api.d.ts +4 -4
  107. package/esm/generated-sdk/trellis-core/schemas.d.ts +113 -0
  108. package/esm/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
  109. package/esm/generated-sdk/trellis-core/schemas.js +81 -0
  110. package/esm/generated-sdk/trellis-core/types.d.ts +54 -7
  111. package/esm/generated-sdk/trellis-core/types.d.ts.map +1 -1
  112. package/esm/generated-sdk/trellis-core/types.js +1 -1
  113. package/esm/health.d.ts +1 -1
  114. package/esm/health.d.ts.map +1 -1
  115. package/esm/health.js +1 -1
  116. package/esm/index.d.ts +4 -4
  117. package/esm/index.d.ts.map +1 -1
  118. package/esm/index.js +2 -2
  119. package/esm/jobs.d.ts +69 -3
  120. package/esm/jobs.d.ts.map +1 -1
  121. package/esm/jobs.js +92 -3
  122. package/esm/server/health.d.ts +2 -6
  123. package/esm/server/health.d.ts.map +1 -1
  124. package/esm/server/health_schemas.d.ts +0 -8
  125. package/esm/server/health_schemas.d.ts.map +1 -1
  126. package/esm/server/health_schemas.js +0 -5
  127. package/esm/server/internal_jobs/bindings.d.ts +11 -0
  128. package/esm/server/internal_jobs/bindings.d.ts.map +1 -1
  129. package/esm/server/internal_jobs/job-manager.d.ts +58 -2
  130. package/esm/server/internal_jobs/job-manager.d.ts.map +1 -1
  131. package/esm/server/internal_jobs/job-manager.js +429 -55
  132. package/esm/server/internal_jobs/key-coordinator.d.ts +269 -0
  133. package/esm/server/internal_jobs/key-coordinator.d.ts.map +1 -0
  134. package/esm/server/internal_jobs/key-coordinator.js +677 -0
  135. package/esm/server/internal_jobs/projection.d.ts.map +1 -1
  136. package/esm/server/internal_jobs/projection.js +2 -0
  137. package/esm/server/internal_jobs/runtime-worker.d.ts +5 -1
  138. package/esm/server/internal_jobs/runtime-worker.d.ts.map +1 -1
  139. package/esm/server/internal_jobs/runtime-worker.js +14 -2
  140. package/esm/server/internal_jobs/types.d.ts +5 -5
  141. package/esm/server/internal_jobs/types.d.ts.map +1 -1
  142. package/esm/server/internal_jobs/types.js +6 -0
  143. package/esm/server/runtime.d.ts.map +1 -1
  144. package/esm/server/service.d.ts +81 -112
  145. package/esm/server/service.d.ts.map +1 -1
  146. package/esm/server/service.js +372 -130
  147. package/esm/server.d.ts +3 -3
  148. package/esm/server.d.ts.map +1 -1
  149. package/esm/service/deno.d.ts +1 -1
  150. package/esm/service/deno.d.ts.map +1 -1
  151. package/esm/service/drizzle.d.ts +52 -0
  152. package/esm/service/drizzle.d.ts.map +1 -0
  153. package/esm/service/drizzle.js +108 -0
  154. package/esm/service/mod.d.ts +3 -3
  155. package/esm/service/mod.d.ts.map +1 -1
  156. package/esm/service/mod.js +2 -2
  157. package/esm/service/node.d.ts +1 -1
  158. package/esm/service/node.d.ts.map +1 -1
  159. package/esm/service/outbox_inbox.d.ts +76 -22
  160. package/esm/service/outbox_inbox.d.ts.map +1 -1
  161. package/esm/service/outbox_inbox.js +231 -70
  162. package/esm/telemetry/metrics.d.ts +55 -0
  163. package/esm/telemetry/metrics.d.ts.map +1 -1
  164. package/esm/telemetry/metrics.js +86 -0
  165. package/esm/telemetry/mod.d.ts +2 -1
  166. package/esm/telemetry/mod.d.ts.map +1 -1
  167. package/esm/telemetry/mod.js +1 -1
  168. package/esm/trellis.d.ts +62 -30
  169. package/esm/trellis.d.ts.map +1 -1
  170. package/esm/trellis.js +125 -42
  171. package/package.json +14 -2
  172. package/script/auth/mod.d.ts +1 -1
  173. package/script/auth/mod.d.ts.map +1 -1
  174. package/script/auth/mod.js +11 -4
  175. package/script/auth/protocol.d.ts +55 -0
  176. package/script/auth/protocol.d.ts.map +1 -1
  177. package/script/browser.d.ts +3 -3
  178. package/script/browser.d.ts.map +1 -1
  179. package/script/browser.js +3 -2
  180. package/script/client_connect.d.ts +12 -3
  181. package/script/client_connect.d.ts.map +1 -1
  182. package/script/client_connect.js +111 -6
  183. package/script/codec.d.ts +7 -6
  184. package/script/codec.d.ts.map +1 -1
  185. package/script/codec.js +124 -5
  186. package/script/contract_support/canonical.d.ts +3 -0
  187. package/script/contract_support/canonical.d.ts.map +1 -1
  188. package/script/contract_support/canonical.js +3 -0
  189. package/script/contract_support/mod.d.ts +125 -10
  190. package/script/contract_support/mod.d.ts.map +1 -1
  191. package/script/contract_support/mod.js +165 -5
  192. package/script/contract_support/protocol.d.ts +92 -7
  193. package/script/contract_support/protocol.d.ts.map +1 -1
  194. package/script/contract_support/protocol.js +42 -8
  195. package/script/contract_support/runtime.d.ts +4 -1
  196. package/script/contract_support/runtime.d.ts.map +1 -1
  197. package/script/errors/SchemaValidationError.d.ts +49 -0
  198. package/script/errors/SchemaValidationError.d.ts.map +1 -0
  199. package/script/errors/SchemaValidationError.js +74 -0
  200. package/script/errors/index.d.ts +38 -9
  201. package/script/errors/index.d.ts.map +1 -1
  202. package/script/errors/index.js +20 -1
  203. package/script/generated-sdk/auth/api.d.ts +33 -605
  204. package/script/generated-sdk/auth/api.d.ts.map +1 -1
  205. package/script/generated-sdk/auth/api.js +6 -1
  206. package/script/generated-sdk/auth/client.d.ts +129 -134
  207. package/script/generated-sdk/auth/client.d.ts.map +1 -1
  208. package/script/generated-sdk/auth/contract.d.ts +2 -601
  209. package/script/generated-sdk/auth/contract.d.ts.map +1 -1
  210. package/script/generated-sdk/auth/contract.js +310 -93
  211. package/script/generated-sdk/auth/mod.d.ts +1 -1
  212. package/script/generated-sdk/auth/mod.d.ts.map +1 -1
  213. package/script/generated-sdk/auth/owned_api.d.ts +88 -86
  214. package/script/generated-sdk/auth/owned_api.d.ts.map +1 -1
  215. package/script/generated-sdk/auth/owned_api.js +6 -0
  216. package/script/generated-sdk/auth/schemas.d.ts +284 -136
  217. package/script/generated-sdk/auth/schemas.d.ts.map +1 -1
  218. package/script/generated-sdk/auth/schemas.js +231 -92
  219. package/script/generated-sdk/auth/types.d.ts +565 -89
  220. package/script/generated-sdk/auth/types.d.ts.map +1 -1
  221. package/script/generated-sdk/auth/types.js +1 -1
  222. package/script/generated-sdk/health/api.d.ts +29 -18
  223. package/script/generated-sdk/health/api.d.ts.map +1 -1
  224. package/script/generated-sdk/health/client.d.ts +7 -21
  225. package/script/generated-sdk/health/client.d.ts.map +1 -1
  226. package/script/generated-sdk/health/contract.d.ts +2 -15
  227. package/script/generated-sdk/health/contract.d.ts.map +1 -1
  228. package/script/generated-sdk/health/contract.js +2 -10
  229. package/script/generated-sdk/health/mod.d.ts +1 -1
  230. package/script/generated-sdk/health/mod.d.ts.map +1 -1
  231. package/script/generated-sdk/health/owned_api.d.ts +2 -2
  232. package/script/generated-sdk/health/schemas.d.ts +1 -14
  233. package/script/generated-sdk/health/schemas.d.ts.map +1 -1
  234. package/script/generated-sdk/health/schemas.js +1 -9
  235. package/script/generated-sdk/health/types.d.ts +10 -5
  236. package/script/generated-sdk/health/types.d.ts.map +1 -1
  237. package/script/generated-sdk/health/types.js +1 -2
  238. package/script/generated-sdk/jobs/api.d.ts +29 -109
  239. package/script/generated-sdk/jobs/api.d.ts.map +1 -1
  240. package/script/generated-sdk/jobs/client.d.ts +21 -33
  241. package/script/generated-sdk/jobs/client.d.ts.map +1 -1
  242. package/script/generated-sdk/jobs/contract.d.ts +2 -106
  243. package/script/generated-sdk/jobs/contract.d.ts.map +1 -1
  244. package/script/generated-sdk/jobs/contract.js +268 -1
  245. package/script/generated-sdk/jobs/mod.d.ts +1 -1
  246. package/script/generated-sdk/jobs/mod.d.ts.map +1 -1
  247. package/script/generated-sdk/jobs/owned_api.d.ts +22 -9
  248. package/script/generated-sdk/jobs/owned_api.d.ts.map +1 -1
  249. package/script/generated-sdk/jobs/owned_api.js +19 -0
  250. package/script/generated-sdk/jobs/schemas.d.ts +437 -0
  251. package/script/generated-sdk/jobs/schemas.d.ts.map +1 -1
  252. package/script/generated-sdk/jobs/schemas.js +212 -1
  253. package/script/generated-sdk/jobs/types.d.ts +195 -12
  254. package/script/generated-sdk/jobs/types.d.ts.map +1 -1
  255. package/script/generated-sdk/jobs/types.js +1 -1
  256. package/script/generated-sdk/state/api.d.ts +29 -68
  257. package/script/generated-sdk/state/api.d.ts.map +1 -1
  258. package/script/generated-sdk/state/client.d.ts +17 -31
  259. package/script/generated-sdk/state/client.d.ts.map +1 -1
  260. package/script/generated-sdk/state/contract.d.ts +1 -64
  261. package/script/generated-sdk/state/contract.d.ts.map +1 -1
  262. package/script/generated-sdk/state/mod.d.ts +1 -1
  263. package/script/generated-sdk/state/mod.d.ts.map +1 -1
  264. package/script/generated-sdk/state/owned_api.d.ts +7 -7
  265. package/script/generated-sdk/state/types.d.ts +52 -9
  266. package/script/generated-sdk/state/types.d.ts.map +1 -1
  267. package/script/generated-sdk/trellis-core/api.d.ts +33 -45
  268. package/script/generated-sdk/trellis-core/api.d.ts.map +1 -1
  269. package/script/generated-sdk/trellis-core/api.js +6 -1
  270. package/script/generated-sdk/trellis-core/client.d.ts +31 -20
  271. package/script/generated-sdk/trellis-core/client.d.ts.map +1 -1
  272. package/script/generated-sdk/trellis-core/contract.d.ts +2 -41
  273. package/script/generated-sdk/trellis-core/contract.d.ts.map +1 -1
  274. package/script/generated-sdk/trellis-core/contract.js +91 -1
  275. package/script/generated-sdk/trellis-core/mod.d.ts +1 -1
  276. package/script/generated-sdk/trellis-core/mod.d.ts.map +1 -1
  277. package/script/generated-sdk/trellis-core/owned_api.d.ts +4 -4
  278. package/script/generated-sdk/trellis-core/schemas.d.ts +113 -0
  279. package/script/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
  280. package/script/generated-sdk/trellis-core/schemas.js +81 -0
  281. package/script/generated-sdk/trellis-core/types.d.ts +54 -7
  282. package/script/generated-sdk/trellis-core/types.d.ts.map +1 -1
  283. package/script/generated-sdk/trellis-core/types.js +1 -1
  284. package/script/health.d.ts +1 -1
  285. package/script/health.d.ts.map +1 -1
  286. package/script/health.js +1 -2
  287. package/script/index.d.ts +4 -4
  288. package/script/index.d.ts.map +1 -1
  289. package/script/index.js +2 -2
  290. package/script/jobs.d.ts +69 -3
  291. package/script/jobs.d.ts.map +1 -1
  292. package/script/jobs.js +93 -3
  293. package/script/server/health.d.ts +2 -6
  294. package/script/server/health.d.ts.map +1 -1
  295. package/script/server/health_schemas.d.ts +0 -8
  296. package/script/server/health_schemas.d.ts.map +1 -1
  297. package/script/server/health_schemas.js +1 -6
  298. package/script/server/internal_jobs/bindings.d.ts +11 -0
  299. package/script/server/internal_jobs/bindings.d.ts.map +1 -1
  300. package/script/server/internal_jobs/job-manager.d.ts +58 -2
  301. package/script/server/internal_jobs/job-manager.d.ts.map +1 -1
  302. package/script/server/internal_jobs/job-manager.js +429 -54
  303. package/script/server/internal_jobs/key-coordinator.d.ts +269 -0
  304. package/script/server/internal_jobs/key-coordinator.d.ts.map +1 -0
  305. package/script/server/internal_jobs/key-coordinator.js +690 -0
  306. package/script/server/internal_jobs/projection.d.ts.map +1 -1
  307. package/script/server/internal_jobs/projection.js +2 -0
  308. package/script/server/internal_jobs/runtime-worker.d.ts +5 -1
  309. package/script/server/internal_jobs/runtime-worker.d.ts.map +1 -1
  310. package/script/server/internal_jobs/runtime-worker.js +14 -2
  311. package/script/server/internal_jobs/types.d.ts +5 -5
  312. package/script/server/internal_jobs/types.d.ts.map +1 -1
  313. package/script/server/internal_jobs/types.js +6 -0
  314. package/script/server/runtime.d.ts.map +1 -1
  315. package/script/server/service.d.ts +81 -112
  316. package/script/server/service.d.ts.map +1 -1
  317. package/script/server/service.js +369 -127
  318. package/script/server.d.ts +3 -3
  319. package/script/server.d.ts.map +1 -1
  320. package/script/service/deno.d.ts +1 -1
  321. package/script/service/deno.d.ts.map +1 -1
  322. package/script/service/drizzle.d.ts +52 -0
  323. package/script/service/drizzle.d.ts.map +1 -0
  324. package/script/service/drizzle.js +113 -0
  325. package/script/service/mod.d.ts +3 -3
  326. package/script/service/mod.d.ts.map +1 -1
  327. package/script/service/mod.js +4 -3
  328. package/script/service/node.d.ts +1 -1
  329. package/script/service/node.d.ts.map +1 -1
  330. package/script/service/outbox_inbox.d.ts +76 -22
  331. package/script/service/outbox_inbox.d.ts.map +1 -1
  332. package/script/service/outbox_inbox.js +233 -70
  333. package/script/telemetry/metrics.d.ts +55 -0
  334. package/script/telemetry/metrics.d.ts.map +1 -1
  335. package/script/telemetry/metrics.js +88 -0
  336. package/script/telemetry/mod.d.ts +2 -1
  337. package/script/telemetry/mod.d.ts.map +1 -1
  338. package/script/telemetry/mod.js +3 -1
  339. package/script/trellis.d.ts +62 -30
  340. package/script/trellis.d.ts.map +1 -1
  341. package/script/trellis.js +124 -41
  342. package/src/auth/mod.ts +7 -0
  343. package/src/browser.ts +5 -7
  344. package/src/client_connect.ts +200 -12
  345. package/src/codec.ts +186 -11
  346. package/src/contract_support/canonical.ts +3 -0
  347. package/src/contract_support/mod.ts +380 -9
  348. package/src/contract_support/protocol.ts +57 -10
  349. package/src/contract_support/runtime.ts +7 -0
  350. package/src/errors/SchemaValidationError.ts +67 -0
  351. package/src/errors/index.ts +28 -0
  352. package/src/health.ts +0 -1
  353. package/src/index.ts +7 -8
  354. package/src/jobs.ts +138 -1
  355. package/src/sdk/_generated/auth/api.ts +38 -7
  356. package/src/sdk/_generated/auth/client.ts +168 -530
  357. package/src/sdk/_generated/auth/contract.ts +310 -93
  358. package/src/sdk/_generated/auth/mod.ts +0 -1
  359. package/src/sdk/_generated/auth/owned_api.ts +92 -86
  360. package/src/sdk/_generated/auth/schemas.ts +231 -92
  361. package/src/sdk/_generated/auth/types.ts +1069 -273
  362. package/src/sdk/_generated/core/api.ts +38 -7
  363. package/src/sdk/_generated/core/client.ts +47 -73
  364. package/src/sdk/_generated/core/contract.ts +91 -1
  365. package/src/sdk/_generated/core/mod.ts +0 -1
  366. package/src/sdk/_generated/core/owned_api.ts +4 -4
  367. package/src/sdk/_generated/core/schemas.ts +81 -0
  368. package/src/sdk/_generated/core/types.ts +79 -15
  369. package/src/sdk/_generated/health/api.ts +29 -5
  370. package/src/sdk/_generated/health/client.ts +11 -58
  371. package/src/sdk/_generated/health/contract.ts +2 -10
  372. package/src/sdk/_generated/health/mod.ts +0 -1
  373. package/src/sdk/_generated/health/owned_api.ts +2 -2
  374. package/src/sdk/_generated/health/schemas.ts +1 -9
  375. package/src/sdk/_generated/health/types.ts +23 -2
  376. package/src/sdk/_generated/jobs/api.ts +29 -5
  377. package/src/sdk/_generated/jobs/client.ts +25 -109
  378. package/src/sdk/_generated/jobs/contract.ts +269 -1
  379. package/src/sdk/_generated/jobs/mod.ts +0 -1
  380. package/src/sdk/_generated/jobs/owned_api.ts +30 -9
  381. package/src/sdk/_generated/jobs/schemas.ts +213 -0
  382. package/src/sdk/_generated/jobs/types.ts +252 -18
  383. package/src/sdk/_generated/state/api.ts +29 -5
  384. package/src/sdk/_generated/state/client.ts +18 -95
  385. package/src/sdk/_generated/state/mod.ts +0 -1
  386. package/src/sdk/_generated/state/owned_api.ts +7 -7
  387. package/src/sdk/_generated/state/types.ts +91 -15
  388. package/src/server/health.ts +2 -6
  389. package/src/server/health_schemas.ts +0 -6
  390. package/src/server/internal_jobs/bindings.ts +11 -0
  391. package/src/server/internal_jobs/job-manager.ts +553 -24
  392. package/src/server/internal_jobs/key-coordinator.ts +1080 -0
  393. package/src/server/internal_jobs/projection.ts +2 -0
  394. package/src/server/internal_jobs/runtime-worker.ts +17 -1
  395. package/src/server/internal_jobs/types.ts +6 -0
  396. package/src/server/runtime.ts +4 -0
  397. package/src/server/service.ts +645 -625
  398. package/src/server.ts +14 -7
  399. package/src/service/deno.ts +0 -5
  400. package/src/service/drizzle.ts +184 -0
  401. package/src/service/mod.ts +17 -7
  402. package/src/service/node.ts +0 -5
  403. package/src/service/outbox_inbox.ts +354 -88
  404. package/src/telemetry/metrics.ts +198 -1
  405. package/src/telemetry/mod.ts +6 -0
  406. package/src/trellis.ts +286 -105
@@ -1,14 +1,31 @@
1
- import { type AsyncResult, type BaseError, isErr } from "@qlever-llc/result";
1
+ import {
2
+ type AsyncResult,
3
+ type BaseError,
4
+ isErr,
5
+ UnexpectedError,
6
+ } from "@qlever-llc/result";
2
7
  import { type StaticDecode, Type } from "typebox";
3
- import type { PreparedTrellisEvent, Trellis } from "../trellis.js";
8
+ import type { PreparedTrellisEvent } from "../trellis.js";
4
9
  import { TypedKV } from "../kv.js";
5
10
  import { recordTrellisError } from "../telemetry/mod.js";
6
11
 
7
12
  export type OutboxMessageState = "pending" | "dispatched" | "failed";
8
13
 
14
+ export type OutboxRecordKind = "event.publish" | "job.create" | "job.submit";
15
+
16
+ export type PreparedOutboxRecord = {
17
+ readonly id: string;
18
+ readonly kind: OutboxRecordKind;
19
+ readonly name: string;
20
+ readonly subject: string;
21
+ readonly payload: string;
22
+ readonly headers: Readonly<Record<string, string>>;
23
+ };
24
+
9
25
  export type OutboxMessage = {
10
26
  id: string;
11
- event: string;
27
+ kind: OutboxRecordKind;
28
+ name: string;
12
29
  subject: string;
13
30
  payload: string;
14
31
  headers: Record<string, string>;
@@ -18,6 +35,7 @@ export type OutboxMessage = {
18
35
  updatedAt: string;
19
36
  nextAttemptAt?: string;
20
37
  lastError?: string;
38
+ outcome?: unknown;
21
39
  };
22
40
 
23
41
  export type OutboxDispatchResult = {
@@ -39,10 +57,21 @@ export type OutboxDispatcherOptions = {
39
57
  onError?: (error: unknown) => void;
40
58
  };
41
59
 
60
+ export type OutboxDispatchRuntime = {
61
+ /** Dispatches an event-publish outbox record. Returns Void on success, err on transient failure. */
62
+ publishPreparedEvent(
63
+ event: PreparedTrellisEvent,
64
+ ): AsyncResult<void, UnexpectedError>;
65
+ /** Dispatches a job create/submit outbox record. Stub until Phase 3. */
66
+ dispatchJobSubmission?(
67
+ message: OutboxMessage,
68
+ ): AsyncResult<void, BaseError>;
69
+ };
70
+
42
71
  export type OutboxRepository = {
43
- enqueue(event: PreparedTrellisEvent): Promise<OutboxMessage>;
72
+ enqueue(record: PreparedOutboxRecord): Promise<OutboxMessage>;
44
73
  claimDue(limit: number, now: Date): Promise<OutboxMessage[]>;
45
- markDispatched(id: string, now: Date): Promise<void>;
74
+ markDispatched(id: string, now: Date, outcome?: unknown): Promise<void>;
46
75
  markFailed(
47
76
  id: string,
48
77
  failure: { error: string; nextAttemptAt: Date; now: Date },
@@ -53,26 +82,55 @@ export type InboxRepository = {
53
82
  record(messageId: string, now?: Date): Promise<boolean>;
54
83
  };
55
84
 
85
+ /** SQL dialects supported by Trellis SQL outbox helpers. */
56
86
  export type SqlDialect = "sqlite" | "postgres";
57
87
 
88
+ /** Table names used by Trellis SQL outbox and inbox helper tables. */
58
89
  export type SqlOutboxTables = {
59
90
  outbox: string;
60
91
  inbox: string;
61
92
  };
62
93
 
94
+ /** Minimal SQL execution surface used by Trellis SQL outbox repositories. */
63
95
  export type SqlExecutor = {
64
96
  query(sql: string, params: readonly unknown[]): Promise<readonly SqlRow[]>;
65
97
  execute(sql: string, params: readonly unknown[]): Promise<void>;
66
98
  };
67
99
 
100
+ /** Row shape returned by a {@link SqlExecutor}. */
68
101
  export type SqlRow = Record<string, unknown>;
69
102
 
103
+ /** SQL outbox adapter bundle over caller-owned SQL execution. */
70
104
  export type SqlOutboxAdapter = {
71
105
  outbox: SqlOutboxRepository;
72
106
  inbox: SqlInboxRepository;
73
107
  ddl: readonly string[];
74
108
  };
75
109
 
110
+ /** Versioned Trellis-owned SQL outbox/inbox migration artifact. */
111
+ export type SqlOutboxMigration = {
112
+ /** Stable migration identifier for the dialect and schema version. */
113
+ readonly id: string;
114
+ /** Monotonic Trellis helper-table schema version. */
115
+ readonly version: number;
116
+ /** SQL dialect targeted by this migration artifact. */
117
+ readonly dialect: SqlDialect;
118
+ /** SQL statements that apply this migration. */
119
+ readonly up: readonly string[];
120
+ /** SQL statements that revert this migration when supported by the runner. */
121
+ readonly down?: readonly string[];
122
+ /** Deterministic checksum over the canonical migration SQL. */
123
+ readonly checksum: string;
124
+ };
125
+
126
+ /** Options for generating Trellis SQL outbox/inbox migration artifacts. */
127
+ export type SqlOutboxMigrationOptions = {
128
+ /** SQL dialect targeted by the generated migration artifacts. */
129
+ readonly dialect: SqlDialect;
130
+ /** Optional helper-table names; omitted names use Trellis defaults. */
131
+ readonly tables?: Partial<SqlOutboxTables>;
132
+ };
133
+
76
134
  type KvAsyncResult<T> = Pick<AsyncResult<T, BaseError>, "take">;
77
135
 
78
136
  export type OutboxKvEntry = {
@@ -88,41 +146,70 @@ export type OutboxKvStore = {
88
146
  keys(filter?: string | string[]): KvAsyncResult<AsyncIterable<string>>;
89
147
  };
90
148
 
149
+ /** Default Trellis helper-table names for SQL outbox and inbox storage. */
91
150
  export const defaultSqlOutboxTables: SqlOutboxTables = Object.freeze({
92
151
  outbox: "trellis_outbox",
93
152
  inbox: "trellis_inbox",
94
153
  });
95
154
 
155
+ const sqlIdentifierPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
156
+
96
157
  /**
97
- * Creates SQL outbox/inbox repositories plus DDL for caller-owned migrations.
158
+ * Creates SQL outbox/inbox repositories plus current helper-table DDL.
98
159
  *
99
- * Trellis does not import Drizzle here to avoid making every service depend on a
100
- * specific migration library. Services should include the returned DDL, or an
101
- * equivalent Drizzle table definition using these table and column names, in
102
- * their own Drizzle-managed migration flow.
160
+ * Use {@link getSqlOutboxMigrations} when services need versioned Trellis-owned
161
+ * migration artifacts for their normal database migration tooling.
103
162
  */
104
163
  export function createSqlOutboxAdapter(
105
164
  executor: SqlExecutor,
106
165
  dialect: SqlDialect,
107
166
  tables: SqlOutboxTables = defaultSqlOutboxTables,
108
167
  ): SqlOutboxAdapter {
168
+ const sqlTables = validateSqlOutboxTables(tables);
109
169
  return {
110
- outbox: new SqlOutboxRepository(executor, dialect, tables),
111
- inbox: new SqlInboxRepository(executor, dialect, tables),
170
+ outbox: new SqlOutboxRepository(executor, dialect, sqlTables),
171
+ inbox: new SqlInboxRepository(executor, dialect, sqlTables),
112
172
  ddl: dialect === "postgres"
113
- ? createPostgresOutboxSchema(tables)
114
- : createSqliteOutboxSchema(tables),
173
+ ? createPostgresOutboxSchema(sqlTables)
174
+ : createSqliteOutboxSchema(sqlTables),
115
175
  };
116
176
  }
117
177
 
178
+ /**
179
+ * Returns Trellis-owned SQL outbox/inbox migration artifacts for a dialect.
180
+ *
181
+ * Trellis generates the helper-table SQL, but services remain responsible for
182
+ * running these artifacts through their normal database migration tooling.
183
+ */
184
+ export function getSqlOutboxMigrations(
185
+ options: SqlOutboxMigrationOptions,
186
+ ): readonly SqlOutboxMigration[] {
187
+ const tables = resolveSqlOutboxTables(options.tables);
188
+ const up = options.dialect === "postgres"
189
+ ? createPostgresOutboxSchema(tables)
190
+ : createSqliteOutboxSchema(tables);
191
+ const down = createSqlOutboxDownSchema(tables);
192
+ return Object.freeze([
193
+ Object.freeze({
194
+ id: `trellis_sql_outbox_inbox_${options.dialect}_v1`,
195
+ version: 1,
196
+ dialect: options.dialect,
197
+ up,
198
+ down,
199
+ checksum: checksumMigrationSql(up, down),
200
+ }),
201
+ ]);
202
+ }
203
+
118
204
  /** Returns SQLite DDL for Trellis outbox and inbox tables. */
119
205
  export function createSqliteOutboxSchema(
120
206
  tables: SqlOutboxTables = defaultSqlOutboxTables,
121
207
  ): readonly string[] {
208
+ const sqlTables = validateSqlOutboxTables(tables);
122
209
  return [
123
- `CREATE TABLE IF NOT EXISTS ${tables.outbox} (id TEXT PRIMARY KEY, event TEXT NOT NULL, subject TEXT NOT NULL, payload TEXT NOT NULL, headers TEXT NOT NULL, state TEXT NOT NULL, attempts INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, next_attempt_at TEXT, last_error TEXT)`,
124
- `CREATE INDEX IF NOT EXISTS ${tables.outbox}_due_idx ON ${tables.outbox} (state, next_attempt_at)`,
125
- `CREATE TABLE IF NOT EXISTS ${tables.inbox} (message_id TEXT PRIMARY KEY, received_at TEXT NOT NULL)`,
210
+ `CREATE TABLE IF NOT EXISTS ${sqlTables.outbox} (id TEXT PRIMARY KEY, kind TEXT NOT NULL, name TEXT NOT NULL, subject TEXT NOT NULL, payload TEXT NOT NULL, headers TEXT NOT NULL, state TEXT NOT NULL, attempts INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, next_attempt_at TEXT, last_error TEXT, outcome TEXT)`,
211
+ `CREATE INDEX IF NOT EXISTS ${sqlTables.outbox}_due_idx ON ${sqlTables.outbox} (state, next_attempt_at)`,
212
+ `CREATE TABLE IF NOT EXISTS ${sqlTables.inbox} (message_id TEXT PRIMARY KEY, received_at TEXT NOT NULL)`,
126
213
  ];
127
214
  }
128
215
 
@@ -130,28 +217,91 @@ export function createSqliteOutboxSchema(
130
217
  export function createPostgresOutboxSchema(
131
218
  tables: SqlOutboxTables = defaultSqlOutboxTables,
132
219
  ): readonly string[] {
220
+ const sqlTables = validateSqlOutboxTables(tables);
221
+ return [
222
+ `CREATE TABLE IF NOT EXISTS ${sqlTables.outbox} (id text PRIMARY KEY, kind text NOT NULL, name text NOT NULL, subject text NOT NULL, payload text NOT NULL, headers jsonb NOT NULL, state text NOT NULL, attempts integer NOT NULL, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, next_attempt_at timestamptz, last_error text, outcome jsonb)`,
223
+ `CREATE INDEX IF NOT EXISTS ${sqlTables.outbox}_due_idx ON ${sqlTables.outbox} (state, next_attempt_at)`,
224
+ `CREATE TABLE IF NOT EXISTS ${sqlTables.inbox} (message_id text PRIMARY KEY, received_at timestamptz NOT NULL)`,
225
+ ];
226
+ }
227
+
228
+ function createSqlOutboxDownSchema(
229
+ tables: SqlOutboxTables,
230
+ ): readonly string[] {
231
+ const sqlTables = validateSqlOutboxTables(tables);
133
232
  return [
134
- `CREATE TABLE IF NOT EXISTS ${tables.outbox} (id text PRIMARY KEY, event text NOT NULL, subject text NOT NULL, payload text NOT NULL, headers jsonb NOT NULL, state text NOT NULL, attempts integer NOT NULL, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, next_attempt_at timestamptz, last_error text)`,
135
- `CREATE INDEX IF NOT EXISTS ${tables.outbox}_due_idx ON ${tables.outbox} (state, next_attempt_at)`,
136
- `CREATE TABLE IF NOT EXISTS ${tables.inbox} (message_id text PRIMARY KEY, received_at timestamptz NOT NULL)`,
233
+ `DROP INDEX IF EXISTS ${sqlTables.outbox}_due_idx`,
234
+ `DROP TABLE IF EXISTS ${sqlTables.inbox}`,
235
+ `DROP TABLE IF EXISTS ${sqlTables.outbox}`,
137
236
  ];
138
237
  }
139
238
 
239
+ function resolveSqlOutboxTables(
240
+ tables: Partial<SqlOutboxTables> | undefined,
241
+ ): SqlOutboxTables {
242
+ return validateSqlOutboxTables({
243
+ outbox: tables?.outbox ?? defaultSqlOutboxTables.outbox,
244
+ inbox: tables?.inbox ?? defaultSqlOutboxTables.inbox,
245
+ });
246
+ }
247
+
248
+ function validateSqlOutboxTables(tables: SqlOutboxTables): SqlOutboxTables {
249
+ validateSqlIdentifier("outbox", tables.outbox);
250
+ validateSqlIdentifier("inbox", tables.inbox);
251
+ return Object.freeze({ outbox: tables.outbox, inbox: tables.inbox });
252
+ }
253
+
254
+ function validateSqlIdentifier(
255
+ kind: keyof SqlOutboxTables,
256
+ name: string,
257
+ ): void {
258
+ if (!sqlIdentifierPattern.test(name)) {
259
+ throw new Error(
260
+ `Invalid SQL ${kind} table name "${name}". Use a simple identifier matching ${sqlIdentifierPattern.source}.`,
261
+ );
262
+ }
263
+ }
264
+
265
+ function checksumMigrationSql(
266
+ up: readonly string[],
267
+ down: readonly string[],
268
+ ): string {
269
+ return `fnv1a64:${
270
+ fnv1a64Hex([
271
+ "-- up",
272
+ ...up,
273
+ "-- down",
274
+ ...down,
275
+ ].join("\n"))
276
+ }`;
277
+ }
278
+
279
+ function fnv1a64Hex(input: string): string {
280
+ let hash = 0xcbf29ce484222325n;
281
+ const prime = 0x100000001b3n;
282
+ for (const character of input) {
283
+ hash ^= BigInt(character.codePointAt(0) ?? 0);
284
+ hash = BigInt.asUintN(64, hash * prime);
285
+ }
286
+ return hash.toString(16).padStart(16, "0");
287
+ }
288
+
140
289
  /** In-memory outbox repository intended for tests and local process adapters. */
141
290
  export class MemoryOutboxRepository implements OutboxRepository {
142
291
  #messages = new Map<string, OutboxMessage>();
143
292
 
144
- async enqueue(event: PreparedTrellisEvent): Promise<OutboxMessage> {
293
+ async enqueue(record: PreparedOutboxRecord): Promise<OutboxMessage> {
145
294
  const now = new Date().toISOString();
146
- const id = messageId(event);
295
+ const id = record.id;
147
296
  const existing = this.#messages.get(id);
148
297
  if (existing) return existing;
149
298
  const message: OutboxMessage = {
150
299
  id,
151
- event: event.event,
152
- subject: event.subject,
153
- payload: event.encodedPayload,
154
- headers: { ...event.headers },
300
+ kind: record.kind,
301
+ name: record.name,
302
+ subject: record.subject,
303
+ payload: record.payload,
304
+ headers: { ...record.headers },
155
305
  state: "pending",
156
306
  attempts: 0,
157
307
  createdAt: now,
@@ -177,7 +327,11 @@ export class MemoryOutboxRepository implements OutboxRepository {
177
327
  return claimed;
178
328
  }
179
329
 
180
- async markDispatched(id: string, now: Date): Promise<void> {
330
+ async markDispatched(
331
+ id: string,
332
+ now: Date,
333
+ outcome?: unknown,
334
+ ): Promise<void> {
181
335
  const message = this.#messages.get(id);
182
336
  if (!message) return;
183
337
  this.#messages.set(id, {
@@ -186,6 +340,7 @@ export class MemoryOutboxRepository implements OutboxRepository {
186
340
  updatedAt: now.toISOString(),
187
341
  nextAttemptAt: undefined,
188
342
  lastError: undefined,
343
+ outcome: outcome ?? message.outcome,
189
344
  });
190
345
  }
191
346
 
@@ -226,20 +381,25 @@ export class MemoryInboxRepository implements InboxRepository {
226
381
 
227
382
  /** SQL-backed outbox repository over a caller-owned executor. */
228
383
  export class SqlOutboxRepository implements OutboxRepository {
384
+ readonly tables: SqlOutboxTables;
385
+
229
386
  constructor(
230
387
  readonly executor: SqlExecutor,
231
388
  readonly dialect: SqlDialect,
232
- readonly tables: SqlOutboxTables = defaultSqlOutboxTables,
233
- ) {}
389
+ tables: SqlOutboxTables = defaultSqlOutboxTables,
390
+ ) {
391
+ this.tables = validateSqlOutboxTables(tables);
392
+ }
234
393
 
235
- async enqueue(event: PreparedTrellisEvent): Promise<OutboxMessage> {
394
+ async enqueue(record: PreparedOutboxRecord): Promise<OutboxMessage> {
236
395
  const now = new Date().toISOString();
237
396
  const message: OutboxMessage = {
238
- id: messageId(event),
239
- event: event.event,
240
- subject: event.subject,
241
- payload: event.encodedPayload,
242
- headers: { ...event.headers },
397
+ id: record.id,
398
+ kind: record.kind,
399
+ name: record.name,
400
+ subject: record.subject,
401
+ payload: record.payload,
402
+ headers: { ...record.headers },
243
403
  state: "pending",
244
404
  attempts: 0,
245
405
  createdAt: now,
@@ -250,12 +410,13 @@ export class SqlOutboxRepository implements OutboxRepository {
250
410
  ? "ON CONFLICT (id) DO NOTHING"
251
411
  : "ON CONFLICT(id) DO NOTHING";
252
412
  await this.executor.execute(
253
- `INSERT INTO ${this.tables.outbox} (id, event, subject, payload, headers, state, attempts, created_at, updated_at, next_attempt_at, last_error) VALUES (${
254
- placeholders(this.dialect, 11)
413
+ `INSERT INTO ${this.tables.outbox} (id, kind, name, subject, payload, headers, state, attempts, created_at, updated_at, next_attempt_at, last_error, outcome) VALUES (${
414
+ placeholders(this.dialect, 13)
255
415
  }) ${conflict}`,
256
416
  [
257
417
  message.id,
258
- message.event,
418
+ message.kind,
419
+ message.name,
259
420
  message.subject,
260
421
  message.payload,
261
422
  headers,
@@ -265,6 +426,7 @@ export class SqlOutboxRepository implements OutboxRepository {
265
426
  message.updatedAt,
266
427
  null,
267
428
  null,
429
+ null,
268
430
  ],
269
431
  );
270
432
  return message;
@@ -272,7 +434,7 @@ export class SqlOutboxRepository implements OutboxRepository {
272
434
 
273
435
  async claimDue(limit: number, now: Date): Promise<OutboxMessage[]> {
274
436
  const rows = await this.executor.query(
275
- `SELECT id, event, subject, payload, headers, state, attempts, created_at, updated_at, next_attempt_at, last_error FROM ${this.tables.outbox} WHERE state != ${
437
+ `SELECT id, kind, name, subject, payload, headers, state, attempts, created_at, updated_at, next_attempt_at, last_error, outcome FROM ${this.tables.outbox} WHERE state != ${
276
438
  placeholder(this.dialect, 1)
277
439
  } AND (next_attempt_at IS NULL OR next_attempt_at <= ${
278
440
  placeholder(this.dialect, 2)
@@ -282,16 +444,21 @@ export class SqlOutboxRepository implements OutboxRepository {
282
444
  return rows.map(rowToOutboxMessage);
283
445
  }
284
446
 
285
- async markDispatched(id: string, now: Date): Promise<void> {
447
+ async markDispatched(
448
+ id: string,
449
+ now: Date,
450
+ outcome?: unknown,
451
+ ): Promise<void> {
452
+ const outcomeJson = outcome !== undefined ? JSON.stringify(outcome) : null;
286
453
  await this.executor.execute(
287
454
  `UPDATE ${this.tables.outbox} SET state = ${
288
455
  placeholder(this.dialect, 1)
289
456
  }, updated_at = ${
290
457
  placeholder(this.dialect, 2)
291
- }, next_attempt_at = NULL, last_error = NULL WHERE id = ${
458
+ }, next_attempt_at = NULL, last_error = NULL, outcome = ${
292
459
  placeholder(this.dialect, 3)
293
- }`,
294
- ["dispatched", now.toISOString(), id],
460
+ } WHERE id = ${placeholder(this.dialect, 4)}`,
461
+ ["dispatched", now.toISOString(), outcomeJson, id],
295
462
  );
296
463
  }
297
464
 
@@ -320,11 +487,15 @@ export class SqlOutboxRepository implements OutboxRepository {
320
487
 
321
488
  /** SQL-backed inbox repository over a caller-owned executor. */
322
489
  export class SqlInboxRepository implements InboxRepository {
490
+ readonly tables: SqlOutboxTables;
491
+
323
492
  constructor(
324
493
  readonly executor: SqlExecutor,
325
494
  readonly dialect: SqlDialect,
326
- readonly tables: SqlOutboxTables = defaultSqlOutboxTables,
327
- ) {}
495
+ tables: SqlOutboxTables = defaultSqlOutboxTables,
496
+ ) {
497
+ this.tables = validateSqlOutboxTables(tables);
498
+ }
328
499
 
329
500
  async record(messageId: string, now: Date = new Date()): Promise<boolean> {
330
501
  try {
@@ -363,7 +534,8 @@ type KvInboxRecord = StaticDecode<typeof KvInboxRecordSchema>;
363
534
 
364
535
  export const KvOutboxRecordSchema = Type.Object({
365
536
  id: Type.String(),
366
- event: Type.String(),
537
+ kind: Type.String(),
538
+ name: Type.String(),
367
539
  subject: Type.String(),
368
540
  payload: Type.String(),
369
541
  headers: Type.Record(Type.String(), Type.String()),
@@ -378,6 +550,7 @@ export const KvOutboxRecordSchema = Type.Object({
378
550
  updatedAt: Type.String(),
379
551
  nextAttemptAt: Type.Optional(Type.String()),
380
552
  lastError: Type.Optional(Type.String()),
553
+ outcome: Type.Optional(Type.String()),
381
554
  });
382
555
 
383
556
  export type KvOutboxRecord = StaticDecode<typeof KvOutboxRecordSchema>;
@@ -386,14 +559,15 @@ export type KvOutboxRecord = StaticDecode<typeof KvOutboxRecordSchema>;
386
559
  export class NatsKvOutboxRepository implements OutboxRepository {
387
560
  constructor(readonly kv: OutboxKvStore) {}
388
561
 
389
- async enqueue(event: PreparedTrellisEvent): Promise<OutboxMessage> {
562
+ async enqueue(rec: PreparedOutboxRecord): Promise<OutboxMessage> {
390
563
  const now = new Date().toISOString();
391
564
  const record: KvOutboxRecord = {
392
- id: messageId(event),
393
- event: event.event,
394
- subject: event.subject,
395
- payload: event.encodedPayload,
396
- headers: { ...event.headers },
565
+ id: rec.id,
566
+ kind: rec.kind,
567
+ name: rec.name,
568
+ subject: rec.subject,
569
+ payload: rec.payload,
570
+ headers: { ...rec.headers },
397
571
  state: "pending",
398
572
  attempts: 0,
399
573
  createdAt: now,
@@ -446,18 +620,26 @@ export class NatsKvOutboxRepository implements OutboxRepository {
446
620
  return claimed;
447
621
  }
448
622
 
449
- async markDispatched(id: string, now: Date): Promise<void> {
623
+ async markDispatched(
624
+ id: string,
625
+ now: Date,
626
+ outcome?: unknown,
627
+ ): Promise<void> {
450
628
  const loaded = await this.kv.get(id).take();
451
629
  if (isErr(loaded)) {
452
630
  if (hasKvReason(loaded.error, "not found")) return;
453
631
  throw loaded.error;
454
632
  }
633
+ const outcomeStr = outcome !== undefined
634
+ ? JSON.stringify(outcome)
635
+ : undefined;
455
636
  const stored = await loaded.put({
456
637
  ...loaded.value,
457
638
  state: "dispatched",
458
639
  updatedAt: now.toISOString(),
459
640
  nextAttemptAt: undefined,
460
641
  lastError: undefined,
642
+ outcome: outcomeStr ?? loaded.value.outcome,
461
643
  }, true).take();
462
644
  if (isErr(stored)) throw stored.error;
463
645
  }
@@ -512,7 +694,7 @@ export class NatsKvInboxRepository implements InboxRepository {
512
694
  */
513
695
  export class OutboxDispatcher {
514
696
  readonly #repository: OutboxRepository;
515
- readonly #runtime: Pick<Trellis, "publishPrepared">;
697
+ readonly #runtime: OutboxDispatchRuntime;
516
698
  readonly #options: OutboxDispatcherOptions;
517
699
  #wakeTimer: ReturnType<typeof setTimeout> | undefined;
518
700
  #retryTimer: ReturnType<typeof setTimeout> | undefined;
@@ -525,7 +707,7 @@ export class OutboxDispatcher {
525
707
  /** Creates a dispatcher over an existing outbox repository and runtime. */
526
708
  constructor(
527
709
  repository: OutboxRepository,
528
- runtime: Pick<Trellis, "publishPrepared">,
710
+ runtime: OutboxDispatchRuntime,
529
711
  options: OutboxDispatcherOptions = {},
530
712
  ) {
531
713
  this.#repository = repository;
@@ -656,10 +838,10 @@ export class OutboxDispatcher {
656
838
  }
657
839
  }
658
840
 
659
- /** Dispatches due outbox messages through a Trellis runtime publisher. */
841
+ /** Dispatches due outbox messages through a dispatch runtime. */
660
842
  export async function dispatchOutbox(
661
843
  repository: OutboxRepository,
662
- runtime: Pick<Trellis, "publishPrepared">,
844
+ runtime: OutboxDispatchRuntime,
663
845
  options: { limit?: number; now?: Date; retryDelayMs?: number } = {},
664
846
  ): Promise<OutboxDispatchResult> {
665
847
  const now = options.now ?? new Date();
@@ -668,58 +850,128 @@ export async function dispatchOutbox(
668
850
  let dispatched = 0;
669
851
  let failed = 0;
670
852
  for (const message of messages) {
671
- const result = await runtime.publishPrepared(
672
- outboxMessageToPrepared(message),
673
- );
674
- const value = result.take();
675
- if (isErr(value)) {
676
- recordTrellisError(value.error, {
853
+ if (message.kind === "event.publish") {
854
+ const result = await runtime.publishPreparedEvent(
855
+ outboxMessageToPreparedEvent(message),
856
+ );
857
+ const value = result.take();
858
+ if (isErr(value)) {
859
+ recordTrellisError(value.error, {
860
+ surface: "outbox",
861
+ direction: "dispatcher",
862
+ operation: message.name,
863
+ phase: "publish",
864
+ messagingSystem: "nats",
865
+ });
866
+ failed += 1;
867
+ await repository.markFailed(message.id, {
868
+ error: value.error.message,
869
+ nextAttemptAt: new Date(now.getTime() + retryDelayMs),
870
+ now,
871
+ });
872
+ continue;
873
+ }
874
+ dispatched += 1;
875
+ await repository.markDispatched(message.id, now);
876
+ } else if (
877
+ message.kind === "job.create" || message.kind === "job.submit"
878
+ ) {
879
+ if (runtime.dispatchJobSubmission) {
880
+ const result = await runtime.dispatchJobSubmission(message);
881
+ const value = result.take();
882
+ if (isErr(value)) {
883
+ recordTrellisError(value.error, {
884
+ surface: "outbox",
885
+ direction: "dispatcher",
886
+ operation: message.name,
887
+ phase: "job.dispatch",
888
+ });
889
+ failed += 1;
890
+ await repository.markFailed(message.id, {
891
+ error: value.error.message,
892
+ nextAttemptAt: new Date(now.getTime() + retryDelayMs),
893
+ now,
894
+ });
895
+ continue;
896
+ }
897
+ dispatched += 1;
898
+ await repository.markDispatched(message.id, now);
899
+ } else {
900
+ recordTrellisError(new Error(`job dispatch not supported`), {
901
+ surface: "outbox",
902
+ direction: "dispatcher",
903
+ operation: message.name,
904
+ phase: "dispatch",
905
+ });
906
+ await repository.markDispatched(message.id, now, {
907
+ error: "job dispatch not supported",
908
+ });
909
+ }
910
+ } else {
911
+ const error = `Unknown outbox record kind: ${message.kind}`;
912
+ recordTrellisError(new Error(error), {
677
913
  surface: "outbox",
678
914
  direction: "dispatcher",
679
- operation: message.event,
680
- phase: "publish",
681
- messagingSystem: "nats",
682
- });
683
- failed += 1;
684
- await repository.markFailed(message.id, {
685
- error: value.error.message,
686
- nextAttemptAt: new Date(now.getTime() + retryDelayMs),
687
- now,
915
+ operation: message.name,
916
+ phase: "dispatch",
688
917
  });
689
- continue;
918
+ await repository.markDispatched(message.id, now, { error });
690
919
  }
691
- dispatched += 1;
692
- await repository.markDispatched(message.id, now);
693
920
  }
694
921
  return { dispatched, failed };
695
922
  }
696
923
 
697
- /** Rehydrates a persisted outbox row into a prepared event. */
698
- export function outboxMessageToPrepared(
924
+ /** Rehydrates a persisted outbox event row into a prepared event. */
925
+ export function outboxMessageToPreparedEvent(
699
926
  message: OutboxMessage,
700
927
  ): PreparedTrellisEvent {
928
+ if (message.kind !== "event.publish") {
929
+ throw new Error(
930
+ `Expected event.publish kind, got ${message.kind}`,
931
+ );
932
+ }
933
+ const payload = JSON.parse(message.payload) as Record<string, unknown>;
934
+ const header = eventHeaderFromMessage(message.headers);
701
935
  return Object.freeze({
702
- event: message.event,
936
+ event: message.name,
703
937
  subject: message.subject,
704
- payload: Object.freeze(
705
- JSON.parse(message.payload) as Record<string, unknown> & {
706
- header: { id: string; time: string };
707
- },
708
- ),
938
+ header: Object.freeze(header),
939
+ payload: Object.freeze(payload),
709
940
  encodedPayload: message.payload,
710
941
  headers: Object.freeze({ ...message.headers }),
711
942
  });
712
943
  }
713
944
 
714
- function messageId(event: PreparedTrellisEvent): string {
715
- return event.headers["Nats-Msg-Id"] ?? event.headers["nats-msg-id"] ??
716
- event.payload.header.id;
945
+ /** Adapts a prepared Trellis event into a generic outbox record. */
946
+ export function preparedTrellisEventToOutboxRecord(
947
+ event: PreparedTrellisEvent,
948
+ ): PreparedOutboxRecord {
949
+ return {
950
+ id: event.headers["Nats-Msg-Id"] ?? event.header.id,
951
+ kind: "event.publish",
952
+ name: event.event,
953
+ subject: event.subject,
954
+ payload: event.encodedPayload,
955
+ headers: event.headers,
956
+ };
957
+ }
958
+
959
+ function eventHeaderFromMessage(
960
+ headers: Record<string, string>,
961
+ ): { id: string; time: string } {
962
+ const id = headers["Nats-Msg-Id"] ?? headers["nats-msg-id"];
963
+ const time = headers["Trellis-Event-Time"] ?? headers["trellis-event-time"];
964
+ return {
965
+ id: typeof id === "string" ? id : "",
966
+ time: typeof time === "string" ? time : new Date(0).toISOString(),
967
+ };
717
968
  }
718
969
 
719
970
  function rowToOutboxMessage(row: SqlRow): OutboxMessage {
720
971
  return {
721
972
  id: stringField(row, "id"),
722
- event: stringField(row, "event"),
973
+ kind: kindField(row, "kind"),
974
+ name: stringField(row, "name"),
723
975
  subject: stringField(row, "subject"),
724
976
  payload: stringField(row, "payload"),
725
977
  headers: parseHeaders(row["headers"]),
@@ -729,13 +981,15 @@ function rowToOutboxMessage(row: SqlRow): OutboxMessage {
729
981
  updatedAt: stringField(row, "updated_at"),
730
982
  nextAttemptAt: optionalStringField(row, "next_attempt_at"),
731
983
  lastError: optionalStringField(row, "last_error"),
984
+ outcome: optionalStringField(row, "outcome"),
732
985
  };
733
986
  }
734
987
 
735
988
  function kvRecordToOutboxMessage(record: KvOutboxRecord): OutboxMessage {
736
989
  return {
737
990
  id: record.id,
738
- event: record.event,
991
+ kind: record.kind as OutboxRecordKind,
992
+ name: record.name,
739
993
  subject: record.subject,
740
994
  payload: record.payload,
741
995
  headers: { ...record.headers },
@@ -745,6 +999,7 @@ function kvRecordToOutboxMessage(record: KvOutboxRecord): OutboxMessage {
745
999
  updatedAt: record.updatedAt,
746
1000
  nextAttemptAt: record.nextAttemptAt,
747
1001
  lastError: record.lastError,
1002
+ outcome: record.outcome,
748
1003
  };
749
1004
  }
750
1005
 
@@ -810,6 +1065,17 @@ function numberField(row: SqlRow, field: string): number {
810
1065
  return value;
811
1066
  }
812
1067
 
1068
+ function kindField(row: SqlRow, field: string): OutboxRecordKind {
1069
+ const value = stringField(row, field);
1070
+ if (
1071
+ value === "event.publish" || value === "job.create" ||
1072
+ value === "job.submit"
1073
+ ) {
1074
+ return value;
1075
+ }
1076
+ throw new Error(`Expected SQL field ${field} to be an outbox record kind`);
1077
+ }
1078
+
813
1079
  function stateField(row: SqlRow, field: string): OutboxMessageState {
814
1080
  const value = stringField(row, field);
815
1081
  if (value === "pending" || value === "dispatched" || value === "failed") {