@graphrefly/ts 0.0.0 → 0.0.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 (307) hide show
  1. package/README.md +135 -0
  2. package/dist/adapters/index.cjs +105 -66
  3. package/dist/adapters/index.cjs.map +1 -1
  4. package/dist/adapters/index.d.cts +11 -136
  5. package/dist/adapters/index.d.ts +11 -136
  6. package/dist/adapters/index.js +27 -225
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/adapters/observe-storage.d.cts +4 -4
  9. package/dist/adapters/observe-storage.d.ts +4 -4
  10. package/dist/adapters/observe-storage.js +1 -0
  11. package/dist/adapters/observe-storage.js.map +1 -1
  12. package/dist/adapters/react.cjs +166 -0
  13. package/dist/adapters/react.cjs.map +1 -0
  14. package/dist/adapters/react.d.cts +31 -0
  15. package/dist/adapters/react.d.ts +31 -0
  16. package/dist/adapters/react.js +51 -0
  17. package/dist/adapters/react.js.map +1 -0
  18. package/dist/adapters/solid.cjs +157 -0
  19. package/dist/adapters/solid.cjs.map +1 -0
  20. package/dist/adapters/solid.d.cts +26 -0
  21. package/dist/adapters/solid.d.ts +26 -0
  22. package/dist/adapters/solid.js +48 -0
  23. package/dist/adapters/solid.js.map +1 -0
  24. package/dist/adapters/svelte.cjs +148 -0
  25. package/dist/adapters/svelte.cjs.map +1 -0
  26. package/dist/adapters/svelte.d.cts +31 -0
  27. package/dist/adapters/svelte.d.ts +31 -0
  28. package/dist/adapters/svelte.js +46 -0
  29. package/dist/adapters/svelte.js.map +1 -0
  30. package/dist/adapters/vue.cjs +155 -0
  31. package/dist/adapters/vue.cjs.map +1 -0
  32. package/dist/adapters/vue.d.cts +26 -0
  33. package/dist/adapters/vue.d.ts +26 -0
  34. package/dist/adapters/vue.js +46 -0
  35. package/dist/adapters/vue.js.map +1 -0
  36. package/dist/agent-runtime-BesGDZcB.d.ts +555 -0
  37. package/dist/agent-runtime-DZTJ3C-p.d.cts +555 -0
  38. package/dist/agentic-memory-BmRCvh0d.d.ts +517 -0
  39. package/dist/agentic-memory-DfuVZ-lY.d.cts +517 -0
  40. package/dist/capability-admission-B0BzqAm5.d.cts +102 -0
  41. package/dist/capability-admission-g0mn9hpq.d.ts +102 -0
  42. package/dist/{chunk-JR4IKCR2.js → chunk-2747WN4Y.js} +5 -5
  43. package/dist/{chunk-2ILL7HQI.js → chunk-3JR3EDB2.js} +17 -17
  44. package/dist/{chunk-2ILL7HQI.js.map → chunk-3JR3EDB2.js.map} +1 -1
  45. package/dist/{chunk-GDL26N2T.js → chunk-3KN7NJDR.js} +2 -2
  46. package/dist/{chunk-DJC4CHLQ.js → chunk-43BKLFZU.js} +9 -9
  47. package/dist/chunk-6XAIOL3X.js +2407 -0
  48. package/dist/chunk-6XAIOL3X.js.map +1 -0
  49. package/dist/{chunk-SCRFRCGR.js → chunk-BSQGE5PS.js} +1 -1
  50. package/dist/{chunk-Y3ZLRGHC.js → chunk-CZNV3VXM.js} +16 -16
  51. package/dist/chunk-DGUM43GV.js +11 -0
  52. package/dist/chunk-DGUM43GV.js.map +1 -0
  53. package/dist/{chunk-JIMUDQN3.js → chunk-FBUU43PM.js} +2 -2
  54. package/dist/chunk-H75VXTGT.js +219 -0
  55. package/dist/chunk-H75VXTGT.js.map +1 -0
  56. package/dist/{chunk-RIIOUTGM.js → chunk-HKK2JD7Z.js} +12 -9
  57. package/dist/chunk-HKK2JD7Z.js.map +1 -0
  58. package/dist/{chunk-D6BG7U6A.js → chunk-IXZBUWSP.js} +3 -3
  59. package/dist/chunk-PC3BTTBE.js +3217 -0
  60. package/dist/chunk-PC3BTTBE.js.map +1 -0
  61. package/dist/chunk-QFY4AFY4.js +879 -0
  62. package/dist/chunk-QFY4AFY4.js.map +1 -0
  63. package/dist/{chunk-DK554R7P.js → chunk-S6HXABGL.js} +2 -1
  64. package/dist/chunk-S6HXABGL.js.map +1 -0
  65. package/dist/{chunk-PKVBHGBM.js → chunk-SS2A464I.js} +7 -7
  66. package/dist/chunk-UT3MHJCW.js +49 -0
  67. package/dist/chunk-UT3MHJCW.js.map +1 -0
  68. package/dist/{chunk-G4M3NHRJ.js → chunk-V7CIJE4B.js} +3 -3
  69. package/dist/{chunk-TEW47IVR.js → chunk-VPEFSDME.js} +2 -2
  70. package/dist/chunk-VTHLCFN4.js +368 -0
  71. package/dist/chunk-VTHLCFN4.js.map +1 -0
  72. package/dist/{chunk-SWTLEQL6.js → chunk-WQIA3GFB.js} +2 -2
  73. package/dist/chunk-WQIA3GFB.js.map +1 -0
  74. package/dist/{chunk-I3IHOHCJ.js → chunk-XKXWK2KP.js} +108 -64
  75. package/dist/chunk-XKXWK2KP.js.map +1 -0
  76. package/dist/{chunk-COHO25CZ.js → chunk-YDSIVHRR.js} +553 -7
  77. package/dist/chunk-YDSIVHRR.js.map +1 -0
  78. package/dist/chunk-YXMZ2N6J.js +1884 -0
  79. package/dist/chunk-YXMZ2N6J.js.map +1 -0
  80. package/dist/composition/index.cjs +102 -57
  81. package/dist/composition/index.cjs.map +1 -1
  82. package/dist/composition/index.d.cts +4 -4
  83. package/dist/composition/index.d.ts +4 -4
  84. package/dist/composition/index.js +6 -5
  85. package/dist/core/index.cjs +102 -57
  86. package/dist/core/index.cjs.map +1 -1
  87. package/dist/core/index.d.cts +2 -2
  88. package/dist/core/index.d.ts +2 -2
  89. package/dist/core/index.js +5 -4
  90. package/dist/cqrs/index.cjs.map +1 -1
  91. package/dist/cqrs/index.d.cts +4 -4
  92. package/dist/cqrs/index.d.ts +4 -4
  93. package/dist/cqrs/index.js +3 -2
  94. package/dist/cqrs/index.js.map +1 -1
  95. package/dist/cqrs/messaging.cjs +312 -0
  96. package/dist/cqrs/messaging.cjs.map +1 -0
  97. package/dist/cqrs/messaging.d.cts +57 -0
  98. package/dist/cqrs/messaging.d.ts +57 -0
  99. package/dist/cqrs/messaging.js +274 -0
  100. package/dist/cqrs/messaging.js.map +1 -0
  101. package/dist/cqrs/work-queue.cjs +329 -0
  102. package/dist/cqrs/work-queue.cjs.map +1 -0
  103. package/dist/cqrs/work-queue.d.cts +90 -0
  104. package/dist/cqrs/work-queue.d.ts +90 -0
  105. package/dist/cqrs/work-queue.js +292 -0
  106. package/dist/cqrs/work-queue.js.map +1 -0
  107. package/dist/data/index.cjs +19 -0
  108. package/dist/data/index.cjs.map +1 -0
  109. package/dist/data/index.d.cts +36 -0
  110. package/dist/data/index.d.ts +36 -0
  111. package/dist/data/index.js +1 -0
  112. package/dist/data/index.js.map +1 -0
  113. package/dist/data-structures/index.cjs +105 -57
  114. package/dist/data-structures/index.cjs.map +1 -1
  115. package/dist/data-structures/index.d.cts +8 -8
  116. package/dist/data-structures/index.d.ts +8 -8
  117. package/dist/data-structures/index.js +7 -6
  118. package/dist/{describe-BZb7AS2y.d.ts → describe-BpyJuWkw.d.ts} +1 -1
  119. package/dist/{describe-Ciu8SUnN.d.cts → describe-DYMw_l1C.d.cts} +1 -1
  120. package/dist/executors/work-queue.cjs +342 -0
  121. package/dist/executors/work-queue.cjs.map +1 -0
  122. package/dist/executors/work-queue.d.cts +90 -0
  123. package/dist/executors/work-queue.d.ts +90 -0
  124. package/dist/executors/work-queue.js +306 -0
  125. package/dist/executors/work-queue.js.map +1 -0
  126. package/dist/graph/index.cjs +112 -58
  127. package/dist/graph/index.cjs.map +1 -1
  128. package/dist/graph/index.d.cts +9 -9
  129. package/dist/graph/index.d.ts +9 -9
  130. package/dist/graph/index.js +18 -17
  131. package/dist/{graph-DfdF9zy4.d.ts → graph-D-1-SQeC.d.ts} +3 -3
  132. package/dist/{graph-s23RbbFO.d.cts → graph-DCEWh4T2.d.cts} +3 -3
  133. package/dist/{index-CAEmLCYF.d.cts → index-72VZC0-E.d.cts} +2 -2
  134. package/dist/{index-SnzZu3x7.d.ts → index-Bmxeu0nK.d.ts} +2 -2
  135. package/dist/index.cjs +112 -58
  136. package/dist/index.cjs.map +1 -1
  137. package/dist/index.d.cts +11 -10
  138. package/dist/index.d.ts +11 -10
  139. package/dist/index.js +118 -113
  140. package/dist/inspection/boundary.cjs +98 -0
  141. package/dist/inspection/boundary.cjs.map +1 -0
  142. package/dist/inspection/boundary.d.cts +61 -0
  143. package/dist/inspection/boundary.d.ts +61 -0
  144. package/dist/inspection/boundary.js +75 -0
  145. package/dist/inspection/boundary.js.map +1 -0
  146. package/dist/{messages-BZqrGGm1.d.ts → messages-CAzUPPJM.d.cts} +10 -5
  147. package/dist/{messages-BZqrGGm1.d.cts → messages-CAzUPPJM.d.ts} +10 -5
  148. package/dist/messaging/index.cjs +744 -613
  149. package/dist/messaging/index.cjs.map +1 -1
  150. package/dist/messaging/index.d.cts +216 -160
  151. package/dist/messaging/index.d.ts +216 -160
  152. package/dist/messaging/index.js +19 -749
  153. package/dist/messaging/index.js.map +1 -1
  154. package/dist/operators/index.cjs +102 -57
  155. package/dist/operators/index.cjs.map +1 -1
  156. package/dist/operators/index.d.cts +6 -6
  157. package/dist/operators/index.d.ts +6 -6
  158. package/dist/operators/index.js +8 -7
  159. package/dist/orchestration/index.cjs +3333 -59
  160. package/dist/orchestration/index.cjs.map +1 -1
  161. package/dist/orchestration/index.d.cts +7 -4
  162. package/dist/orchestration/index.d.ts +7 -4
  163. package/dist/orchestration/index.js +49 -8
  164. package/dist/orchestration/index.js.map +1 -1
  165. package/dist/orchestration/messaging.cjs +314 -0
  166. package/dist/orchestration/messaging.cjs.map +1 -0
  167. package/dist/orchestration/messaging.d.cts +59 -0
  168. package/dist/orchestration/messaging.d.ts +59 -0
  169. package/dist/orchestration/messaging.js +276 -0
  170. package/dist/orchestration/messaging.js.map +1 -0
  171. package/dist/orchestration/work-queue.cjs +262 -0
  172. package/dist/orchestration/work-queue.cjs.map +1 -0
  173. package/dist/orchestration/work-queue.d.cts +85 -0
  174. package/dist/orchestration/work-queue.d.ts +85 -0
  175. package/dist/orchestration/work-queue.js +226 -0
  176. package/dist/orchestration/work-queue.js.map +1 -0
  177. package/dist/patterns/event-flow.cjs +447 -0
  178. package/dist/patterns/event-flow.cjs.map +1 -0
  179. package/dist/patterns/event-flow.d.cts +115 -0
  180. package/dist/patterns/event-flow.d.ts +115 -0
  181. package/dist/patterns/event-flow.js +14 -0
  182. package/dist/patterns/event-flow.js.map +1 -0
  183. package/dist/patterns/index.cjs +423 -18
  184. package/dist/patterns/index.cjs.map +1 -1
  185. package/dist/patterns/index.d.cts +8 -5
  186. package/dist/patterns/index.d.ts +8 -5
  187. package/dist/patterns/index.js +13 -4
  188. package/dist/{reactive-collection-Cb1II1Fi.d.ts → reactive-collection-BAmfrGjd.d.ts} +9 -9
  189. package/dist/{reactive-collection-BxBqtIOE.d.cts → reactive-collection-DNV0-gFi.d.cts} +9 -9
  190. package/dist/{reactive-collection-storage-D-KmjE0J.d.cts → reactive-collection-storage-BNEldDyi.d.cts} +5 -5
  191. package/dist/{reactive-collection-storage-BNiEc-Gd.d.ts → reactive-collection-storage-YZsAQsWK.d.ts} +5 -5
  192. package/dist/{reactive-map-D-g3dv8I.d.cts → reactive-map-DRD1LJ2H.d.cts} +6 -6
  193. package/dist/{reactive-map-BcB4KsQR.d.ts → reactive-map-EsDiCmJk.d.ts} +6 -6
  194. package/dist/render/index.d.cts +3 -3
  195. package/dist/render/index.d.ts +3 -3
  196. package/dist/render/index.js +1 -0
  197. package/dist/{semantic-memory-graph-BYoIlP35.d.cts → semantic-memory-graph-DKuNzuWw.d.cts} +2 -2
  198. package/dist/{semantic-memory-graph-CxyXLms3.d.ts → semantic-memory-graph-uwbwJdhe.d.ts} +2 -2
  199. package/dist/solutions/index.cjs +4478 -2
  200. package/dist/solutions/index.cjs.map +1 -1
  201. package/dist/solutions/index.d.cts +13 -519
  202. package/dist/solutions/index.d.ts +13 -519
  203. package/dist/solutions/index.js +90 -6
  204. package/dist/solutions/reactive-layout/browser/index.cjs +449 -0
  205. package/dist/solutions/reactive-layout/browser/index.cjs.map +1 -0
  206. package/dist/solutions/reactive-layout/browser/index.d.cts +44 -0
  207. package/dist/solutions/reactive-layout/browser/index.d.ts +44 -0
  208. package/dist/solutions/reactive-layout/browser/index.js +69 -0
  209. package/dist/solutions/reactive-layout/browser/index.js.map +1 -0
  210. package/dist/solutions/reactive-layout/index.cjs +4266 -0
  211. package/dist/solutions/reactive-layout/index.cjs.map +1 -0
  212. package/dist/solutions/reactive-layout/index.d.cts +581 -0
  213. package/dist/solutions/reactive-layout/index.d.ts +581 -0
  214. package/dist/solutions/reactive-layout/index.js +86 -0
  215. package/dist/solutions/reactive-layout/index.js.map +1 -0
  216. package/dist/solutions/reactive-layout/node-canvas/index.cjs +545 -0
  217. package/dist/solutions/reactive-layout/node-canvas/index.cjs.map +1 -0
  218. package/dist/solutions/reactive-layout/node-canvas/index.d.cts +54 -0
  219. package/dist/solutions/reactive-layout/node-canvas/index.d.ts +54 -0
  220. package/dist/solutions/reactive-layout/node-canvas/index.js +131 -0
  221. package/dist/solutions/reactive-layout/node-canvas/index.js.map +1 -0
  222. package/dist/solutions/reactive-layout/react-native/index.cjs +494 -0
  223. package/dist/solutions/reactive-layout/react-native/index.cjs.map +1 -0
  224. package/dist/solutions/reactive-layout/react-native/index.d.cts +51 -0
  225. package/dist/solutions/reactive-layout/react-native/index.d.ts +51 -0
  226. package/dist/solutions/reactive-layout/react-native/index.js +77 -0
  227. package/dist/solutions/reactive-layout/react-native/index.js.map +1 -0
  228. package/dist/solutions/reactive-layout/skia/index.cjs +547 -0
  229. package/dist/solutions/reactive-layout/skia/index.cjs.map +1 -0
  230. package/dist/solutions/reactive-layout/skia/index.d.cts +64 -0
  231. package/dist/solutions/reactive-layout/skia/index.d.ts +64 -0
  232. package/dist/solutions/reactive-layout/skia/index.js +70 -0
  233. package/dist/solutions/reactive-layout/skia/index.js.map +1 -0
  234. package/dist/solutions/work-item/actions.cjs +2164 -0
  235. package/dist/solutions/work-item/actions.cjs.map +1 -0
  236. package/dist/solutions/work-item/actions.d.cts +201 -0
  237. package/dist/solutions/work-item/actions.d.ts +201 -0
  238. package/dist/solutions/work-item/actions.js +1454 -0
  239. package/dist/solutions/work-item/actions.js.map +1 -0
  240. package/dist/solutions/work-item/scheduling.cjs +2454 -0
  241. package/dist/solutions/work-item/scheduling.cjs.map +1 -0
  242. package/dist/solutions/work-item/scheduling.d.cts +433 -0
  243. package/dist/solutions/work-item/scheduling.d.ts +433 -0
  244. package/dist/solutions/work-item/scheduling.js +32 -0
  245. package/dist/solutions/work-item/scheduling.js.map +1 -0
  246. package/dist/solutions/work-item/work-queue.cjs +306 -0
  247. package/dist/solutions/work-item/work-queue.cjs.map +1 -0
  248. package/dist/solutions/work-item/work-queue.d.cts +71 -0
  249. package/dist/solutions/work-item/work-queue.d.ts +71 -0
  250. package/dist/solutions/work-item/work-queue.js +270 -0
  251. package/dist/solutions/work-item/work-queue.js.map +1 -0
  252. package/dist/sources/browser.cjs +169 -57
  253. package/dist/sources/browser.cjs.map +1 -1
  254. package/dist/sources/browser.d.cts +33 -6
  255. package/dist/sources/browser.d.ts +33 -6
  256. package/dist/sources/browser.js +73 -6
  257. package/dist/sources/browser.js.map +1 -1
  258. package/dist/sources/index.cjs +102 -57
  259. package/dist/sources/index.cjs.map +1 -1
  260. package/dist/sources/index.d.cts +5 -5
  261. package/dist/sources/index.d.ts +5 -5
  262. package/dist/sources/index.js +6 -5
  263. package/dist/sources/node.cjs.map +1 -1
  264. package/dist/sources/node.d.cts +4 -4
  265. package/dist/sources/node.d.ts +4 -4
  266. package/dist/sources/node.js +2 -1
  267. package/dist/sources/node.js.map +1 -1
  268. package/dist/storage/browser.js +1 -0
  269. package/dist/storage/browser.js.map +1 -1
  270. package/dist/storage/index.d.cts +5 -5
  271. package/dist/storage/index.d.ts +5 -5
  272. package/dist/storage/index.js +5 -4
  273. package/dist/storage/node.js +1 -0
  274. package/dist/storage/node.js.map +1 -1
  275. package/dist/store-C-joM4gh.d.ts +123 -0
  276. package/dist/store-CNJwmT-h.d.cts +123 -0
  277. package/dist/testing/index.cjs +1 -0
  278. package/dist/testing/index.cjs.map +1 -1
  279. package/dist/testing/index.d.cts +1 -1
  280. package/dist/testing/index.d.ts +1 -1
  281. package/dist/testing/index.js +3 -2
  282. package/dist/{types-h12mNYbq.d.ts → types-BsJvElYZ.d.ts} +31 -31
  283. package/dist/{types-bNMSWKqF.d.cts → types-CcPIn7sy.d.cts} +31 -31
  284. package/dist/work-item-runtime-C3M79vE9.d.ts +228 -0
  285. package/dist/work-item-runtime-qaVgsL0T.d.cts +228 -0
  286. package/dist/work-queue/index.cjs +1132 -0
  287. package/dist/work-queue/index.cjs.map +1 -0
  288. package/dist/work-queue/index.d.cts +369 -0
  289. package/dist/work-queue/index.d.ts +369 -0
  290. package/dist/work-queue/index.js +1078 -0
  291. package/dist/work-queue/index.js.map +1 -0
  292. package/package.json +266 -2
  293. package/dist/chunk-COHO25CZ.js.map +0 -1
  294. package/dist/chunk-DK554R7P.js.map +0 -1
  295. package/dist/chunk-I3IHOHCJ.js.map +0 -1
  296. package/dist/chunk-RIIOUTGM.js.map +0 -1
  297. package/dist/chunk-SWTLEQL6.js.map +0 -1
  298. /package/dist/{chunk-JR4IKCR2.js.map → chunk-2747WN4Y.js.map} +0 -0
  299. /package/dist/{chunk-GDL26N2T.js.map → chunk-3KN7NJDR.js.map} +0 -0
  300. /package/dist/{chunk-DJC4CHLQ.js.map → chunk-43BKLFZU.js.map} +0 -0
  301. /package/dist/{chunk-SCRFRCGR.js.map → chunk-BSQGE5PS.js.map} +0 -0
  302. /package/dist/{chunk-Y3ZLRGHC.js.map → chunk-CZNV3VXM.js.map} +0 -0
  303. /package/dist/{chunk-JIMUDQN3.js.map → chunk-FBUU43PM.js.map} +0 -0
  304. /package/dist/{chunk-D6BG7U6A.js.map → chunk-IXZBUWSP.js.map} +0 -0
  305. /package/dist/{chunk-PKVBHGBM.js.map → chunk-SS2A464I.js.map} +0 -0
  306. /package/dist/{chunk-G4M3NHRJ.js.map → chunk-V7CIJE4B.js.map} +0 -0
  307. /package/dist/{chunk-TEW47IVR.js.map → chunk-VPEFSDME.js.map} +0 -0
@@ -0,0 +1,1078 @@
1
+ import {
2
+ attachMessageBusDeferredCommandSink
3
+ } from "../chunk-UT3MHJCW.js";
4
+ import {
5
+ depBatch
6
+ } from "../chunk-WQIA3GFB.js";
7
+ import "../chunk-S6HXABGL.js";
8
+ import "../chunk-DGUM43GV.js";
9
+
10
+ // src/work-queue/index.ts
11
+ function workQueue(graph, opts) {
12
+ assertNonEmpty(opts.queueId, "workQueue.queueId");
13
+ assertNonEmpty(opts.topic, "workQueue.topic");
14
+ assertNonEmpty(opts.subscriptionId, "workQueue.subscriptionId");
15
+ const name = opts.name ?? `workQueue/${opts.queueId}`;
16
+ const now = opts.now ?? Date.now;
17
+ const leaseDurationMs = opts.leaseDurationMs ?? 3e4;
18
+ const retry = opts.retry ?? { maxAttempts: 3, delayMs: 0 };
19
+ const admission = opts.bus.subscription({
20
+ topic: opts.topic,
21
+ subscriptionId: opts.subscriptionId,
22
+ from: opts.from ?? "earliest",
23
+ name: `${name}/admission`
24
+ });
25
+ const commands = graph.node(
26
+ [],
27
+ (ctx) => {
28
+ for (const command of depBatch(ctx, 0) ?? []) ctx.down([["DATA", command]]);
29
+ },
30
+ {
31
+ name: `${name}/commands`,
32
+ factory: "workQueueCommands",
33
+ completeWhenDepsComplete: false,
34
+ errorWhenDepsError: false
35
+ }
36
+ );
37
+ const state = {
38
+ recordSeq: 0,
39
+ leaseSeq: 0,
40
+ works: /* @__PURE__ */ new Map(),
41
+ sourceSeqs: /* @__PURE__ */ new Set(),
42
+ commandIds: /* @__PURE__ */ new Set(),
43
+ idempotencyKeys: /* @__PURE__ */ new Set(),
44
+ records: [],
45
+ deadLetters: []
46
+ };
47
+ const admissionKick = graph.node([], null, {
48
+ name: `${name}/admissionKick`,
49
+ factory: "workQueueAdmissionKick",
50
+ initial: "poll",
51
+ completeWhenDepsComplete: false,
52
+ errorWhenDepsError: false
53
+ });
54
+ const admissionPages = graph.node(
55
+ [admission.available, admissionKick, opts.bus.messages, opts.bus.status],
56
+ (ctx) => {
57
+ for (const page of depBatch(ctx, 0) ?? []) {
58
+ ctx.down([["DATA", page]]);
59
+ }
60
+ let shouldPull = (depBatch(ctx, 1)?.length ?? 0) > 0;
61
+ for (const message of depBatch(ctx, 2) ?? []) {
62
+ if (message.topic === opts.topic) shouldPull = true;
63
+ }
64
+ for (const status2 of depBatch(ctx, 3) ?? []) {
65
+ if (shouldPollAdmission(status2, opts.topic, opts.subscriptionId)) {
66
+ shouldPull = true;
67
+ }
68
+ }
69
+ if (shouldPull) {
70
+ ctx.upNext([["PULL", { pullId: admission.availablePullId, params: { limit: 100 } }]], 0);
71
+ }
72
+ },
73
+ {
74
+ name: `${name}/admissionPages`,
75
+ factory: "workQueueAdmissionPages",
76
+ partial: true,
77
+ completeWhenDepsComplete: false,
78
+ errorWhenDepsError: false
79
+ }
80
+ );
81
+ const runtime = graph.node(
82
+ [commands, admissionPages],
83
+ (ctx) => {
84
+ for (const page of depBatch(ctx, 1) ?? []) {
85
+ for (const msg of page.messages) {
86
+ for (const event of admitMessage(opts, state, msg, now())) ctx.down([["DATA", event]]);
87
+ }
88
+ }
89
+ for (const command of depBatch(ctx, 0) ?? []) {
90
+ const commandTime = commandNowMs(command, now());
91
+ for (const event of reduceQueueCommand(
92
+ opts,
93
+ state,
94
+ command,
95
+ commandTime,
96
+ leaseDurationMs,
97
+ retry
98
+ )) {
99
+ ctx.down([["DATA", event]]);
100
+ }
101
+ }
102
+ },
103
+ {
104
+ name: `${name}/runtime`,
105
+ factory: "workQueueRuntime",
106
+ partial: true,
107
+ completeWhenDepsComplete: false,
108
+ errorWhenDepsError: false
109
+ }
110
+ );
111
+ const records = graph.node(
112
+ [runtime],
113
+ (ctx) => {
114
+ for (const event of depBatch(ctx, 0) ?? []) {
115
+ const typed = event;
116
+ if (typed.kind === "record") ctx.down([["DATA", typed.record]]);
117
+ }
118
+ },
119
+ {
120
+ name: `${name}/records`,
121
+ factory: "workQueueRecords",
122
+ completeWhenDepsComplete: false,
123
+ errorWhenDepsError: false
124
+ }
125
+ );
126
+ const status = graph.node(
127
+ [runtime],
128
+ (ctx) => {
129
+ for (const event of depBatch(ctx, 0) ?? []) {
130
+ const typed = event;
131
+ if (typed.kind === "status") ctx.down([["DATA", typed.status]]);
132
+ }
133
+ },
134
+ {
135
+ name: `${name}/status`,
136
+ factory: "workQueueStatus",
137
+ completeWhenDepsComplete: false,
138
+ errorWhenDepsError: false
139
+ }
140
+ );
141
+ const issues = graph.node(
142
+ [runtime],
143
+ (ctx) => {
144
+ for (const event of depBatch(ctx, 0) ?? []) {
145
+ const typed = event;
146
+ if (typed.kind === "issue") ctx.down([["DATA", typed.issue]]);
147
+ }
148
+ },
149
+ {
150
+ name: `${name}/issues`,
151
+ factory: "workQueueIssues",
152
+ completeWhenDepsComplete: false,
153
+ errorWhenDepsError: false
154
+ }
155
+ );
156
+ const admissionAckCommands = graph.node(
157
+ [records],
158
+ (ctx) => {
159
+ for (const record of depBatch(ctx, 0) ?? []) {
160
+ const command = admissionAckCommand(record);
161
+ if (command !== void 0) ctx.down([["DATA", command]]);
162
+ }
163
+ },
164
+ {
165
+ name: `${name}/admissionAckCommands`,
166
+ factory: "workQueueAdmissionAckCommands",
167
+ completeWhenDepsComplete: false,
168
+ errorWhenDepsError: false
169
+ }
170
+ );
171
+ attachMessageBusDeferredCommandSink(graph, opts.bus, admissionAckCommands);
172
+ graph.retain(runtime, { reason: `${name}.workQueue.runtime` });
173
+ const queue = {
174
+ commands,
175
+ records,
176
+ status,
177
+ issues,
178
+ submit(payload, submitOpts = {}) {
179
+ const command = opts.bus.publish(
180
+ opts.topic,
181
+ submitPayload(payload, {
182
+ ...submitOpts.workId === void 0 ? {} : { workId: submitOpts.workId },
183
+ ...submitOpts.priority === void 0 ? {} : { priority: submitOpts.priority },
184
+ ...submitOpts.tags === void 0 ? {} : { tags: submitOpts.tags },
185
+ ...submitOpts.requirements === void 0 ? {} : { requirements: submitOpts.requirements },
186
+ ...submitOpts.notBeforeMs === void 0 ? {} : { notBeforeMs: submitOpts.notBeforeMs },
187
+ ...submitOpts.deadlineMs === void 0 ? {} : { deadlineMs: submitOpts.deadlineMs }
188
+ }),
189
+ {
190
+ commandId: submitOpts.commandId ?? nextCommandId(opts.queueId, "submit"),
191
+ idempotencyKey: submitOpts.idempotencyKey
192
+ }
193
+ );
194
+ return command;
195
+ },
196
+ claim(claimOpts) {
197
+ return publishQueueCommand(commands, {
198
+ ...claimOpts,
199
+ kind: "claim",
200
+ commandId: claimOpts.commandId ?? nextCommandId(opts.queueId, "claim")
201
+ });
202
+ },
203
+ renewLease(renewOpts) {
204
+ return publishQueueCommand(commands, {
205
+ ...renewOpts,
206
+ kind: "renew-lease",
207
+ commandId: renewOpts.commandId ?? nextCommandId(opts.queueId, "renew")
208
+ });
209
+ },
210
+ release(releaseOpts) {
211
+ return publishQueueCommand(commands, {
212
+ ...releaseOpts,
213
+ kind: "release",
214
+ commandId: releaseOpts.commandId ?? nextCommandId(opts.queueId, "release")
215
+ });
216
+ },
217
+ complete(completeOpts) {
218
+ return publishQueueCommand(commands, {
219
+ ...completeOpts,
220
+ kind: "complete",
221
+ commandId: completeOpts.commandId ?? nextCommandId(opts.queueId, "complete")
222
+ });
223
+ },
224
+ fail(failOpts) {
225
+ return publishQueueCommand(commands, {
226
+ ...failOpts,
227
+ kind: "fail",
228
+ commandId: failOpts.commandId ?? nextCommandId(opts.queueId, "fail")
229
+ });
230
+ },
231
+ cancel(cancelOpts) {
232
+ return publishQueueCommand(commands, {
233
+ ...cancelOpts,
234
+ kind: "cancel",
235
+ commandId: cancelOpts.commandId ?? nextCommandId(opts.queueId, "cancel")
236
+ });
237
+ },
238
+ schedule(scheduleOpts) {
239
+ return publishQueueCommand(commands, {
240
+ ...scheduleOpts,
241
+ kind: "schedule",
242
+ commandId: scheduleOpts.commandId ?? nextCommandId(opts.queueId, "schedule")
243
+ });
244
+ },
245
+ expireLeases(expireOpts = {}) {
246
+ return publishQueueCommand(commands, {
247
+ ...expireOpts,
248
+ kind: "expire-leases",
249
+ commandId: expireOpts.commandId ?? nextCommandId(opts.queueId, "expire")
250
+ });
251
+ },
252
+ available(projectionOpts = {}) {
253
+ const availablePullId = /* @__PURE__ */ Symbol(`${name}/available`);
254
+ const available = graph.node(
255
+ [records],
256
+ (ctx) => {
257
+ const params = pullParams(ctx.pull);
258
+ ctx.down([["DATA", availablePage(state, params)]]);
259
+ },
260
+ {
261
+ name: projectionOpts.name ?? `${name}/available`,
262
+ factory: "workQueueAvailable",
263
+ pullId: availablePullId,
264
+ partial: true,
265
+ completeWhenDepsComplete: false,
266
+ errorWhenDepsError: false
267
+ }
268
+ );
269
+ return { available, availablePullId, status, issues };
270
+ },
271
+ work(workId, projectionOpts = {}) {
272
+ const snapshotPullId = /* @__PURE__ */ Symbol(`${name}/${workId}/snapshot`);
273
+ const snapshot = graph.node(
274
+ [records],
275
+ (ctx) => {
276
+ ctx.down([["DATA", workSnapshot(state, workId)]]);
277
+ },
278
+ {
279
+ name: projectionOpts.name ?? `${name}/${workId}`,
280
+ factory: "workQueueWorkSnapshot",
281
+ pullId: snapshotPullId,
282
+ partial: true,
283
+ completeWhenDepsComplete: false,
284
+ errorWhenDepsError: false
285
+ }
286
+ );
287
+ return { snapshot, snapshotPullId, status, issues };
288
+ },
289
+ deadLetter(projectionOpts = {}) {
290
+ const snapshotPullId = /* @__PURE__ */ Symbol(`${name}/deadLetter`);
291
+ const snapshot = graph.node(
292
+ [records],
293
+ (ctx) => {
294
+ const params = pullParams(ctx.pull);
295
+ ctx.down([["DATA", deadLetterPage(state, params)]]);
296
+ },
297
+ {
298
+ name: projectionOpts.name ?? `${name}/deadLetter`,
299
+ factory: "workQueueDeadLetter",
300
+ pullId: snapshotPullId,
301
+ partial: true,
302
+ completeWhenDepsComplete: false,
303
+ errorWhenDepsError: false
304
+ }
305
+ );
306
+ return { snapshot, snapshotPullId, status, issues };
307
+ }
308
+ };
309
+ return queue;
310
+ }
311
+ function shouldPollAdmission(status, topic, subscriptionId) {
312
+ if (status.topic !== topic) return false;
313
+ if (status.kind === "message-published" || status.kind === "retention-trimmed") return true;
314
+ return status.subscriptionId === subscriptionId && (status.kind === "subscription-acked" || status.kind === "subscription-sought");
315
+ }
316
+ function admissionAckCommand(record) {
317
+ const messageBus = admissionMessageBus(record);
318
+ if (messageBus === void 0) return void 0;
319
+ return {
320
+ kind: "ack",
321
+ topic: messageBus.topic,
322
+ subscriptionId: messageBus.subscriptionId,
323
+ seq: messageBus.seq,
324
+ commandId: `${record.queueId}:admission-ack:${messageBus.topic}:${messageBus.subscriptionId}:${messageBus.seq}`
325
+ };
326
+ }
327
+ function admissionMessageBus(record) {
328
+ return "messageBus" in record ? record.messageBus : void 0;
329
+ }
330
+ function admitMessage(opts, state, message, nowMs) {
331
+ const source = `${message.topic}:${message.seq}`;
332
+ if (state.sourceSeqs.has(source)) return [];
333
+ state.sourceSeqs.add(source);
334
+ const submitted = decodeSubmittedPayload(message.payload);
335
+ const payload = submitted.payload;
336
+ const payloadRecord = payloadAsRecord(submitted.meta);
337
+ const workId = typeof payloadRecord.workId === "string" ? payloadRecord.workId : `${opts.queueId}:${message.topic}:${message.seq}`;
338
+ if (state.works.has(workId)) {
339
+ const record2 = appendRecord(state, opts.queueId, {
340
+ kind: "admission-deduped",
341
+ workId,
342
+ messageBus: { topic: message.topic, seq: message.seq, subscriptionId: opts.subscriptionId },
343
+ reason: "duplicate-work",
344
+ existingWorkId: workId,
345
+ recordedAtMs: nowMs
346
+ });
347
+ return [
348
+ recordEvent(record2),
349
+ statusEvent(opts.queueId, "admission-rejected", nowMs, {
350
+ workId,
351
+ recordSeq: record2.recordSeq,
352
+ issueCode: "duplicate-work"
353
+ })
354
+ ];
355
+ }
356
+ const work = {
357
+ workId,
358
+ payload,
359
+ state: typeof payloadRecord.notBeforeMs === "number" && payloadRecord.notBeforeMs > nowMs ? "scheduled" : "ready",
360
+ admissionSeq: message.seq,
361
+ priority: numberOrUndefined(payloadRecord.priority),
362
+ tags: stringArrayOrUndefined(payloadRecord.tags),
363
+ requirements: stringArrayOrUndefined(payloadRecord.requirements),
364
+ notBeforeMs: numberOrUndefined(payloadRecord.notBeforeMs),
365
+ deadlineMs: numberOrUndefined(payloadRecord.deadlineMs),
366
+ attempt: 0
367
+ };
368
+ state.works.set(workId, work);
369
+ const record = appendRecord(state, opts.queueId, {
370
+ kind: "work-admitted",
371
+ workId,
372
+ payload,
373
+ messageBus: { topic: message.topic, seq: message.seq, subscriptionId: opts.subscriptionId },
374
+ priority: work.priority,
375
+ tags: work.tags,
376
+ requirements: work.requirements,
377
+ notBeforeMs: work.notBeforeMs,
378
+ deadlineMs: work.deadlineMs,
379
+ recordedAtMs: nowMs
380
+ });
381
+ return [
382
+ recordEvent(record),
383
+ statusEvent(opts.queueId, "admission-accepted", nowMs, { workId, recordSeq: record.recordSeq })
384
+ ];
385
+ }
386
+ function reduceQueueCommand(opts, state, command, nowMs, leaseDurationMs, retry) {
387
+ const invalid = validateQueueCommand(opts.queueId, command);
388
+ if (invalid !== void 0)
389
+ return rejectQueueCommand(opts.queueId, command, invalid.code, invalid.message, nowMs);
390
+ if (state.commandIds.has(command.commandId)) {
391
+ return rejectQueueCommand(
392
+ opts.queueId,
393
+ command,
394
+ "duplicate-command",
395
+ "duplicate commandId",
396
+ nowMs
397
+ );
398
+ }
399
+ if (command.idempotencyKey !== void 0 && state.idempotencyKeys.has(command.idempotencyKey)) {
400
+ return rejectQueueCommand(
401
+ opts.queueId,
402
+ command,
403
+ "duplicate-command",
404
+ "duplicate idempotencyKey",
405
+ nowMs
406
+ );
407
+ }
408
+ state.commandIds.add(command.commandId);
409
+ if (command.idempotencyKey !== void 0) state.idempotencyKeys.add(command.idempotencyKey);
410
+ switch (command.kind) {
411
+ case "claim":
412
+ return claimWork(opts.queueId, state, command, nowMs, leaseDurationMs);
413
+ case "renew-lease":
414
+ return renewLease(opts.queueId, state, command, nowMs, leaseDurationMs);
415
+ case "release":
416
+ return releaseWork(opts.queueId, state, command, nowMs);
417
+ case "complete":
418
+ return completeWork(opts.queueId, state, command, nowMs);
419
+ case "fail":
420
+ return failWork(opts.queueId, state, command, nowMs, retry);
421
+ case "cancel":
422
+ return cancelWork(opts.queueId, state, command, nowMs);
423
+ case "schedule":
424
+ return scheduleWork(opts.queueId, state, command, nowMs);
425
+ case "expire-leases":
426
+ return expireLeases(opts.queueId, state, command, nowMs);
427
+ case "submit":
428
+ return [
429
+ statusEvent(opts.queueId, "command-accepted", nowMs, {
430
+ commandId: command.commandId,
431
+ details: { submitUsesMessageBus: true }
432
+ })
433
+ ];
434
+ }
435
+ }
436
+ function claimWork(queueId, state, command, nowMs, defaultLeaseDurationMs) {
437
+ const limit = positiveLimit(command.limit ?? command.requestedWorkIds?.length ?? 1);
438
+ const requested = new Set(command.requestedWorkIds ?? []);
439
+ const events = [];
440
+ for (const work of [...state.works.values()].filter((entry) => requested.size === 0 ? true : requested.has(entry.workId)).filter((entry) => isExpiredLease(entry, nowMs))) {
441
+ events.push(...materializeLeaseExpired(queueId, state, work, command.commandId, nowMs));
442
+ }
443
+ const candidates = [...state.works.values()].filter((work) => requested.size === 0 ? true : requested.has(work.workId)).filter((work) => isReady(work, nowMs)).sort((a, b) => a.admissionSeq - b.admissionSeq).slice(0, limit);
444
+ if (candidates.length === 0)
445
+ return [
446
+ ...events,
447
+ ...requested.size === 0 ? rejectQueueCommand(queueId, command, "not-ready", "no ready work", nowMs) : claimMissEvents(queueId, state, command, requested, /* @__PURE__ */ new Set(), nowMs)
448
+ ];
449
+ const claimed = /* @__PURE__ */ new Set();
450
+ for (const work of candidates) {
451
+ claimed.add(work.workId);
452
+ work.state = "leased";
453
+ work.attempt += 1;
454
+ work.leaseId = `${work.workId}:lease:${++state.leaseSeq}`;
455
+ work.workerId = command.workerId;
456
+ work.leaseExpiresAtMs = nowMs + (command.leaseDurationMs ?? defaultLeaseDurationMs);
457
+ const record = appendRecord(state, queueId, {
458
+ kind: "work-claimed",
459
+ workId: work.workId,
460
+ commandId: command.commandId,
461
+ leaseId: work.leaseId,
462
+ attempt: work.attempt,
463
+ workerId: command.workerId,
464
+ claimedAtMs: nowMs,
465
+ leaseExpiresAtMs: work.leaseExpiresAtMs,
466
+ recordedAtMs: nowMs
467
+ });
468
+ events.push(
469
+ recordEvent(record),
470
+ statusEvent(queueId, "command-accepted", nowMs, {
471
+ workId: work.workId,
472
+ commandId: command.commandId,
473
+ recordSeq: record.recordSeq
474
+ })
475
+ );
476
+ }
477
+ if (requested.size > 0) {
478
+ events.push(...claimMissEvents(queueId, state, command, requested, claimed, nowMs));
479
+ }
480
+ return events;
481
+ }
482
+ function claimMissEvents(queueId, state, command, requested, claimed, nowMs) {
483
+ const events = [];
484
+ for (const workId of requested) {
485
+ if (claimed.has(workId)) continue;
486
+ const work = state.works.get(workId);
487
+ if (work === void 0) {
488
+ events.push(
489
+ ...rejectQueueCommand(
490
+ queueId,
491
+ command,
492
+ "unknown-work",
493
+ `unknown work '${workId}'`,
494
+ nowMs
495
+ )
496
+ );
497
+ continue;
498
+ }
499
+ if (isReady(work, nowMs)) continue;
500
+ const code = isTerminal(work.state) ? "terminal-work" : work.state === "leased" ? "already-leased" : "not-ready";
501
+ events.push(
502
+ ...rejectQueueCommand(
503
+ queueId,
504
+ command,
505
+ code,
506
+ `requested work '${workId}' is not claimable`,
507
+ nowMs
508
+ )
509
+ );
510
+ }
511
+ return events;
512
+ }
513
+ function renewLease(queueId, state, command, nowMs, defaultLeaseDurationMs) {
514
+ const checked = currentLease(queueId, state, command, nowMs);
515
+ if ("issue" in checked) return [checked.issue];
516
+ if ("events" in checked) return checked.events;
517
+ const work = checked.work;
518
+ const previous = work.leaseExpiresAtMs;
519
+ work.leaseExpiresAtMs = command.leaseExpiresAtMs ?? nowMs + (command.leaseDurationMs ?? defaultLeaseDurationMs);
520
+ const record = appendRecord(state, queueId, {
521
+ kind: "lease-renewed",
522
+ workId: work.workId,
523
+ commandId: command.commandId,
524
+ leaseId: command.leaseId,
525
+ attempt: command.attempt,
526
+ workerId: command.workerId,
527
+ previousLeaseExpiresAtMs: previous,
528
+ leaseExpiresAtMs: work.leaseExpiresAtMs,
529
+ renewedAtMs: nowMs,
530
+ recordedAtMs: nowMs
531
+ });
532
+ return [
533
+ recordEvent(record),
534
+ statusEvent(queueId, "command-accepted", nowMs, {
535
+ workId: work.workId,
536
+ commandId: command.commandId,
537
+ recordSeq: record.recordSeq
538
+ })
539
+ ];
540
+ }
541
+ function releaseWork(queueId, state, command, nowMs) {
542
+ const checked = currentLease(queueId, state, command, nowMs);
543
+ if ("issue" in checked) return [checked.issue];
544
+ if ("events" in checked) return checked.events;
545
+ const work = checked.work;
546
+ work.state = "ready";
547
+ work.leaseId = void 0;
548
+ work.workerId = void 0;
549
+ work.leaseExpiresAtMs = void 0;
550
+ const record = appendRecord(state, queueId, {
551
+ kind: "work-released",
552
+ workId: work.workId,
553
+ commandId: command.commandId,
554
+ leaseId: command.leaseId,
555
+ attempt: command.attempt,
556
+ workerId: command.workerId,
557
+ releasedAtMs: nowMs,
558
+ reason: command.reason,
559
+ recordedAtMs: nowMs
560
+ });
561
+ return [
562
+ recordEvent(record),
563
+ statusEvent(queueId, "command-accepted", nowMs, {
564
+ workId: work.workId,
565
+ commandId: command.commandId,
566
+ recordSeq: record.recordSeq
567
+ })
568
+ ];
569
+ }
570
+ function completeWork(queueId, state, command, nowMs) {
571
+ const checked = currentLease(queueId, state, command, nowMs);
572
+ if ("issue" in checked) return [checked.issue];
573
+ if ("events" in checked) return checked.events;
574
+ const work = checked.work;
575
+ work.state = "completed";
576
+ clearLease(work);
577
+ work.terminalRecordSeq = state.recordSeq + 2;
578
+ const attempt = appendRecord(state, queueId, {
579
+ kind: "attempt-completed",
580
+ workId: work.workId,
581
+ commandId: command.commandId,
582
+ leaseId: command.leaseId,
583
+ attempt: command.attempt,
584
+ workerId: command.workerId,
585
+ result: command.result,
586
+ recordedAtMs: nowMs
587
+ });
588
+ const done = appendRecord(state, queueId, {
589
+ kind: "work-completed",
590
+ workId: work.workId,
591
+ commandId: command.commandId,
592
+ leaseId: command.leaseId,
593
+ attempt: command.attempt,
594
+ workerId: command.workerId,
595
+ result: command.result,
596
+ recordedAtMs: nowMs
597
+ });
598
+ return [
599
+ recordEvent(attempt),
600
+ recordEvent(done),
601
+ statusEvent(queueId, "command-accepted", nowMs, {
602
+ workId: work.workId,
603
+ commandId: command.commandId,
604
+ recordSeq: done.recordSeq
605
+ })
606
+ ];
607
+ }
608
+ function failWork(queueId, state, command, nowMs, retry) {
609
+ const checked = currentLease(queueId, state, command, nowMs);
610
+ if ("issue" in checked) return [checked.issue];
611
+ if ("events" in checked) return checked.events;
612
+ const work = checked.work;
613
+ const failed = appendRecord(state, queueId, {
614
+ kind: "attempt-failed",
615
+ workId: work.workId,
616
+ commandId: command.commandId,
617
+ leaseId: command.leaseId,
618
+ attempt: command.attempt,
619
+ workerId: command.workerId,
620
+ error: command.error,
621
+ retryable: command.retryable,
622
+ recordedAtMs: nowMs
623
+ });
624
+ const maxAttempts = retry.maxAttempts ?? 3;
625
+ if (command.retryable === false || work.attempt >= maxAttempts) {
626
+ work.state = "dead-lettered";
627
+ clearLease(work);
628
+ work.retryAtMs = void 0;
629
+ const dead = appendRecord(state, queueId, {
630
+ kind: "work-dead-lettered",
631
+ workId: work.workId,
632
+ commandId: command.commandId,
633
+ reason: command.retryable === false ? "non-retryable" : "attempts-exhausted",
634
+ exhaustedAttempts: work.attempt,
635
+ recordedAtMs: nowMs
636
+ });
637
+ state.deadLetters.push(dead);
638
+ return [
639
+ recordEvent(failed),
640
+ recordEvent(dead),
641
+ statusEvent(queueId, "command-accepted", nowMs, {
642
+ workId: work.workId,
643
+ commandId: command.commandId,
644
+ recordSeq: dead.recordSeq
645
+ })
646
+ ];
647
+ }
648
+ const delayMs = retry.delayMs ?? 0;
649
+ work.state = delayMs > 0 ? "retry-wait" : "ready";
650
+ work.retryAtMs = nowMs + delayMs;
651
+ clearLease(work);
652
+ const retryRecord = appendRecord(state, queueId, {
653
+ kind: "retry-scheduled",
654
+ workId: work.workId,
655
+ commandId: command.commandId,
656
+ retryAtMs: work.retryAtMs,
657
+ delayMs,
658
+ reason: "retry-policy",
659
+ recordedAtMs: nowMs
660
+ });
661
+ return [
662
+ recordEvent(failed),
663
+ recordEvent(retryRecord),
664
+ statusEvent(queueId, "command-accepted", nowMs, {
665
+ workId: work.workId,
666
+ commandId: command.commandId,
667
+ recordSeq: retryRecord.recordSeq
668
+ })
669
+ ];
670
+ }
671
+ function cancelWork(queueId, state, command, nowMs) {
672
+ const work = state.works.get(command.workId);
673
+ if (work === void 0)
674
+ return [issueEvent(queueId, "unknown-work", "unknown work", nowMs, command)];
675
+ if (isTerminal(work.state))
676
+ return [issueEvent(queueId, "terminal-work", "work is terminal", nowMs, command)];
677
+ work.state = "canceled";
678
+ const canceledLeaseId = work.leaseId;
679
+ const attempt = work.attempt || void 0;
680
+ const record = appendRecord(state, queueId, {
681
+ kind: "work-canceled",
682
+ workId: work.workId,
683
+ commandId: command.commandId,
684
+ reason: command.reason,
685
+ canceledAtMs: nowMs,
686
+ canceledLeaseId,
687
+ attempt,
688
+ recordedAtMs: nowMs
689
+ });
690
+ clearLease(work);
691
+ return [
692
+ recordEvent(record),
693
+ statusEvent(queueId, "command-accepted", nowMs, {
694
+ workId: work.workId,
695
+ commandId: command.commandId,
696
+ recordSeq: record.recordSeq
697
+ })
698
+ ];
699
+ }
700
+ function scheduleWork(queueId, state, command, nowMs) {
701
+ const work = state.works.get(command.workId);
702
+ if (work === void 0)
703
+ return [issueEvent(queueId, "unknown-work", "unknown work", nowMs, command)];
704
+ if (isTerminal(work.state))
705
+ return [issueEvent(queueId, "terminal-work", "work is terminal", nowMs, command)];
706
+ work.state = "scheduled";
707
+ work.notBeforeMs = command.notBeforeMs;
708
+ work.deadlineMs = command.deadlineMs;
709
+ const record = appendRecord(state, queueId, {
710
+ kind: "work-scheduled",
711
+ workId: work.workId,
712
+ commandId: command.commandId,
713
+ scheduleId: command.scheduleId,
714
+ scheduleAtMs: command.scheduleAtMs,
715
+ runAtMs: command.runAtMs,
716
+ notBeforeMs: command.notBeforeMs,
717
+ deadlineMs: command.deadlineMs,
718
+ reason: command.reason,
719
+ recordedAtMs: nowMs
720
+ });
721
+ return [
722
+ recordEvent(record),
723
+ statusEvent(queueId, "command-accepted", nowMs, {
724
+ workId: work.workId,
725
+ commandId: command.commandId,
726
+ recordSeq: record.recordSeq
727
+ })
728
+ ];
729
+ }
730
+ function expireLeases(queueId, state, command, nowMs) {
731
+ const workIds = new Set(command.workIds ?? []);
732
+ const limit = positiveLimit(command.limit ?? Number.MAX_SAFE_INTEGER);
733
+ const expired = [...state.works.values()].filter(
734
+ (work) => work.state === "leased" && work.leaseExpiresAtMs !== void 0 && work.leaseExpiresAtMs <= nowMs
735
+ ).filter((work) => workIds.size === 0 || workIds.has(work.workId)).slice(0, limit);
736
+ if (expired.length === 0)
737
+ return [statusEvent(queueId, "maintenance-noop", nowMs, { commandId: command.commandId })];
738
+ const events = [];
739
+ for (const work of expired) {
740
+ events.push(...materializeLeaseExpired(queueId, state, work, command.commandId, nowMs));
741
+ }
742
+ events.push(
743
+ statusEvent(queueId, "maintenance-applied", nowMs, {
744
+ commandId: command.commandId,
745
+ details: { expired: expired.length }
746
+ })
747
+ );
748
+ return events;
749
+ }
750
+ function currentLease(queueId, state, command, nowMs) {
751
+ const work = state.works.get(command.workId);
752
+ if (work === void 0)
753
+ return { issue: issueEvent(queueId, "unknown-work", "unknown work", nowMs, command) };
754
+ if (isTerminal(work.state))
755
+ return { issue: issueEvent(queueId, "terminal-work", "work is terminal", nowMs, command) };
756
+ if (work.state !== "leased")
757
+ return {
758
+ issue: issueEvent(queueId, "lease-not-current", "work is not leased", nowMs, command)
759
+ };
760
+ if (work.leaseId !== command.leaseId)
761
+ return { issue: issueEvent(queueId, "stale-lease", "lease is not current", nowMs, command) };
762
+ if (work.attempt !== command.attempt)
763
+ return { issue: issueEvent(queueId, "attempt-mismatch", "attempt mismatch", nowMs, command) };
764
+ if (work.workerId !== command.workerId)
765
+ return { issue: issueEvent(queueId, "worker-mismatch", "worker mismatch", nowMs, command) };
766
+ if (isExpiredLease(work, nowMs))
767
+ return {
768
+ events: materializeLeaseExpired(queueId, state, work, command.commandId, nowMs, {
769
+ command,
770
+ code: "lease-expired",
771
+ message: "lease expired"
772
+ })
773
+ };
774
+ return { work };
775
+ }
776
+ function availablePage(state, params) {
777
+ const limit = positiveLimit(params.limit ?? 100);
778
+ const orderByWorkId = params.afterWorkId !== void 0 && params.afterAdmissionSeq === void 0;
779
+ const all = [...state.works.values()].filter((work) => isReadyForProjection(work, params.nowMs)).filter((work) => params.afterWorkId === void 0 || work.workId > params.afterWorkId).filter(
780
+ (work) => params.afterAdmissionSeq === void 0 || work.admissionSeq > params.afterAdmissionSeq
781
+ ).sort(
782
+ (a, b) => orderByWorkId ? a.workId.localeCompare(b.workId) : a.admissionSeq - b.admissionSeq || a.workId.localeCompare(b.workId)
783
+ );
784
+ const items = all.map((work) => ({
785
+ workId: work.workId,
786
+ state: work.state,
787
+ payload: work.payload,
788
+ admissionSeq: work.admissionSeq,
789
+ priority: work.priority,
790
+ tags: work.tags,
791
+ requirements: work.requirements,
792
+ notBeforeMs: work.notBeforeMs,
793
+ retryAtMs: work.retryAtMs,
794
+ deadlineMs: work.deadlineMs
795
+ }));
796
+ const page = items.slice(0, limit);
797
+ return {
798
+ items: page,
799
+ ...items.length > limit && page.length > 0 ? { nextAfterWorkId: page[page.length - 1]?.workId } : {},
800
+ ...items.length > limit && page.length > 0 ? { nextAfterAdmissionSeq: page[page.length - 1]?.admissionSeq } : {},
801
+ hasMore: items.length > limit,
802
+ asOfRecordSeq: state.recordSeq
803
+ };
804
+ }
805
+ function workSnapshot(state, workId) {
806
+ const work = state.works.get(workId);
807
+ return {
808
+ workId,
809
+ state: work?.state,
810
+ payload: work?.payload,
811
+ ...work?.state !== "leased" || work.leaseId === void 0 ? {} : {
812
+ activeLease: {
813
+ leaseId: work.leaseId,
814
+ attempt: work.attempt,
815
+ workerId: work.workerId,
816
+ leaseExpiresAtMs: work.leaseExpiresAtMs
817
+ }
818
+ },
819
+ records: state.records.filter((record) => record.workId === workId),
820
+ asOfRecordSeq: state.recordSeq
821
+ };
822
+ }
823
+ function materializeLeaseExpired(queueId, state, work, commandId, nowMs, rejection) {
824
+ const record = appendRecord(state, queueId, {
825
+ kind: "lease-expired",
826
+ workId: work.workId,
827
+ ...commandId === void 0 ? {} : { commandId },
828
+ leaseId: work.leaseId,
829
+ attempt: work.attempt,
830
+ workerId: work.workerId,
831
+ leaseExpiresAtMs: work.leaseExpiresAtMs,
832
+ expiredAtMs: nowMs,
833
+ recordedAtMs: nowMs
834
+ });
835
+ work.state = "ready";
836
+ clearLease(work);
837
+ const events = [recordEvent(record)];
838
+ if (rejection !== void 0) {
839
+ events.push(
840
+ ...rejectQueueCommand(
841
+ queueId,
842
+ rejection.command,
843
+ rejection.code,
844
+ rejection.message,
845
+ nowMs,
846
+ commandId
847
+ )
848
+ );
849
+ }
850
+ return events;
851
+ }
852
+ function isExpiredLease(work, nowMs) {
853
+ return work.state === "leased" && work.leaseExpiresAtMs !== void 0 && work.leaseExpiresAtMs <= nowMs;
854
+ }
855
+ function clearLease(work) {
856
+ work.leaseId = void 0;
857
+ work.workerId = void 0;
858
+ work.leaseExpiresAtMs = void 0;
859
+ }
860
+ function deadLetterPage(state, params) {
861
+ const limit = positiveLimit(params.limit ?? 100);
862
+ const entries = state.deadLetters.filter((record) => {
863
+ if (params.afterDeadLetterSeq !== void 0 && record.recordSeq <= params.afterDeadLetterSeq)
864
+ return false;
865
+ if (params.afterWorkId !== void 0 && (record.workId ?? "") <= params.afterWorkId)
866
+ return false;
867
+ return true;
868
+ });
869
+ const page = entries.slice(0, limit);
870
+ return {
871
+ entries: page,
872
+ ...entries.length > limit && page.length > 0 ? { nextAfterDeadLetterSeq: page[page.length - 1]?.recordSeq } : {},
873
+ hasMore: entries.length > limit,
874
+ asOfRecordSeq: state.recordSeq
875
+ };
876
+ }
877
+ function appendRecord(state, queueId, record) {
878
+ const full = { ...record, queueId, recordSeq: ++state.recordSeq };
879
+ state.records.push(full);
880
+ return full;
881
+ }
882
+ function recordEvent(record) {
883
+ return { kind: "record", record };
884
+ }
885
+ function statusEvent(queueId, kind, timestampMs, fields = {}) {
886
+ return {
887
+ kind: "status",
888
+ status: {
889
+ kind,
890
+ queueId,
891
+ timestampMs,
892
+ ...fields.workId === void 0 ? {} : { workId: fields.workId },
893
+ ...fields.commandId === void 0 ? {} : { commandId: fields.commandId },
894
+ ...fields.recordSeq === void 0 ? {} : { recordSeq: fields.recordSeq },
895
+ ...fields.asOfRecordSeq === void 0 ? {} : { asOfRecordSeq: fields.asOfRecordSeq },
896
+ ...fields.issueCode === void 0 ? {} : { issueCode: fields.issueCode },
897
+ ...fields.details === void 0 ? {} : { details: fields.details }
898
+ }
899
+ };
900
+ }
901
+ function issueEvent(queueId, code, message, timestampMs, details) {
902
+ return {
903
+ kind: "issue",
904
+ issue: {
905
+ kind: "issue",
906
+ code,
907
+ message,
908
+ severity: "error",
909
+ source: "workQueue",
910
+ details,
911
+ metadata: { queueId, timestampMs }
912
+ }
913
+ };
914
+ }
915
+ function rejectQueueCommand(queueId, command, code, message, nowMs, commandId = commandIdFrom(command)) {
916
+ return [
917
+ issueEvent(queueId, code, message, nowMs, command),
918
+ statusEvent(queueId, "command-rejected", nowMs, {
919
+ ...commandId === void 0 ? {} : { commandId },
920
+ issueCode: code
921
+ })
922
+ ];
923
+ }
924
+ function validateQueueCommand(queueId, command) {
925
+ if (!isObjectRecord(command))
926
+ return { code: "malformed-command", message: "command must be an object" };
927
+ if (typeof command.kind !== "string")
928
+ return { code: "malformed-command", message: "command kind is required" };
929
+ if (!isWorkQueueCommandKind(command.kind))
930
+ return { code: "malformed-command", message: `unknown command kind '${command.kind}'` };
931
+ if (typeof command.commandId !== "string" || command.commandId.length === 0)
932
+ return { code: "malformed-command", message: "commandId must be a non-empty string" };
933
+ if (command.queueId !== void 0 && command.queueId !== queueId)
934
+ return { code: "queue-mismatch", message: "command queueId does not match this queue" };
935
+ switch (command.kind) {
936
+ case "submit":
937
+ return command.payload === void 0 ? { code: "malformed-command", message: "submit payload is required" } : void 0;
938
+ case "claim":
939
+ if (typeof command.workerId !== "string" || command.workerId.length === 0)
940
+ return { code: "malformed-command", message: "claim workerId is required" };
941
+ if (command.limit !== void 0 && !isPositiveInteger(command.limit))
942
+ return { code: "malformed-command", message: "claim limit must be a positive integer" };
943
+ if (command.requestedWorkIds !== void 0 && (!Array.isArray(command.requestedWorkIds) || !command.requestedWorkIds.every((id) => typeof id === "string" && id.length > 0))) {
944
+ return { code: "malformed-command", message: "requestedWorkIds must be strings" };
945
+ }
946
+ if (command.leaseDurationMs !== void 0 && !isPositiveInteger(command.leaseDurationMs))
947
+ return {
948
+ code: "malformed-command",
949
+ message: "leaseDurationMs must be a positive integer"
950
+ };
951
+ return void 0;
952
+ case "renew-lease":
953
+ if (command.leaseDurationMs !== void 0 && !isPositiveInteger(command.leaseDurationMs))
954
+ return {
955
+ code: "malformed-command",
956
+ message: "leaseDurationMs must be a positive integer"
957
+ };
958
+ if (command.leaseExpiresAtMs !== void 0 && !isFiniteNumber(command.leaseExpiresAtMs))
959
+ return { code: "malformed-command", message: "leaseExpiresAtMs must be finite" };
960
+ return validateLeaseCommand(command);
961
+ case "release":
962
+ case "complete":
963
+ case "fail":
964
+ return validateLeaseCommand(command);
965
+ case "cancel":
966
+ return requiredString(command.workId, "workId");
967
+ case "schedule": {
968
+ const workIdError = requiredString(command.workId, "workId");
969
+ if (workIdError !== void 0) return workIdError;
970
+ if (!isFiniteNumber(command.notBeforeMs))
971
+ return { code: "malformed-command", message: "notBeforeMs must be finite" };
972
+ if (command.deadlineMs !== void 0 && !isFiniteNumber(command.deadlineMs))
973
+ return { code: "malformed-command", message: "deadlineMs must be finite" };
974
+ return void 0;
975
+ }
976
+ case "expire-leases":
977
+ if (command.limit !== void 0 && !isPositiveInteger(command.limit))
978
+ return {
979
+ code: "malformed-command",
980
+ message: "expire-leases limit must be a positive integer"
981
+ };
982
+ if (command.workIds !== void 0 && (!Array.isArray(command.workIds) || !command.workIds.every((id) => typeof id === "string" && id.length > 0))) {
983
+ return { code: "malformed-command", message: "workIds must be strings" };
984
+ }
985
+ return void 0;
986
+ }
987
+ }
988
+ function validateLeaseCommand(command) {
989
+ for (const key of ["workId", "leaseId", "workerId"]) {
990
+ const err = requiredString(command[key], key);
991
+ if (err !== void 0) return err;
992
+ }
993
+ if (!isPositiveInteger(command.attempt))
994
+ return { code: "malformed-command", message: "attempt must be a positive integer" };
995
+ return void 0;
996
+ }
997
+ function requiredString(value, name) {
998
+ return typeof value === "string" && value.length > 0 ? void 0 : { code: "malformed-command", message: `${name} must be a non-empty string` };
999
+ }
1000
+ function commandIdFrom(command) {
1001
+ return isObjectRecord(command) && typeof command.commandId === "string" ? command.commandId : void 0;
1002
+ }
1003
+ function isWorkQueueCommandKind(kind) {
1004
+ return kind === "submit" || kind === "claim" || kind === "renew-lease" || kind === "release" || kind === "complete" || kind === "fail" || kind === "cancel" || kind === "schedule" || kind === "expire-leases";
1005
+ }
1006
+ function isObjectRecord(value) {
1007
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1008
+ }
1009
+ function isPositiveInteger(value) {
1010
+ return Number.isInteger(value) && value > 0;
1011
+ }
1012
+ function isFiniteNumber(value) {
1013
+ return typeof value === "number" && Number.isFinite(value);
1014
+ }
1015
+ function publishQueueCommand(node, command) {
1016
+ node.down([["DATA", command]]);
1017
+ return command;
1018
+ }
1019
+ var commandSeq = 0;
1020
+ function nextCommandId(queueId, kind) {
1021
+ commandSeq += 1;
1022
+ return `${queueId}:${kind}:${commandSeq}`;
1023
+ }
1024
+ function isReady(work, nowMs) {
1025
+ if (work.state === "scheduled")
1026
+ return work.notBeforeMs !== void 0 && work.notBeforeMs <= nowMs;
1027
+ if (work.state === "retry-wait") return work.retryAtMs !== void 0 && work.retryAtMs <= nowMs;
1028
+ return work.state === "ready";
1029
+ }
1030
+ function isReadyForProjection(work, nowMs) {
1031
+ if (work.state === "scheduled" || work.state === "retry-wait") {
1032
+ return nowMs === void 0 ? false : isReady(work, nowMs);
1033
+ }
1034
+ return isReady(work, 0);
1035
+ }
1036
+ function isTerminal(state) {
1037
+ return state === "completed" || state === "canceled" || state === "dead-lettered";
1038
+ }
1039
+ function payloadAsRecord(value) {
1040
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1041
+ }
1042
+ function numberOrUndefined(value) {
1043
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1044
+ }
1045
+ function stringArrayOrUndefined(value) {
1046
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
1047
+ }
1048
+ function pullParams(pull) {
1049
+ return typeof pull?.params === "object" && pull.params !== null && !Array.isArray(pull.params) ? pull.params : {};
1050
+ }
1051
+ var SUBMIT_ENVELOPE = "__graphreflyWorkQueueSubmit";
1052
+ function submitPayload(payload, meta) {
1053
+ if (Object.keys(meta).length === 0) return payload;
1054
+ return { [SUBMIT_ENVELOPE]: true, payload, ...meta };
1055
+ }
1056
+ function decodeSubmittedPayload(payload) {
1057
+ if (isObjectRecord(payload) && payload[SUBMIT_ENVELOPE] === true && Object.hasOwn(payload, "payload")) {
1058
+ const { payload: inner, [SUBMIT_ENVELOPE]: _tag, ...meta } = payload;
1059
+ return { payload: inner, meta };
1060
+ }
1061
+ return { payload, meta: payloadAsRecord(payload) };
1062
+ }
1063
+ function commandNowMs(command, fallback) {
1064
+ return typeof command.nowMs === "number" && Number.isFinite(command.nowMs) ? command.nowMs : fallback;
1065
+ }
1066
+ function positiveLimit(limit) {
1067
+ if (!Number.isInteger(limit) || limit < 1)
1068
+ throw new Error("workQueue: limit must be a positive integer");
1069
+ return limit;
1070
+ }
1071
+ function assertNonEmpty(value, owner) {
1072
+ if (typeof value !== "string" || value.length === 0)
1073
+ throw new Error(`${owner} must be a non-empty string`);
1074
+ }
1075
+ export {
1076
+ workQueue
1077
+ };
1078
+ //# sourceMappingURL=index.js.map