@qlever-llc/trellis 0.8.4 → 0.9.0-rc.10

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 (693) hide show
  1. package/README.md +1 -1
  2. package/bin/trellis-generate.js +0 -0
  3. package/esm/auth/browser/login.d.ts.map +1 -1
  4. package/esm/auth/browser/login.js +46 -3
  5. package/esm/auth/browser/portal.d.ts.map +1 -1
  6. package/esm/auth/browser/portal.js +5 -1
  7. package/esm/auth/browser/session.d.ts +18 -7
  8. package/esm/auth/browser/session.d.ts.map +1 -1
  9. package/esm/auth/browser/session.js +47 -11
  10. package/esm/auth/browser/storage.d.ts +6 -1
  11. package/esm/auth/browser/storage.d.ts.map +1 -1
  12. package/esm/auth/browser/storage.js +15 -3
  13. package/esm/auth/browser.d.ts +2 -2
  14. package/esm/auth/browser.d.ts.map +1 -1
  15. package/esm/auth/browser.js +1 -1
  16. package/esm/auth/device_activation.d.ts +36 -33
  17. package/esm/auth/device_activation.d.ts.map +1 -1
  18. package/esm/auth/device_activation.js +26 -22
  19. package/esm/auth/mod.d.ts +4 -4
  20. package/esm/auth/mod.d.ts.map +1 -1
  21. package/esm/auth/mod.js +2 -2
  22. package/esm/auth/proof.d.ts +3 -1
  23. package/esm/auth/proof.d.ts.map +1 -1
  24. package/esm/auth/proof.js +21 -15
  25. package/esm/auth/protocol.d.ts +2457 -941
  26. package/esm/auth/protocol.d.ts.map +1 -1
  27. package/esm/auth/protocol.js +747 -375
  28. package/esm/auth/schemas.d.ts +25 -4
  29. package/esm/auth/schemas.d.ts.map +1 -1
  30. package/esm/auth/schemas.js +14 -4
  31. package/esm/auth/session_auth.d.ts +1 -1
  32. package/esm/auth/session_auth.d.ts.map +1 -1
  33. package/esm/auth/session_auth.js +7 -1
  34. package/esm/client_connect.d.ts +2 -0
  35. package/esm/client_connect.d.ts.map +1 -1
  36. package/esm/client_connect.js +76 -15
  37. package/esm/contract.d.ts +3 -0
  38. package/esm/contract.d.ts.map +1 -1
  39. package/esm/contract_support/mod.d.ts +422 -43
  40. package/esm/contract_support/mod.d.ts.map +1 -1
  41. package/esm/contract_support/mod.js +734 -33
  42. package/esm/contract_support/protocol.d.ts +20 -5
  43. package/esm/contract_support/protocol.d.ts.map +1 -1
  44. package/esm/contract_support/protocol.js +18 -10
  45. package/esm/contract_support/runtime.d.ts +11 -0
  46. package/esm/contract_support/runtime.d.ts.map +1 -1
  47. package/esm/contract_support/schema_pointers.d.ts.map +1 -1
  48. package/esm/contract_support/schema_pointers.js +32 -14
  49. package/esm/device.d.ts +2 -0
  50. package/esm/device.d.ts.map +1 -1
  51. package/esm/device.js +3 -0
  52. package/esm/errors/AuthError.d.ts +2 -1
  53. package/esm/errors/AuthError.d.ts.map +1 -1
  54. package/esm/errors/AuthError.js +8 -3
  55. package/esm/errors/index.d.ts +4 -4
  56. package/esm/errors/index.d.ts.map +1 -1
  57. package/esm/errors/index.js +1 -0
  58. package/esm/generated-sdk/auth/api.d.ts +27 -9
  59. package/esm/generated-sdk/auth/api.d.ts.map +1 -1
  60. package/esm/generated-sdk/auth/api.js +16 -590
  61. package/esm/generated-sdk/auth/client.d.ts +91 -85
  62. package/esm/generated-sdk/auth/client.d.ts.map +1 -1
  63. package/esm/generated-sdk/auth/contract.d.ts +1 -1
  64. package/esm/generated-sdk/auth/contract.d.ts.map +1 -1
  65. package/esm/generated-sdk/auth/contract.js +4 -2
  66. package/esm/generated-sdk/auth/mod.d.ts +1 -0
  67. package/esm/generated-sdk/auth/mod.d.ts.map +1 -1
  68. package/esm/generated-sdk/auth/owned_api.d.ts +3 -0
  69. package/esm/generated-sdk/auth/owned_api.d.ts.map +1 -0
  70. package/esm/generated-sdk/auth/owned_api.js +594 -0
  71. package/esm/generated-sdk/auth/schemas.d.ts +9959 -5160
  72. package/esm/generated-sdk/auth/schemas.d.ts.map +1 -1
  73. package/esm/generated-sdk/auth/schemas.js +136 -137
  74. package/esm/generated-sdk/auth/types.d.ts +2418 -1557
  75. package/esm/generated-sdk/auth/types.d.ts.map +1 -1
  76. package/esm/generated-sdk/auth/types.js +1 -1
  77. package/esm/generated-sdk/health/api.d.ts +24 -9
  78. package/esm/generated-sdk/health/api.d.ts.map +1 -1
  79. package/esm/generated-sdk/health/api.js +12 -20
  80. package/esm/generated-sdk/health/client.d.ts +2 -1
  81. package/esm/generated-sdk/health/client.d.ts.map +1 -1
  82. package/esm/generated-sdk/health/contract.d.ts.map +1 -1
  83. package/esm/generated-sdk/health/contract.js +2 -0
  84. package/esm/generated-sdk/health/owned_api.d.ts +3 -0
  85. package/esm/generated-sdk/health/owned_api.d.ts.map +1 -0
  86. package/esm/generated-sdk/health/owned_api.js +16 -0
  87. package/esm/generated-sdk/health/types.d.ts +2 -0
  88. package/esm/generated-sdk/health/types.d.ts.map +1 -1
  89. package/esm/generated-sdk/jobs/api.d.ts +33 -9
  90. package/esm/generated-sdk/jobs/api.d.ts.map +1 -1
  91. package/esm/generated-sdk/jobs/api.js +22 -87
  92. package/esm/generated-sdk/jobs/client.d.ts +9 -2
  93. package/esm/generated-sdk/jobs/client.d.ts.map +1 -1
  94. package/esm/generated-sdk/jobs/contract.d.ts +1 -1
  95. package/esm/generated-sdk/jobs/contract.d.ts.map +1 -1
  96. package/esm/generated-sdk/jobs/contract.js +4 -2
  97. package/esm/generated-sdk/jobs/owned_api.d.ts +3 -0
  98. package/esm/generated-sdk/jobs/owned_api.d.ts.map +1 -0
  99. package/esm/generated-sdk/jobs/owned_api.js +118 -0
  100. package/esm/generated-sdk/jobs/schemas.d.ts +336 -123
  101. package/esm/generated-sdk/jobs/schemas.d.ts.map +1 -1
  102. package/esm/generated-sdk/jobs/schemas.js +17 -15
  103. package/esm/generated-sdk/jobs/types.d.ts +144 -34
  104. package/esm/generated-sdk/jobs/types.d.ts.map +1 -1
  105. package/esm/generated-sdk/jobs/types.js +36 -1
  106. package/esm/generated-sdk/state/api.d.ts +27 -9
  107. package/esm/generated-sdk/state/api.d.ts.map +1 -1
  108. package/esm/generated-sdk/state/api.js +16 -71
  109. package/esm/generated-sdk/state/client.d.ts +4 -2
  110. package/esm/generated-sdk/state/client.d.ts.map +1 -1
  111. package/esm/generated-sdk/state/contract.d.ts +1 -1
  112. package/esm/generated-sdk/state/contract.d.ts.map +1 -1
  113. package/esm/generated-sdk/state/contract.js +4 -2
  114. package/esm/generated-sdk/state/owned_api.d.ts +3 -0
  115. package/esm/generated-sdk/state/owned_api.d.ts.map +1 -0
  116. package/esm/generated-sdk/state/owned_api.js +66 -0
  117. package/esm/generated-sdk/state/schemas.d.ts +264 -284
  118. package/esm/generated-sdk/state/schemas.d.ts.map +1 -1
  119. package/esm/generated-sdk/state/schemas.js +6 -6
  120. package/esm/generated-sdk/state/types.d.ts +24 -23
  121. package/esm/generated-sdk/state/types.d.ts.map +1 -1
  122. package/esm/generated-sdk/state/types.js +1 -1
  123. package/esm/generated-sdk/trellis-core/api.d.ts +27 -9
  124. package/esm/generated-sdk/trellis-core/api.d.ts.map +1 -1
  125. package/esm/generated-sdk/trellis-core/api.js +16 -39
  126. package/esm/generated-sdk/trellis-core/client.d.ts +5 -2
  127. package/esm/generated-sdk/trellis-core/client.d.ts.map +1 -1
  128. package/esm/generated-sdk/trellis-core/contract.d.ts +1 -1
  129. package/esm/generated-sdk/trellis-core/contract.d.ts.map +1 -1
  130. package/esm/generated-sdk/trellis-core/contract.js +4 -2
  131. package/esm/generated-sdk/trellis-core/owned_api.d.ts +3 -0
  132. package/esm/generated-sdk/trellis-core/owned_api.d.ts.map +1 -0
  133. package/esm/generated-sdk/trellis-core/owned_api.js +42 -0
  134. package/esm/generated-sdk/trellis-core/schemas.d.ts +259 -11
  135. package/esm/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
  136. package/esm/generated-sdk/trellis-core/schemas.js +5 -3
  137. package/esm/generated-sdk/trellis-core/types.d.ts +56 -1
  138. package/esm/generated-sdk/trellis-core/types.d.ts.map +1 -1
  139. package/esm/generated-sdk/trellis-core/types.js +1 -1
  140. package/esm/helpers.d.ts.map +1 -1
  141. package/esm/index.d.ts +4 -3
  142. package/esm/index.d.ts.map +1 -1
  143. package/esm/index.js +1 -0
  144. package/esm/jobs.d.ts +10 -1
  145. package/esm/jobs.d.ts.map +1 -1
  146. package/esm/jobs.js +16 -1
  147. package/esm/kv.d.ts.map +1 -1
  148. package/esm/kv.js +10 -4
  149. package/esm/models/auth/rpc/Logout.d.ts +4 -4
  150. package/esm/models/auth/rpc/Logout.d.ts.map +1 -1
  151. package/esm/models/auth/rpc/Logout.js +2 -2
  152. package/esm/models/trellis/Page.d.ts +2 -0
  153. package/esm/models/trellis/Page.d.ts.map +1 -0
  154. package/esm/models/trellis/Page.js +1 -0
  155. package/esm/models/trellis/State.d.ts +1 -0
  156. package/esm/models/trellis/State.d.ts.map +1 -1
  157. package/esm/models/trellis/State.js +1 -0
  158. package/esm/models/trellis/rpc/StateList.d.ts +9 -12
  159. package/esm/models/trellis/rpc/StateList.d.ts.map +1 -1
  160. package/esm/models/trellis/rpc/StateList.js +16 -18
  161. package/esm/npm/src/auth/browser/login.d.ts.map +1 -1
  162. package/esm/npm/src/auth/browser/login.js +46 -3
  163. package/esm/npm/src/auth/browser/portal.d.ts.map +1 -1
  164. package/esm/npm/src/auth/browser/portal.js +5 -1
  165. package/esm/npm/src/auth/browser/session.d.ts +18 -7
  166. package/esm/npm/src/auth/browser/session.d.ts.map +1 -1
  167. package/esm/npm/src/auth/browser/session.js +47 -11
  168. package/esm/npm/src/auth/browser/storage.d.ts +6 -1
  169. package/esm/npm/src/auth/browser/storage.d.ts.map +1 -1
  170. package/esm/npm/src/auth/browser/storage.js +15 -3
  171. package/esm/npm/src/auth/browser.d.ts +2 -2
  172. package/esm/npm/src/auth/browser.d.ts.map +1 -1
  173. package/esm/npm/src/auth/browser.js +1 -1
  174. package/esm/npm/src/auth/device_activation.d.ts +36 -33
  175. package/esm/npm/src/auth/device_activation.d.ts.map +1 -1
  176. package/esm/npm/src/auth/device_activation.js +26 -22
  177. package/esm/npm/src/auth/mod.d.ts +4 -4
  178. package/esm/npm/src/auth/mod.d.ts.map +1 -1
  179. package/esm/npm/src/auth/mod.js +2 -2
  180. package/esm/npm/src/auth/proof.d.ts +3 -1
  181. package/esm/npm/src/auth/proof.d.ts.map +1 -1
  182. package/esm/npm/src/auth/proof.js +21 -15
  183. package/esm/npm/src/auth/protocol.d.ts +2457 -941
  184. package/esm/npm/src/auth/protocol.d.ts.map +1 -1
  185. package/esm/npm/src/auth/protocol.js +747 -375
  186. package/esm/npm/src/auth/schemas.d.ts +25 -4
  187. package/esm/npm/src/auth/schemas.d.ts.map +1 -1
  188. package/esm/npm/src/auth/schemas.js +14 -4
  189. package/esm/npm/src/auth/session_auth.d.ts +1 -1
  190. package/esm/npm/src/auth/session_auth.d.ts.map +1 -1
  191. package/esm/npm/src/auth/session_auth.js +7 -1
  192. package/esm/npm/src/client_connect.d.ts +2 -0
  193. package/esm/npm/src/client_connect.d.ts.map +1 -1
  194. package/esm/npm/src/client_connect.js +76 -15
  195. package/esm/npm/src/contract.d.ts +3 -0
  196. package/esm/npm/src/contract.d.ts.map +1 -1
  197. package/esm/npm/src/contract_support/mod.d.ts +422 -43
  198. package/esm/npm/src/contract_support/mod.d.ts.map +1 -1
  199. package/esm/npm/src/contract_support/mod.js +734 -33
  200. package/esm/npm/src/contract_support/protocol.d.ts +20 -5
  201. package/esm/npm/src/contract_support/protocol.d.ts.map +1 -1
  202. package/esm/npm/src/contract_support/protocol.js +18 -10
  203. package/esm/npm/src/contract_support/runtime.d.ts +11 -0
  204. package/esm/npm/src/contract_support/runtime.d.ts.map +1 -1
  205. package/esm/npm/src/contract_support/schema_pointers.d.ts.map +1 -1
  206. package/esm/npm/src/contract_support/schema_pointers.js +32 -14
  207. package/esm/npm/src/device/deno.d.ts.map +1 -1
  208. package/esm/npm/src/device/deno.js +6 -0
  209. package/esm/npm/src/device.d.ts +2 -0
  210. package/esm/npm/src/device.d.ts.map +1 -1
  211. package/esm/npm/src/device.js +3 -0
  212. package/esm/npm/src/errors/AuthError.d.ts +2 -1
  213. package/esm/npm/src/errors/AuthError.d.ts.map +1 -1
  214. package/esm/npm/src/errors/AuthError.js +8 -3
  215. package/esm/npm/src/errors/index.d.ts +4 -4
  216. package/esm/npm/src/errors/index.d.ts.map +1 -1
  217. package/esm/npm/src/errors/index.js +1 -0
  218. package/esm/npm/src/generate.js +39 -26
  219. package/esm/npm/src/helpers.d.ts.map +1 -1
  220. package/esm/npm/src/index.d.ts +4 -3
  221. package/esm/npm/src/index.d.ts.map +1 -1
  222. package/esm/npm/src/index.js +1 -0
  223. package/esm/npm/src/jobs.d.ts +10 -1
  224. package/esm/npm/src/jobs.d.ts.map +1 -1
  225. package/esm/npm/src/jobs.js +16 -1
  226. package/esm/npm/src/kv.d.ts.map +1 -1
  227. package/esm/npm/src/kv.js +10 -4
  228. package/esm/npm/src/models/auth/rpc/Logout.d.ts +4 -4
  229. package/esm/npm/src/models/auth/rpc/Logout.d.ts.map +1 -1
  230. package/esm/npm/src/models/auth/rpc/Logout.js +2 -2
  231. package/esm/npm/src/models/trellis/Page.d.ts +2 -0
  232. package/esm/npm/src/models/trellis/Page.d.ts.map +1 -0
  233. package/esm/npm/src/models/trellis/Page.js +1 -0
  234. package/esm/npm/src/models/trellis/State.d.ts +1 -0
  235. package/esm/npm/src/models/trellis/State.d.ts.map +1 -1
  236. package/esm/npm/src/models/trellis/State.js +1 -0
  237. package/esm/npm/src/models/trellis/rpc/StateList.d.ts +9 -12
  238. package/esm/npm/src/models/trellis/rpc/StateList.d.ts.map +1 -1
  239. package/esm/npm/src/models/trellis/rpc/StateList.js +16 -18
  240. package/esm/npm/src/operations.d.ts +16 -7
  241. package/esm/npm/src/operations.d.ts.map +1 -1
  242. package/esm/npm/src/operations.js +84 -19
  243. package/esm/npm/src/runtime_transport.d.ts +2 -0
  244. package/esm/npm/src/runtime_transport.d.ts.map +1 -1
  245. package/esm/npm/src/runtime_transport.js +1 -0
  246. package/esm/npm/src/server/internal_jobs/active-job.d.ts +2 -1
  247. package/esm/npm/src/server/internal_jobs/active-job.d.ts.map +1 -1
  248. package/esm/npm/src/server/internal_jobs/active-job.js +3 -0
  249. package/esm/npm/src/server/internal_jobs/job-manager.d.ts +4 -1
  250. package/esm/npm/src/server/internal_jobs/job-manager.d.ts.map +1 -1
  251. package/esm/npm/src/server/internal_jobs/job-manager.js +61 -1
  252. package/esm/npm/src/server/internal_jobs/projection.js +1 -0
  253. package/esm/npm/src/server/internal_jobs/runtime-worker.d.ts +13 -1
  254. package/esm/npm/src/server/internal_jobs/runtime-worker.d.ts.map +1 -1
  255. package/esm/npm/src/server/internal_jobs/runtime-worker.js +73 -13
  256. package/esm/npm/src/server/internal_jobs/types.d.ts +19 -0
  257. package/esm/npm/src/server/internal_jobs/types.d.ts.map +1 -1
  258. package/esm/npm/src/server/internal_jobs/types.js +10 -0
  259. package/esm/npm/src/server/runtime.d.ts +1 -0
  260. package/esm/npm/src/server/runtime.d.ts.map +1 -1
  261. package/esm/npm/src/server/service.d.ts +10 -1
  262. package/esm/npm/src/server/service.d.ts.map +1 -1
  263. package/esm/npm/src/server/service.js +210 -64
  264. package/esm/npm/src/server/transfer.d.ts.map +1 -1
  265. package/esm/npm/src/server/transfer.js +4 -0
  266. package/esm/npm/src/server.d.ts.map +1 -1
  267. package/esm/npm/src/server.js +337 -34
  268. package/esm/npm/src/store.d.ts +8 -1
  269. package/esm/npm/src/store.d.ts.map +1 -1
  270. package/esm/npm/src/store.js +46 -8
  271. package/esm/npm/src/transfer.d.ts +3 -0
  272. package/esm/npm/src/transfer.d.ts.map +1 -1
  273. package/esm/npm/src/transfer.js +20 -30
  274. package/esm/npm/src/trellis.d.ts +85 -22
  275. package/esm/npm/src/trellis.d.ts.map +1 -1
  276. package/esm/npm/src/trellis.js +525 -61
  277. package/esm/operations.d.ts +16 -7
  278. package/esm/operations.d.ts.map +1 -1
  279. package/esm/operations.js +84 -19
  280. package/esm/runtime_transport.d.ts +2 -0
  281. package/esm/runtime_transport.d.ts.map +1 -1
  282. package/esm/runtime_transport.js +1 -0
  283. package/esm/store.d.ts +8 -1
  284. package/esm/store.d.ts.map +1 -1
  285. package/esm/store.js +46 -8
  286. package/esm/transfer.d.ts +3 -0
  287. package/esm/transfer.d.ts.map +1 -1
  288. package/esm/transfer.js +20 -30
  289. package/esm/trellis.d.ts +85 -22
  290. package/esm/trellis.d.ts.map +1 -1
  291. package/esm/trellis.js +525 -61
  292. package/package.json +6 -3
  293. package/script/auth/browser/login.d.ts.map +1 -1
  294. package/script/auth/browser/login.js +46 -3
  295. package/script/auth/browser/portal.d.ts.map +1 -1
  296. package/script/auth/browser/portal.js +5 -1
  297. package/script/auth/browser/session.d.ts +18 -7
  298. package/script/auth/browser/session.d.ts.map +1 -1
  299. package/script/auth/browser/session.js +47 -11
  300. package/script/auth/browser/storage.d.ts +6 -1
  301. package/script/auth/browser/storage.d.ts.map +1 -1
  302. package/script/auth/browser/storage.js +15 -3
  303. package/script/auth/browser.d.ts +2 -2
  304. package/script/auth/browser.d.ts.map +1 -1
  305. package/script/auth/browser.js +2 -1
  306. package/script/auth/device_activation.d.ts +36 -33
  307. package/script/auth/device_activation.d.ts.map +1 -1
  308. package/script/auth/device_activation.js +25 -21
  309. package/script/auth/mod.d.ts +4 -4
  310. package/script/auth/mod.d.ts.map +1 -1
  311. package/script/auth/mod.js +132 -137
  312. package/script/auth/proof.d.ts +3 -1
  313. package/script/auth/proof.d.ts.map +1 -1
  314. package/script/auth/proof.js +21 -15
  315. package/script/auth/protocol.d.ts +2457 -941
  316. package/script/auth/protocol.d.ts.map +1 -1
  317. package/script/auth/protocol.js +749 -377
  318. package/script/auth/schemas.d.ts +25 -4
  319. package/script/auth/schemas.d.ts.map +1 -1
  320. package/script/auth/schemas.js +16 -5
  321. package/script/auth/session_auth.d.ts +1 -1
  322. package/script/auth/session_auth.d.ts.map +1 -1
  323. package/script/auth/session_auth.js +7 -1
  324. package/script/client_connect.d.ts +2 -0
  325. package/script/client_connect.d.ts.map +1 -1
  326. package/script/client_connect.js +76 -15
  327. package/script/contract.d.ts +3 -0
  328. package/script/contract.d.ts.map +1 -1
  329. package/script/contract_support/mod.d.ts +422 -43
  330. package/script/contract_support/mod.d.ts.map +1 -1
  331. package/script/contract_support/mod.js +757 -51
  332. package/script/contract_support/protocol.d.ts +20 -5
  333. package/script/contract_support/protocol.d.ts.map +1 -1
  334. package/script/contract_support/protocol.js +20 -11
  335. package/script/contract_support/runtime.d.ts +11 -0
  336. package/script/contract_support/runtime.d.ts.map +1 -1
  337. package/script/contract_support/schema_pointers.d.ts.map +1 -1
  338. package/script/contract_support/schema_pointers.js +32 -14
  339. package/script/device.d.ts +2 -0
  340. package/script/device.d.ts.map +1 -1
  341. package/script/device.js +3 -0
  342. package/script/errors/AuthError.d.ts +2 -1
  343. package/script/errors/AuthError.d.ts.map +1 -1
  344. package/script/errors/AuthError.js +8 -3
  345. package/script/errors/index.d.ts +4 -4
  346. package/script/errors/index.d.ts.map +1 -1
  347. package/script/errors/index.js +1 -0
  348. package/script/generated-sdk/auth/api.d.ts +27 -9
  349. package/script/generated-sdk/auth/api.d.ts.map +1 -1
  350. package/script/generated-sdk/auth/api.js +17 -591
  351. package/script/generated-sdk/auth/client.d.ts +91 -85
  352. package/script/generated-sdk/auth/client.d.ts.map +1 -1
  353. package/script/generated-sdk/auth/contract.d.ts +1 -1
  354. package/script/generated-sdk/auth/contract.d.ts.map +1 -1
  355. package/script/generated-sdk/auth/contract.js +4 -2
  356. package/script/generated-sdk/auth/mod.d.ts +1 -0
  357. package/script/generated-sdk/auth/mod.d.ts.map +1 -1
  358. package/script/generated-sdk/auth/owned_api.d.ts +3 -0
  359. package/script/generated-sdk/auth/owned_api.d.ts.map +1 -0
  360. package/script/generated-sdk/auth/owned_api.js +597 -0
  361. package/script/generated-sdk/auth/schemas.d.ts +9959 -5160
  362. package/script/generated-sdk/auth/schemas.d.ts.map +1 -1
  363. package/script/generated-sdk/auth/schemas.js +139 -140
  364. package/script/generated-sdk/auth/types.d.ts +2418 -1557
  365. package/script/generated-sdk/auth/types.d.ts.map +1 -1
  366. package/script/generated-sdk/auth/types.js +1 -1
  367. package/script/generated-sdk/health/api.d.ts +24 -9
  368. package/script/generated-sdk/health/api.d.ts.map +1 -1
  369. package/script/generated-sdk/health/api.js +13 -21
  370. package/script/generated-sdk/health/client.d.ts +2 -1
  371. package/script/generated-sdk/health/client.d.ts.map +1 -1
  372. package/script/generated-sdk/health/contract.d.ts.map +1 -1
  373. package/script/generated-sdk/health/contract.js +2 -0
  374. package/script/generated-sdk/health/owned_api.d.ts +3 -0
  375. package/script/generated-sdk/health/owned_api.d.ts.map +1 -0
  376. package/script/generated-sdk/health/owned_api.js +19 -0
  377. package/script/generated-sdk/health/types.d.ts +2 -0
  378. package/script/generated-sdk/health/types.d.ts.map +1 -1
  379. package/script/generated-sdk/jobs/api.d.ts +33 -9
  380. package/script/generated-sdk/jobs/api.d.ts.map +1 -1
  381. package/script/generated-sdk/jobs/api.js +23 -88
  382. package/script/generated-sdk/jobs/client.d.ts +9 -2
  383. package/script/generated-sdk/jobs/client.d.ts.map +1 -1
  384. package/script/generated-sdk/jobs/contract.d.ts +1 -1
  385. package/script/generated-sdk/jobs/contract.d.ts.map +1 -1
  386. package/script/generated-sdk/jobs/contract.js +4 -2
  387. package/script/generated-sdk/jobs/owned_api.d.ts +3 -0
  388. package/script/generated-sdk/jobs/owned_api.d.ts.map +1 -0
  389. package/script/generated-sdk/jobs/owned_api.js +154 -0
  390. package/script/generated-sdk/jobs/schemas.d.ts +336 -123
  391. package/script/generated-sdk/jobs/schemas.d.ts.map +1 -1
  392. package/script/generated-sdk/jobs/schemas.js +18 -16
  393. package/script/generated-sdk/jobs/types.d.ts +144 -34
  394. package/script/generated-sdk/jobs/types.d.ts.map +1 -1
  395. package/script/generated-sdk/jobs/types.js +38 -2
  396. package/script/generated-sdk/state/api.d.ts +27 -9
  397. package/script/generated-sdk/state/api.d.ts.map +1 -1
  398. package/script/generated-sdk/state/api.js +17 -72
  399. package/script/generated-sdk/state/client.d.ts +4 -2
  400. package/script/generated-sdk/state/client.d.ts.map +1 -1
  401. package/script/generated-sdk/state/contract.d.ts +1 -1
  402. package/script/generated-sdk/state/contract.d.ts.map +1 -1
  403. package/script/generated-sdk/state/contract.js +4 -2
  404. package/script/generated-sdk/state/owned_api.d.ts +3 -0
  405. package/script/generated-sdk/state/owned_api.d.ts.map +1 -0
  406. package/script/generated-sdk/state/owned_api.js +69 -0
  407. package/script/generated-sdk/state/schemas.d.ts +264 -284
  408. package/script/generated-sdk/state/schemas.d.ts.map +1 -1
  409. package/script/generated-sdk/state/schemas.js +6 -6
  410. package/script/generated-sdk/state/types.d.ts +24 -23
  411. package/script/generated-sdk/state/types.d.ts.map +1 -1
  412. package/script/generated-sdk/state/types.js +1 -1
  413. package/script/generated-sdk/trellis-core/api.d.ts +27 -9
  414. package/script/generated-sdk/trellis-core/api.d.ts.map +1 -1
  415. package/script/generated-sdk/trellis-core/api.js +17 -40
  416. package/script/generated-sdk/trellis-core/client.d.ts +5 -2
  417. package/script/generated-sdk/trellis-core/client.d.ts.map +1 -1
  418. package/script/generated-sdk/trellis-core/contract.d.ts +1 -1
  419. package/script/generated-sdk/trellis-core/contract.d.ts.map +1 -1
  420. package/script/generated-sdk/trellis-core/contract.js +4 -2
  421. package/script/generated-sdk/trellis-core/owned_api.d.ts +3 -0
  422. package/script/generated-sdk/trellis-core/owned_api.d.ts.map +1 -0
  423. package/script/generated-sdk/trellis-core/owned_api.js +45 -0
  424. package/script/generated-sdk/trellis-core/schemas.d.ts +259 -11
  425. package/script/generated-sdk/trellis-core/schemas.d.ts.map +1 -1
  426. package/script/generated-sdk/trellis-core/schemas.js +6 -4
  427. package/script/generated-sdk/trellis-core/types.d.ts +56 -1
  428. package/script/generated-sdk/trellis-core/types.d.ts.map +1 -1
  429. package/script/generated-sdk/trellis-core/types.js +1 -1
  430. package/script/helpers.d.ts.map +1 -1
  431. package/script/index.d.ts +4 -3
  432. package/script/index.d.ts.map +1 -1
  433. package/script/index.js +5 -2
  434. package/script/jobs.d.ts +10 -1
  435. package/script/jobs.d.ts.map +1 -1
  436. package/script/jobs.js +17 -2
  437. package/script/kv.d.ts.map +1 -1
  438. package/script/kv.js +10 -4
  439. package/script/models/auth/rpc/Logout.d.ts +4 -4
  440. package/script/models/auth/rpc/Logout.d.ts.map +1 -1
  441. package/script/models/auth/rpc/Logout.js +3 -3
  442. package/script/models/trellis/Page.d.ts +2 -0
  443. package/script/models/trellis/Page.d.ts.map +1 -0
  444. package/script/models/trellis/Page.js +6 -0
  445. package/script/models/trellis/State.d.ts +1 -0
  446. package/script/models/trellis/State.d.ts.map +1 -1
  447. package/script/models/trellis/State.js +1 -0
  448. package/script/models/trellis/rpc/StateList.d.ts +9 -12
  449. package/script/models/trellis/rpc/StateList.d.ts.map +1 -1
  450. package/script/models/trellis/rpc/StateList.js +16 -18
  451. package/script/npm/src/auth/browser/login.d.ts.map +1 -1
  452. package/script/npm/src/auth/browser/login.js +46 -3
  453. package/script/npm/src/auth/browser/portal.d.ts.map +1 -1
  454. package/script/npm/src/auth/browser/portal.js +5 -1
  455. package/script/npm/src/auth/browser/session.d.ts +18 -7
  456. package/script/npm/src/auth/browser/session.d.ts.map +1 -1
  457. package/script/npm/src/auth/browser/session.js +47 -11
  458. package/script/npm/src/auth/browser/storage.d.ts +6 -1
  459. package/script/npm/src/auth/browser/storage.d.ts.map +1 -1
  460. package/script/npm/src/auth/browser/storage.js +15 -3
  461. package/script/npm/src/auth/browser.d.ts +2 -2
  462. package/script/npm/src/auth/browser.d.ts.map +1 -1
  463. package/script/npm/src/auth/browser.js +2 -1
  464. package/script/npm/src/auth/device_activation.d.ts +36 -33
  465. package/script/npm/src/auth/device_activation.d.ts.map +1 -1
  466. package/script/npm/src/auth/device_activation.js +25 -21
  467. package/script/npm/src/auth/mod.d.ts +4 -4
  468. package/script/npm/src/auth/mod.d.ts.map +1 -1
  469. package/script/npm/src/auth/mod.js +132 -137
  470. package/script/npm/src/auth/proof.d.ts +3 -1
  471. package/script/npm/src/auth/proof.d.ts.map +1 -1
  472. package/script/npm/src/auth/proof.js +21 -15
  473. package/script/npm/src/auth/protocol.d.ts +2457 -941
  474. package/script/npm/src/auth/protocol.d.ts.map +1 -1
  475. package/script/npm/src/auth/protocol.js +749 -377
  476. package/script/npm/src/auth/schemas.d.ts +25 -4
  477. package/script/npm/src/auth/schemas.d.ts.map +1 -1
  478. package/script/npm/src/auth/schemas.js +16 -5
  479. package/script/npm/src/auth/session_auth.d.ts +1 -1
  480. package/script/npm/src/auth/session_auth.d.ts.map +1 -1
  481. package/script/npm/src/auth/session_auth.js +7 -1
  482. package/script/npm/src/client_connect.d.ts +2 -0
  483. package/script/npm/src/client_connect.d.ts.map +1 -1
  484. package/script/npm/src/client_connect.js +76 -15
  485. package/script/npm/src/contract.d.ts +3 -0
  486. package/script/npm/src/contract.d.ts.map +1 -1
  487. package/script/npm/src/contract_support/mod.d.ts +422 -43
  488. package/script/npm/src/contract_support/mod.d.ts.map +1 -1
  489. package/script/npm/src/contract_support/mod.js +757 -51
  490. package/script/npm/src/contract_support/protocol.d.ts +20 -5
  491. package/script/npm/src/contract_support/protocol.d.ts.map +1 -1
  492. package/script/npm/src/contract_support/protocol.js +20 -11
  493. package/script/npm/src/contract_support/runtime.d.ts +11 -0
  494. package/script/npm/src/contract_support/runtime.d.ts.map +1 -1
  495. package/script/npm/src/contract_support/schema_pointers.d.ts.map +1 -1
  496. package/script/npm/src/contract_support/schema_pointers.js +32 -14
  497. package/script/npm/src/device/deno.d.ts.map +1 -1
  498. package/script/npm/src/device/deno.js +6 -0
  499. package/script/npm/src/device.d.ts +2 -0
  500. package/script/npm/src/device.d.ts.map +1 -1
  501. package/script/npm/src/device.js +3 -0
  502. package/script/npm/src/errors/AuthError.d.ts +2 -1
  503. package/script/npm/src/errors/AuthError.d.ts.map +1 -1
  504. package/script/npm/src/errors/AuthError.js +8 -3
  505. package/script/npm/src/errors/index.d.ts +4 -4
  506. package/script/npm/src/errors/index.d.ts.map +1 -1
  507. package/script/npm/src/errors/index.js +1 -0
  508. package/script/npm/src/generate.js +39 -59
  509. package/script/npm/src/helpers.d.ts.map +1 -1
  510. package/script/npm/src/index.d.ts +4 -3
  511. package/script/npm/src/index.d.ts.map +1 -1
  512. package/script/npm/src/index.js +5 -2
  513. package/script/npm/src/jobs.d.ts +10 -1
  514. package/script/npm/src/jobs.d.ts.map +1 -1
  515. package/script/npm/src/jobs.js +17 -2
  516. package/script/npm/src/kv.d.ts.map +1 -1
  517. package/script/npm/src/kv.js +10 -4
  518. package/script/npm/src/models/auth/rpc/Logout.d.ts +4 -4
  519. package/script/npm/src/models/auth/rpc/Logout.d.ts.map +1 -1
  520. package/script/npm/src/models/auth/rpc/Logout.js +3 -3
  521. package/script/npm/src/models/trellis/Page.d.ts +2 -0
  522. package/script/npm/src/models/trellis/Page.d.ts.map +1 -0
  523. package/script/npm/src/models/trellis/Page.js +6 -0
  524. package/script/npm/src/models/trellis/State.d.ts +1 -0
  525. package/script/npm/src/models/trellis/State.d.ts.map +1 -1
  526. package/script/npm/src/models/trellis/State.js +1 -0
  527. package/script/npm/src/models/trellis/rpc/StateList.d.ts +9 -12
  528. package/script/npm/src/models/trellis/rpc/StateList.d.ts.map +1 -1
  529. package/script/npm/src/models/trellis/rpc/StateList.js +16 -18
  530. package/script/npm/src/operations.d.ts +16 -7
  531. package/script/npm/src/operations.d.ts.map +1 -1
  532. package/script/npm/src/operations.js +84 -19
  533. package/script/npm/src/runtime_transport.d.ts +2 -0
  534. package/script/npm/src/runtime_transport.d.ts.map +1 -1
  535. package/script/npm/src/runtime_transport.js +2 -1
  536. package/script/npm/src/server/internal_jobs/active-job.d.ts +2 -1
  537. package/script/npm/src/server/internal_jobs/active-job.d.ts.map +1 -1
  538. package/script/npm/src/server/internal_jobs/active-job.js +3 -0
  539. package/script/npm/src/server/internal_jobs/job-manager.d.ts +4 -1
  540. package/script/npm/src/server/internal_jobs/job-manager.d.ts.map +1 -1
  541. package/script/npm/src/server/internal_jobs/job-manager.js +61 -1
  542. package/script/npm/src/server/internal_jobs/projection.js +1 -0
  543. package/script/npm/src/server/internal_jobs/runtime-worker.d.ts +13 -1
  544. package/script/npm/src/server/internal_jobs/runtime-worker.d.ts.map +1 -1
  545. package/script/npm/src/server/internal_jobs/runtime-worker.js +74 -13
  546. package/script/npm/src/server/internal_jobs/types.d.ts +19 -0
  547. package/script/npm/src/server/internal_jobs/types.d.ts.map +1 -1
  548. package/script/npm/src/server/internal_jobs/types.js +11 -1
  549. package/script/npm/src/server/runtime.d.ts +1 -0
  550. package/script/npm/src/server/runtime.d.ts.map +1 -1
  551. package/script/npm/src/server/service.d.ts +10 -1
  552. package/script/npm/src/server/service.d.ts.map +1 -1
  553. package/script/npm/src/server/service.js +208 -62
  554. package/script/npm/src/server/transfer.d.ts.map +1 -1
  555. package/script/npm/src/server/transfer.js +4 -0
  556. package/script/npm/src/server.d.ts.map +1 -1
  557. package/script/npm/src/server.js +336 -33
  558. package/script/npm/src/store.d.ts +8 -1
  559. package/script/npm/src/store.d.ts.map +1 -1
  560. package/script/npm/src/store.js +46 -8
  561. package/script/npm/src/transfer.d.ts +3 -0
  562. package/script/npm/src/transfer.d.ts.map +1 -1
  563. package/script/npm/src/transfer.js +19 -29
  564. package/script/npm/src/trellis.d.ts +85 -22
  565. package/script/npm/src/trellis.d.ts.map +1 -1
  566. package/script/npm/src/trellis.js +525 -61
  567. package/script/operations.d.ts +16 -7
  568. package/script/operations.d.ts.map +1 -1
  569. package/script/operations.js +84 -19
  570. package/script/runtime_transport.d.ts +2 -0
  571. package/script/runtime_transport.d.ts.map +1 -1
  572. package/script/runtime_transport.js +2 -1
  573. package/script/store.d.ts +8 -1
  574. package/script/store.d.ts.map +1 -1
  575. package/script/store.js +46 -8
  576. package/script/transfer.d.ts +3 -0
  577. package/script/transfer.d.ts.map +1 -1
  578. package/script/transfer.js +19 -29
  579. package/script/trellis.d.ts +85 -22
  580. package/script/trellis.d.ts.map +1 -1
  581. package/script/trellis.js +525 -61
  582. package/src/_dnt.polyfills.ts +274 -0
  583. package/src/_dnt.shims.ts +64 -0
  584. package/src/auth/browser/login.ts +295 -0
  585. package/src/auth/browser/portal.ts +75 -0
  586. package/src/auth/browser/session.ts +197 -0
  587. package/src/auth/browser/storage.ts +105 -0
  588. package/src/auth/browser.ts +82 -0
  589. package/src/auth/device_activation.ts +715 -0
  590. package/src/auth/keys.ts +116 -0
  591. package/src/auth/mod.ts +298 -0
  592. package/src/auth/proof.ts +111 -0
  593. package/src/auth/protocol.ts +1629 -0
  594. package/src/auth/schemas.ts +145 -0
  595. package/src/auth/session_auth.ts +167 -0
  596. package/src/auth/time.ts +15 -0
  597. package/src/auth/trellis_id.ts +9 -0
  598. package/src/auth/types.ts +4 -0
  599. package/src/auth/utils.ts +87 -0
  600. package/src/auth.ts +2 -0
  601. package/src/browser.ts +8 -0
  602. package/src/client.ts +164 -0
  603. package/src/client_connect.ts +1328 -0
  604. package/src/codec.ts +107 -0
  605. package/src/connection.ts +466 -0
  606. package/src/contract.ts +84 -0
  607. package/src/contract_support/canonical.ts +217 -0
  608. package/src/contract_support/mod.ts +5079 -0
  609. package/src/contract_support/protocol.ts +213 -0
  610. package/src/contract_support/runtime.ts +129 -0
  611. package/src/contract_support/schema_pointers.ts +161 -0
  612. package/src/contracts.ts +9 -0
  613. package/src/device/deno.ts +941 -0
  614. package/src/device.ts +989 -0
  615. package/src/env.ts +1 -0
  616. package/src/errors/AuthError.ts +82 -0
  617. package/src/errors/KVError.ts +47 -0
  618. package/src/errors/RemoteError.ts +111 -0
  619. package/src/errors/StoreError.ts +43 -0
  620. package/src/errors/TransferError.ts +43 -0
  621. package/src/errors/TransportError.ts +48 -0
  622. package/src/errors/TrellisError.ts +20 -0
  623. package/src/errors/ValidationError.ts +80 -0
  624. package/src/errors/index.ts +195 -0
  625. package/src/generate.ts +329 -0
  626. package/src/globals.ts +26 -0
  627. package/src/health.ts +28 -0
  628. package/src/helpers.ts +63 -0
  629. package/src/host/mod.ts +9 -0
  630. package/src/host/node.ts +9 -0
  631. package/src/index.ts +233 -0
  632. package/src/jobs.ts +344 -0
  633. package/src/kv.ts +564 -0
  634. package/src/models/auth/rpc/Logout.ts +15 -0
  635. package/src/models/trellis/Page.ts +6 -0
  636. package/src/models/trellis/State.ts +55 -0
  637. package/src/models/trellis/TrellisError.ts +21 -0
  638. package/src/models/trellis/rpc/StateDelete.ts +13 -0
  639. package/src/models/trellis/rpc/StateGet.ts +25 -0
  640. package/src/models/trellis/rpc/StateList.ts +26 -0
  641. package/src/models/trellis/rpc/StatePut.ts +42 -0
  642. package/src/operations.ts +1508 -0
  643. package/src/runtime_transport.ts +132 -0
  644. package/src/sdk/auth.ts +2 -0
  645. package/src/sdk/core.ts +2 -0
  646. package/src/sdk/health.ts +2 -0
  647. package/src/sdk/jobs.ts +2 -0
  648. package/src/sdk/state.ts +2 -0
  649. package/src/server/health.ts +379 -0
  650. package/src/server/health_rpc.ts +51 -0
  651. package/src/server/health_schemas.ts +61 -0
  652. package/src/server/internal_jobs/active-job.ts +115 -0
  653. package/src/server/internal_jobs/bindings.ts +26 -0
  654. package/src/server/internal_jobs/cancellation-registry.ts +71 -0
  655. package/src/server/internal_jobs/heartbeat.ts +120 -0
  656. package/src/server/internal_jobs/job-manager.ts +456 -0
  657. package/src/server/internal_jobs/projection.ts +48 -0
  658. package/src/server/internal_jobs/runtime-worker.ts +741 -0
  659. package/src/server/internal_jobs/types.ts +124 -0
  660. package/src/server/runtime.ts +27 -0
  661. package/src/server/service.ts +2377 -0
  662. package/src/server/subscription.ts +143 -0
  663. package/src/server/transfer.ts +962 -0
  664. package/src/server.ts +1725 -0
  665. package/src/server_logger.ts +10 -0
  666. package/src/service/deno.ts +18 -0
  667. package/src/service/mod.ts +68 -0
  668. package/src/service/node.ts +18 -0
  669. package/src/store.ts +658 -0
  670. package/src/tasks.ts +34 -0
  671. package/src/telemetry/carrier.ts +35 -0
  672. package/src/telemetry/core.ts +31 -0
  673. package/src/telemetry/env.ts +23 -0
  674. package/src/telemetry/mod.ts +26 -0
  675. package/src/telemetry/nats.ts +15 -0
  676. package/src/telemetry/result.ts +20 -0
  677. package/src/telemetry/trace.ts +39 -0
  678. package/src/telemetry/trellis.ts +1 -0
  679. package/src/tracing.ts +28 -0
  680. package/src/transfer.ts +602 -0
  681. package/src/trellis.ts +3650 -0
  682. package/esm/models/trellis/Paginate.d.ts +0 -7
  683. package/esm/models/trellis/Paginate.d.ts.map +0 -1
  684. package/esm/models/trellis/Paginate.js +0 -5
  685. package/esm/npm/src/models/trellis/Paginate.d.ts +0 -7
  686. package/esm/npm/src/models/trellis/Paginate.d.ts.map +0 -1
  687. package/esm/npm/src/models/trellis/Paginate.js +0 -5
  688. package/script/models/trellis/Paginate.d.ts +0 -7
  689. package/script/models/trellis/Paginate.d.ts.map +0 -1
  690. package/script/models/trellis/Paginate.js +0 -11
  691. package/script/npm/src/models/trellis/Paginate.d.ts +0 -7
  692. package/script/npm/src/models/trellis/Paginate.d.ts.map +0 -1
  693. package/script/npm/src/models/trellis/Paginate.js +0 -11
package/src/server.ts ADDED
@@ -0,0 +1,1725 @@
1
+ import type { Msg, NatsConnection } from "@nats-io/nats-core";
2
+ import { Pointer } from "typebox/value";
3
+ import type { TrellisAPI } from "./contracts.js";
4
+ import {
5
+ AsyncResult,
6
+ type BaseError,
7
+ err,
8
+ isErr,
9
+ ok,
10
+ Result,
11
+ } from "@qlever-llc/result";
12
+ import { ulid } from "ulid";
13
+
14
+ import { type JsonValue, parseSchema } from "./codec.js";
15
+ import {
16
+ AuthError,
17
+ TransferError,
18
+ type TrellisErrorInstance,
19
+ UnexpectedError,
20
+ ValidationError,
21
+ } from "./errors/index.js";
22
+ import { RemoteError } from "./errors/RemoteError.js";
23
+ import type { LoggerLike } from "./globals.js";
24
+ import { serverLogger } from "./server_logger.js";
25
+ import {
26
+ type AcceptedOperation,
27
+ type AnyTrellisAPI,
28
+ type AuthRequestsValidateResponse,
29
+ base64urlDecode,
30
+ base64urlEncode,
31
+ buildProofInput,
32
+ buildRuntimeOperationSnapshot,
33
+ type HandlerFn,
34
+ isOperationDeferred,
35
+ isResultLike,
36
+ isTerminalRuntimeOperationSnapshot,
37
+ type MethodsOf,
38
+ type OperationHandlerContext,
39
+ type OperationInputOf,
40
+ type OperationOutputOf,
41
+ type OperationProgressOf,
42
+ type OperationRegistration,
43
+ type OperationRuntimeHandle,
44
+ type OperationsOf,
45
+ type OperationTransferContextOf,
46
+ type OperationTransferHandle,
47
+ type RuntimeOperationAcceptedEnvelope,
48
+ type RuntimeOperationController,
49
+ type RuntimeOperationControlRequest,
50
+ type RuntimeOperationDesc,
51
+ type RuntimeOperationRecord,
52
+ type RuntimeOperationSignal,
53
+ type RuntimeOperationSnapshot,
54
+ type RuntimeOperationState,
55
+ safeJson,
56
+ sha256,
57
+ toArrayBuffer,
58
+ Trellis,
59
+ type TrellisAuth,
60
+ type TrellisMode,
61
+ type TrellisOpts,
62
+ } from "./trellis.js";
63
+ import type { SendTransferGrant } from "./transfer.js";
64
+
65
+ type TrellisServiceRuntimeOpts<TA extends AnyTrellisAPI> =
66
+ & Omit<TrellisOpts<TA>, "api">
67
+ & {
68
+ api: TA;
69
+ transferSupport?: RuntimeOperationTransferSupport;
70
+ version?: string;
71
+ };
72
+
73
+ export type TrellisServiceRuntimeFor<TA extends AnyTrellisAPI = TrellisAPI> =
74
+ & Omit<TrellisServiceRuntime, "mount" | "operation">
75
+ & {
76
+ mount<M extends MethodsOf<TA>>(
77
+ method: M,
78
+ fn: HandlerFn<TA, M>,
79
+ ): Promise<void>;
80
+ operation<O extends OperationsOf<TA>>(
81
+ operation: O,
82
+ ): OperationRegistration<
83
+ OperationInputOf<TA, O>,
84
+ OperationProgressOf<TA, O>,
85
+ OperationOutputOf<TA, O>,
86
+ OperationTransferContextOf<TA, O>
87
+ >;
88
+ };
89
+
90
+ type RegisteredRuntimeOperationDesc = RuntimeOperationDesc & {
91
+ callerCapabilities?: readonly string[];
92
+ };
93
+
94
+ type RuntimeOperationTransferSession = {
95
+ grant: SendTransferGrant;
96
+ transfer: OperationTransferHandle;
97
+ };
98
+
99
+ type RuntimeOperationTransferSupport = {
100
+ openOperationTransfer(args: {
101
+ sessionKey: string;
102
+ store: string;
103
+ key: string;
104
+ expiresInMs: number;
105
+ maxBytes?: number;
106
+ contentType?: string;
107
+ metadata?: Record<string, string>;
108
+ }): AsyncResult<RuntimeOperationTransferSession, TransferError>;
109
+ };
110
+
111
+ function isJsonValue(value: unknown): value is JsonValue {
112
+ if (
113
+ value === null || typeof value === "string" || typeof value === "number" ||
114
+ typeof value === "boolean"
115
+ ) {
116
+ return true;
117
+ }
118
+ if (Array.isArray(value)) return value.every(isJsonValue);
119
+ if (typeof value !== "object") return false;
120
+ if (Object.getPrototypeOf(value) !== Object.prototype) return false;
121
+ return Object.values(value).every(isJsonValue);
122
+ }
123
+
124
+ function asStringPointerValue(
125
+ operation: string,
126
+ input: unknown,
127
+ pointer: `/${string}`,
128
+ field: string,
129
+ ): Result<string, TransferError> {
130
+ const value = Pointer.Get(input as Record<string, unknown>, pointer);
131
+ if (typeof value !== "string" || value.length === 0) {
132
+ return err(
133
+ new TransferError({
134
+ operation: "transfer",
135
+ context: { reason: "invalid_input", operation, field, pointer },
136
+ }),
137
+ );
138
+ }
139
+ return ok(value);
140
+ }
141
+
142
+ function asOptionalStringPointerValue(
143
+ input: unknown,
144
+ pointer?: `/${string}`,
145
+ ): Result<string | undefined, TransferError> {
146
+ if (!pointer) {
147
+ return ok(undefined);
148
+ }
149
+ const value = Pointer.Get(input as Record<string, unknown>, pointer);
150
+ if (value === undefined) {
151
+ return ok(undefined);
152
+ }
153
+ if (typeof value !== "string" || value.length === 0) {
154
+ return err(
155
+ new TransferError({
156
+ operation: "transfer",
157
+ context: { reason: "invalid_input", field: pointer, pointer },
158
+ }),
159
+ );
160
+ }
161
+ return ok(value);
162
+ }
163
+
164
+ function asOptionalStringRecordPointerValue(
165
+ input: unknown,
166
+ pointer?: `/${string}`,
167
+ ): Result<Record<string, string> | undefined, TransferError> {
168
+ if (!pointer) {
169
+ return ok(undefined);
170
+ }
171
+ const value = Pointer.Get(input as Record<string, unknown>, pointer);
172
+ if (value === undefined) {
173
+ return ok(undefined);
174
+ }
175
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
176
+ return err(
177
+ new TransferError({
178
+ operation: "transfer",
179
+ context: { reason: "invalid_input", field: pointer, pointer },
180
+ }),
181
+ );
182
+ }
183
+
184
+ const entries = Object.entries(value as Record<string, unknown>);
185
+ if (
186
+ entries.some(([key, item]) => key.length === 0 || typeof item !== "string")
187
+ ) {
188
+ return err(
189
+ new TransferError({
190
+ operation: "transfer",
191
+ context: { reason: "invalid_input", field: pointer, pointer },
192
+ }),
193
+ );
194
+ }
195
+
196
+ return ok(Object.fromEntries(entries) as Record<string, string>);
197
+ }
198
+
199
+ export class TrellisServiceRuntime extends Trellis<TrellisAPI, TrellisMode> {
200
+ #version?: string;
201
+ #log: LoggerLike;
202
+ #operations = new Map<string, RuntimeOperationRecord>();
203
+ #mountedOperationControls = new Set<string>();
204
+ #stopPromise?: Promise<void>;
205
+ #transferSupport?: RuntimeOperationTransferSupport;
206
+ readonly operations: RuntimeOperationController;
207
+
208
+ private constructor(
209
+ name: string,
210
+ nats: NatsConnection,
211
+ auth: TrellisAuth,
212
+ opts?: TrellisServiceRuntimeOpts<TrellisAPI>,
213
+ ) {
214
+ super(name, nats, auth, { ...opts, log: opts?.log ?? serverLogger });
215
+ this.#version = opts?.version;
216
+ this.#log = (opts?.log ?? serverLogger).child({ lib: "trellis-server" });
217
+ this.#transferSupport = opts?.transferSupport;
218
+ this.operations = {
219
+ get: (operationId) =>
220
+ AsyncResult.from((async () => {
221
+ const runtime = await this.#resolveOperation(operationId);
222
+ if (!runtime) {
223
+ return err(
224
+ new UnexpectedError({
225
+ cause: new Error(`Unknown operation '${operationId}'`),
226
+ }),
227
+ );
228
+ }
229
+ return ok(runtime.snapshot);
230
+ })()),
231
+ started: (operationId) =>
232
+ this.#applyOperationUpdate(operationId, "running", {
233
+ event: { type: "started" },
234
+ }),
235
+ progress: (operationId, progress) =>
236
+ this.#applyOperationUpdate(operationId, "running", {
237
+ patch: { progress },
238
+ event: { type: "progress", progress },
239
+ }),
240
+ complete: (operationId, output) =>
241
+ this.#applyOperationUpdate(operationId, "completed", {
242
+ patch: { output },
243
+ event: { type: "completed" },
244
+ }),
245
+ fail: (operationId, error) =>
246
+ this.#applyOperationUpdate(operationId, "failed", {
247
+ patch: { error: { type: error.name, message: error.message } },
248
+ event: { type: "failed" },
249
+ }),
250
+ cancel: (operationId) =>
251
+ this.#applyOperationUpdate(operationId, "cancelled", {
252
+ event: { type: "cancelled" },
253
+ }),
254
+ signals: (operationId) => this.#signals(operationId),
255
+ nextSignal: (operationId, name) => this.#nextSignal(operationId, name),
256
+ };
257
+ }
258
+
259
+ async *#signals(operationId: string): AsyncIterable<RuntimeOperationSignal> {
260
+ let cursor = 0;
261
+ while (true) {
262
+ const next = await this.#nextSignalAfter(operationId, cursor).take();
263
+ if (isErr(next)) {
264
+ throw next.error;
265
+ }
266
+ cursor = next.sequence;
267
+ yield next;
268
+ }
269
+ }
270
+
271
+ #nextSignal(
272
+ operationId: string,
273
+ name?: string,
274
+ ): AsyncResult<RuntimeOperationSignal, BaseError> {
275
+ return AsyncResult.from((async () => {
276
+ let cursor = 0;
277
+ while (true) {
278
+ const next = await this.#nextSignalAfter(operationId, cursor).take();
279
+ if (isErr(next)) return next;
280
+ cursor = next.sequence;
281
+ if (!name || next.signal === name) return ok(next);
282
+ }
283
+ })());
284
+ }
285
+
286
+ #nextSignalAfter(
287
+ operationId: string,
288
+ afterSequence: number,
289
+ ): AsyncResult<RuntimeOperationSignal, BaseError> {
290
+ return AsyncResult.from((async () => {
291
+ const runtime = await this.#resolveOperation(operationId);
292
+ if (!runtime) {
293
+ return err(
294
+ new UnexpectedError({
295
+ cause: new Error(`Unknown operation '${operationId}'`),
296
+ }),
297
+ );
298
+ }
299
+
300
+ const queued = runtime.signals.find((signal) =>
301
+ signal.sequence > afterSequence
302
+ );
303
+ if (queued) return ok(queued);
304
+ if (runtime.terminal) {
305
+ return err(
306
+ new UnexpectedError({
307
+ cause: new Error("operation already terminal"),
308
+ }),
309
+ );
310
+ }
311
+
312
+ return await new Promise<Result<RuntimeOperationSignal, BaseError>>(
313
+ (resolve) => {
314
+ const waiter = (
315
+ result: Result<RuntimeOperationSignal, BaseError>,
316
+ ) => {
317
+ const value = result.take();
318
+ if (!isErr(value) && value.sequence <= afterSequence) {
319
+ return;
320
+ }
321
+ runtime.signalWaiters.delete(waiter);
322
+ resolve(result);
323
+ };
324
+ runtime.signalWaiters.add(waiter);
325
+ },
326
+ );
327
+ })());
328
+ }
329
+
330
+ async #resolveOperation(
331
+ operationId: string,
332
+ ): Promise<RuntimeOperationRecord | null> {
333
+ const existing = this.#operations.get(operationId);
334
+ if (existing) return existing;
335
+
336
+ const durable = await this.loadOperationRecord(operationId);
337
+ if (!durable) return null;
338
+
339
+ const runtime: RuntimeOperationRecord = {
340
+ id: durable.snapshot.id,
341
+ service: durable.snapshot.service,
342
+ operation: durable.snapshot.operation,
343
+ ownerSessionKey: durable.ownerSessionKey,
344
+ snapshot: durable.snapshot,
345
+ sequence: durable.sequence,
346
+ terminal: durable.snapshot.state === "completed" ||
347
+ durable.snapshot.state === "failed" ||
348
+ durable.snapshot.state === "cancelled",
349
+ signalSequence: durable.signalSequence ?? 0,
350
+ signals: durable.signals ?? [],
351
+ watchers: new Set(),
352
+ waiters: new Set(),
353
+ signalWaiters: new Set(),
354
+ };
355
+ this.#operations.set(operationId, runtime);
356
+ return runtime;
357
+ }
358
+
359
+ #applyOperationUpdate(
360
+ operationId: string,
361
+ state: RuntimeOperationState,
362
+ opts: {
363
+ patch?: Partial<RuntimeOperationSnapshot>;
364
+ event: Record<string, unknown> & { type: string };
365
+ },
366
+ ): AsyncResult<RuntimeOperationSnapshot, BaseError> {
367
+ return AsyncResult.from((async () => {
368
+ const runtime = await this.#resolveOperation(operationId);
369
+ if (!runtime) {
370
+ return err(
371
+ new UnexpectedError({
372
+ cause: new Error(`Unknown operation '${operationId}'`),
373
+ }),
374
+ );
375
+ }
376
+
377
+ if (runtime.terminal) {
378
+ return err(
379
+ new UnexpectedError({
380
+ cause: new Error("operation already terminal"),
381
+ }),
382
+ );
383
+ }
384
+
385
+ runtime.sequence += 1;
386
+ runtime.snapshot = buildRuntimeOperationSnapshot(
387
+ runtime,
388
+ state,
389
+ opts.patch,
390
+ );
391
+ runtime.terminal = state === "completed" || state === "failed" ||
392
+ state === "cancelled";
393
+
394
+ await this.saveOperationRecord(runtime);
395
+
396
+ const frame = {
397
+ kind: "event",
398
+ sequence: runtime.sequence,
399
+ event: {
400
+ snapshot: runtime.snapshot,
401
+ ...opts.event,
402
+ },
403
+ };
404
+ for (const reply of runtime.watchers) {
405
+ await this.nats.publish(reply, JSON.stringify(frame));
406
+ }
407
+
408
+ if (runtime.terminal) {
409
+ const terminalFrame = { kind: "snapshot", snapshot: runtime.snapshot };
410
+ for (const reply of runtime.waiters) {
411
+ await this.nats.publish(reply, JSON.stringify(terminalFrame));
412
+ }
413
+ runtime.waiters.clear();
414
+ this.#rejectSignalWaiters(runtime);
415
+ }
416
+
417
+ return ok(runtime.snapshot);
418
+ })());
419
+ }
420
+
421
+ #validateOperationValue(
422
+ ctx: RegisteredRuntimeOperationDesc,
423
+ kind: "progress" | "output",
424
+ value: unknown,
425
+ ): Result<unknown, BaseError> {
426
+ const schema = kind === "progress" ? ctx.progress : ctx.output;
427
+ if (schema === undefined) return ok(value);
428
+ if (!isJsonValue(value)) {
429
+ return err(
430
+ new ValidationError({
431
+ errors: [{
432
+ path: "/",
433
+ message: `Operation ${kind} must be JSON-serializable`,
434
+ }],
435
+ context: { kind },
436
+ }),
437
+ );
438
+ }
439
+ const parsed = parseSchema(
440
+ schema as Parameters<typeof parseSchema>[0],
441
+ value,
442
+ ).take();
443
+ if (isErr(parsed)) return err(parsed.error);
444
+ return ok(parsed);
445
+ }
446
+
447
+ #applyControlledOperationUpdate(
448
+ runtime: RuntimeOperationRecord,
449
+ ctx: RegisteredRuntimeOperationDesc,
450
+ state: RuntimeOperationState,
451
+ opts: {
452
+ patch?: Partial<RuntimeOperationSnapshot>;
453
+ event: Record<string, unknown> & { type: string };
454
+ },
455
+ ): AsyncResult<RuntimeOperationSnapshot, BaseError> {
456
+ return AsyncResult.from((async () => {
457
+ if (opts.patch?.progress !== undefined) {
458
+ const parsed = this.#validateOperationValue(
459
+ ctx,
460
+ "progress",
461
+ opts.patch.progress,
462
+ ).take();
463
+ if (isErr(parsed)) return parsed;
464
+ opts.patch.progress = parsed;
465
+ if ("progress" in opts.event) opts.event.progress = parsed;
466
+ }
467
+ if (opts.patch?.output !== undefined) {
468
+ const parsed = this.#validateOperationValue(
469
+ ctx,
470
+ "output",
471
+ opts.patch.output,
472
+ ).take();
473
+ if (isErr(parsed)) return parsed;
474
+ opts.patch.output = parsed;
475
+ }
476
+ return await this.#applyOperationUpdate(
477
+ runtime.id,
478
+ state,
479
+ opts,
480
+ );
481
+ })());
482
+ }
483
+
484
+ #controlOperation(
485
+ operation: string,
486
+ ctx: RegisteredRuntimeOperationDesc,
487
+ operationId: string,
488
+ ): AsyncResult<OperationRuntimeHandle<unknown, unknown>, BaseError> {
489
+ return AsyncResult.from((async () => {
490
+ const runtime = await this.#resolveOperation(operationId);
491
+ if (!runtime) {
492
+ return err(
493
+ new UnexpectedError({
494
+ cause: new Error(`Unknown operation '${operationId}'`),
495
+ }),
496
+ );
497
+ }
498
+ if (runtime.service !== this.name) {
499
+ return err(
500
+ new UnexpectedError({
501
+ cause: new Error(
502
+ `Operation '${operationId}' belongs to service '${runtime.service}', not '${this.name}'`,
503
+ ),
504
+ }),
505
+ );
506
+ }
507
+ if (runtime.operation !== operation) {
508
+ return err(
509
+ new UnexpectedError({
510
+ cause: new Error(
511
+ `Operation '${operationId}' is '${runtime.operation}', not '${operation}'`,
512
+ ),
513
+ }),
514
+ );
515
+ }
516
+ return ok(this.#makeControlledOperation(runtime, ctx));
517
+ })());
518
+ }
519
+
520
+ #makeControlledOperation(
521
+ runtime: RuntimeOperationRecord,
522
+ ctx: RegisteredRuntimeOperationDesc,
523
+ ): OperationRuntimeHandle<unknown, unknown> {
524
+ return {
525
+ id: runtime.id,
526
+ started: () =>
527
+ this.#applyControlledOperationUpdate(runtime, ctx, "running", {
528
+ event: { type: "started" },
529
+ }),
530
+ progress: (value: unknown) =>
531
+ this.#applyControlledOperationUpdate(runtime, ctx, "running", {
532
+ patch: { progress: value },
533
+ event: { type: "progress", progress: value },
534
+ }),
535
+ complete: (value: unknown) =>
536
+ this.#applyControlledOperationUpdate(runtime, ctx, "completed", {
537
+ patch: { output: value },
538
+ event: { type: "completed" },
539
+ }),
540
+ fail: (error: BaseError) =>
541
+ this.#applyControlledOperationUpdate(runtime, ctx, "failed", {
542
+ patch: { error: { type: error.name, message: error.message } },
543
+ event: { type: "failed" },
544
+ }),
545
+ cancel: () => {
546
+ if (ctx.cancel !== true) {
547
+ return AsyncResult.err(
548
+ this.#unsupportedCancelError(runtime.operation),
549
+ );
550
+ }
551
+ return this.#applyControlledOperationUpdate(runtime, ctx, "cancelled", {
552
+ event: { type: "cancelled" },
553
+ });
554
+ },
555
+ attach: (job: { wait(): AsyncResult<unknown, BaseError> }) =>
556
+ AsyncResult.from((async () => {
557
+ const waited = await job.wait();
558
+ const waitedValue = waited.take();
559
+ if (isErr(waitedValue)) {
560
+ return err(new UnexpectedError({ cause: waitedValue.error }));
561
+ }
562
+
563
+ const finalRuntime = await this.#resolveOperation(runtime.id);
564
+ if (!finalRuntime || !finalRuntime.terminal) {
565
+ return err(
566
+ new UnexpectedError({
567
+ cause: new Error(
568
+ "attached job completed without terminal operation state",
569
+ ),
570
+ }),
571
+ );
572
+ }
573
+
574
+ return ok(finalRuntime.snapshot);
575
+ })()),
576
+ signals: () => this.#signals(runtime.id),
577
+ nextSignal: (name?: string) => this.#nextSignal(runtime.id, name),
578
+ defer: () => ({ kind: "deferred" as const }),
579
+ };
580
+ }
581
+
582
+ #makeAcceptedOperation(
583
+ runtime: RuntimeOperationRecord,
584
+ ctx: RegisteredRuntimeOperationDesc,
585
+ ): AcceptedOperation<unknown, unknown> {
586
+ return {
587
+ id: runtime.id,
588
+ ref: {
589
+ id: runtime.id,
590
+ service: runtime.service,
591
+ operation: runtime.operation,
592
+ },
593
+ snapshot: runtime.snapshot,
594
+ started: () => this.operations.started(runtime.id),
595
+ progress: (value: unknown) => this.operations.progress(runtime.id, value),
596
+ complete: (value: unknown) => this.operations.complete(runtime.id, value),
597
+ fail: (error: BaseError) => this.operations.fail(runtime.id, error),
598
+ cancel: () => {
599
+ if (ctx.cancel !== true) {
600
+ return AsyncResult.err(
601
+ this.#unsupportedCancelError(runtime.operation),
602
+ );
603
+ }
604
+ return this.operations.cancel(runtime.id);
605
+ },
606
+ attach: (job: { wait(): AsyncResult<unknown, BaseError> }) =>
607
+ AsyncResult.from((async () => {
608
+ const waited = await job.wait();
609
+ const waitedValue = waited.take();
610
+ if (isErr(waitedValue)) {
611
+ return err(new UnexpectedError({ cause: waitedValue.error }));
612
+ }
613
+
614
+ const finalRuntime = await this.#resolveOperation(runtime.id);
615
+ if (!finalRuntime || !finalRuntime.terminal) {
616
+ return err(
617
+ new UnexpectedError({
618
+ cause: new Error(
619
+ "attached job completed without terminal operation state",
620
+ ),
621
+ }),
622
+ );
623
+ }
624
+
625
+ return ok(finalRuntime.snapshot);
626
+ })()),
627
+ signals: () => this.#signals(runtime.id),
628
+ nextSignal: (name?: string) => this.#nextSignal(runtime.id, name),
629
+ defer: () => ({ kind: "deferred" as const }),
630
+ };
631
+ }
632
+
633
+ #controlAuthContext(
634
+ ctx: RegisteredRuntimeOperationDesc,
635
+ action: RuntimeOperationControlRequest["action"],
636
+ ): RegisteredRuntimeOperationDesc {
637
+ if (action === "signal" && ctx.controlCapabilities !== undefined) {
638
+ return { ...ctx, callerCapabilities: ctx.controlCapabilities };
639
+ }
640
+ if (action === "cancel" && ctx.cancelCapabilities !== undefined) {
641
+ return { ...ctx, callerCapabilities: ctx.cancelCapabilities };
642
+ }
643
+ return ctx;
644
+ }
645
+
646
+ #terminalSignalError(): UnexpectedError {
647
+ return new UnexpectedError({
648
+ cause: new Error("operation already terminal"),
649
+ });
650
+ }
651
+
652
+ #unsupportedCancelError(operation: string): ValidationError {
653
+ return new ValidationError({
654
+ errors: [{
655
+ path: "/action",
656
+ message: `Operation '${operation}' does not support cancel`,
657
+ }],
658
+ context: { operation, action: "cancel" },
659
+ });
660
+ }
661
+
662
+ #rejectSignalWaiters(runtime: RuntimeOperationRecord): void {
663
+ const result = err(this.#terminalSignalError());
664
+ for (const waiter of runtime.signalWaiters) {
665
+ waiter(result);
666
+ }
667
+ runtime.signalWaiters.clear();
668
+ }
669
+
670
+ async #acceptSignal(
671
+ runtime: RuntimeOperationRecord,
672
+ ctx: RegisteredRuntimeOperationDesc,
673
+ control: Extract<RuntimeOperationControlRequest, { action: "signal" }>,
674
+ ): Promise<
675
+ Result<{
676
+ kind: "signal-accepted";
677
+ operationId: string;
678
+ signal: string;
679
+ signalSequence: number;
680
+ acceptedAt: string;
681
+ snapshot: RuntimeOperationSnapshot;
682
+ }, ValidationError | UnexpectedError>
683
+ > {
684
+ if (runtime.terminal) {
685
+ return err(this.#terminalSignalError());
686
+ }
687
+
688
+ const descriptor = ctx.signals?.[control.signal];
689
+ if (!descriptor) {
690
+ return err(
691
+ new ValidationError({
692
+ errors: [{
693
+ path: "/signal",
694
+ message: `Unknown operation signal '${control.signal}'`,
695
+ }],
696
+ context: { operation: runtime.operation, signal: control.signal },
697
+ }),
698
+ );
699
+ }
700
+
701
+ const input = control.input as JsonValue;
702
+ const parsed = parseSchema(
703
+ descriptor.input as Parameters<typeof parseSchema>[0],
704
+ input,
705
+ ).take();
706
+ if (isErr(parsed)) {
707
+ return err(parsed.error as ValidationError | UnexpectedError);
708
+ }
709
+
710
+ runtime.signalSequence += 1;
711
+ const acceptedAt = new Date().toISOString();
712
+ const signal: RuntimeOperationSignal = {
713
+ operationId: runtime.id,
714
+ sequence: runtime.signalSequence,
715
+ signal: control.signal,
716
+ ...(control.input !== undefined ? { input: control.input } : {}),
717
+ acceptedAt,
718
+ };
719
+ runtime.signals.push(signal);
720
+ await this.saveOperationRecord(runtime);
721
+ const result = ok(signal);
722
+ for (const waiter of runtime.signalWaiters) {
723
+ waiter(result);
724
+ }
725
+
726
+ return ok({
727
+ kind: "signal-accepted",
728
+ operationId: runtime.id,
729
+ signal: signal.signal,
730
+ signalSequence: signal.sequence,
731
+ acceptedAt,
732
+ snapshot: runtime.snapshot,
733
+ });
734
+ }
735
+
736
+ async #acceptOperation(
737
+ operation: string,
738
+ ctx: RegisteredRuntimeOperationDesc,
739
+ sessionKey: string,
740
+ ): Promise<Result<AcceptedOperation<unknown, unknown>, UnexpectedError>> {
741
+ const createdAt = new Date().toISOString();
742
+ const operationId = ulid();
743
+ const runtime: RuntimeOperationRecord = {
744
+ id: operationId,
745
+ service: this.name,
746
+ operation,
747
+ ownerSessionKey: sessionKey,
748
+ snapshot: {
749
+ id: operationId,
750
+ service: this.name,
751
+ operation,
752
+ revision: 1,
753
+ state: "pending",
754
+ createdAt,
755
+ updatedAt: createdAt,
756
+ },
757
+ sequence: 0,
758
+ signalSequence: 0,
759
+ signals: [],
760
+ terminal: false,
761
+ watchers: new Set(),
762
+ waiters: new Set(),
763
+ signalWaiters: new Set(),
764
+ };
765
+ this.#operations.set(operationId, runtime);
766
+ await this.saveOperationRecord(runtime);
767
+ return ok(this.#makeAcceptedOperation(runtime, ctx));
768
+ }
769
+
770
+ async #authenticateOperationMessage(
771
+ msg: Msg,
772
+ ctx: RegisteredRuntimeOperationDesc,
773
+ parseInput: boolean,
774
+ ): Promise<
775
+ Result<{
776
+ input: unknown;
777
+ caller: AuthRequestsValidateResponse["caller"];
778
+ sessionKey: string;
779
+ auth: AuthRequestsValidateResponse;
780
+ }, UnexpectedError | AuthError | ValidationError | RemoteError>
781
+ > {
782
+ const jsonData = safeJson(msg).take();
783
+ if (isErr(jsonData)) return jsonData;
784
+
785
+ let parsedInput: unknown;
786
+ if (parseInput) {
787
+ const parsedInputResult = parseSchema(
788
+ ctx.input as Parameters<typeof parseSchema>[0],
789
+ jsonData,
790
+ ).take();
791
+ if (isErr(parsedInputResult)) {
792
+ return err(
793
+ parsedInputResult.error as ValidationError | UnexpectedError,
794
+ );
795
+ }
796
+ parsedInput = parsedInputResult;
797
+ } else {
798
+ parsedInput = jsonData;
799
+ }
800
+
801
+ const sessionKey = msg.headers?.get("session-key");
802
+ const proof = msg.headers?.get("proof");
803
+ const iatHeader = msg.headers?.get("iat");
804
+ const requestId = msg.headers?.get("request-id");
805
+ if (!sessionKey) {
806
+ return err(new AuthError({ reason: "missing_session_key" }));
807
+ }
808
+ if (!proof) return err(new AuthError({ reason: "missing_proof" }));
809
+ const iat = Number(iatHeader);
810
+ if (!Number.isSafeInteger(iat) || !requestId) {
811
+ return err(new AuthError({ reason: "invalid_signature" }));
812
+ }
813
+
814
+ const payloadBytes = msg.data ?? new Uint8Array();
815
+ const payloadHash = await sha256(payloadBytes);
816
+ const proofInput = buildProofInput(
817
+ sessionKey,
818
+ msg.subject,
819
+ payloadHash,
820
+ iat,
821
+ requestId,
822
+ );
823
+ const digest = await sha256(proofInput);
824
+
825
+ const verifyResult = await AsyncResult.try(async () => {
826
+ const publicKeyRaw = base64urlDecode(sessionKey);
827
+ const pub = await crypto.subtle.importKey(
828
+ "raw",
829
+ toArrayBuffer(publicKeyRaw),
830
+ { name: "Ed25519" },
831
+ true,
832
+ ["verify"],
833
+ );
834
+ return crypto.subtle.verify(
835
+ { name: "Ed25519" },
836
+ pub,
837
+ toArrayBuffer(base64urlDecode(proof)),
838
+ toArrayBuffer(digest),
839
+ );
840
+ });
841
+ const signatureOk = verifyResult.isOk() &&
842
+ verifyResult.take() === true;
843
+ if (!signatureOk) {
844
+ return err(
845
+ new AuthError({
846
+ reason: "invalid_signature",
847
+ context: { sessionKey },
848
+ }),
849
+ );
850
+ }
851
+
852
+ const auth = await this.requestAuthValidate({
853
+ sessionKey,
854
+ proof,
855
+ subject: msg.subject,
856
+ payloadHash: base64urlEncode(payloadHash),
857
+ iat,
858
+ requestId,
859
+ capabilities: ctx.callerCapabilities
860
+ ? [...ctx.callerCapabilities]
861
+ : undefined,
862
+ }).take();
863
+ if (isErr(auth)) {
864
+ return err(
865
+ auth.error as
866
+ | RemoteError
867
+ | ValidationError
868
+ | UnexpectedError
869
+ | AuthError,
870
+ );
871
+ }
872
+
873
+ if (!auth.allowed) {
874
+ return err(
875
+ new AuthError({
876
+ reason: "insufficient_permissions",
877
+ context: {
878
+ requiredCapabilities: ctx.callerCapabilities,
879
+ userCapabilities: auth.caller.capabilities,
880
+ },
881
+ }),
882
+ );
883
+ }
884
+
885
+ if (
886
+ typeof msg.reply !== "string" ||
887
+ !msg.reply.startsWith(`${auth.inboxPrefix}.`)
888
+ ) {
889
+ return err(
890
+ new AuthError({
891
+ reason: "reply_subject_mismatch",
892
+ context: { expected: auth.inboxPrefix, actual: msg.reply },
893
+ }),
894
+ );
895
+ }
896
+
897
+ return ok({
898
+ input: parsedInput,
899
+ caller: auth.caller,
900
+ sessionKey,
901
+ auth,
902
+ });
903
+ }
904
+
905
+ #ensureOperationControlLoop(
906
+ operation: string,
907
+ ctx: RegisteredRuntimeOperationDesc,
908
+ ): void {
909
+ const controlSubject = `${ctx.subject}.control`;
910
+ if (this.#mountedOperationControls.has(controlSubject)) {
911
+ return;
912
+ }
913
+ this.#mountedOperationControls.add(controlSubject);
914
+
915
+ const publishFrame = async (reply: string, frame: unknown) => {
916
+ await this.nats.publish(reply, JSON.stringify(frame));
917
+ };
918
+
919
+ const publishSnapshot = async (
920
+ reply: string,
921
+ snapshot: RuntimeOperationSnapshot,
922
+ ) => {
923
+ await publishFrame(reply, { kind: "snapshot", snapshot });
924
+ };
925
+
926
+ const respondControlError = (msg: Msg, error: Error | BaseError) => {
927
+ msg.respond(JSON.stringify({
928
+ kind: "error",
929
+ error: { type: error.name, message: error.message },
930
+ }));
931
+ };
932
+
933
+ const controlSub = this.nats.subscribe(controlSubject);
934
+ void (async () => {
935
+ for await (const msg of controlSub) {
936
+ const request = safeJson(msg).take();
937
+ if (isErr(request)) {
938
+ respondControlError(msg, request.error);
939
+ continue;
940
+ }
941
+
942
+ if (
943
+ !request ||
944
+ typeof request !== "object" ||
945
+ typeof (request as RuntimeOperationControlRequest).action !==
946
+ "string" ||
947
+ typeof (request as RuntimeOperationControlRequest).operationId !==
948
+ "string"
949
+ ) {
950
+ respondControlError(
951
+ msg,
952
+ new UnexpectedError({
953
+ cause: new Error("Invalid operation control request"),
954
+ }),
955
+ );
956
+ continue;
957
+ }
958
+
959
+ const control = request as RuntimeOperationControlRequest;
960
+ const validated = await this.#authenticateOperationMessage(
961
+ msg,
962
+ this.#controlAuthContext(ctx, control.action),
963
+ false,
964
+ );
965
+ const value = validated.take();
966
+ if (isErr(value)) {
967
+ respondControlError(msg, value.error);
968
+ continue;
969
+ }
970
+
971
+ const runtime = await this.#resolveOperation(control.operationId);
972
+ if (!runtime) {
973
+ respondControlError(
974
+ msg,
975
+ new UnexpectedError({
976
+ cause: new Error(`Unknown operation '${control.operationId}'`),
977
+ }),
978
+ );
979
+ continue;
980
+ }
981
+
982
+ if (runtime.service !== this.name || runtime.operation !== operation) {
983
+ respondControlError(
984
+ msg,
985
+ new UnexpectedError({
986
+ cause: new Error(
987
+ `Operation '${control.operationId}' belongs to service '${runtime.service}' operation '${runtime.operation}', not service '${this.name}' operation '${operation}'`,
988
+ ),
989
+ }),
990
+ );
991
+ continue;
992
+ }
993
+
994
+ const snapshot = runtime.snapshot;
995
+ const ownerSessionKey = runtime.ownerSessionKey;
996
+
997
+ if (ownerSessionKey !== value.sessionKey) {
998
+ respondControlError(
999
+ msg,
1000
+ new AuthError({
1001
+ reason: "forbidden",
1002
+ context: { ownerSessionKey },
1003
+ }),
1004
+ );
1005
+ continue;
1006
+ }
1007
+
1008
+ if (control.action === "watch") {
1009
+ if (msg.reply) {
1010
+ await publishSnapshot(msg.reply, snapshot);
1011
+ if (!runtime || runtime.terminal) continue;
1012
+ runtime.watchers.add(msg.reply);
1013
+ }
1014
+ continue;
1015
+ }
1016
+
1017
+ if (control.action === "wait") {
1018
+ if (
1019
+ snapshot.state === "completed" || snapshot.state === "failed" ||
1020
+ snapshot.state === "cancelled"
1021
+ ) {
1022
+ msg.respond(JSON.stringify({ kind: "snapshot", snapshot }));
1023
+ } else if (runtime && msg.reply) {
1024
+ runtime.waiters.add(msg.reply);
1025
+ } else if (msg.reply) {
1026
+ respondControlError(
1027
+ msg,
1028
+ new UnexpectedError({
1029
+ cause: new Error("operation is not running in this process"),
1030
+ }),
1031
+ );
1032
+ } else {
1033
+ respondControlError(
1034
+ msg,
1035
+ new UnexpectedError({
1036
+ cause: new Error("missing reply subject for wait request"),
1037
+ }),
1038
+ );
1039
+ }
1040
+ continue;
1041
+ }
1042
+
1043
+ if (control.action === "get") {
1044
+ msg.respond(JSON.stringify({ kind: "snapshot", snapshot }));
1045
+ continue;
1046
+ }
1047
+
1048
+ if (control.action === "cancel") {
1049
+ if (ctx.cancel !== true) {
1050
+ respondControlError(msg, this.#unsupportedCancelError(operation));
1051
+ continue;
1052
+ }
1053
+ if (runtime.terminal) {
1054
+ respondControlError(msg, this.#terminalSignalError());
1055
+ continue;
1056
+ }
1057
+ runtime.snapshot = {
1058
+ ...runtime.snapshot,
1059
+ revision: runtime.snapshot.revision + 1,
1060
+ state: "cancelled",
1061
+ updatedAt: new Date().toISOString(),
1062
+ completedAt: new Date().toISOString(),
1063
+ };
1064
+ runtime.terminal = true;
1065
+ runtime.sequence += 1;
1066
+ await this.saveOperationRecord(runtime);
1067
+ const frame = {
1068
+ kind: "event",
1069
+ sequence: runtime.sequence,
1070
+ event: {
1071
+ type: "cancelled",
1072
+ snapshot: runtime.snapshot,
1073
+ },
1074
+ };
1075
+ for (const reply of runtime.watchers) {
1076
+ await this.nats.publish(reply, JSON.stringify(frame));
1077
+ }
1078
+ for (const reply of runtime.waiters) {
1079
+ await this.nats.publish(
1080
+ reply,
1081
+ JSON.stringify({ kind: "snapshot", snapshot: runtime.snapshot }),
1082
+ );
1083
+ }
1084
+ runtime.waiters.clear();
1085
+ this.#rejectSignalWaiters(runtime);
1086
+ msg.respond(
1087
+ JSON.stringify({ kind: "snapshot", snapshot: runtime.snapshot }),
1088
+ );
1089
+ continue;
1090
+ }
1091
+
1092
+ if (control.action === "signal") {
1093
+ if (!runtime) {
1094
+ respondControlError(
1095
+ msg,
1096
+ new UnexpectedError({
1097
+ cause: new Error("operation is not running in this process"),
1098
+ }),
1099
+ );
1100
+ continue;
1101
+ }
1102
+
1103
+ const accepted = await this.#acceptSignal(runtime, ctx, control);
1104
+ const acceptedValue = accepted.take();
1105
+ if (isErr(acceptedValue)) {
1106
+ respondControlError(msg, acceptedValue.error);
1107
+ continue;
1108
+ }
1109
+ msg.respond(JSON.stringify(acceptedValue));
1110
+ continue;
1111
+ }
1112
+
1113
+ respondControlError(
1114
+ msg,
1115
+ new UnexpectedError({
1116
+ cause: new Error(
1117
+ `Unknown operation control action '${control.action}' for '${operation}'`,
1118
+ ),
1119
+ }),
1120
+ );
1121
+ }
1122
+ })();
1123
+ }
1124
+
1125
+ mountRuntime(
1126
+ method: string,
1127
+ fn: Parameters<Trellis<TrellisAPI, TrellisMode>["mount"]>[1],
1128
+ ): Promise<void> {
1129
+ return super.mount(method, fn);
1130
+ }
1131
+
1132
+ static create<TA extends AnyTrellisAPI>(
1133
+ name: string,
1134
+ nats: NatsConnection,
1135
+ auth: TrellisAuth,
1136
+ opts: TrellisServiceRuntimeOpts<TA>,
1137
+ ): TrellisServiceRuntimeFor<TA> {
1138
+ const runtime = new TrellisServiceRuntime(
1139
+ name,
1140
+ nats,
1141
+ auth,
1142
+ opts as TrellisServiceRuntimeOpts<TrellisAPI>,
1143
+ );
1144
+ return runtime as TrellisServiceRuntime & TrellisServiceRuntimeFor<TA>;
1145
+ }
1146
+
1147
+ override operation(
1148
+ operation: string,
1149
+ ): OperationRegistration<unknown, unknown, unknown> {
1150
+ const ctx = this.api["operations"]
1151
+ ?.[operation as keyof typeof this.api.operations] as
1152
+ | RegisteredRuntimeOperationDesc
1153
+ | undefined;
1154
+ if (!ctx) {
1155
+ throw new Error(
1156
+ `Unknown operation '${operation.toString()}'. Did you forget to include its API module?`,
1157
+ );
1158
+ }
1159
+
1160
+ return {
1161
+ control: (operationId) => {
1162
+ this.#ensureOperationControlLoop(String(operation), ctx);
1163
+ return this.#controlOperation(String(operation), ctx, operationId);
1164
+ },
1165
+ accept: ({ sessionKey }) => {
1166
+ this.#ensureOperationControlLoop(String(operation), ctx);
1167
+ if (ctx.transfer) {
1168
+ return AsyncResult.err(
1169
+ new UnexpectedError({
1170
+ cause: new Error(
1171
+ `Operation '${
1172
+ String(operation)
1173
+ }' uses transfer-capable start semantics and cannot be accepted manually`,
1174
+ ),
1175
+ }),
1176
+ );
1177
+ }
1178
+ return AsyncResult.from(
1179
+ this.#acceptOperation(String(operation), ctx, sessionKey),
1180
+ );
1181
+ },
1182
+ handle: async (
1183
+ handler: (
1184
+ context: OperationHandlerContext<
1185
+ unknown,
1186
+ unknown,
1187
+ unknown,
1188
+ OperationTransferHandle | undefined
1189
+ >,
1190
+ ) => unknown | Promise<unknown>,
1191
+ ) => {
1192
+ const startSubject = ctx.subject;
1193
+ const now = () => new Date().toISOString();
1194
+
1195
+ const publishFrame = async (reply: string, frame: unknown) => {
1196
+ await this.nats.publish(reply, JSON.stringify(frame));
1197
+ };
1198
+
1199
+ const publishSnapshot = async (
1200
+ reply: string,
1201
+ snapshot: RuntimeOperationSnapshot,
1202
+ ) => {
1203
+ await publishFrame(reply, { kind: "snapshot", snapshot });
1204
+ };
1205
+
1206
+ const publishEventToWatchers = async (
1207
+ runtime: RuntimeOperationRecord,
1208
+ event: unknown,
1209
+ ) => {
1210
+ const frame = { kind: "event", sequence: runtime.sequence, event };
1211
+ for (const reply of runtime.watchers) {
1212
+ await publishFrame(reply, frame);
1213
+ }
1214
+ };
1215
+
1216
+ const flushWaiters = async (runtime: RuntimeOperationRecord) => {
1217
+ const frame = { kind: "snapshot", snapshot: runtime.snapshot };
1218
+ for (const reply of runtime.waiters) {
1219
+ await publishFrame(reply, frame);
1220
+ }
1221
+ runtime.waiters.clear();
1222
+ };
1223
+
1224
+ const makeOperation = (runtime: RuntimeOperationRecord) => {
1225
+ const ensureActive = () => {
1226
+ if (runtime.terminal) {
1227
+ return err(
1228
+ new UnexpectedError({
1229
+ cause: new Error("operation already terminal"),
1230
+ }),
1231
+ );
1232
+ }
1233
+ return null;
1234
+ };
1235
+
1236
+ const transition = async (
1237
+ state: RuntimeOperationState,
1238
+ patch?: Partial<RuntimeOperationSnapshot>,
1239
+ event?: unknown,
1240
+ ) => {
1241
+ runtime.sequence += 1;
1242
+ runtime.snapshot = buildRuntimeOperationSnapshot(
1243
+ runtime,
1244
+ state,
1245
+ patch,
1246
+ );
1247
+ await this.saveOperationRecord(runtime);
1248
+ if (event) {
1249
+ await publishEventToWatchers(runtime, event);
1250
+ }
1251
+ return ok(runtime.snapshot);
1252
+ };
1253
+
1254
+ return {
1255
+ id: runtime.id,
1256
+ started: () =>
1257
+ AsyncResult.from((async () => {
1258
+ const active = ensureActive();
1259
+ if (active) return active;
1260
+ return transition("running", undefined, {
1261
+ type: "started",
1262
+ snapshot: buildRuntimeOperationSnapshot(runtime, "running", {
1263
+ revision: runtime.snapshot.revision + 1,
1264
+ }),
1265
+ });
1266
+ })()),
1267
+ progress: (value: unknown) =>
1268
+ AsyncResult.from((async () => {
1269
+ const active = ensureActive();
1270
+ if (active) return active;
1271
+ return transition("running", { progress: value }, {
1272
+ type: "progress",
1273
+ snapshot: buildRuntimeOperationSnapshot(runtime, "running", {
1274
+ revision: runtime.snapshot.revision + 1,
1275
+ progress: value,
1276
+ }),
1277
+ });
1278
+ })()),
1279
+ complete: (value: unknown) =>
1280
+ AsyncResult.from((async () => {
1281
+ const active = ensureActive();
1282
+ if (active) return active;
1283
+ const snapshot = buildRuntimeOperationSnapshot(
1284
+ runtime,
1285
+ "completed",
1286
+ {
1287
+ output: value,
1288
+ completedAt: now(),
1289
+ },
1290
+ );
1291
+ runtime.sequence += 1;
1292
+ runtime.snapshot = snapshot;
1293
+ runtime.terminal = true;
1294
+ await this.saveOperationRecord(runtime);
1295
+ await publishEventToWatchers(runtime, {
1296
+ type: "completed",
1297
+ snapshot,
1298
+ });
1299
+ await flushWaiters(runtime);
1300
+ this.#rejectSignalWaiters(runtime);
1301
+ return ok(snapshot);
1302
+ })()),
1303
+ fail: (error: BaseError) =>
1304
+ AsyncResult.from((async () => {
1305
+ const active = ensureActive();
1306
+ if (active) return active;
1307
+ const snapshot = buildRuntimeOperationSnapshot(
1308
+ runtime,
1309
+ "failed",
1310
+ {
1311
+ error: { type: error.name, message: error.message },
1312
+ completedAt: now(),
1313
+ },
1314
+ );
1315
+ runtime.sequence += 1;
1316
+ runtime.snapshot = snapshot;
1317
+ runtime.terminal = true;
1318
+ await this.saveOperationRecord(runtime);
1319
+ await publishEventToWatchers(runtime, {
1320
+ type: "failed",
1321
+ snapshot,
1322
+ });
1323
+ await flushWaiters(runtime);
1324
+ this.#rejectSignalWaiters(runtime);
1325
+ return ok(snapshot);
1326
+ })()),
1327
+ cancel: () =>
1328
+ AsyncResult.from((async () => {
1329
+ const active = ensureActive();
1330
+ if (active) return active;
1331
+ const snapshot = buildRuntimeOperationSnapshot(
1332
+ runtime,
1333
+ "cancelled",
1334
+ {
1335
+ completedAt: now(),
1336
+ },
1337
+ );
1338
+ runtime.sequence += 1;
1339
+ runtime.snapshot = snapshot;
1340
+ runtime.terminal = true;
1341
+ await this.saveOperationRecord(runtime);
1342
+ await publishEventToWatchers(runtime, {
1343
+ type: "cancelled",
1344
+ snapshot,
1345
+ });
1346
+ await flushWaiters(runtime);
1347
+ this.#rejectSignalWaiters(runtime);
1348
+ return ok(snapshot);
1349
+ })()),
1350
+ attach: (job: { wait: () => AsyncResult<unknown, BaseError> }) =>
1351
+ AsyncResult.from((async () => {
1352
+ const waited = await job.wait();
1353
+ const waitedValue = waited.take();
1354
+ if (isErr(waitedValue)) {
1355
+ return err(new UnexpectedError({ cause: waitedValue.error }));
1356
+ }
1357
+
1358
+ const finalRuntime = await this.#resolveOperation(runtime.id);
1359
+ if (!finalRuntime || !finalRuntime.terminal) {
1360
+ return err(
1361
+ new UnexpectedError({
1362
+ cause: new Error(
1363
+ "attached job completed without terminal operation state",
1364
+ ),
1365
+ }),
1366
+ );
1367
+ }
1368
+
1369
+ return ok(finalRuntime.snapshot);
1370
+ })()),
1371
+ signals: () => this.#signals(runtime.id),
1372
+ nextSignal: (name?: string) => this.#nextSignal(runtime.id, name),
1373
+ defer: () => ({ kind: "deferred" as const }),
1374
+ };
1375
+ };
1376
+
1377
+ const authenticate = async (msg: Msg, parseInput = true): Promise<
1378
+ Result<{
1379
+ input: unknown;
1380
+ caller: AuthRequestsValidateResponse["caller"];
1381
+ sessionKey: string;
1382
+ auth: AuthRequestsValidateResponse;
1383
+ }, UnexpectedError | AuthError | ValidationError | RemoteError>
1384
+ > => {
1385
+ const jsonData = safeJson(msg).take();
1386
+ if (isErr(jsonData)) return jsonData;
1387
+
1388
+ let parsedInput: unknown;
1389
+ if (parseInput) {
1390
+ const parsedInputResult = parseSchema(
1391
+ ctx.input as Parameters<typeof parseSchema>[0],
1392
+ jsonData,
1393
+ ).take();
1394
+ if (isErr(parsedInputResult)) {
1395
+ return err(
1396
+ parsedInputResult.error as ValidationError | UnexpectedError,
1397
+ );
1398
+ }
1399
+ parsedInput = parsedInputResult;
1400
+ } else {
1401
+ parsedInput = jsonData;
1402
+ }
1403
+
1404
+ const sessionKey = msg.headers?.get("session-key");
1405
+ const proof = msg.headers?.get("proof");
1406
+ const iatHeader = msg.headers?.get("iat");
1407
+ const requestId = msg.headers?.get("request-id");
1408
+ if (!sessionKey) {
1409
+ return err(new AuthError({ reason: "missing_session_key" }));
1410
+ }
1411
+ if (!proof) return err(new AuthError({ reason: "missing_proof" }));
1412
+ const iat = Number(iatHeader);
1413
+ if (!Number.isSafeInteger(iat) || !requestId) {
1414
+ return err(new AuthError({ reason: "invalid_signature" }));
1415
+ }
1416
+
1417
+ const payloadBytes = msg.data ?? new Uint8Array();
1418
+ const payloadHash = await sha256(payloadBytes);
1419
+ const proofInput = buildProofInput(
1420
+ sessionKey,
1421
+ msg.subject,
1422
+ payloadHash,
1423
+ iat,
1424
+ requestId,
1425
+ );
1426
+ const digest = await sha256(proofInput);
1427
+
1428
+ const verifyResult = await AsyncResult.try(async () => {
1429
+ const publicKeyRaw = base64urlDecode(sessionKey);
1430
+ const pub = await crypto.subtle.importKey(
1431
+ "raw",
1432
+ toArrayBuffer(publicKeyRaw),
1433
+ { name: "Ed25519" },
1434
+ true,
1435
+ ["verify"],
1436
+ );
1437
+ return crypto.subtle.verify(
1438
+ { name: "Ed25519" },
1439
+ pub,
1440
+ toArrayBuffer(base64urlDecode(proof)),
1441
+ toArrayBuffer(digest),
1442
+ );
1443
+ });
1444
+ const signatureOk = verifyResult.isOk() &&
1445
+ verifyResult.take() === true;
1446
+ if (!signatureOk) {
1447
+ return err(
1448
+ new AuthError({
1449
+ reason: "invalid_signature",
1450
+ context: { sessionKey },
1451
+ }),
1452
+ );
1453
+ }
1454
+
1455
+ const authResult = await this.requestAuthValidate({
1456
+ sessionKey,
1457
+ proof,
1458
+ subject: msg.subject,
1459
+ payloadHash: base64urlEncode(payloadHash),
1460
+ iat,
1461
+ requestId,
1462
+ capabilities: ctx.callerCapabilities
1463
+ ? [...ctx.callerCapabilities]
1464
+ : undefined,
1465
+ });
1466
+ const auth = authResult.take();
1467
+ if (isErr(auth)) {
1468
+ return err(
1469
+ auth.error as
1470
+ | RemoteError
1471
+ | ValidationError
1472
+ | UnexpectedError
1473
+ | AuthError,
1474
+ );
1475
+ }
1476
+
1477
+ if (!auth.allowed) {
1478
+ return err(
1479
+ new AuthError({
1480
+ reason: "insufficient_permissions",
1481
+ context: {
1482
+ requiredCapabilities: ctx.callerCapabilities,
1483
+ userCapabilities: auth.caller.capabilities,
1484
+ },
1485
+ }),
1486
+ );
1487
+ }
1488
+
1489
+ if (
1490
+ typeof msg.reply !== "string" ||
1491
+ !msg.reply.startsWith(`${auth.inboxPrefix}.`)
1492
+ ) {
1493
+ return err(
1494
+ new AuthError({
1495
+ reason: "reply_subject_mismatch",
1496
+ context: { expected: auth.inboxPrefix, actual: msg.reply },
1497
+ }),
1498
+ );
1499
+ }
1500
+
1501
+ return ok({
1502
+ input: parsedInput,
1503
+ caller: auth.caller,
1504
+ sessionKey,
1505
+ auth,
1506
+ });
1507
+ };
1508
+
1509
+ this.#log.info(
1510
+ { operation: String(operation) },
1511
+ `Mounting ${String(operation)} operation handler`,
1512
+ );
1513
+
1514
+ this.#ensureOperationControlLoop(String(operation), ctx);
1515
+ const startSub = this.nats.subscribe(startSubject);
1516
+ await this.nats.flush();
1517
+
1518
+ void (async () => {
1519
+ for await (const msg of startSub) {
1520
+ const validated = await authenticate(msg, true);
1521
+ const value = validated.take();
1522
+ if (isErr(value)) {
1523
+ this.respondWithError(msg, value.error);
1524
+ continue;
1525
+ }
1526
+
1527
+ let transferSession: RuntimeOperationTransferSession | undefined;
1528
+ if (ctx.transfer) {
1529
+ if (!this.#transferSupport) {
1530
+ this.respondWithError(
1531
+ msg,
1532
+ new UnexpectedError({
1533
+ cause: new Error(
1534
+ `Operation '${
1535
+ String(operation)
1536
+ }' declared transfer support but no runtime transfer support is configured`,
1537
+ ),
1538
+ }),
1539
+ );
1540
+ continue;
1541
+ }
1542
+
1543
+ const key = asStringPointerValue(
1544
+ String(operation),
1545
+ value.input,
1546
+ ctx.transfer.key,
1547
+ "key",
1548
+ ).take();
1549
+ if (isErr(key)) {
1550
+ this.respondWithError(msg, key.error);
1551
+ continue;
1552
+ }
1553
+
1554
+ const contentType = asOptionalStringPointerValue(
1555
+ value.input,
1556
+ ctx.transfer.contentType,
1557
+ ).take();
1558
+ if (isErr(contentType)) {
1559
+ this.respondWithError(msg, contentType.error);
1560
+ continue;
1561
+ }
1562
+
1563
+ const metadata = asOptionalStringRecordPointerValue(
1564
+ value.input,
1565
+ ctx.transfer.metadata,
1566
+ ).take();
1567
+ if (isErr(metadata)) {
1568
+ this.respondWithError(msg, metadata.error);
1569
+ continue;
1570
+ }
1571
+
1572
+ const openedTransferValue = await this.#transferSupport
1573
+ .openOperationTransfer({
1574
+ sessionKey: value.sessionKey,
1575
+ store: ctx.transfer.store,
1576
+ key,
1577
+ expiresInMs: ctx.transfer.expiresInMs ?? 60_000,
1578
+ ...(ctx.transfer.maxBytes !== undefined
1579
+ ? { maxBytes: ctx.transfer.maxBytes }
1580
+ : {}),
1581
+ ...(contentType !== undefined ? { contentType } : {}),
1582
+ ...(metadata !== undefined ? { metadata } : {}),
1583
+ }).take();
1584
+ if (isErr(openedTransferValue)) {
1585
+ this.respondWithError(msg, openedTransferValue.error);
1586
+ continue;
1587
+ }
1588
+ transferSession = openedTransferValue;
1589
+ }
1590
+
1591
+ const operationId = ulid();
1592
+ const createdAt = now();
1593
+ const runtime: RuntimeOperationRecord = {
1594
+ id: operationId,
1595
+ service: this.name,
1596
+ operation: String(operation),
1597
+ ownerSessionKey: value.sessionKey,
1598
+ snapshot: {
1599
+ id: operationId,
1600
+ service: this.name,
1601
+ operation: String(operation),
1602
+ revision: 1,
1603
+ state: "pending",
1604
+ createdAt,
1605
+ updatedAt: createdAt,
1606
+ },
1607
+ sequence: 0,
1608
+ signalSequence: 0,
1609
+ signals: [],
1610
+ terminal: false,
1611
+ watchers: new Set(),
1612
+ waiters: new Set(),
1613
+ signalWaiters: new Set(),
1614
+ };
1615
+ this.#operations.set(operationId, runtime);
1616
+ await this.saveOperationRecord(runtime);
1617
+
1618
+ if (transferSession) {
1619
+ void (async () => {
1620
+ for await (
1621
+ const progress of transferSession.transfer.updates()
1622
+ ) {
1623
+ runtime.sequence += 1;
1624
+ runtime.snapshot = buildRuntimeOperationSnapshot(
1625
+ runtime,
1626
+ "running",
1627
+ { transfer: progress },
1628
+ );
1629
+ await this.saveOperationRecord(runtime);
1630
+ await publishEventToWatchers(runtime, {
1631
+ type: "transfer",
1632
+ transfer: progress,
1633
+ snapshot: runtime.snapshot,
1634
+ });
1635
+ }
1636
+ })();
1637
+ }
1638
+
1639
+ const accepted: RuntimeOperationAcceptedEnvelope = {
1640
+ kind: "accepted",
1641
+ ref: {
1642
+ id: operationId,
1643
+ service: this.name,
1644
+ operation: String(operation),
1645
+ },
1646
+ snapshot: runtime.snapshot,
1647
+ ...(transferSession ? { transfer: transferSession.grant } : {}),
1648
+ };
1649
+ msg.respond(JSON.stringify(accepted));
1650
+
1651
+ void (async () => {
1652
+ const op = makeOperation(runtime);
1653
+ try {
1654
+ const handlerResult: unknown = await handler(
1655
+ transferSession
1656
+ ? {
1657
+ input: value.input,
1658
+ op,
1659
+ caller: value.caller,
1660
+ transfer: transferSession.transfer,
1661
+ }
1662
+ : {
1663
+ input: value.input,
1664
+ op,
1665
+ caller: value.caller,
1666
+ },
1667
+ );
1668
+ const handlerOutcome = isResultLike(handlerResult)
1669
+ ? handlerResult.take()
1670
+ : handlerResult;
1671
+ if (isErr(handlerOutcome)) {
1672
+ await op.fail(handlerOutcome.error);
1673
+ return;
1674
+ }
1675
+
1676
+ if (isOperationDeferred(handlerOutcome)) {
1677
+ return;
1678
+ }
1679
+
1680
+ if (isTerminalRuntimeOperationSnapshot(handlerOutcome)) {
1681
+ runtime.sequence = handlerOutcome.revision;
1682
+ runtime.snapshot = handlerOutcome;
1683
+ runtime.terminal = true;
1684
+ await this.saveOperationRecord(runtime);
1685
+ return;
1686
+ }
1687
+
1688
+ if (!runtime.terminal) {
1689
+ await op.complete(handlerOutcome);
1690
+ }
1691
+ } catch (cause) {
1692
+ await op.fail(new UnexpectedError({ cause }));
1693
+ }
1694
+ })();
1695
+ }
1696
+ })();
1697
+
1698
+ return Promise.resolve();
1699
+ },
1700
+ };
1701
+ }
1702
+
1703
+ async stop(): Promise<void> {
1704
+ this.#stopPromise ??= (async () => {
1705
+ if (this.natsConnection.isClosed()) {
1706
+ return;
1707
+ }
1708
+
1709
+ try {
1710
+ await this.natsConnection.drain();
1711
+ } catch (cause) {
1712
+ if (
1713
+ !(cause instanceof Error) ||
1714
+ cause.name !== "DrainingConnectionError"
1715
+ ) {
1716
+ throw cause;
1717
+ }
1718
+
1719
+ await this.natsConnection.closed().catch(() => undefined);
1720
+ }
1721
+ })();
1722
+
1723
+ await this.#stopPromise;
1724
+ }
1725
+ }