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