gemi 0.93.0 → 0.94.0

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 (208) hide show
  1. package/dist/ai/index.js +2 -2
  2. package/dist/ai/index.js.map +2 -2
  3. package/dist/app/index.js +1 -1
  4. package/dist/bun/plugin.js +1 -1
  5. package/dist/bun/preload.js +1 -1
  6. package/dist/{chunk-g2xbpr8r.js → chunk-1hnpr3xt.js} +2 -2
  7. package/dist/{chunk-g2xbpr8r.js.map → chunk-1hnpr3xt.js.map} +1 -1
  8. package/dist/{chunk-v51a4g6w.js → chunk-2jse74pn.js} +2 -2
  9. package/dist/{chunk-v51a4g6w.js.map → chunk-2jse74pn.js.map} +1 -1
  10. package/dist/{chunk-1yt4hjqx.js → chunk-2shkf2gs.js} +2 -2
  11. package/dist/{chunk-1yt4hjqx.js.map → chunk-2shkf2gs.js.map} +1 -1
  12. package/dist/{chunk-dve6ryfd.js → chunk-2yyr5tdc.js} +1 -1
  13. package/dist/{chunk-4k4mh3gh.js → chunk-323715e8.js} +2 -2
  14. package/dist/{chunk-4k4mh3gh.js.map → chunk-323715e8.js.map} +1 -1
  15. package/dist/{chunk-xk4r9372.js → chunk-37jb6q13.js} +2 -2
  16. package/dist/{chunk-xk4r9372.js.map → chunk-37jb6q13.js.map} +1 -1
  17. package/dist/{chunk-wd9zab3e.js → chunk-38k1kjyb.js} +2 -2
  18. package/dist/{chunk-wd9zab3e.js.map → chunk-38k1kjyb.js.map} +1 -1
  19. package/dist/{chunk-aq1hjk00.js → chunk-3fjkbcra.js} +1 -1
  20. package/dist/{chunk-6qyc4e22.js → chunk-3fr22rtg.js} +2 -2
  21. package/dist/{chunk-6qyc4e22.js.map → chunk-3fr22rtg.js.map} +1 -1
  22. package/dist/{chunk-jj0ynn5t.js → chunk-56gce2z7.js} +2 -2
  23. package/dist/{chunk-jj0ynn5t.js.map → chunk-56gce2z7.js.map} +1 -1
  24. package/dist/{chunk-qs9drxja.js → chunk-5en4ydde.js} +2 -2
  25. package/dist/{chunk-qs9drxja.js.map → chunk-5en4ydde.js.map} +1 -1
  26. package/dist/{chunk-z10c81a9.js → chunk-5px4rkzf.js} +2 -2
  27. package/dist/{chunk-z10c81a9.js.map → chunk-5px4rkzf.js.map} +1 -1
  28. package/dist/{chunk-bb8e7rhf.js → chunk-7dnb21s9.js} +2 -2
  29. package/dist/{chunk-bb8e7rhf.js.map → chunk-7dnb21s9.js.map} +1 -1
  30. package/dist/chunk-7hfv5n00.js +5 -0
  31. package/dist/{chunk-cyze295m.js.map → chunk-7hfv5n00.js.map} +4 -3
  32. package/dist/{chunk-p0px3ym0.js → chunk-7kedxtbp.js} +2 -2
  33. package/dist/{chunk-p0px3ym0.js.map → chunk-7kedxtbp.js.map} +1 -1
  34. package/dist/{chunk-vpcys0ty.js → chunk-7ktsceyz.js} +2 -2
  35. package/dist/{chunk-vpcys0ty.js.map → chunk-7ktsceyz.js.map} +1 -1
  36. package/dist/{chunk-s22afmm9.js → chunk-7s5nh5mx.js} +2 -2
  37. package/dist/{chunk-s22afmm9.js.map → chunk-7s5nh5mx.js.map} +1 -1
  38. package/dist/{chunk-fz805r97.js → chunk-7vjbebst.js} +2 -2
  39. package/dist/{chunk-fz805r97.js.map → chunk-7vjbebst.js.map} +1 -1
  40. package/dist/{chunk-a1y7pa5z.js → chunk-8g99gnsd.js} +2 -2
  41. package/dist/{chunk-a1y7pa5z.js.map → chunk-8g99gnsd.js.map} +1 -1
  42. package/dist/{chunk-khvnm11p.js → chunk-9g5cpkkk.js} +2 -2
  43. package/dist/{chunk-khvnm11p.js.map → chunk-9g5cpkkk.js.map} +1 -1
  44. package/dist/{chunk-v6vsjdp7.js → chunk-9xf2fx98.js} +2 -2
  45. package/dist/{chunk-v6vsjdp7.js.map → chunk-9xf2fx98.js.map} +1 -1
  46. package/dist/{chunk-9pcsm8g3.js → chunk-adm5g1v8.js} +2 -2
  47. package/dist/{chunk-9pcsm8g3.js.map → chunk-adm5g1v8.js.map} +1 -1
  48. package/dist/{chunk-ej5mkm7t.js → chunk-b8m6zs7e.js} +2 -2
  49. package/dist/{chunk-ej5mkm7t.js.map → chunk-b8m6zs7e.js.map} +1 -1
  50. package/dist/{chunk-1wdfjmkx.js → chunk-bedkys40.js} +2 -2
  51. package/dist/{chunk-1wdfjmkx.js.map → chunk-bedkys40.js.map} +1 -1
  52. package/dist/chunk-bk4f99s4.js +5 -0
  53. package/dist/{chunk-svzdv2rf.js.map → chunk-bk4f99s4.js.map} +5 -5
  54. package/dist/{chunk-0rs8a7re.js → chunk-bk8wjf00.js} +2 -2
  55. package/dist/{chunk-0rs8a7re.js.map → chunk-bk8wjf00.js.map} +1 -1
  56. package/dist/chunk-br9p390p.js +5 -0
  57. package/dist/chunk-br9p390p.js.map +11 -0
  58. package/dist/chunk-dhe96f7z.js +5 -0
  59. package/dist/{chunk-4bh0maq0.js.map → chunk-dhe96f7z.js.map} +1 -1
  60. package/dist/{chunk-3prm2e2t.js → chunk-epwaxctc.js} +2 -2
  61. package/dist/{chunk-3prm2e2t.js.map → chunk-epwaxctc.js.map} +1 -1
  62. package/dist/{chunk-pwchwgkc.js → chunk-eqtsfvvd.js} +2 -2
  63. package/dist/{chunk-pwchwgkc.js.map → chunk-eqtsfvvd.js.map} +1 -1
  64. package/dist/{chunk-ffpm75zs.js → chunk-et6thczw.js} +2 -2
  65. package/dist/{chunk-ffpm75zs.js.map → chunk-et6thczw.js.map} +1 -1
  66. package/dist/{chunk-3m10ve1n.js → chunk-f6e2t4vb.js} +3 -3
  67. package/dist/{chunk-3m10ve1n.js.map → chunk-f6e2t4vb.js.map} +1 -1
  68. package/dist/{chunk-6ak8jfhm.js → chunk-f6veb369.js} +2 -2
  69. package/dist/{chunk-6ak8jfhm.js.map → chunk-f6veb369.js.map} +1 -1
  70. package/dist/{chunk-5y9jk4h4.js → chunk-fq03tz45.js} +2 -2
  71. package/dist/{chunk-5y9jk4h4.js.map → chunk-fq03tz45.js.map} +1 -1
  72. package/dist/{chunk-vbfwvyb8.js → chunk-ghz3e8b7.js} +2 -2
  73. package/dist/{chunk-vbfwvyb8.js.map → chunk-ghz3e8b7.js.map} +1 -1
  74. package/dist/{chunk-qrczhr16.js → chunk-h24rv10h.js} +2 -2
  75. package/dist/{chunk-qrczhr16.js.map → chunk-h24rv10h.js.map} +1 -1
  76. package/dist/{chunk-g6byqmdq.js → chunk-j2nx0dtq.js} +2 -2
  77. package/dist/{chunk-6xh4e55j.js.map → chunk-j2nx0dtq.js.map} +1 -1
  78. package/dist/chunk-j9x86xsc.js +19 -0
  79. package/dist/{chunk-g8tgp3ja.js.map → chunk-j9x86xsc.js.map} +4 -5
  80. package/dist/{chunk-7jshf4ms.js → chunk-jrp35qrk.js} +1 -1
  81. package/dist/{chunk-sppjqk4n.js → chunk-jxhznqhe.js} +3 -3
  82. package/dist/{chunk-sppjqk4n.js.map → chunk-jxhznqhe.js.map} +1 -1
  83. package/dist/{chunk-5v2rx0rk.js → chunk-kzh8bkg5.js} +2 -2
  84. package/dist/{chunk-5v2rx0rk.js.map → chunk-kzh8bkg5.js.map} +1 -1
  85. package/dist/{chunk-dwkhs81y.js → chunk-m3z60v9d.js} +2 -2
  86. package/dist/{chunk-dwkhs81y.js.map → chunk-m3z60v9d.js.map} +1 -1
  87. package/dist/{chunk-d869bd41.js → chunk-mekw2ap4.js} +2 -2
  88. package/dist/{chunk-d869bd41.js.map → chunk-mekw2ap4.js.map} +1 -1
  89. package/dist/{chunk-sqd77ksy.js → chunk-mym0zxxp.js} +3 -3
  90. package/dist/{chunk-sqd77ksy.js.map → chunk-mym0zxxp.js.map} +1 -1
  91. package/dist/{chunk-qv2sdxbq.js → chunk-n4807648.js} +3 -3
  92. package/dist/{chunk-qv2sdxbq.js.map → chunk-n4807648.js.map} +1 -1
  93. package/dist/{chunk-nvbbgbdk.js → chunk-nqabsqty.js} +4 -4
  94. package/dist/{chunk-nvbbgbdk.js.map → chunk-nqabsqty.js.map} +1 -1
  95. package/dist/{chunk-6xh4e55j.js → chunk-p3mdtnvv.js} +2 -2
  96. package/dist/{chunk-g6byqmdq.js.map → chunk-p3mdtnvv.js.map} +1 -1
  97. package/dist/{chunk-y3hdh6q6.js → chunk-qj5ts9d5.js} +2 -2
  98. package/dist/{chunk-y3hdh6q6.js.map → chunk-qj5ts9d5.js.map} +1 -1
  99. package/dist/{chunk-aawtyngp.js → chunk-qpek76vn.js} +3 -3
  100. package/dist/{chunk-aawtyngp.js.map → chunk-qpek76vn.js.map} +1 -1
  101. package/dist/{chunk-vryvdzmk.js → chunk-qqev9e50.js} +2 -2
  102. package/dist/{chunk-vryvdzmk.js.map → chunk-qqev9e50.js.map} +1 -1
  103. package/dist/{chunk-241aehp3.js → chunk-qxda7pzm.js} +2 -2
  104. package/dist/{chunk-241aehp3.js.map → chunk-qxda7pzm.js.map} +1 -1
  105. package/dist/{chunk-za1ncjh0.js → chunk-qz2p0hv5.js} +2 -2
  106. package/dist/{chunk-za1ncjh0.js.map → chunk-qz2p0hv5.js.map} +1 -1
  107. package/dist/{chunk-7k7a6h9j.js → chunk-r7qzdmew.js} +2 -2
  108. package/dist/{chunk-7k7a6h9j.js.map → chunk-r7qzdmew.js.map} +1 -1
  109. package/dist/{chunk-7bmctvep.js → chunk-safv6h9x.js} +2 -2
  110. package/dist/{chunk-7bmctvep.js.map → chunk-safv6h9x.js.map} +1 -1
  111. package/dist/{chunk-mmfstb0z.js → chunk-sh6pjjgc.js} +2 -2
  112. package/dist/{chunk-mmfstb0z.js.map → chunk-sh6pjjgc.js.map} +1 -1
  113. package/dist/{chunk-pgn3yh5k.js → chunk-ssqxzcad.js} +3 -3
  114. package/dist/{chunk-pgn3yh5k.js.map → chunk-ssqxzcad.js.map} +1 -1
  115. package/dist/{chunk-3jpetvsj.js → chunk-tcefqn3d.js} +2 -2
  116. package/dist/{chunk-3jpetvsj.js.map → chunk-tcefqn3d.js.map} +1 -1
  117. package/dist/{chunk-t328955a.js → chunk-twhf7w1x.js} +2 -2
  118. package/dist/{chunk-t328955a.js.map → chunk-twhf7w1x.js.map} +1 -1
  119. package/dist/{chunk-knw4t9e6.js → chunk-w98tp5fa.js} +2 -2
  120. package/dist/{chunk-knw4t9e6.js.map → chunk-w98tp5fa.js.map} +1 -1
  121. package/dist/{chunk-z405ks42.js → chunk-x24davqq.js} +2 -2
  122. package/dist/{chunk-z405ks42.js.map → chunk-x24davqq.js.map} +1 -1
  123. package/dist/{chunk-dmvrx8nm.js → chunk-xtqxy492.js} +3 -3
  124. package/dist/{chunk-dmvrx8nm.js.map → chunk-xtqxy492.js.map} +1 -1
  125. package/dist/chunk-ykbehyp9.js +17 -0
  126. package/dist/{chunk-c37g1gzy.js.map → chunk-ykbehyp9.js.map} +4 -3
  127. package/dist/{chunk-0n8t329w.js → chunk-z16nyxb8.js} +2 -2
  128. package/dist/{chunk-0n8t329w.js.map → chunk-z16nyxb8.js.map} +1 -1
  129. package/dist/{chunk-fx02sqe2.js → chunk-z9ydpvc8.js} +3 -3
  130. package/dist/{chunk-fx02sqe2.js.map → chunk-z9ydpvc8.js.map} +1 -1
  131. package/dist/chunk-zyw42zny.js +217 -0
  132. package/dist/chunk-zyw42zny.js.map +16 -0
  133. package/dist/config/index.js +1 -1
  134. package/dist/console/run.js +2 -2
  135. package/dist/console/run.js.map +1 -1
  136. package/dist/container/index.js +2 -2
  137. package/dist/container/index.js.map +1 -1
  138. package/dist/database/index.js +2 -2
  139. package/dist/database/index.js.map +1 -1
  140. package/dist/email/index.js +2 -2
  141. package/dist/email/index.js.map +2 -2
  142. package/dist/facades/Lock.d.ts +24 -0
  143. package/dist/facades/Lock.d.ts.map +1 -0
  144. package/dist/facades/index.d.ts +1 -0
  145. package/dist/facades/index.d.ts.map +1 -1
  146. package/dist/facades/index.js +2 -2
  147. package/dist/facades/index.js.map +1 -1
  148. package/dist/foundation/index.js +2 -2
  149. package/dist/foundation/index.js.map +1 -1
  150. package/dist/http/index.js +2 -2
  151. package/dist/http/index.js.map +1 -1
  152. package/dist/i18n/dictionaryRuntime.js +2 -2
  153. package/dist/i18n/dictionaryRuntime.js.map +1 -1
  154. package/dist/i18n/index.js +2 -2
  155. package/dist/i18n/index.js.map +1 -1
  156. package/dist/kernel/index.js +2 -2
  157. package/dist/kernel/index.js.map +2 -2
  158. package/dist/orm/index.js +2 -2
  159. package/dist/orm/index.js.map +2 -2
  160. package/dist/server/index.js +2 -2
  161. package/dist/server/index.js.map +1 -1
  162. package/dist/services/cron/CronJob.d.ts +37 -1
  163. package/dist/services/cron/CronJob.d.ts.map +1 -1
  164. package/dist/services/cron/ScheduleServiceProvider.d.ts.map +1 -1
  165. package/dist/services/cron/Scheduler.d.ts +19 -1
  166. package/dist/services/cron/Scheduler.d.ts.map +1 -1
  167. package/dist/services/index.d.ts +5 -1
  168. package/dist/services/index.d.ts.map +1 -1
  169. package/dist/services/index.js +3 -3
  170. package/dist/services/index.js.map +2 -2
  171. package/dist/services/lock/DatabaseLockStore.d.ts +84 -0
  172. package/dist/services/lock/DatabaseLockStore.d.ts.map +1 -0
  173. package/dist/services/lock/LockManager.d.ts +93 -0
  174. package/dist/services/lock/LockManager.d.ts.map +1 -0
  175. package/dist/services/lock/LockStore.d.ts +87 -0
  176. package/dist/services/lock/LockStore.d.ts.map +1 -0
  177. package/dist/services/lock/MemoryLockStore.d.ts +28 -0
  178. package/dist/services/lock/MemoryLockStore.d.ts.map +1 -0
  179. package/dist/services/lock/lockStoreContract.d.ts +13 -0
  180. package/dist/services/lock/lockStoreContract.d.ts.map +1 -0
  181. package/dist/services/queue/DatabaseQueueDriver.d.ts +7 -0
  182. package/dist/services/queue/DatabaseQueueDriver.d.ts.map +1 -1
  183. package/dist/services/queue/Job.d.ts +66 -0
  184. package/dist/services/queue/Job.d.ts.map +1 -1
  185. package/dist/services/queue/QueueDriver.d.ts +9 -0
  186. package/dist/services/queue/QueueDriver.d.ts.map +1 -1
  187. package/dist/services/queue/QueueManager.d.ts +46 -0
  188. package/dist/services/queue/QueueManager.d.ts.map +1 -1
  189. package/dist/services/queue/QueueServiceProvider.d.ts.map +1 -1
  190. package/dist/services/queue/config.d.ts +13 -0
  191. package/dist/services/queue/config.d.ts.map +1 -1
  192. package/dist/services/queue/work.js +2 -2
  193. package/dist/services/queue/work.js.map +2 -2
  194. package/dist/support/index.js +2 -2
  195. package/dist/support/index.js.map +2 -2
  196. package/package.json +1 -1
  197. package/dist/chunk-4bh0maq0.js +0 -5
  198. package/dist/chunk-c37g1gzy.js +0 -17
  199. package/dist/chunk-cyze295m.js +0 -5
  200. package/dist/chunk-g8tgp3ja.js +0 -19
  201. package/dist/chunk-rgcfqq21.js +0 -5
  202. package/dist/chunk-rgcfqq21.js.map +0 -10
  203. package/dist/chunk-svzdv2rf.js +0 -5
  204. package/dist/chunk-t4x7q9x8.js +0 -142
  205. package/dist/chunk-t4x7q9x8.js.map +0 -13
  206. /package/dist/{chunk-dve6ryfd.js.map → chunk-2yyr5tdc.js.map} +0 -0
  207. /package/dist/{chunk-aq1hjk00.js.map → chunk-3fjkbcra.js.map} +0 -0
  208. /package/dist/{chunk-7jshf4ms.js.map → chunk-jrp35qrk.js.map} +0 -0
@@ -0,0 +1,93 @@
1
+ import { type LockStore } from "./LockStore";
2
+ export type AcquireOptions = {
3
+ /** The lease, in milliseconds. Default one minute. */
4
+ ttl?: number;
5
+ /**
6
+ * How long to keep trying when the lock is held, in milliseconds. Default
7
+ * `0`: one try.
8
+ */
9
+ wait?: number;
10
+ /**
11
+ * Renew the lease every third of `ttl` until the lock is released. Default
12
+ * `true` for `run`, `false` for `acquire`, whose caller may never release.
13
+ */
14
+ renew?: boolean;
15
+ };
16
+ export type LockRunResult<T> = {
17
+ acquired: true;
18
+ value: T;
19
+ } | {
20
+ acquired: false;
21
+ };
22
+ /**
23
+ * One hold on a lock, from a successful `acquire` until `release` or the end
24
+ * of its lease.
25
+ *
26
+ * `token` is the fencing token: it grows with each new holder of the name, so
27
+ * a write that records it can be refused when an older holder tries to make
28
+ * it late. `fence` does that refusal for writes to the application's own
29
+ * database.
30
+ *
31
+ * `lost` aborts when the hold is known to be gone: a renewal found another
32
+ * holder, or the lease ran out by this process's clock without a successful
33
+ * renewal. The local deadline is measured from just before each request that
34
+ * set the lease, so it always falls before the store's own expiry.
35
+ */
36
+ export declare class HeldLock {
37
+ private readonly store;
38
+ readonly name: string;
39
+ readonly owner: string;
40
+ readonly token: number;
41
+ readonly ttl: number;
42
+ readonly lost: AbortSignal;
43
+ private readonly controller;
44
+ private deadline;
45
+ private expiryTimer;
46
+ private renewTimer;
47
+ private done;
48
+ constructor(store: LockStore, name: string, owner: string, token: number, ttl: number, requestedAt: number);
49
+ /** Whether the lease has run out by this process's clock, or the hold was lost. */
50
+ get expired(): boolean;
51
+ /** Renews the lease. `false`, and `lost` aborted, when the hold was already gone. */
52
+ extend(ttl?: number): Promise<boolean>;
53
+ /** Asks the store whether this hold is still current. */
54
+ isHeld(): Promise<boolean>;
55
+ /**
56
+ * Runs `fn` only while this hold is current, and keeps the lock from
57
+ * changing hands until it settles; rejects with `LockLostError` otherwise.
58
+ * On the database store `fn` runs in a transaction on the store's
59
+ * connection, so ORM writes inside it commit only if the hold was current.
60
+ */
61
+ fence<T>(fn: () => Promise<T>): Promise<T>;
62
+ /** Ends the hold. `false` when it had already been lost. */
63
+ release(): Promise<boolean>;
64
+ /** Renews every third of the lease until `release`. */
65
+ startRenewing(): void;
66
+ /** @internal Marks the hold as gone and stops every timer. */
67
+ markLost(detail?: string): void;
68
+ private stop;
69
+ private armExpiry;
70
+ }
71
+ /**
72
+ * Named locks with leases and fencing tokens, over a `LockStore`. The queue
73
+ * builds one over its own storage (`app(QueueManager).locks`), so with the
74
+ * database driver the locks hold across every process sharing the database.
75
+ * The `Lock` facade fronts it.
76
+ */
77
+ export declare class LockManager {
78
+ readonly store: LockStore;
79
+ static token: string;
80
+ constructor(store: LockStore);
81
+ /** Takes the lock, or resolves `null` if it is held (after `wait`, when given). */
82
+ acquire(name: string, options?: AcquireOptions): Promise<HeldLock | null>;
83
+ /**
84
+ * Runs `fn` while holding the lock, renewing the lease as it runs, and
85
+ * releases it after. `{ acquired: false }` when the lock was held.
86
+ *
87
+ * If the hold is lost while `fn` runs, `lock.lost` aborts, and once `fn`
88
+ * settles this rejects with `LockLostError` instead of resolving: the caller
89
+ * must not treat work done without the lock as done under it.
90
+ */
91
+ run<T>(name: string, options: AcquireOptions, fn: (lock: HeldLock) => Promise<T> | T): Promise<LockRunResult<T>>;
92
+ }
93
+ //# sourceMappingURL=LockManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LockManager.d.ts","sourceRoot":"","sources":["../../../services/lock/LockManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,MAAM,cAAc,GAAG;IAC3B,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAA;CAAE,CAAC;AAOlF;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAQ;IASjB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM;IAZtB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAA4C;IAC/D,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,IAAI,CAAS;gBAGF,KAAK,EAAE,SAAS,EACxB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM;IAOrB,mFAAmF;IACnF,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,qFAAqF;IAC/E,MAAM,CAAC,GAAG,SAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAc9C,yDAAyD;IACnD,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAOhC;;;;;OAKG;IACG,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAUhD,4DAA4D;IACtD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAMjC,uDAAuD;IACvD,aAAa;IAeb,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAOxB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,SAAS;CAQlB;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IAGV,QAAQ,CAAC,KAAK,EAAE,SAAS;IAFrC,MAAM,CAAC,KAAK,SAAW;gBAEF,KAAK,EAAE,SAAS;IAErC,mFAAmF;IAC7E,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAoBnF;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,EACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACrC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;CAkB7B"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Where locks are kept: one record per lock name, with the current holder, a
3
+ * fencing token and a lease.
4
+ *
5
+ * ### The model
6
+ *
7
+ * A lock is held by an **owner** (a random id per acquisition, or a job's id)
8
+ * until its **lease** runs out or the owner releases it. Every successful
9
+ * `acquire` raises the name's **token** by one, so tokens only grow per name and
10
+ * a newer holder always has a larger one than any older holder. A store never
11
+ * forgets a name on release, which is what keeps the token growing; `prune`
12
+ * (on a store that has it) removes names idle for long enough.
13
+ *
14
+ * A lease that runs out is lost, even if nobody has taken the lock since:
15
+ * `extend`, `held` and `fence` all refuse it. That is the rule that makes the
16
+ * token mean something. A holder whose process stalled past its lease cannot
17
+ * renew its way back in after another process may have started the same work.
18
+ *
19
+ * ### Time
20
+ *
21
+ * Durations cross this interface as relative milliseconds, as they do for
22
+ * `QueueDriver`, so a store shared between machines can measure every lease on
23
+ * one clock, its own.
24
+ */
25
+ export interface LockStore {
26
+ /**
27
+ * Takes `name` for `owner` for `ttlMs`, if nobody holds it or the holder's
28
+ * lease ran out, and resolves to the new fencing token. `null` when it is
29
+ * held. Two concurrent calls, from any processes sharing the store, never
30
+ * both succeed.
31
+ */
32
+ acquire(name: string, owner: string, ttlMs: number): Promise<number | null>;
33
+ /**
34
+ * Renews a lease to `ttlMs` from now, and resolves to whether `owner` still
35
+ * held it with `token`. A lease that already ran out is not renewed.
36
+ */
37
+ extend(name: string, owner: string, token: number, ttlMs: number): Promise<boolean>;
38
+ /**
39
+ * Ends a hold, and resolves to whether `owner` held it (with `token`, when
40
+ * given) and its lease had not run out. Releasing a lock someone else now
41
+ * holds does nothing.
42
+ */
43
+ release(name: string, owner: string, token?: number): Promise<boolean>;
44
+ /** The current holder, or `null` when the lock is free or its lease ran out. */
45
+ holder(name: string): Promise<{
46
+ owner: string;
47
+ token: number;
48
+ } | null>;
49
+ /** Whether `owner` still holds `name` with `token`, its lease running. */
50
+ held(name: string, owner: string, token: number): Promise<boolean>;
51
+ /**
52
+ * Raises a counter kept under `name` to `value` if it is below it, and
53
+ * resolves to whether it did. For "once per tick across instances": every
54
+ * instance advances to the tick's number and only the first one gets `true`.
55
+ * A name used here must not also be used with `acquire`.
56
+ */
57
+ advance(name: string, value: number): Promise<boolean>;
58
+ /**
59
+ * Runs `fn` only if `owner` still holds `name` with `token`, and keeps the
60
+ * lock from changing hands until `fn` settles. Rejects with `LockLostError`
61
+ * when the hold is gone. On the database store this is a transaction that
62
+ * locks the lock's row first, so writes `fn` makes through the ORM on the
63
+ * same connection commit only while the hold is current, and a new holder's
64
+ * `acquire` waits for that commit.
65
+ */
66
+ fence<T>(name: string, owner: string, token: number, fn: () => Promise<T>): Promise<T>;
67
+ /**
68
+ * Counts one hit against a fixed window kept under `name`: allowed when
69
+ * fewer than `limit` were counted in the current window, which starts at the
70
+ * first hit after the last one ended and lasts `windowMs`. A refused hit is
71
+ * not counted. `resetInMs` is how long until the window ends. A name used
72
+ * here must not also be used with `acquire` or `advance`.
73
+ */
74
+ hit(name: string, limit: number, windowMs: number): Promise<{
75
+ allowed: boolean;
76
+ resetInMs: number;
77
+ }>;
78
+ /** Takes back one counted hit from the current window, if it has one. */
79
+ refund(name: string): Promise<void>;
80
+ }
81
+ /** The hold on a lock ended before the work it guarded did. */
82
+ export declare class LockLostError extends Error {
83
+ readonly lock: string;
84
+ readonly token: number;
85
+ constructor(lock: string, token: number, detail?: string);
86
+ }
87
+ //# sourceMappingURL=LockStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LockStore.d.ts","sourceRoot":"","sources":["../../../services/lock/LockStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE5E;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpF;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvE,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAEvE,0EAA0E;IAC1E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnE;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvD;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEvF;;;;;;OAMG;IACH,GAAG,CACD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEpD,yEAAyE;IACzE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,+DAA+D;AAC/D,qBAAa,aAAc,SAAQ,KAAK;IAEpC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM;gBADb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACtB,MAAM,SAAgD;CAKzD"}
@@ -0,0 +1,28 @@
1
+ import { type LockStore } from "./LockStore";
2
+ /**
3
+ * Locks in this process's memory. Correct for one process and useless across
4
+ * several: two servers each have their own map, so each takes every lock. It
5
+ * is what the queue uses with the memory driver, and in tests.
6
+ */
7
+ export declare class MemoryLockStore implements LockStore {
8
+ private readonly entries;
9
+ private readonly counters;
10
+ acquire(name: string, owner: string, ttlMs: number): Promise<number | null>;
11
+ extend(name: string, owner: string, token: number, ttlMs: number): Promise<boolean>;
12
+ release(name: string, owner: string, token?: number): Promise<boolean>;
13
+ holder(name: string): Promise<{
14
+ owner: string;
15
+ token: number;
16
+ }>;
17
+ held(name: string, owner: string, token: number): Promise<boolean>;
18
+ advance(name: string, value: number): Promise<boolean>;
19
+ fence<T>(name: string, owner: string, token: number, fn: () => Promise<T>): Promise<T>;
20
+ private readonly windows;
21
+ hit(name: string, limit: number, windowMs: number): Promise<{
22
+ allowed: boolean;
23
+ resetInMs: number;
24
+ }>;
25
+ refund(name: string): Promise<void>;
26
+ private current;
27
+ }
28
+ //# sourceMappingURL=MemoryLockStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemoryLockStore.d.ts","sourceRoot":"","sources":["../../../services/lock/MemoryLockStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAU5D;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEhD,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAS3E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOnF,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQtE,MAAM,CAAC,IAAI,EAAE,MAAM;;;;IAMnB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMtD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAW5F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwD;IAE1E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;IAYjD,MAAM,CAAC,IAAI,EAAE,MAAM;IAKzB,OAAO,CAAC,OAAO;CAMhB"}
@@ -0,0 +1,13 @@
1
+ import { type LockStore } from "./LockStore";
2
+ /**
3
+ * What every `LockStore` promises, run by each store's test file against
4
+ * itself. `open` returns a fresh, empty storage; `store()` hands out a store
5
+ * over it, and for a database a new client each time, so "two processes" is
6
+ * two clients on one database. Not a `*.test.ts` file, so vitest only runs it
7
+ * through those files.
8
+ */
9
+ export declare function lockStoreContract(name: string, open: () => Promise<{
10
+ store(): LockStore;
11
+ dispose(): Promise<void>;
12
+ }>): void;
13
+ //# sourceMappingURL=lockStoreContract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockStoreContract.d.ts","sourceRoot":"","sources":["../../../services/lock/lockStoreContract.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,OAAO,CAAC;IAAE,KAAK,IAAI,SAAS,CAAC;IAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,QA8JtE"}
@@ -1,5 +1,6 @@
1
1
  import type { DatabaseConnection } from "../../database/Connection";
2
2
  import type { Dialect } from "../../database/dialect";
3
+ import { DatabaseLockStore } from "../lock/DatabaseLockStore";
3
4
  import type { ClaimOptions, ClaimedJob, EnqueueJob, JobFailure, JobRelease, QueueDriver } from "./QueueDriver";
4
5
  export type DatabaseQueueDriverOptions = {
5
6
  /**
@@ -127,6 +128,12 @@ export declare class DatabaseQueueDriver implements QueueDriver {
127
128
  createTable(): Promise<void>;
128
129
  enqueue(job: EnqueueJob): Promise<string>;
129
130
  private insert;
131
+ /**
132
+ * Locks in the `gemi_locks` table of the same connection, so unique jobs,
133
+ * `withoutOverlapping` and `onOneServer` hold across every process sharing
134
+ * this database. The table is only touched when one of those is used.
135
+ */
136
+ lockStore(): DatabaseLockStore;
130
137
  joinsTransaction(): boolean;
131
138
  /**
132
139
  * The open ORM transaction, when an `enqueue` should be written on it.
@@ -1 +1 @@
1
- {"version":3,"file":"DatabaseQueueDriver.d.ts","sourceRoot":"","sources":["../../../services/queue/DatabaseQueueDriver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACZ,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAW/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,UAAU,CAA4B;gBAG5C,UAAU,EAAE,IAAI,CAAC,kBAAkB,EAAE,KAAK,GAAG,SAAS,CAAC,GACrD,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,EAC3C,OAAO,GAAE,0BAA+B;IAsB1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,CAAC,SAAS;IAejB;;;;;;;OAOG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAOlC,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;YAgB3B,MAAM;IAepB,gBAAgB,IAAI,OAAO;IAI3B;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,WAAW;IAOb,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IA0BlE,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IASxC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB/D;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlE;;;OAGG;IACG,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBzE;;;;OAIG;IACG,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAa7C;;;;;OAKG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUjD;;;;;OAKG;YACW,aAAa;IAsC3B;;;;;;;;;;;;;;OAcG;YACW,YAAY;YA6BZ,cAAc;IAgD5B;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAUjB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,QAAQ;IAahB;;;;;OAKG;IACH,OAAO,CAAC,IAAI;IAKZ,mDAAmD;IACnD,OAAO,CAAC,GAAG;IAcX;;;OAGG;IACH,OAAO,CAAC,EAAE;CAMX;AAYD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA2C/E"}
1
+ {"version":3,"file":"DatabaseQueueDriver.d.ts","sourceRoot":"","sources":["../../../services/queue/DatabaseQueueDriver.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,61 @@ export declare class Job {
9
9
  * free.
10
10
  */
11
11
  backoff: number | number[];
12
+ /**
13
+ * Makes the job unique per key, across every process sharing the queue's
14
+ * storage. Return a key from the dispatch arguments, or `undefined` for a
15
+ * dispatch that should not be unique. While a job with the same key is
16
+ * waiting or running, another dispatch is not queued: it resolves to the
17
+ * id of the job already there. The key is freed when that job completes or
18
+ * is dead-lettered, and at the latest after `uniqueFor`.
19
+ *
20
+ * The default returns `undefined`: a job is not unique unless it says so.
21
+ */
22
+ uniqueId(..._args: any[]): string | number | undefined | null;
23
+ /**
24
+ * How long a unique job's key is held at most, in milliseconds, so a job
25
+ * lost with its process does not block its key forever. Default one hour.
26
+ */
27
+ uniqueFor: number;
28
+ /**
29
+ * Rate limits per key, computed from the dispatch arguments: at most
30
+ * `limit` jobs admitted per fixed `window` (milliseconds) under each `key`,
31
+ * across every process sharing the queue's storage. A job over a limit is
32
+ * put back until the window resets, without spending an attempt. Counted
33
+ * when the job is admitted to run, whether it then succeeds or not.
34
+ */
35
+ throttle(..._args: any[]): JobThrottle | JobThrottle[] | undefined | null;
36
+ /**
37
+ * At most `limit` jobs with this `key` running at once, across every process
38
+ * sharing the queue's storage. A job with no free slot waits, without
39
+ * spending an attempt.
40
+ */
41
+ concurrency(..._args: any[]): JobConcurrency | undefined | null;
42
+ /**
43
+ * Called from `run`: puts the job back to wait `delayMs` once `run`
44
+ * returns, without counting the attempt or calling any hook. For a provider
45
+ * that answered "slow down". Not available to `worker` jobs.
46
+ */
47
+ release(delayMs?: number): void;
48
+ /**
49
+ * Called from `run`: fails this attempt once `run` returns, as a throw
50
+ * would. With `retry: false` it is dead-lettered at once, whatever
51
+ * `maxAttempts` says: for work that must not be repeated, such as a send
52
+ * whose acceptance is uncertain. A throw after `fail(..., { retry: false })`
53
+ * is dead-lettered too. Not available to `worker` jobs.
54
+ */
55
+ fail(error: unknown, options?: {
56
+ retry?: boolean;
57
+ }): void;
58
+ /** @internal What `release` or `fail` asked for during this run. */
59
+ $outcome: {
60
+ kind: "release";
61
+ delayMs: number;
62
+ } | {
63
+ kind: "fail";
64
+ error: Error;
65
+ retry: boolean;
66
+ } | undefined;
12
67
  run(..._args: any[]): Promise<any> | any;
13
68
  onFail(_error: Error, ..._args: any[]): void;
14
69
  onSuccess(_result: any, ..._args: any[]): void;
@@ -24,4 +79,15 @@ export declare class Job {
24
79
  */
25
80
  static dispatch<T extends Job>(this: new () => T, ...args: Parameters<T["run"]>): Promise<string>;
26
81
  }
82
+ export type JobThrottle = {
83
+ /** Shared by every job that counts against the same budget. */
84
+ key: string;
85
+ limit: number;
86
+ /** The window, in milliseconds. */
87
+ window: number;
88
+ };
89
+ export type JobConcurrency = {
90
+ key: string;
91
+ limit: number;
92
+ };
27
93
  //# sourceMappingURL=Job.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Job.d.ts","sourceRoot":"","sources":["../../../services/queue/Job.ts"],"names":[],"mappings":"AAGA,qBAAa,GAAG;IACd,MAAM,CAAC,IAAI,SAAW;IACtB,MAAM,UAAS;IACf,WAAW,SAAK;IAEhB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAK;IAE/B,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;IAExC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAC5C,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAC9C,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAElD;;;;;;;;OAQG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,GAAG,EAC3B,IAAI,EAAE,UAAU,CAAC,EACjB,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC;CAOnB"}
1
+ {"version":3,"file":"Job.d.ts","sourceRoot":"","sources":["../../../services/queue/Job.ts"],"names":[],"mappings":"AAGA,qBAAa,GAAG;IACd,MAAM,CAAC,IAAI,SAAW;IACtB,MAAM,UAAS;IACf,WAAW,SAAK;IAEhB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAK;IAE/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI;IAI7D;;;OAGG;IACH,SAAS,SAAe;IAExB;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,WAAW,GAAG,WAAW,EAAE,GAAG,SAAS,GAAG,IAAI;IAIzE;;;;OAIG;IACH,WAAW,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,cAAc,GAAG,SAAS,GAAG,IAAI;IAI/D;;;;OAIG;IACH,OAAO,CAAC,OAAO,SAAI,GAAG,IAAI;IAI1B;;;;;;OAMG;IACH,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,IAAI;IAQ7D,oEAAoE;IACpE,QAAQ,EACJ;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GACpC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAC9C,SAAS,CAAC;IAEd,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;IAExC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAC5C,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAC9C,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAElD;;;;;;;;OAQG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,GAAG,EAC3B,IAAI,EAAE,UAAU,CAAC,EACjB,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC;CAOnB;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -40,6 +40,7 @@
40
40
  * computed on the dispatching machine would carry that machine's skew into
41
41
  * every other one.
42
42
  */
43
+ import type { LockStore } from "../lock/LockStore";
43
44
  export interface QueueDriver {
44
45
  /**
45
46
  * Records a job and returns its id. The job is claimable once `delayMs` has
@@ -127,6 +128,14 @@ export interface QueueDriver {
127
128
  * that cannot guarantee it should leave this out and be polled.
128
129
  */
129
130
  subscribe?(wake: () => void): () => void;
131
+ /**
132
+ * A `LockStore` over the same storage, for the locks the queue and the
133
+ * scheduler take: unique jobs, `withoutOverlapping` and `onOneServer`, and
134
+ * the `Lock` facade. Optional: without it the locks are kept in this
135
+ * process's memory, which only serializes work within one process. Called
136
+ * once per manager.
137
+ */
138
+ lockStore?(): LockStore;
130
139
  }
131
140
  export type EnqueueJob = {
132
141
  /** The job's registered name — its class's `static name`. */
@@ -1 +1 @@
1
- {"version":3,"file":"QueueDriver.d.ts","sourceRoot":"","sources":["../../../services/queue/QueueDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1C;;;;;;;;;;;;;;;;;;OAkBG;IACH,gBAAgB,CAAC,IAAI,OAAO,CAAC;IAE7B;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAEnE,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;;OAGG;IACH,SAAS,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErE;;;;;;;OAOG;IACH,SAAS,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzC;;;;;;OAMG;IACH,SAAS,CAAC,CAAC,IAAI,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC1C;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC"}
1
+ {"version":3,"file":"QueueDriver.d.ts","sourceRoot":"","sources":["../../../services/queue/QueueDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1C;;;;;;;;;;;;;;;;;;OAkBG;IACH,gBAAgB,CAAC,IAAI,OAAO,CAAC;IAE7B;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAEnE,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;;OAGG;IACH,SAAS,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErE;;;;;;;OAOG;IACH,SAAS,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzC;;;;;;OAMG;IACH,SAAS,CAAC,CAAC,IAAI,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAEzC;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,SAAS,CAAC;CACzB;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { Application } from "../../foundation/Application";
2
+ import { LockManager } from "../lock/LockManager";
2
3
  import { Job } from "./Job";
3
4
  import { type QueueConfig } from "./config";
4
5
  import type { ClaimedJob, QueueDriver } from "./QueueDriver";
@@ -57,6 +58,7 @@ export declare class QueueManager {
57
58
  private heartbeatTimer;
58
59
  /** Unknown names already reported as left for another replica; see `run`. */
59
60
  private readonly releasedNames;
61
+ private lockManager;
60
62
  /**
61
63
  * `application` is entered around every job, so `app()` inside one resolves
62
64
  * to it rather than to whichever application happens to be the static
@@ -161,6 +163,38 @@ export declare class QueueManager {
161
163
  */
162
164
  reportFailure?: boolean;
163
165
  }): Promise<string>;
166
+ /**
167
+ * The locks unique jobs, cron jobs' `withoutOverlapping` and `onOneServer`,
168
+ * and the `Lock` facade use, over the store the `locks` setting names. Built
169
+ * on first use, so an app that uses none of them never touches the store.
170
+ */
171
+ get locks(): LockManager;
172
+ private resolveLockStore;
173
+ /**
174
+ * The lock key a dispatch of a unique job takes, or `undefined` for a job
175
+ * that does not declare `uniqueId` or a dispatch it returned nothing for.
176
+ */
177
+ private uniqueKey;
178
+ /**
179
+ * Records a unique job if its key is free, and otherwise resolves to the id
180
+ * of the job holding it. The key's lock is owned by the job's id, which is
181
+ * how the job frees it when it ends and how a duplicate finds it.
182
+ *
183
+ * Inside a transaction the whole dispatch waits for the commit, even on a
184
+ * driver that could join the transaction. The key is taken outside it, so
185
+ * written on the transaction the job could roll back and leave its key held
186
+ * for `uniqueFor` with nothing queued under it. A held dispatch resolves to
187
+ * the id the job will have, and is dropped at the commit if a job with the
188
+ * same key is queued by then.
189
+ */
190
+ private pushUnique;
191
+ private recordUnique;
192
+ /**
193
+ * Frees a unique job's key once the job has ended for good: completed or
194
+ * dead-lettered. Released by the job's id, so a key a newer dispatch holds
195
+ * after `uniqueFor` ran out is left alone.
196
+ */
197
+ private releaseUnique;
164
198
  /** `push`, once it is known that the job is recorded now. */
165
199
  private record;
166
200
  /**
@@ -275,6 +309,18 @@ export declare class QueueManager {
275
309
  * escape the queue with the job's slot still counted as taken.
276
310
  */
277
311
  private run;
312
+ /**
313
+ * Takes a slot under the job's `concurrency` key and a hit under each of
314
+ * its `throttle` keys, all in the queue's lock store, so the limits hold
315
+ * across every process sharing it. Resolves to what to undo after the run,
316
+ * or `undefined` when the job was released to wait for capacity.
317
+ *
318
+ * Concurrency is `limit` slot locks per key, each leased for the
319
+ * visibility timeout and renewed while the job runs, so a slot held by a
320
+ * dead process frees itself like the job's own lease. Throttles are fixed
321
+ * windows counted when a job is admitted, whether it then succeeds or not.
322
+ */
323
+ private admit;
278
324
  }
279
325
  /**
280
326
  * Whether this process should claim from a driver shared with other
@@ -1 +1 @@
1
- {"version":3,"file":"QueueManager.d.ts","sourceRoot":"","sources":["../../../services/queue/QueueManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"QueueManager.d.ts","sourceRoot":"","sources":["../../../services/queue/QueueManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAMhE,OAAO,EAAiB,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIjE,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEjE,OAAO,KAAK,EAAgB,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAmG3E,OAAO,CAAC,MAAM,CAAC;IACb;;;;;;;;;;OAUG;IACH,IAAI,cAAc,EAAE;QAAE,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC;CAC9D;AAED,uCAAuC;AACvC,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;OAMG;IACH,UAAU,EAAE,UAAU,EAAE,CAAC;CAC1B,CAAC;AAEF,qBAAa,YAAY;IACvB,MAAM,CAAC,KAAK,SAAW;IAEvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,CAAM;IAEzC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAE7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD;;;;;;;;;OASG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAGrB;IACJ,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,KAAK,CAAS;IACtB,oFAAoF;IACpF,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,cAAc,CAA6C;IACnE,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,WAAW,CAA0B;IAE7C;;;;;OAKG;gBAED,MAAM,GAAE,WAAgB,EACxB,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,WAAW,CAAA;KAAO;IAQ7C;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,GAAG,CAAC;IAyBlC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG;IAK9B;;;;;;OAMG;IACH,OAAO,CAAC,KAAK;IAab;;;;;;;;;;;;OAYG;IACH,IAAI,cAAc,IAAI,aAAa,CAAC,UAAU,GAAG,CAAC,CAEjD;IAED,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAOzC;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CACF,GAAG,EAAE,UAAU,GAAG,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;QACP;;;;;;;;WAQG;QACH,aAAa,CAAC,EAAE,OAAO,CAAC;KACpB,GACL,OAAO,CAAC,MAAM,CAAC;IAwClB;;;;OAIG;IACH,IAAI,KAAK,IAAI,WAAW,CAGvB;IAED,OAAO,CAAC,gBAAgB;IAoCxB;;;OAGG;IACH,OAAO,CAAC,SAAS;IAQjB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,YAAY;IA2CpB;;;;OAIG;YACW,aAAa;IAe3B,6DAA6D;IAC7D,OAAO,CAAC,MAAM;IAkEd;;;;;;;;;;;;;;;;OAgBG;IACG,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAY7C;;;OAGG;IACH,OAAO,CAAC,WAAW;IAMnB;;;OAGG;IACH,OAAO,CAAC,SAAS;IAKjB;;;;;OAKG;IACH,KAAK;IAgBL;;;;;;;;;;;;OAYG;IACG,KAAK,CAAC,SAAS,SAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IA0BvD,6EAA6E;IAC7E,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IAI5B,uDAAuD;IACvD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;;;;;;;;;OAUG;YACW,IAAI;YASJ,iBAAiB;IAmD/B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,KAAK;IAoBb,0EAA0E;IAC1E,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,IAAI;IAOZ;;;OAGG;IACH,IAAI,OAAO,YAEV;IAED,qEAAqE;IACrE,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,OAAO;IAwBf;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAoBjB;;;;;;;OAOG;YACW,GAAG;IAgMjB;;;;;;;;;;OAUG;YACW,KAAK;CAiDpB;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,YAIlC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,WAEtF;AAQD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,MAAM,UAAO,QAE5C;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,UAKrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"QueueServiceProvider.d.ts","sourceRoot":"","sources":["../../../services/queue/QueueServiceProvider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAwBhE,qBAAa,oBAAqB,SAAQ,eAAe;IACvD,QAAQ;IAUR;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,IAAI;IAQV;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;;;CAqB/C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,WAAW,QAO9D"}
1
+ {"version":3,"file":"QueueServiceProvider.d.ts","sourceRoot":"","sources":["../../../services/queue/QueueServiceProvider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAyBhE,qBAAa,oBAAqB,SAAQ,eAAe;IACvD,QAAQ;IAYR;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,IAAI;IAQV;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;;;CAqB/C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,WAAW,QAO9D"}
@@ -1,5 +1,6 @@
1
1
  import type { Application } from "../../foundation/Application";
2
2
  import type { Job } from "./Job";
3
+ import type { LockStore } from "../lock/LockStore";
3
4
  import type { QueueDriver } from "./QueueDriver";
4
5
  export interface QueueConfig {
5
6
  /**
@@ -105,6 +106,18 @@ export interface QueueConfig {
105
106
  * unknown name there is dead-lettered at once.
106
107
  */
107
108
  unknownJobGrace?: number;
109
+ /**
110
+ * Where locks are kept: the ones unique jobs, a cron job's
111
+ * `withoutOverlapping` and `onOneServer`, and the `Lock` facade take.
112
+ *
113
+ * `"auto"`, the default, keeps them where the driver keeps jobs: with
114
+ * `"database"` that is the `gemi_locks` table of the same connection, so
115
+ * they hold across every process sharing it; with `"memory"` it is this
116
+ * process. `"memory"` and `"database"` (the default connection) choose
117
+ * explicitly, and a `LockStore`, or a function returning one, is used as
118
+ * given. Nothing is read or written until one of those features is used.
119
+ */
120
+ locks?: "auto" | "memory" | "database" | LockStore | ((application: Application) => LockStore);
108
121
  }
109
122
  export declare function defineQueueConfig(config: QueueConfig): QueueConfig;
110
123
  export declare function queueConfigDefaults(): Required<QueueConfig>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../services/queue/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;IAE5B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,EACH,QAAQ,GACR,UAAU,GACV,WAAW,GACX,CAAC,CAAC,WAAW,EAAE,WAAW,KAAK,WAAW,CAAC,CAAC;IAEhD;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;;;;;;;;OAkBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAElE;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,WAAW,CAAC,CAU3D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../services/queue/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;IAE5B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,EACH,QAAQ,GACR,UAAU,GACV,WAAW,GACX,CAAC,CAAC,WAAW,EAAE,WAAW,KAAK,WAAW,CAAC,CAAC;IAEhD;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;;;;;;;;OAkBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EACF,MAAM,GACN,QAAQ,GACR,UAAU,GACV,SAAS,GACT,CAAC,CAAC,WAAW,EAAE,WAAW,KAAK,SAAS,CAAC,CAAC;CAC/C;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAElE;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,WAAW,CAAC,CAW3D"}
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{$b as T,Yb as E}from"../../chunk-t4x7q9x8.js";import"../../chunk-qv2sdxbq.js";import"../../chunk-vbfwvyb8.js";import"../../chunk-6qyc4e22.js";import"../../chunk-rgcfqq21.js";import{Fg as I,Gg as J,Jg as O}from"../../chunk-ffpm75zs.js";import{Ng as N}from"../../chunk-5v2rx0rk.js";import"../../chunk-g6byqmdq.js";import L from"path";class F extends Error{name="QueueWorkerRefused"}class H{kernel;stopping;keepAlive;settings;constructor(y,z={}){this.kernel=y;this.settings={...J(),...z}}async start(){if(await this.kernel.waitForBoot(),this.stopping)return;let y=this.kernel.app;if(!y.bound(E))throw new F("[gemi] `gemi queue:work` has no queue to claim from: this application does not register QueueServiceProvider.");let z=y.make(E);if(!z.durable)throw new F('[gemi] `gemi queue:work` refuses to start: the queue uses the memory driver, which keeps each job in the process that dispatched it, so a worker never sees one. Set `driver: "database"` (or another driver every process can reach) in app/config/queue.ts, or run jobs in the server and drop the worker.');z.start(),this.keepAlive=setInterval(()=>{},1073741824),console.log(`[gemi] Queue worker started: claiming up to ${z.config.concurrency} job(s) at a time.`)}stop(y={}){return this.stopping??=this.drain({...this.settings,...y}),this.stopping}async drain(y){I();let z=this.kernel.app,A=z.resolved(E)?z.make(E):void 0;if(A)console.log(`[gemi] Shutting down: waiting for ${A.running} running job(s) (up to ${y.timeoutMs/1000}s).`),await A.drain(y.timeoutMs);let B=await this.kernel.shutdown({timeoutMs:y.providerTimeoutMs});clearInterval(this.keepAlive);let C=(A?.running??0)===0&&B.failed.length===0&&B.timedOut.length===0;return console.log(`[gemi] Shutdown ${C?"complete":"finished with errors"}.`),C?0:1}}async function U(){let y=N();process.env.ROOT_DIR=y,process.env.APP_DIR=L.join(y,"app"),T();let z=L.join(y,"app/kernel/Kernel.ts"),{default:A}=await import(z);if(typeof A!=="function")return console.error(`[gemi] ${z} has no default export to boot the worker from.`),1;let B=new A;B.boot();let C=new H(B);O(()=>C.stop());try{await C.start()}catch(G){if(!(G instanceof F))throw G;return console.error(G.message),1}return}U().then((y)=>{if(y!==void 0)process.exit(y)},(y)=>{console.error("[gemi] The queue worker failed to start:",y),process.exit(1)});
2
+ import{_b as E,bc as T}from"../../chunk-zyw42zny.js";import"../../chunk-br9p390p.js";import"../../chunk-n4807648.js";import"../../chunk-ghz3e8b7.js";import"../../chunk-3fr22rtg.js";import"../../chunk-7hfv5n00.js";import"../../chunk-qxda7pzm.js";import{Lg as I,Mg as J,Pg as O}from"../../chunk-et6thczw.js";import{Tg as N}from"../../chunk-kzh8bkg5.js";import"../../chunk-j2nx0dtq.js";import L from"path";class F extends Error{name="QueueWorkerRefused"}class H{kernel;stopping;keepAlive;settings;constructor(y,z={}){this.kernel=y;this.settings={...J(),...z}}async start(){if(await this.kernel.waitForBoot(),this.stopping)return;let y=this.kernel.app;if(!y.bound(E))throw new F("[gemi] `gemi queue:work` has no queue to claim from: this application does not register QueueServiceProvider.");let z=y.make(E);if(!z.durable)throw new F('[gemi] `gemi queue:work` refuses to start: the queue uses the memory driver, which keeps each job in the process that dispatched it, so a worker never sees one. Set `driver: "database"` (or another driver every process can reach) in app/config/queue.ts, or run jobs in the server and drop the worker.');z.start(),this.keepAlive=setInterval(()=>{},1073741824),console.log(`[gemi] Queue worker started: claiming up to ${z.config.concurrency} job(s) at a time.`)}stop(y={}){return this.stopping??=this.drain({...this.settings,...y}),this.stopping}async drain(y){I();let z=this.kernel.app,A=z.resolved(E)?z.make(E):void 0;if(A)console.log(`[gemi] Shutting down: waiting for ${A.running} running job(s) (up to ${y.timeoutMs/1000}s).`),await A.drain(y.timeoutMs);let B=await this.kernel.shutdown({timeoutMs:y.providerTimeoutMs});clearInterval(this.keepAlive);let C=(A?.running??0)===0&&B.failed.length===0&&B.timedOut.length===0;return console.log(`[gemi] Shutdown ${C?"complete":"finished with errors"}.`),C?0:1}}async function U(){let y=N();process.env.ROOT_DIR=y,process.env.APP_DIR=L.join(y,"app"),T();let z=L.join(y,"app/kernel/Kernel.ts"),{default:A}=await import(z);if(typeof A!=="function")return console.error(`[gemi] ${z} has no default export to boot the worker from.`),1;let B=new A;B.boot();let C=new H(B);O(()=>C.stop());try{await C.start()}catch(G){if(!(G instanceof F))throw G;return console.error(G.message),1}return}U().then((y)=>{if(y!==void 0)process.exit(y)},(y)=>{console.error("[gemi] The queue worker failed to start:",y),process.exit(1)});
3
3
 
4
- //# debugId=AD398EFE10DD87FA64756E2164756E21
4
+ //# debugId=1ACF53BA60E3CBB764756E2164756E21
@@ -5,7 +5,7 @@
5
5
  "/**\n * The entry point `gemi queue:work` spawns.\n *\n * Exposed as `gemi/queue/work` and resolved by the CLI out of the\n * *application's* `node_modules`, for the reason `gemi/console/run` is: the\n * application's kernel, providers and jobs are built from its copy of gemi,\n * and the worker that drives them should be the same copy, not one bundled\n * into the CLI.\n *\n * Everything worth testing is in `QueueWorker`; this file is the order the\n * process does things in.\n */\nimport path from \"node:path\";\n\nimport { installShutdownSignals } from \"../../server/shutdown\";\nimport { projectRoot } from \"../../support/discover\";\nimport { markQueueWorker } from \"./QueueManager\";\nimport { QueueWorker, QueueWorkerRefused, type WorkerKernel } from \"./WorkerProcess\";\n\nasync function main(): Promise<number | undefined> {\n const rootDir = projectRoot();\n // What `Server.start` and `httpProd` set, and for the same readers: a\n // `worker = true` job's thread finds the application through them, and the\n // log provider resolves its paths from `ROOT_DIR`.\n process.env.ROOT_DIR = rootDir;\n process.env.APP_DIR = path.join(rootDir, \"app\");\n // Before the kernel is imported, so no claim rule can be asked first.\n markQueueWorker();\n\n const kernelPath = path.join(rootDir, \"app/kernel/Kernel.ts\");\n const { default: Kernel } = (await import(kernelPath)) as {\n default?: new () => WorkerKernel & { boot(): void };\n };\n if (typeof Kernel !== \"function\") {\n console.error(`[gemi] ${kernelPath} has no default export to boot the worker from.`);\n return 1;\n }\n const kernel = new Kernel();\n kernel.boot();\n\n const worker = new QueueWorker(kernel);\n // Before the boot, as `Server.start` does in production: the boot is the\n // slow part, and a signal that lands in it would otherwise kill the process\n // on the spot. In development too, unlike a server — no `bun --hot` re-runs\n // this, and a Ctrl+C that lets the jobs finish is what a worker is for.\n installShutdownSignals(() => worker.stop());\n\n try {\n await worker.start();\n } catch (error) {\n if (!(error instanceof QueueWorkerRefused)) throw error;\n console.error(error.message);\n return 1;\n }\n // Running: the signal handler exits the process once it has drained.\n return undefined;\n}\n\nmain().then(\n (code) => {\n if (code !== undefined) process.exit(code);\n },\n (error) => {\n console.error(\"[gemi] The queue worker failed to start:\", error);\n process.exit(1);\n },\n);\n",
6
6
  "import type { Application, ShutdownReport } from \"../../foundation/Application\";\nimport { markShuttingDown, shutdownSettings, type ShutdownSettings } from \"../../server/shutdown\";\nimport { QueueManager } from \"./QueueManager\";\n\n/** The part of a `Kernel` a worker drives. */\nexport type WorkerKernel = {\n readonly app: Application;\n waitForBoot(): Promise<void>;\n shutdown(options?: { timeoutMs?: number }): Promise<ShutdownReport>;\n};\n\n/**\n * A worker that was told to start in an application it cannot claim for. The\n * message is written for the operator reading the deploy's logs.\n */\nexport class QueueWorkerRefused extends Error {\n name = \"QueueWorkerRefused\";\n}\n\n/**\n * The process `gemi queue:work` runs: the application booted as a server boots\n * it, claiming from the queue, and no `Bun.serve`. It exists so job capacity\n * can be scaled apart from request traffic, which needs a driver both kinds of\n * process can reach; see `start` for why the memory driver is refused.\n *\n * The caller marks the process as a worker (`markQueueWorker`) before the\n * kernel registers, so every claim rule that asks `claimsInThisProcess` sees a\n * process that claims, whatever `GEMI_QUEUE_CLAIM` says.\n */\nexport class QueueWorker {\n private stopping: Promise<number> | undefined;\n private keepAlive: ReturnType<typeof setInterval> | undefined;\n private readonly settings: ShutdownSettings;\n\n /**\n * `settings` default to the `GEMI_SHUTDOWN_*` variables, read here rather\n * than at the signal so a bad value is warned about while the deploy is\n * being watched, as `Server.start` does.\n */\n constructor(\n private readonly kernel: WorkerKernel,\n settings: Partial<ShutdownSettings> = {},\n ) {\n this.settings = { ...shutdownSettings(), ...settings };\n }\n\n /**\n * Boots the application and starts claiming. Rejects with\n * `QueueWorkerRefused` when the application has no queue, or its queue uses\n * the memory driver.\n *\n * Refused rather than warned about, because a memory worker can never do\n * anything: its jobs live in a `Map` in the process that dispatched them,\n * so a worker's is always empty. Left running, it looks like a healthy\n * deploy — a process up, no errors — that runs nothing, and scaling it out\n * adds no capacity. An exit the platform reports as a crash loop is a form\n * of the mistake someone will see.\n */\n async start(): Promise<void> {\n await this.kernel.waitForBoot();\n // A signal during the boot has already drained and cleared the handle\n // below. Going on would start the loop again and arm a handle nothing\n // clears, holding open a worker that has finished shutting down.\n if (this.stopping) return;\n\n const application = this.kernel.app;\n // `bound`, not `make` in a try: an application built without the queue\n // provider is a configuration to name, not an unbound-token stack trace.\n if (!application.bound(QueueManager)) {\n throw new QueueWorkerRefused(\n \"[gemi] `gemi queue:work` has no queue to claim from: this application \" +\n \"does not register QueueServiceProvider.\",\n );\n }\n const queue = application.make(QueueManager);\n if (!queue.durable) {\n throw new QueueWorkerRefused(\n \"[gemi] `gemi queue:work` refuses to start: the queue uses the memory \" +\n \"driver, which keeps each job in the process that dispatched it, so \" +\n 'a worker never sees one. Set `driver: \"database\"` (or another driver ' +\n \"every process can reach) in app/config/queue.ts, or run jobs in the \" +\n \"server and drop the worker.\",\n );\n }\n\n // Idempotent: a production boot with a durable driver has already started\n // it (`startClaimingIfServing`). Development has not, and a worker claims\n // in either.\n queue.start();\n // The queue's poll timer is unref'd, so that a script's queue does not\n // hold it open; here the queue is the process, and without a handle of its\n // own an idle worker would exit 0 as soon as it booted.\n this.keepAlive = setInterval(() => {}, 1 << 30);\n console.log(\n `[gemi] Queue worker started: claiming up to ${queue.config.concurrency} job(s) at a time.`,\n );\n }\n\n /**\n * Stops claiming, waits for the running jobs, runs every provider's\n * `shutdown()`, and resolves with the exit code: 0 when no job was left\n * running and every provider finished in time, 1 otherwise. Idempotent.\n *\n * `gemi start`'s drain, with the jobs where the requests were:\n * `isShuttingDown()` turns true, which stops a durable queue claiming;\n * running jobs get `timeoutMs` (`GEMI_SHUTDOWN_TIMEOUT`), which a server\n * spends on requests; then `Application.shutdown` gets `providerTimeoutMs`,\n * and in it the queue provider waits for whatever is still running and\n * names it. The two budgets add up as they do for a server, so a worker\n * fits the same grace period. `delayMs` is not waited: it is for a load\n * balancer, and nothing routes to a worker.\n */\n stop(settings: Partial<ShutdownSettings> = {}): Promise<number> {\n this.stopping ??= this.drain({ ...this.settings, ...settings });\n return this.stopping;\n }\n\n private async drain(settings: ShutdownSettings): Promise<number> {\n markShuttingDown();\n const application = this.kernel.app;\n // Resolving the manager now would build a driver only to stop it, which\n // is what a signal during the boot, before the provider resolved it, finds.\n const queue = application.resolved(QueueManager) ? application.make(QueueManager) : undefined;\n if (queue) {\n console.log(\n `[gemi] Shutting down: waiting for ${queue.running} running job(s) ` +\n `(up to ${settings.timeoutMs / 1000}s).`,\n );\n await queue.drain(settings.timeoutMs);\n }\n\n const report = await this.kernel.shutdown({ timeoutMs: settings.providerTimeoutMs });\n clearInterval(this.keepAlive);\n const clean =\n (queue?.running ?? 0) === 0 && report.failed.length === 0 && report.timedOut.length === 0;\n console.log(`[gemi] Shutdown ${clean ? \"complete\" : \"finished with errors\"}.`);\n return clean ? 0 : 1;\n }\n}\n"
7
7
  ],
8
- "mappings": ";6UAYA,kBCGO,IAAM,QAA2B,GAAM,DAC5C,GAAO,kBACT,CAYO,MAAM,CAAY,CAWJ,OAVX,SACA,UACS,SAOjB,WAAW,CACQ,EACjB,EAAsC,CAAC,EACvC,CAFiB,cAGjB,KAAK,SAAW,IAAK,EAAiB,KAAM,CAAS,OAejD,MAAK,EAAkB,CAK3B,GAJA,MAAM,KAAK,OAAO,YAAY,EAI1B,KAAK,SAAU,OAEnB,IAAM,EAAc,KAAK,OAAO,IAGhC,GAAI,CAAC,EAAY,MAAM,CAAY,EACjC,MAAM,IAAI,EACR,+GAEF,EAEF,IAAM,EAAQ,EAAY,KAAK,CAAY,EAC3C,GAAI,CAAC,EAAM,QACT,MAAM,IAAI,EACR,8SAKF,EAMF,EAAM,MAAM,EAIZ,KAAK,UAAY,YAAY,IAAM,GAAI,UAAO,EAC9C,QAAQ,IACN,+CAA+C,EAAM,OAAO,+BAC9D,EAiBF,IAAI,CAAC,EAAsC,CAAC,EAAoB,CAE9D,OADA,KAAK,WAAa,KAAK,MAAM,IAAK,KAAK,YAAa,CAAS,CAAC,EACvD,KAAK,cAGA,MAAK,CAAC,EAA6C,CAC/D,EAAiB,EACjB,IAAM,EAAc,KAAK,OAAO,IAG1B,EAAQ,EAAY,SAAS,CAAY,EAAI,EAAY,KAAK,CAAY,EAAI,OACpF,GAAI,EACF,QAAQ,IACN,qCAAqC,EAAM,iCAC/B,EAAS,UAAY,SACnC,EACA,MAAM,EAAM,MAAM,EAAS,SAAS,EAGtC,IAAM,EAAS,MAAM,KAAK,OAAO,SAAS,CAAE,UAAW,EAAS,iBAAkB,CAAC,EACnF,cAAc,KAAK,SAAS,EAC5B,IAAM,GACH,GAAO,SAAW,KAAO,GAAK,EAAO,OAAO,SAAW,GAAK,EAAO,SAAS,SAAW,EAE1F,OADA,QAAQ,IAAI,mBAAmB,EAAQ,WAAa,yBAAyB,EACtE,EAAQ,EAAI,EAEvB,CDvHA,eAAe,CAAI,EAAgC,CACjD,IAAM,EAAU,EAAY,EAI5B,QAAQ,IAAI,SAAW,EACvB,QAAQ,IAAI,QAAU,EAAK,KAAK,EAAS,KAAK,EAE9C,EAAgB,EAEhB,IAAM,EAAa,EAAK,KAAK,EAAS,sBAAsB,GACpD,QAAS,GAAY,MAAa,UAG1C,GAAI,OAAO,IAAW,WAEpB,OADA,QAAQ,MAAM,UAAU,kDAA2D,EAC5E,EAET,IAAM,EAAS,IAAI,EACnB,EAAO,KAAK,EAEZ,IAAM,EAAS,IAAI,EAAY,CAAM,EAKrC,EAAuB,IAAM,EAAO,KAAK,CAAC,EAE1C,GAAI,CACF,MAAM,EAAO,MAAM,EACnB,MAAO,EAAO,CACd,GAAI,EAAE,aAAiB,GAAqB,MAAM,EAElD,OADA,QAAQ,MAAM,EAAM,OAAO,EACpB,EAGT,OAGF,EAAK,EAAE,KACL,CAAC,IAAS,CACR,GAAI,IAAS,OAAW,QAAQ,KAAK,CAAI,GAE3C,CAAC,IAAU,CACT,QAAQ,MAAM,2CAA4C,CAAK,EAC/D,QAAQ,KAAK,CAAC,EAElB",
9
- "debugId": "AD398EFE10DD87FA64756E2164756E21",
8
+ "mappings": ";iZAYA,kBCGO,IAAM,QAA2B,GAAM,DAC5C,GAAO,kBACT,DAYO,IAAM,CAAY,CAWJ,OAVX,SACA,UACS,SAOjB,WAAW,CACQ,EACjB,EAAsC,CAAC,EACvC,CAFiB,cAGjB,KAAK,SAAW,IAAK,EAAiB,KAAM,CAAS,OAejD,MAAK,EAAkB,CAK3B,GAJA,MAAM,KAAK,OAAO,YAAY,EAI1B,KAAK,SAAU,OAEnB,IAAM,EAAc,KAAK,OAAO,IAGhC,GAAI,CAAC,EAAY,MAAM,CAAY,EACjC,MAAM,IAAI,EACR,+GAEF,EAEF,IAAM,EAAQ,EAAY,KAAK,CAAY,EAC3C,GAAI,CAAC,EAAM,QACT,MAAM,IAAI,EACR,8SAKF,EAMF,EAAM,MAAM,EAIZ,KAAK,UAAY,YAAY,IAAM,GAAI,UAAO,EAC9C,QAAQ,IACN,+CAA+C,EAAM,OAAO,+BAC9D,EAiBF,IAAI,CAAC,EAAsC,CAAC,EAAoB,CAE9D,OADA,KAAK,WAAa,KAAK,MAAM,IAAK,KAAK,YAAa,CAAS,CAAC,EACvD,KAAK,cAGA,MAAK,CAAC,EAA6C,CAC/D,EAAiB,EACjB,IAAM,EAAc,KAAK,OAAO,IAG1B,EAAQ,EAAY,SAAS,CAAY,EAAI,EAAY,KAAK,CAAY,EAAI,OACpF,GAAI,EACF,QAAQ,IACN,qCAAqC,EAAM,iCAC/B,EAAS,UAAY,SACnC,EACA,MAAM,EAAM,MAAM,EAAS,SAAS,EAGtC,IAAM,EAAS,MAAM,KAAK,OAAO,SAAS,CAAE,UAAW,EAAS,iBAAkB,CAAC,EACnF,cAAc,KAAK,SAAS,EAC5B,IAAM,GACH,GAAO,SAAW,KAAO,GAAK,EAAO,OAAO,SAAW,GAAK,EAAO,SAAS,SAAW,EAE1F,OADA,QAAQ,IAAI,mBAAmB,EAAQ,WAAa,yBAAyB,EACtE,EAAQ,EAAI,EAEvB,CDvHA,eAAe,CAAI,EAAgC,CACjD,IAAM,EAAU,EAAY,EAI5B,QAAQ,IAAI,SAAW,EACvB,QAAQ,IAAI,QAAU,EAAK,KAAK,EAAS,KAAK,EAE9C,EAAgB,EAEhB,IAAM,EAAa,EAAK,KAAK,EAAS,sBAAsB,GACpD,QAAS,GAAY,MAAa,UAG1C,GAAI,OAAO,IAAW,WAEpB,OADA,QAAQ,MAAM,UAAU,kDAA2D,EAC5E,EAET,IAAM,EAAS,IAAI,EACnB,EAAO,KAAK,EAEZ,IAAM,EAAS,IAAI,EAAY,CAAM,EAKrC,EAAuB,IAAM,EAAO,KAAK,CAAC,EAE1C,GAAI,CACF,MAAM,EAAO,MAAM,EACnB,MAAO,EAAO,CACd,GAAI,EAAE,aAAiB,GAAqB,MAAM,EAElD,OADA,QAAQ,MAAM,EAAM,OAAO,EACpB,EAGT,OAGF,EAAK,EAAE,KACL,CAAC,IAAS,CACR,GAAI,IAAS,OAAW,QAAQ,KAAK,CAAI,GAE3C,CAAC,IAAU,CACT,QAAQ,MAAM,2CAA4C,CAAK,EAC/D,QAAQ,KAAK,CAAC,EAElB",
9
+ "debugId": "1ACF53BA60E3CBB764756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{dc as Y}from"../chunk-p0px3ym0.js";import{pg as M}from"../chunk-6qyc4e22.js";import{Ag as R,yg as P}from"../chunk-cyze295m.js";import"../chunk-rgcfqq21.js";import"../chunk-241aehp3.js";import{Kg as Q,Lg as U,Mg as V,Ng as W}from"../chunk-5v2rx0rk.js";import"../chunk-g6byqmdq.js";class S{static token;async boot(){}static inject(){if(!this.token)throw Error(`Service [${this.name}] does not declare a \`static token\`, so the container has no key to resolve it by.`);try{return R(this)}catch(x){throw Error(`Could not inject [${this.name}]. Services are constructed when the kernel boots, so `+"inject() only works from code that runs after that \u2014 a constructor default or a method "+`body, never module top level. Check that [${this.name}] is listed in \`services\` on your Kernel.`,{cause:x})}}static with(x){let f=new this;for(let[C,g]of Object.entries(x))if(g!==void 0)f[C]=g;return f}}import{readdir as z}from"fs/promises";import{join as B,parse as j}from"path";var H=[".ts",".tsx",".js",".mjs",".jsx"];function J(x={}){let f={};for(let[C,g]of Object.entries(x))f[C]=q(g);return new P(f)}async function L(x,f=new P){let C=[];try{C=await z(x)}catch(D){return f}let g=C.filter((D)=>{let{ext:v,name:F}=j(D);return H.includes(v)&&!F.startsWith(".")}).sort();for(let D of g){let{name:v}=j(D),F=await import(B(x,D));f.set(v,q(F))}return f}function q(x){if(x&&typeof x==="object"&&"default"in x)return x.default;return x}export{M as withDefaults,Q as sourceFiles,W as projectRoot,L as loadConfigFrom,V as discoverClasses,J as createConfigRepository,Y as ServiceProvider,S as Service,P as Repository,U as DiscoveryError};
2
+ import{fc as Y}from"../chunk-7kedxtbp.js";import{vg as M}from"../chunk-3fr22rtg.js";import{Fg as P,Hg as R}from"../chunk-7hfv5n00.js";import"../chunk-qxda7pzm.js";import{Qg as Q,Rg as U,Sg as V,Tg as W}from"../chunk-kzh8bkg5.js";import"../chunk-j2nx0dtq.js";class S{static token;async boot(){}static inject(){if(!this.token)throw Error(`Service [${this.name}] does not declare a \`static token\`, so the container has no key to resolve it by.`);try{return R(this)}catch(x){throw Error(`Could not inject [${this.name}]. Services are constructed when the kernel boots, so `+"inject() only works from code that runs after that \u2014 a constructor default or a method "+`body, never module top level. Check that [${this.name}] is listed in \`services\` on your Kernel.`,{cause:x})}}static with(x){let f=new this;for(let[C,g]of Object.entries(x))if(g!==void 0)f[C]=g;return f}}import{readdir as z}from"fs/promises";import{join as B,parse as j}from"path";var H=[".ts",".tsx",".js",".mjs",".jsx"];function J(x={}){let f={};for(let[C,g]of Object.entries(x))f[C]=q(g);return new P(f)}async function L(x,f=new P){let C=[];try{C=await z(x)}catch(D){return f}let g=C.filter((D)=>{let{ext:v,name:F}=j(D);return H.includes(v)&&!F.startsWith(".")}).sort();for(let D of g){let{name:v}=j(D),F=await import(B(x,D));f.set(v,q(F))}return f}function q(x){if(x&&typeof x==="object"&&"default"in x)return x.default;return x}export{M as withDefaults,Q as sourceFiles,W as projectRoot,L as loadConfigFrom,V as discoverClasses,J as createConfigRepository,Y as ServiceProvider,S as Service,P as Repository,U as DiscoveryError};
3
3
 
4
- //# debugId=AA7F687EC4E8740264756E2164756E21
4
+ //# debugId=8F48516D7E7C7EEB64756E2164756E21