effect 4.0.0-rc.112 → 4.0.0-rc.113

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 (1229) hide show
  1. package/AGENTS.md +24 -9
  2. package/CLAUDE.md +24 -9
  3. package/ai-docs/package.json +2 -2
  4. package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +18 -5
  5. package/ai-docs/src/01_effect/01_basics/index.md +5 -3
  6. package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +1 -1
  7. package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +2 -2
  8. package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +2 -2
  9. package/ai-docs/src/03_stream/30_encoding.ts +5 -7
  10. package/ai-docs/src/08_observability/10_logging.ts +1 -1
  11. package/ai-docs/src/70_cli/10_basics.ts +7 -7
  12. package/ai-docs/src/71_ai/10_language-model.ts +2 -2
  13. package/ai-docs/src/71_ai/20_tools.ts +1 -1
  14. package/ai-docs/src/71_ai/30_chat.ts +1 -1
  15. package/dist/Array.d.ts +86 -50
  16. package/dist/Array.d.ts.map +1 -1
  17. package/dist/Array.js +58 -32
  18. package/dist/Array.js.map +1 -1
  19. package/dist/BigDecimal.d.ts +5 -1
  20. package/dist/BigDecimal.d.ts.map +1 -1
  21. package/dist/BigDecimal.js +40 -23
  22. package/dist/BigDecimal.js.map +1 -1
  23. package/dist/ByteSize.d.ts +633 -0
  24. package/dist/ByteSize.d.ts.map +1 -0
  25. package/dist/ByteSize.js +524 -0
  26. package/dist/ByteSize.js.map +1 -0
  27. package/dist/Cache.d.ts.map +1 -1
  28. package/dist/Cache.js +17 -3
  29. package/dist/Cache.js.map +1 -1
  30. package/dist/Cause.d.ts +1 -1
  31. package/dist/Cause.d.ts.map +1 -1
  32. package/dist/Channel.d.ts +97 -24
  33. package/dist/Channel.d.ts.map +1 -1
  34. package/dist/Channel.js +66 -39
  35. package/dist/Channel.js.map +1 -1
  36. package/dist/Chunk.d.ts +49 -28
  37. package/dist/Chunk.d.ts.map +1 -1
  38. package/dist/Chunk.js +27 -18
  39. package/dist/Chunk.js.map +1 -1
  40. package/dist/Config.d.ts +162 -201
  41. package/dist/Config.d.ts.map +1 -1
  42. package/dist/Config.js +103 -212
  43. package/dist/Config.js.map +1 -1
  44. package/dist/ConfigProvider.d.ts +4 -4
  45. package/dist/ConfigProvider.d.ts.map +1 -1
  46. package/dist/ConfigProvider.js +7 -7
  47. package/dist/ConfigProvider.js.map +1 -1
  48. package/dist/Context.d.ts +31 -0
  49. package/dist/Context.d.ts.map +1 -1
  50. package/dist/Context.js.map +1 -1
  51. package/dist/Cron.d.ts +2 -2
  52. package/dist/Cron.d.ts.map +1 -1
  53. package/dist/Cron.js +2 -2
  54. package/dist/Cron.js.map +1 -1
  55. package/dist/Crypto.d.ts +1 -1
  56. package/dist/Crypto.d.ts.map +1 -1
  57. package/dist/Crypto.js +3 -2
  58. package/dist/Crypto.js.map +1 -1
  59. package/dist/DateTime.d.ts +2 -2
  60. package/dist/DateTime.d.ts.map +1 -1
  61. package/dist/Deferred.d.ts.map +1 -1
  62. package/dist/Deferred.js +6 -6
  63. package/dist/Deferred.js.map +1 -1
  64. package/dist/Duration.d.ts +1 -1
  65. package/dist/Duration.d.ts.map +1 -1
  66. package/dist/Duration.js +1 -1
  67. package/dist/Duration.js.map +1 -1
  68. package/dist/Effect.d.ts +220 -113
  69. package/dist/Effect.d.ts.map +1 -1
  70. package/dist/Effect.js +27 -26
  71. package/dist/Effect.js.map +1 -1
  72. package/dist/Effectable.d.ts +58 -2
  73. package/dist/Effectable.d.ts.map +1 -1
  74. package/dist/Effectable.js +58 -6
  75. package/dist/Effectable.js.map +1 -1
  76. package/dist/Encoding.d.ts +3 -1
  77. package/dist/Encoding.d.ts.map +1 -1
  78. package/dist/Encoding.js +40 -10
  79. package/dist/Encoding.js.map +1 -1
  80. package/dist/Equal.d.ts +1 -1
  81. package/dist/Equal.d.ts.map +1 -1
  82. package/dist/Equal.js +1 -1
  83. package/dist/Equal.js.map +1 -1
  84. package/dist/ExecutionPlan.d.ts.map +1 -1
  85. package/dist/ExecutionPlan.js +1 -0
  86. package/dist/ExecutionPlan.js.map +1 -1
  87. package/dist/Fiber.d.ts +32 -8
  88. package/dist/Fiber.d.ts.map +1 -1
  89. package/dist/Fiber.js.map +1 -1
  90. package/dist/FiberHandle.js +3 -3
  91. package/dist/FiberHandle.js.map +1 -1
  92. package/dist/FiberMap.d.ts +12 -6
  93. package/dist/FiberMap.d.ts.map +1 -1
  94. package/dist/FiberMap.js +11 -6
  95. package/dist/FiberMap.js.map +1 -1
  96. package/dist/FileSystem.d.ts +42 -210
  97. package/dist/FileSystem.d.ts.map +1 -1
  98. package/dist/FileSystem.js +16 -148
  99. package/dist/FileSystem.js.map +1 -1
  100. package/dist/Formatter.d.ts +1 -0
  101. package/dist/Formatter.d.ts.map +1 -1
  102. package/dist/Formatter.js +19 -3
  103. package/dist/Formatter.js.map +1 -1
  104. package/dist/Graph.d.ts +48 -20
  105. package/dist/Graph.d.ts.map +1 -1
  106. package/dist/Graph.js +20 -5
  107. package/dist/Graph.js.map +1 -1
  108. package/dist/Hash.d.ts +1 -1
  109. package/dist/Hash.d.ts.map +1 -1
  110. package/dist/Hash.js +5 -14
  111. package/dist/Hash.js.map +1 -1
  112. package/dist/HashMap.d.ts +1 -1
  113. package/dist/HashMap.d.ts.map +1 -1
  114. package/dist/HashRing.d.ts +6 -1
  115. package/dist/HashRing.d.ts.map +1 -1
  116. package/dist/HashRing.js +9 -2
  117. package/dist/HashRing.js.map +1 -1
  118. package/dist/HashSet.d.ts +1 -1
  119. package/dist/HashSet.d.ts.map +1 -1
  120. package/dist/HashSet.js.map +1 -1
  121. package/dist/Iterable.d.ts +33 -15
  122. package/dist/Iterable.d.ts.map +1 -1
  123. package/dist/Iterable.js +58 -45
  124. package/dist/Iterable.js.map +1 -1
  125. package/dist/JsonPatch.js +4 -4
  126. package/dist/JsonPatch.js.map +1 -1
  127. package/dist/JsonPointer.d.ts +68 -4
  128. package/dist/JsonPointer.d.ts.map +1 -1
  129. package/dist/JsonPointer.js +90 -4
  130. package/dist/JsonPointer.js.map +1 -1
  131. package/dist/JsonSchema.js +8 -22
  132. package/dist/JsonSchema.js.map +1 -1
  133. package/dist/Layer.d.ts +47 -4
  134. package/dist/Layer.d.ts.map +1 -1
  135. package/dist/Layer.js +4 -6
  136. package/dist/Layer.js.map +1 -1
  137. package/dist/LayerMap.d.ts +1 -3
  138. package/dist/LayerMap.d.ts.map +1 -1
  139. package/dist/LayerMap.js +7 -5
  140. package/dist/LayerMap.js.map +1 -1
  141. package/dist/LayerRef.d.ts.map +1 -1
  142. package/dist/LayerRef.js +7 -5
  143. package/dist/LayerRef.js.map +1 -1
  144. package/dist/Logger.d.ts +114 -9
  145. package/dist/Logger.d.ts.map +1 -1
  146. package/dist/Logger.js +109 -9
  147. package/dist/Logger.js.map +1 -1
  148. package/dist/ManagedRuntime.js +1 -1
  149. package/dist/ManagedRuntime.js.map +1 -1
  150. package/dist/Match.d.ts +43 -31
  151. package/dist/Match.d.ts.map +1 -1
  152. package/dist/Match.js +5 -2
  153. package/dist/Match.js.map +1 -1
  154. package/dist/Metric.d.ts +11 -4
  155. package/dist/Metric.d.ts.map +1 -1
  156. package/dist/Metric.js +14 -18
  157. package/dist/Metric.js.map +1 -1
  158. package/dist/MutableHashMap.d.ts +1 -1
  159. package/dist/MutableHashMap.d.ts.map +1 -1
  160. package/dist/MutableHashMap.js +1 -1
  161. package/dist/MutableHashMap.js.map +1 -1
  162. package/dist/MutableHashSet.d.ts +1 -1
  163. package/dist/MutableHashSet.d.ts.map +1 -1
  164. package/dist/MutableHashSet.js +1 -1
  165. package/dist/MutableHashSet.js.map +1 -1
  166. package/dist/MutableList.d.ts +13 -2
  167. package/dist/MutableList.d.ts.map +1 -1
  168. package/dist/MutableList.js +18 -2
  169. package/dist/MutableList.js.map +1 -1
  170. package/dist/Number.d.ts +6 -0
  171. package/dist/Number.d.ts.map +1 -1
  172. package/dist/Number.js +3 -1
  173. package/dist/Number.js.map +1 -1
  174. package/dist/Optic.d.ts.map +1 -1
  175. package/dist/Optic.js +5 -4
  176. package/dist/Optic.js.map +1 -1
  177. package/dist/Option.d.ts +1 -1
  178. package/dist/Option.d.ts.map +1 -1
  179. package/dist/Option.js +1 -1
  180. package/dist/Option.js.map +1 -1
  181. package/dist/Order.d.ts +1 -0
  182. package/dist/Order.d.ts.map +1 -1
  183. package/dist/Order.js +3 -1
  184. package/dist/Order.js.map +1 -1
  185. package/dist/PartitionedSemaphore.d.ts.map +1 -1
  186. package/dist/PartitionedSemaphore.js +1 -2
  187. package/dist/PartitionedSemaphore.js.map +1 -1
  188. package/dist/Path.d.ts +1 -1
  189. package/dist/Path.d.ts.map +1 -1
  190. package/dist/Path.js +1 -1
  191. package/dist/Path.js.map +1 -1
  192. package/dist/PlatformError.d.ts +1 -1
  193. package/dist/PlatformError.d.ts.map +1 -1
  194. package/dist/PlatformError.js +1 -1
  195. package/dist/PlatformError.js.map +1 -1
  196. package/dist/Pool.d.ts +38 -1
  197. package/dist/Pool.d.ts.map +1 -1
  198. package/dist/Pool.js +81 -5
  199. package/dist/Pool.js.map +1 -1
  200. package/dist/PrimaryKey.d.ts +1 -1
  201. package/dist/PrimaryKey.d.ts.map +1 -1
  202. package/dist/PrimaryKey.js +1 -1
  203. package/dist/PrimaryKey.js.map +1 -1
  204. package/dist/PubSub.d.ts +21 -9
  205. package/dist/PubSub.d.ts.map +1 -1
  206. package/dist/PubSub.js +18 -14
  207. package/dist/PubSub.js.map +1 -1
  208. package/dist/Queue.d.ts +82 -6
  209. package/dist/Queue.d.ts.map +1 -1
  210. package/dist/Queue.js +116 -27
  211. package/dist/Queue.js.map +1 -1
  212. package/dist/Random.d.ts +17 -4
  213. package/dist/Random.d.ts.map +1 -1
  214. package/dist/Random.js +1 -1
  215. package/dist/Random.js.map +1 -1
  216. package/dist/RcMap.d.ts.map +1 -1
  217. package/dist/RcMap.js +15 -9
  218. package/dist/RcMap.js.map +1 -1
  219. package/dist/Redacted.d.ts +1 -1
  220. package/dist/Redacted.d.ts.map +1 -1
  221. package/dist/Redacted.js +1 -1
  222. package/dist/Redacted.js.map +1 -1
  223. package/dist/RequestResolver.d.ts +15 -9
  224. package/dist/RequestResolver.d.ts.map +1 -1
  225. package/dist/RequestResolver.js +33 -15
  226. package/dist/RequestResolver.js.map +1 -1
  227. package/dist/Result.d.ts +1 -1
  228. package/dist/Result.d.ts.map +1 -1
  229. package/dist/Result.js +1 -1
  230. package/dist/Result.js.map +1 -1
  231. package/dist/Scheduler.d.ts.map +1 -1
  232. package/dist/Scheduler.js +19 -10
  233. package/dist/Scheduler.js.map +1 -1
  234. package/dist/Schema.d.ts +2155 -2415
  235. package/dist/Schema.d.ts.map +1 -1
  236. package/dist/Schema.js +2755 -4174
  237. package/dist/Schema.js.map +1 -1
  238. package/dist/SchemaAST.d.ts +354 -183
  239. package/dist/SchemaAST.d.ts.map +1 -1
  240. package/dist/SchemaAST.js +277 -601
  241. package/dist/SchemaAST.js.map +1 -1
  242. package/dist/SchemaGetter.d.ts +46 -22
  243. package/dist/SchemaGetter.d.ts.map +1 -1
  244. package/dist/SchemaGetter.js +56 -53
  245. package/dist/SchemaGetter.js.map +1 -1
  246. package/dist/SchemaIssue.d.ts +122 -188
  247. package/dist/SchemaIssue.d.ts.map +1 -1
  248. package/dist/SchemaIssue.js +48 -263
  249. package/dist/SchemaIssue.js.map +1 -1
  250. package/dist/SchemaParser.js +2 -9
  251. package/dist/SchemaParser.js.map +1 -1
  252. package/dist/SchemaRepresentation.d.ts +1099 -16
  253. package/dist/SchemaRepresentation.d.ts.map +1 -1
  254. package/dist/SchemaRepresentation.js +1341 -20
  255. package/dist/SchemaRepresentation.js.map +1 -1
  256. package/dist/SchemaTransformation.d.ts +49 -16
  257. package/dist/SchemaTransformation.d.ts.map +1 -1
  258. package/dist/SchemaTransformation.js +75 -98
  259. package/dist/SchemaTransformation.js.map +1 -1
  260. package/dist/ScopedCache.d.ts.map +1 -1
  261. package/dist/ScopedCache.js +10 -3
  262. package/dist/ScopedCache.js.map +1 -1
  263. package/dist/Sink.d.ts +9 -4
  264. package/dist/Sink.d.ts.map +1 -1
  265. package/dist/Sink.js +49 -31
  266. package/dist/Sink.js.map +1 -1
  267. package/dist/Stream.d.ts +221 -35
  268. package/dist/Stream.d.ts.map +1 -1
  269. package/dist/Stream.js +168 -84
  270. package/dist/Stream.js.map +1 -1
  271. package/dist/String.d.ts +16 -6
  272. package/dist/String.d.ts.map +1 -1
  273. package/dist/String.js +12 -5
  274. package/dist/String.js.map +1 -1
  275. package/dist/Struct.d.ts.map +1 -1
  276. package/dist/Struct.js +7 -4
  277. package/dist/Struct.js.map +1 -1
  278. package/dist/SynchronizedRef.d.ts +3 -2
  279. package/dist/SynchronizedRef.d.ts.map +1 -1
  280. package/dist/SynchronizedRef.js.map +1 -1
  281. package/dist/Terminal.d.ts +3 -3
  282. package/dist/Terminal.d.ts.map +1 -1
  283. package/dist/Terminal.js +3 -3
  284. package/dist/Terminal.js.map +1 -1
  285. package/dist/Tracer.d.ts +27 -7
  286. package/dist/Tracer.d.ts.map +1 -1
  287. package/dist/Tracer.js +38 -13
  288. package/dist/Tracer.js.map +1 -1
  289. package/dist/Trie.d.ts +1 -1
  290. package/dist/Trie.d.ts.map +1 -1
  291. package/dist/Tuple.d.ts +2 -1
  292. package/dist/Tuple.d.ts.map +1 -1
  293. package/dist/Tuple.js.map +1 -1
  294. package/dist/TxChunk.d.ts +1 -1
  295. package/dist/TxChunk.d.ts.map +1 -1
  296. package/dist/TxChunk.js +1 -1
  297. package/dist/TxChunk.js.map +1 -1
  298. package/dist/TxDeferred.d.ts +1 -1
  299. package/dist/TxDeferred.d.ts.map +1 -1
  300. package/dist/TxDeferred.js +1 -1
  301. package/dist/TxDeferred.js.map +1 -1
  302. package/dist/TxHashMap.d.ts +1 -1
  303. package/dist/TxHashMap.d.ts.map +1 -1
  304. package/dist/TxHashMap.js +1 -1
  305. package/dist/TxHashMap.js.map +1 -1
  306. package/dist/TxHashSet.d.ts +1 -1
  307. package/dist/TxHashSet.d.ts.map +1 -1
  308. package/dist/TxHashSet.js +1 -1
  309. package/dist/TxHashSet.js.map +1 -1
  310. package/dist/TxPriorityQueue.d.ts +1 -1
  311. package/dist/TxPriorityQueue.d.ts.map +1 -1
  312. package/dist/TxPriorityQueue.js +1 -1
  313. package/dist/TxPriorityQueue.js.map +1 -1
  314. package/dist/TxPubSub.d.ts +1 -1
  315. package/dist/TxPubSub.d.ts.map +1 -1
  316. package/dist/TxPubSub.js +1 -1
  317. package/dist/TxPubSub.js.map +1 -1
  318. package/dist/TxQueue.d.ts +9 -9
  319. package/dist/TxQueue.d.ts.map +1 -1
  320. package/dist/TxQueue.js +93 -86
  321. package/dist/TxQueue.js.map +1 -1
  322. package/dist/TxReentrantLock.d.ts +29 -1
  323. package/dist/TxReentrantLock.d.ts.map +1 -1
  324. package/dist/TxReentrantLock.js +29 -1
  325. package/dist/TxReentrantLock.js.map +1 -1
  326. package/dist/TxRef.d.ts +1 -1
  327. package/dist/TxRef.d.ts.map +1 -1
  328. package/dist/TxRef.js +1 -1
  329. package/dist/TxRef.js.map +1 -1
  330. package/dist/TxSemaphore.d.ts +1 -1
  331. package/dist/TxSemaphore.d.ts.map +1 -1
  332. package/dist/TxSemaphore.js +1 -1
  333. package/dist/TxSemaphore.js.map +1 -1
  334. package/dist/TxSubscriptionRef.d.ts +1 -1
  335. package/dist/TxSubscriptionRef.d.ts.map +1 -1
  336. package/dist/TxSubscriptionRef.js +1 -1
  337. package/dist/TxSubscriptionRef.js.map +1 -1
  338. package/dist/Types.d.ts +33 -19
  339. package/dist/Types.d.ts.map +1 -1
  340. package/dist/Utils.d.ts.map +1 -1
  341. package/dist/Utils.js +2 -1
  342. package/dist/Utils.js.map +1 -1
  343. package/dist/index.d.ts +4 -0
  344. package/dist/index.d.ts.map +1 -1
  345. package/dist/index.js +4 -0
  346. package/dist/index.js.map +1 -1
  347. package/dist/internal/arbitrary/model.d.ts +2 -0
  348. package/dist/internal/arbitrary/model.d.ts.map +1 -0
  349. package/dist/internal/arbitrary/model.js +495 -0
  350. package/dist/internal/arbitrary/model.js.map +1 -0
  351. package/dist/internal/arbitrary/regexp.d.ts +2 -0
  352. package/dist/internal/arbitrary/regexp.d.ts.map +1 -0
  353. package/dist/internal/arbitrary/regexp.js +789 -0
  354. package/dist/internal/arbitrary/regexp.js.map +1 -0
  355. package/dist/internal/arbitrary/runner.d.ts +2 -0
  356. package/dist/internal/arbitrary/runner.d.ts.map +1 -0
  357. package/dist/internal/arbitrary/runner.js +496 -0
  358. package/dist/internal/arbitrary/runner.js.map +1 -0
  359. package/dist/internal/arbitrary/schema.d.ts.map +1 -0
  360. package/dist/internal/arbitrary/schema.js +1340 -0
  361. package/dist/internal/arbitrary/schema.js.map +1 -0
  362. package/dist/internal/core.js +27 -13
  363. package/dist/internal/core.js.map +1 -1
  364. package/dist/internal/count.d.ts +2 -0
  365. package/dist/internal/count.d.ts.map +1 -0
  366. package/dist/internal/count.js +14 -0
  367. package/dist/internal/count.js.map +1 -0
  368. package/dist/internal/dateTime.js +4 -10
  369. package/dist/internal/dateTime.js.map +1 -1
  370. package/dist/internal/effect.js +386 -251
  371. package/dist/internal/effect.js.map +1 -1
  372. package/dist/internal/graph.js +3 -1
  373. package/dist/internal/graph.js.map +1 -1
  374. package/dist/internal/hashMap.js +5 -3
  375. package/dist/internal/hashMap.js.map +1 -1
  376. package/dist/internal/hashSet.js +1 -1
  377. package/dist/internal/hashSet.js.map +1 -1
  378. package/dist/internal/matcher.js +1 -1
  379. package/dist/internal/matcher.js.map +1 -1
  380. package/dist/internal/metric.js +1 -1
  381. package/dist/internal/metric.js.map +1 -1
  382. package/dist/internal/option.js +6 -5
  383. package/dist/internal/option.js.map +1 -1
  384. package/dist/internal/random.js +17 -0
  385. package/dist/internal/random.js.map +1 -1
  386. package/dist/internal/rcRef.js +10 -7
  387. package/dist/internal/rcRef.js.map +1 -1
  388. package/dist/internal/references.js +1 -0
  389. package/dist/internal/references.js.map +1 -1
  390. package/dist/internal/request.js +13 -6
  391. package/dist/internal/request.js.map +1 -1
  392. package/dist/internal/result.js +11 -9
  393. package/dist/internal/result.js.map +1 -1
  394. package/dist/internal/schema/annotations.js +1 -1
  395. package/dist/internal/schema/annotations.js.map +1 -1
  396. package/dist/internal/schema/fromJsonSchemaDocument.js +14 -12
  397. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  398. package/dist/internal/schema/fromRepresentation.js +5 -5
  399. package/dist/internal/schema/fromRepresentation.js.map +1 -1
  400. package/dist/internal/schema/make.d.ts +2 -0
  401. package/dist/internal/schema/make.d.ts.map +1 -0
  402. package/dist/internal/schema/{schema.js → make.js} +1 -25
  403. package/dist/internal/schema/make.js.map +1 -0
  404. package/dist/internal/schema/parser.d.ts +0 -12
  405. package/dist/internal/schema/parser.d.ts.map +1 -1
  406. package/dist/internal/schema/parser.js +7 -0
  407. package/dist/internal/schema/parser.js.map +1 -1
  408. package/dist/internal/schema/standardSchema.d.ts +2 -0
  409. package/dist/internal/schema/standardSchema.d.ts.map +1 -0
  410. package/dist/internal/schema/standardSchema.js +112 -0
  411. package/dist/internal/schema/standardSchema.js.map +1 -0
  412. package/dist/internal/schema/toCodeDocument.js +34 -22
  413. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  414. package/dist/internal/schema/toCodec.d.ts +2 -0
  415. package/dist/internal/schema/toCodec.d.ts.map +1 -0
  416. package/dist/internal/schema/toCodec.js +242 -0
  417. package/dist/internal/schema/toCodec.js.map +1 -0
  418. package/dist/internal/schema/toDifferJsonPatch.d.ts +2 -0
  419. package/dist/internal/schema/toDifferJsonPatch.d.ts.map +1 -0
  420. package/dist/internal/schema/toDifferJsonPatch.js +20 -0
  421. package/dist/internal/schema/toDifferJsonPatch.js.map +1 -0
  422. package/dist/internal/schema/toEncoderXml.d.ts +2 -0
  423. package/dist/internal/schema/toEncoderXml.d.ts.map +1 -0
  424. package/dist/internal/schema/toEncoderXml.js +104 -0
  425. package/dist/internal/schema/toEncoderXml.js.map +1 -0
  426. package/dist/internal/schema/toEquivalence.js +66 -0
  427. package/dist/internal/schema/toEquivalence.js.map +1 -1
  428. package/dist/internal/schema/toFormatter.d.ts +2 -0
  429. package/dist/internal/schema/toFormatter.d.ts.map +1 -0
  430. package/dist/internal/schema/toFormatter.js +227 -0
  431. package/dist/internal/schema/toFormatter.js.map +1 -0
  432. package/dist/internal/schema/toIso.d.ts +2 -0
  433. package/dist/internal/schema/toIso.d.ts.map +1 -0
  434. package/dist/internal/schema/toIso.js +17 -0
  435. package/dist/internal/schema/toIso.js.map +1 -0
  436. package/dist/internal/schema/toJsonSchemaDocument.js +122 -71
  437. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  438. package/dist/internal/schema/toRepresentation.js +3 -1
  439. package/dist/internal/schema/toRepresentation.js.map +1 -1
  440. package/dist/internal/schemaError.d.ts +4 -0
  441. package/dist/internal/schemaError.d.ts.map +1 -0
  442. package/dist/internal/schemaError.js +6 -0
  443. package/dist/internal/schemaError.js.map +1 -0
  444. package/dist/internal/stream.js +5 -6
  445. package/dist/internal/stream.js.map +1 -1
  446. package/dist/internal/tracer.d.ts +1 -3
  447. package/dist/internal/tracer.d.ts.map +1 -1
  448. package/dist/internal/tracer.js +6 -0
  449. package/dist/internal/tracer.js.map +1 -1
  450. package/dist/internal/trie.js +2 -2
  451. package/dist/internal/trie.js.map +1 -1
  452. package/dist/internal/uuid.d.ts +1 -2
  453. package/dist/internal/uuid.d.ts.map +1 -1
  454. package/dist/internal/uuid.js +2 -0
  455. package/dist/internal/uuid.js.map +1 -1
  456. package/dist/testing/TestSchema.d.ts +9 -11
  457. package/dist/testing/TestSchema.d.ts.map +1 -1
  458. package/dist/testing/TestSchema.js +20 -18
  459. package/dist/testing/TestSchema.js.map +1 -1
  460. package/dist/testing/index.d.ts +0 -4
  461. package/dist/testing/index.d.ts.map +1 -1
  462. package/dist/testing/index.js +0 -4
  463. package/dist/testing/index.js.map +1 -1
  464. package/dist/unstable/ai/AiError.d.ts +74 -23
  465. package/dist/unstable/ai/AiError.d.ts.map +1 -1
  466. package/dist/unstable/ai/AiError.js +59 -5
  467. package/dist/unstable/ai/AiError.js.map +1 -1
  468. package/dist/unstable/ai/AnthropicStructuredOutput.d.ts.map +1 -1
  469. package/dist/unstable/ai/AnthropicStructuredOutput.js +2 -1
  470. package/dist/unstable/ai/AnthropicStructuredOutput.js.map +1 -1
  471. package/dist/unstable/ai/Chat.d.ts +27 -32
  472. package/dist/unstable/ai/Chat.d.ts.map +1 -1
  473. package/dist/unstable/ai/Chat.js +10 -13
  474. package/dist/unstable/ai/Chat.js.map +1 -1
  475. package/dist/unstable/ai/EmbeddingModel.d.ts +23 -16
  476. package/dist/unstable/ai/EmbeddingModel.d.ts.map +1 -1
  477. package/dist/unstable/ai/EmbeddingModel.js +13 -11
  478. package/dist/unstable/ai/EmbeddingModel.js.map +1 -1
  479. package/dist/unstable/ai/LanguageModel.d.ts +48 -37
  480. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  481. package/dist/unstable/ai/LanguageModel.js +150 -47
  482. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  483. package/dist/unstable/ai/McpServer.d.ts +2 -2
  484. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  485. package/dist/unstable/ai/McpServer.js +21 -12
  486. package/dist/unstable/ai/McpServer.js.map +1 -1
  487. package/dist/unstable/ai/OpenAiStructuredOutput.d.ts.map +1 -1
  488. package/dist/unstable/ai/OpenAiStructuredOutput.js +2 -1
  489. package/dist/unstable/ai/OpenAiStructuredOutput.js.map +1 -1
  490. package/dist/unstable/ai/Prompt.d.ts +1 -1
  491. package/dist/unstable/ai/Prompt.d.ts.map +1 -1
  492. package/dist/unstable/ai/Prompt.js +15 -4
  493. package/dist/unstable/ai/Prompt.js.map +1 -1
  494. package/dist/unstable/ai/Response.d.ts +37 -94
  495. package/dist/unstable/ai/Response.d.ts.map +1 -1
  496. package/dist/unstable/ai/Response.js +46 -96
  497. package/dist/unstable/ai/Response.js.map +1 -1
  498. package/dist/unstable/ai/Tokenizer.js +4 -5
  499. package/dist/unstable/ai/Tokenizer.js.map +1 -1
  500. package/dist/unstable/ai/Tool.d.ts +38 -20
  501. package/dist/unstable/ai/Tool.d.ts.map +1 -1
  502. package/dist/unstable/ai/Tool.js +27 -1
  503. package/dist/unstable/ai/Tool.js.map +1 -1
  504. package/dist/unstable/ai/Toolkit.d.ts +1 -1
  505. package/dist/unstable/ai/Toolkit.d.ts.map +1 -1
  506. package/dist/unstable/ai/Toolkit.js +30 -18
  507. package/dist/unstable/ai/Toolkit.js.map +1 -1
  508. package/dist/unstable/ai/internal/structured-output.js +8 -5
  509. package/dist/unstable/ai/internal/structured-output.js.map +1 -1
  510. package/dist/unstable/arbitrary/Arbitrary.d.ts +626 -0
  511. package/dist/unstable/arbitrary/Arbitrary.d.ts.map +1 -0
  512. package/dist/unstable/arbitrary/Arbitrary.js +238 -0
  513. package/dist/unstable/arbitrary/Arbitrary.js.map +1 -0
  514. package/dist/unstable/arbitrary/index.d.ts +8 -0
  515. package/dist/unstable/arbitrary/index.d.ts.map +1 -0
  516. package/dist/unstable/arbitrary/index.js +9 -0
  517. package/dist/unstable/arbitrary/index.js.map +1 -0
  518. package/dist/unstable/cli/Argument.d.ts +104 -108
  519. package/dist/unstable/cli/Argument.d.ts.map +1 -1
  520. package/dist/unstable/cli/Argument.js +56 -57
  521. package/dist/unstable/cli/Argument.js.map +1 -1
  522. package/dist/unstable/cli/CliError.d.ts +2 -0
  523. package/dist/unstable/cli/CliError.d.ts.map +1 -1
  524. package/dist/unstable/cli/CliError.js +2 -0
  525. package/dist/unstable/cli/CliError.js.map +1 -1
  526. package/dist/unstable/cli/CliOutput.js +26 -3
  527. package/dist/unstable/cli/CliOutput.js.map +1 -1
  528. package/dist/unstable/cli/Command.d.ts +73 -73
  529. package/dist/unstable/cli/Command.js +18 -18
  530. package/dist/unstable/cli/Completions.d.ts +4 -4
  531. package/dist/unstable/cli/Completions.d.ts.map +1 -1
  532. package/dist/unstable/cli/Flag.d.ts +150 -161
  533. package/dist/unstable/cli/Flag.d.ts.map +1 -1
  534. package/dist/unstable/cli/Flag.js +79 -90
  535. package/dist/unstable/cli/Flag.js.map +1 -1
  536. package/dist/unstable/cli/GlobalFlag.d.ts +3 -3
  537. package/dist/unstable/cli/GlobalFlag.js +13 -13
  538. package/dist/unstable/cli/GlobalFlag.js.map +1 -1
  539. package/dist/unstable/cli/Param.d.ts +118 -126
  540. package/dist/unstable/cli/Param.d.ts.map +1 -1
  541. package/dist/unstable/cli/Param.js +104 -113
  542. package/dist/unstable/cli/Param.js.map +1 -1
  543. package/dist/unstable/cli/Primitive.d.ts +69 -58
  544. package/dist/unstable/cli/Primitive.d.ts.map +1 -1
  545. package/dist/unstable/cli/Primitive.js +65 -66
  546. package/dist/unstable/cli/Primitive.js.map +1 -1
  547. package/dist/unstable/cli/Prompt.d.ts +23 -24
  548. package/dist/unstable/cli/Prompt.d.ts.map +1 -1
  549. package/dist/unstable/cli/Prompt.js +68 -63
  550. package/dist/unstable/cli/Prompt.js.map +1 -1
  551. package/dist/unstable/cli/internal/completions/descriptor.js +23 -11
  552. package/dist/unstable/cli/internal/completions/descriptor.js.map +1 -1
  553. package/dist/unstable/cli/internal/completions/zsh.js +2 -2
  554. package/dist/unstable/cli/internal/completions/zsh.js.map +1 -1
  555. package/dist/unstable/cli/internal/config.js +4 -4
  556. package/dist/unstable/cli/internal/parser.js +1 -1
  557. package/dist/unstable/cli/internal/parser.js.map +1 -1
  558. package/dist/unstable/cli/internal/wizard.d.ts.map +1 -1
  559. package/dist/unstable/cli/internal/wizard.js +14 -13
  560. package/dist/unstable/cli/internal/wizard.js.map +1 -1
  561. package/dist/unstable/cluster/ClusterSchema.d.ts +13 -0
  562. package/dist/unstable/cluster/ClusterSchema.d.ts.map +1 -1
  563. package/dist/unstable/cluster/ClusterSchema.js +16 -1
  564. package/dist/unstable/cluster/ClusterSchema.js.map +1 -1
  565. package/dist/unstable/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  566. package/dist/unstable/cluster/ClusterWorkflowEngine.js +57 -14
  567. package/dist/unstable/cluster/ClusterWorkflowEngine.js.map +1 -1
  568. package/dist/unstable/cluster/Entity.d.ts.map +1 -1
  569. package/dist/unstable/cluster/Entity.js +2 -0
  570. package/dist/unstable/cluster/Entity.js.map +1 -1
  571. package/dist/unstable/cluster/EntityProxyServer.d.ts +2 -2
  572. package/dist/unstable/cluster/EntityProxyServer.d.ts.map +1 -1
  573. package/dist/unstable/cluster/EntityProxyServer.js.map +1 -1
  574. package/dist/unstable/cluster/Envelope.d.ts +4 -4
  575. package/dist/unstable/cluster/Envelope.d.ts.map +1 -1
  576. package/dist/unstable/cluster/Envelope.js +2 -3
  577. package/dist/unstable/cluster/Envelope.js.map +1 -1
  578. package/dist/unstable/cluster/HttpRunner.d.ts.map +1 -1
  579. package/dist/unstable/cluster/HttpRunner.js +5 -2
  580. package/dist/unstable/cluster/HttpRunner.js.map +1 -1
  581. package/dist/unstable/cluster/MessageStorage.d.ts +34 -4
  582. package/dist/unstable/cluster/MessageStorage.d.ts.map +1 -1
  583. package/dist/unstable/cluster/MessageStorage.js +28 -9
  584. package/dist/unstable/cluster/MessageStorage.js.map +1 -1
  585. package/dist/unstable/cluster/Reply.d.ts +1 -1
  586. package/dist/unstable/cluster/Sharding.d.ts +1 -1
  587. package/dist/unstable/cluster/Sharding.d.ts.map +1 -1
  588. package/dist/unstable/cluster/Sharding.js +32 -17
  589. package/dist/unstable/cluster/Sharding.js.map +1 -1
  590. package/dist/unstable/cluster/ShardingConfig.js +23 -23
  591. package/dist/unstable/cluster/ShardingConfig.js.map +1 -1
  592. package/dist/unstable/cluster/SocketRunner.d.ts +2 -2
  593. package/dist/unstable/cluster/SocketRunner.d.ts.map +1 -1
  594. package/dist/unstable/cluster/SocketRunner.js +4 -3
  595. package/dist/unstable/cluster/SocketRunner.js.map +1 -1
  596. package/dist/unstable/cluster/SqlMessageStorage.js +11 -8
  597. package/dist/unstable/cluster/SqlMessageStorage.js.map +1 -1
  598. package/dist/unstable/cluster/SqlRunnerStorage.d.ts.map +1 -1
  599. package/dist/unstable/cluster/SqlRunnerStorage.js +11 -4
  600. package/dist/unstable/cluster/SqlRunnerStorage.js.map +1 -1
  601. package/dist/unstable/cluster/internal/clusterAbandon.d.ts +5 -0
  602. package/dist/unstable/cluster/internal/clusterAbandon.d.ts.map +1 -0
  603. package/dist/unstable/cluster/internal/clusterAbandon.js +9 -0
  604. package/dist/unstable/cluster/internal/clusterAbandon.js.map +1 -0
  605. package/dist/unstable/cluster/internal/entityManager.js +7 -4
  606. package/dist/unstable/cluster/internal/entityManager.js.map +1 -1
  607. package/dist/unstable/cluster/internal/interruptors.js +30 -1
  608. package/dist/unstable/cluster/internal/interruptors.js.map +1 -1
  609. package/dist/unstable/cluster/internal/resourceRef.js +12 -7
  610. package/dist/unstable/cluster/internal/resourceRef.js.map +1 -1
  611. package/dist/unstable/devtools/DevToolsClient.d.ts.map +1 -1
  612. package/dist/unstable/devtools/DevToolsClient.js +12 -7
  613. package/dist/unstable/devtools/DevToolsClient.js.map +1 -1
  614. package/dist/unstable/encoding/Ini.d.ts +2 -0
  615. package/dist/unstable/encoding/Ini.d.ts.map +1 -1
  616. package/dist/unstable/encoding/Ini.js +2 -0
  617. package/dist/unstable/encoding/Ini.js.map +1 -1
  618. package/dist/unstable/encoding/SchemaBinary.d.ts +27 -3
  619. package/dist/unstable/encoding/SchemaBinary.d.ts.map +1 -1
  620. package/dist/unstable/encoding/SchemaBinary.js +108 -39
  621. package/dist/unstable/encoding/SchemaBinary.js.map +1 -1
  622. package/dist/unstable/encoding/Sse.js +1 -1
  623. package/dist/unstable/encoding/Sse.js.map +1 -1
  624. package/dist/unstable/encoding/Toml.d.ts +2 -0
  625. package/dist/unstable/encoding/Toml.d.ts.map +1 -1
  626. package/dist/unstable/encoding/Toml.js +6 -4
  627. package/dist/unstable/encoding/Toml.js.map +1 -1
  628. package/dist/unstable/encoding/Yaml.d.ts +2 -0
  629. package/dist/unstable/encoding/Yaml.d.ts.map +1 -1
  630. package/dist/unstable/encoding/Yaml.js +16 -5
  631. package/dist/unstable/encoding/Yaml.js.map +1 -1
  632. package/dist/unstable/encoding/index.d.ts +0 -4
  633. package/dist/unstable/encoding/index.d.ts.map +1 -1
  634. package/dist/unstable/encoding/index.js +0 -4
  635. package/dist/unstable/encoding/index.js.map +1 -1
  636. package/dist/unstable/eventlog/Event.d.ts +5 -5
  637. package/dist/unstable/eventlog/Event.d.ts.map +1 -1
  638. package/dist/unstable/eventlog/Event.js +3 -3
  639. package/dist/unstable/eventlog/Event.js.map +1 -1
  640. package/dist/unstable/eventlog/EventJournal.d.ts +7 -7
  641. package/dist/unstable/eventlog/EventJournal.d.ts.map +1 -1
  642. package/dist/unstable/eventlog/EventJournal.js +9 -9
  643. package/dist/unstable/eventlog/EventJournal.js.map +1 -1
  644. package/dist/unstable/eventlog/EventLog.d.ts +1 -1
  645. package/dist/unstable/eventlog/EventLog.d.ts.map +1 -1
  646. package/dist/unstable/eventlog/EventLog.js +4 -4
  647. package/dist/unstable/eventlog/EventLog.js.map +1 -1
  648. package/dist/unstable/eventlog/EventLogMessage.d.ts +11 -5
  649. package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -1
  650. package/dist/unstable/eventlog/EventLogMessage.js +25 -15
  651. package/dist/unstable/eventlog/EventLogMessage.js.map +1 -1
  652. package/dist/unstable/eventlog/EventLogRemote.d.ts +7 -1
  653. package/dist/unstable/eventlog/EventLogRemote.d.ts.map +1 -1
  654. package/dist/unstable/eventlog/EventLogRemote.js +13 -3
  655. package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
  656. package/dist/unstable/eventlog/EventLogServerUnencrypted.js +2 -2
  657. package/dist/unstable/eventlog/EventLogServerUnencrypted.js.map +1 -1
  658. package/dist/unstable/eventlog/SqlEventJournal.d.ts.map +1 -1
  659. package/dist/unstable/eventlog/SqlEventJournal.js +26 -13
  660. package/dist/unstable/eventlog/SqlEventJournal.js.map +1 -1
  661. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts.map +1 -1
  662. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js +5 -1
  663. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js.map +1 -1
  664. package/dist/unstable/http/Cookies.d.ts +1 -45
  665. package/dist/unstable/http/Cookies.d.ts.map +1 -1
  666. package/dist/unstable/http/Cookies.js +2 -64
  667. package/dist/unstable/http/Cookies.js.map +1 -1
  668. package/dist/unstable/http/FetchHttpClient.js +1 -1
  669. package/dist/unstable/http/FetchHttpClient.js.map +1 -1
  670. package/dist/unstable/http/FindMyWay/internal/router.js +124 -33
  671. package/dist/unstable/http/FindMyWay/internal/router.js.map +1 -1
  672. package/dist/unstable/http/FindMyWay.d.ts +15 -5
  673. package/dist/unstable/http/FindMyWay.d.ts.map +1 -1
  674. package/dist/unstable/http/FindMyWay.js +3 -1
  675. package/dist/unstable/http/FindMyWay.js.map +1 -1
  676. package/dist/unstable/http/Headers.d.ts +0 -22
  677. package/dist/unstable/http/Headers.d.ts.map +1 -1
  678. package/dist/unstable/http/Headers.js +3 -34
  679. package/dist/unstable/http/Headers.js.map +1 -1
  680. package/dist/unstable/http/HttpBody.d.ts +41 -10
  681. package/dist/unstable/http/HttpBody.d.ts.map +1 -1
  682. package/dist/unstable/http/HttpBody.js +64 -10
  683. package/dist/unstable/http/HttpBody.js.map +1 -1
  684. package/dist/unstable/http/HttpClient.d.ts +1 -1
  685. package/dist/unstable/http/HttpClient.d.ts.map +1 -1
  686. package/dist/unstable/http/HttpClient.js +28 -18
  687. package/dist/unstable/http/HttpClient.js.map +1 -1
  688. package/dist/unstable/http/HttpClientRequest.d.ts +28 -6
  689. package/dist/unstable/http/HttpClientRequest.d.ts.map +1 -1
  690. package/dist/unstable/http/HttpClientRequest.js +8 -19
  691. package/dist/unstable/http/HttpClientRequest.js.map +1 -1
  692. package/dist/unstable/http/HttpClientResponse.d.ts +6 -1
  693. package/dist/unstable/http/HttpClientResponse.d.ts.map +1 -1
  694. package/dist/unstable/http/HttpClientResponse.js +10 -6
  695. package/dist/unstable/http/HttpClientResponse.js.map +1 -1
  696. package/dist/unstable/http/HttpEffect.d.ts +16 -0
  697. package/dist/unstable/http/HttpEffect.d.ts.map +1 -1
  698. package/dist/unstable/http/HttpEffect.js +121 -65
  699. package/dist/unstable/http/HttpEffect.js.map +1 -1
  700. package/dist/unstable/http/HttpIncomingMessage.d.ts +3 -3
  701. package/dist/unstable/http/HttpIncomingMessage.d.ts.map +1 -1
  702. package/dist/unstable/http/HttpIncomingMessage.js +1 -13
  703. package/dist/unstable/http/HttpIncomingMessage.js.map +1 -1
  704. package/dist/unstable/http/HttpMiddleware.d.ts +1 -1
  705. package/dist/unstable/http/HttpMiddleware.d.ts.map +1 -1
  706. package/dist/unstable/http/HttpMiddleware.js +29 -21
  707. package/dist/unstable/http/HttpMiddleware.js.map +1 -1
  708. package/dist/unstable/http/HttpPlatform.d.ts +12 -11
  709. package/dist/unstable/http/HttpPlatform.d.ts.map +1 -1
  710. package/dist/unstable/http/HttpPlatform.js +36 -10
  711. package/dist/unstable/http/HttpPlatform.js.map +1 -1
  712. package/dist/unstable/http/HttpRouter.d.ts +5 -1
  713. package/dist/unstable/http/HttpRouter.d.ts.map +1 -1
  714. package/dist/unstable/http/HttpRouter.js +22 -8
  715. package/dist/unstable/http/HttpRouter.js.map +1 -1
  716. package/dist/unstable/http/HttpServer.d.ts +11 -42
  717. package/dist/unstable/http/HttpServer.d.ts.map +1 -1
  718. package/dist/unstable/http/HttpServer.js +16 -20
  719. package/dist/unstable/http/HttpServer.js.map +1 -1
  720. package/dist/unstable/http/HttpServerRequest.d.ts.map +1 -1
  721. package/dist/unstable/http/HttpServerRequest.js +11 -11
  722. package/dist/unstable/http/HttpServerRequest.js.map +1 -1
  723. package/dist/unstable/http/HttpServerRespondable.js +1 -1
  724. package/dist/unstable/http/HttpServerRespondable.js.map +1 -1
  725. package/dist/unstable/http/HttpServerResponse.d.ts +9 -13
  726. package/dist/unstable/http/HttpServerResponse.d.ts.map +1 -1
  727. package/dist/unstable/http/HttpServerResponse.js +34 -44
  728. package/dist/unstable/http/HttpServerResponse.js.map +1 -1
  729. package/dist/unstable/http/HttpStaticServer.d.ts +2 -2
  730. package/dist/unstable/http/HttpStaticServer.d.ts.map +1 -1
  731. package/dist/unstable/http/HttpStaticServer.js +19 -19
  732. package/dist/unstable/http/HttpStaticServer.js.map +1 -1
  733. package/dist/unstable/http/Mime.d.ts +28 -0
  734. package/dist/unstable/http/Mime.d.ts.map +1 -0
  735. package/dist/unstable/http/Mime.js +72 -0
  736. package/dist/unstable/http/Mime.js.map +1 -0
  737. package/dist/unstable/http/Multipart.d.ts +22 -9
  738. package/dist/unstable/http/Multipart.d.ts.map +1 -1
  739. package/dist/unstable/http/Multipart.js +26 -10
  740. package/dist/unstable/http/Multipart.js.map +1 -1
  741. package/dist/unstable/http/MultipartParser/internal/multipart.d.ts +1 -1
  742. package/dist/unstable/http/MultipartParser/internal/multipart.d.ts.map +1 -1
  743. package/dist/unstable/http/MultipartParser/internal/multipart.js +8 -3
  744. package/dist/unstable/http/MultipartParser/internal/multipart.js.map +1 -1
  745. package/dist/unstable/http/MultipartParser.d.ts +4 -3
  746. package/dist/unstable/http/MultipartParser.d.ts.map +1 -1
  747. package/dist/unstable/http/MultipartParser.js.map +1 -1
  748. package/dist/unstable/http/UrlParams.d.ts +0 -97
  749. package/dist/unstable/http/UrlParams.d.ts.map +1 -1
  750. package/dist/unstable/http/UrlParams.js +3 -104
  751. package/dist/unstable/http/UrlParams.js.map +1 -1
  752. package/dist/unstable/http/index.d.ts +4 -0
  753. package/dist/unstable/http/index.d.ts.map +1 -1
  754. package/dist/unstable/http/index.js +4 -0
  755. package/dist/unstable/http/index.js.map +1 -1
  756. package/dist/unstable/http/internal/compression.js +8 -11
  757. package/dist/unstable/http/internal/compression.js.map +1 -1
  758. package/dist/unstable/http/internal/headers.d.ts +2 -0
  759. package/dist/unstable/http/internal/headers.d.ts.map +1 -0
  760. package/dist/unstable/http/internal/headers.js +5 -0
  761. package/dist/unstable/http/internal/headers.js.map +1 -0
  762. package/dist/unstable/http/internal/httpBody.js +8 -0
  763. package/dist/unstable/http/internal/httpBody.js.map +1 -1
  764. package/dist/unstable/http/internal/mimeTypes.d.ts +361 -0
  765. package/dist/unstable/http/internal/mimeTypes.d.ts.map +1 -0
  766. package/dist/unstable/http/internal/mimeTypes.js +363 -0
  767. package/dist/unstable/http/internal/mimeTypes.js.map +1 -0
  768. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +13 -0
  769. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  770. package/dist/unstable/httpapi/HttpApiBuilder.js +23 -6
  771. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  772. package/dist/unstable/httpapi/HttpApiClient.d.ts +4 -3
  773. package/dist/unstable/httpapi/HttpApiClient.d.ts.map +1 -1
  774. package/dist/unstable/httpapi/HttpApiClient.js +25 -13
  775. package/dist/unstable/httpapi/HttpApiClient.js.map +1 -1
  776. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts +8 -3
  777. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts.map +1 -1
  778. package/dist/unstable/httpapi/HttpApiEndpoint.js +9 -6
  779. package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
  780. package/dist/unstable/httpapi/HttpApiError.d.ts.map +1 -1
  781. package/dist/unstable/httpapi/HttpApiError.js +3 -1
  782. package/dist/unstable/httpapi/HttpApiError.js.map +1 -1
  783. package/dist/unstable/httpapi/HttpApiMiddleware.d.ts.map +1 -1
  784. package/dist/unstable/httpapi/HttpApiMiddleware.js +7 -5
  785. package/dist/unstable/httpapi/HttpApiMiddleware.js.map +1 -1
  786. package/dist/unstable/httpapi/HttpApiSchema.d.ts +12 -0
  787. package/dist/unstable/httpapi/HttpApiSchema.d.ts.map +1 -1
  788. package/dist/unstable/httpapi/HttpApiSchema.js +8 -10
  789. package/dist/unstable/httpapi/HttpApiSchema.js.map +1 -1
  790. package/dist/unstable/httpapi/HttpApiTest.d.ts.map +1 -1
  791. package/dist/unstable/httpapi/HttpApiTest.js +8 -2
  792. package/dist/unstable/httpapi/HttpApiTest.js.map +1 -1
  793. package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
  794. package/dist/unstable/httpapi/OpenApi.js +31 -20
  795. package/dist/unstable/httpapi/OpenApi.js.map +1 -1
  796. package/dist/unstable/net/IpInterface.d.ts +140 -0
  797. package/dist/unstable/net/IpInterface.d.ts.map +1 -0
  798. package/dist/unstable/net/IpInterface.js +149 -0
  799. package/dist/unstable/net/IpInterface.js.map +1 -0
  800. package/dist/unstable/net/IpNetwork.d.ts +227 -0
  801. package/dist/unstable/net/IpNetwork.d.ts.map +1 -0
  802. package/dist/unstable/net/IpNetwork.js +239 -0
  803. package/dist/unstable/net/IpNetwork.js.map +1 -0
  804. package/dist/unstable/net/NetAddress.d.ts +705 -0
  805. package/dist/unstable/net/NetAddress.d.ts.map +1 -0
  806. package/dist/unstable/net/NetAddress.js +956 -0
  807. package/dist/unstable/net/NetAddress.js.map +1 -0
  808. package/dist/unstable/net/index.d.ts +16 -0
  809. package/dist/unstable/net/index.d.ts.map +1 -0
  810. package/dist/unstable/net/index.js +17 -0
  811. package/dist/unstable/net/index.js.map +1 -0
  812. package/dist/unstable/observability/OtlpExporter.d.ts +1 -0
  813. package/dist/unstable/observability/OtlpExporter.d.ts.map +1 -1
  814. package/dist/unstable/observability/OtlpExporter.js +4 -6
  815. package/dist/unstable/observability/OtlpExporter.js.map +1 -1
  816. package/dist/unstable/observability/OtlpLogger.js +9 -9
  817. package/dist/unstable/observability/OtlpLogger.js.map +1 -1
  818. package/dist/unstable/observability/OtlpMetrics.d.ts.map +1 -1
  819. package/dist/unstable/observability/OtlpMetrics.js +9 -8
  820. package/dist/unstable/observability/OtlpMetrics.js.map +1 -1
  821. package/dist/unstable/observability/OtlpResource.d.ts.map +1 -1
  822. package/dist/unstable/observability/OtlpResource.js +2 -2
  823. package/dist/unstable/observability/OtlpResource.js.map +1 -1
  824. package/dist/unstable/observability/OtlpTracer.d.ts.map +1 -1
  825. package/dist/unstable/observability/OtlpTracer.js +82 -55
  826. package/dist/unstable/observability/OtlpTracer.js.map +1 -1
  827. package/dist/unstable/observability/internal/otlpEnv.d.ts +1 -1
  828. package/dist/unstable/observability/internal/otlpEnv.d.ts.map +1 -1
  829. package/dist/unstable/observability/internal/otlpEnv.js +5 -9
  830. package/dist/unstable/observability/internal/otlpEnv.js.map +1 -1
  831. package/dist/unstable/persistence/PersistedCache.js +1 -1
  832. package/dist/unstable/persistence/PersistedCache.js.map +1 -1
  833. package/dist/unstable/persistence/PersistedQueue.d.ts +114 -14
  834. package/dist/unstable/persistence/PersistedQueue.d.ts.map +1 -1
  835. package/dist/unstable/persistence/PersistedQueue.js +799 -234
  836. package/dist/unstable/persistence/PersistedQueue.js.map +1 -1
  837. package/dist/unstable/persistence/RateLimiter.d.ts +28 -15
  838. package/dist/unstable/persistence/RateLimiter.d.ts.map +1 -1
  839. package/dist/unstable/persistence/RateLimiter.js +33 -41
  840. package/dist/unstable/persistence/RateLimiter.js.map +1 -1
  841. package/dist/unstable/process/ChildProcess.d.ts +8 -2
  842. package/dist/unstable/process/ChildProcess.d.ts.map +1 -1
  843. package/dist/unstable/process/ChildProcess.js +7 -2
  844. package/dist/unstable/process/ChildProcess.js.map +1 -1
  845. package/dist/unstable/process/ChildProcessSpawner.d.ts +1 -1
  846. package/dist/unstable/process/ChildProcessSpawner.d.ts.map +1 -1
  847. package/dist/unstable/process/ChildProcessSpawner.js +1 -1
  848. package/dist/unstable/process/ChildProcessSpawner.js.map +1 -1
  849. package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
  850. package/dist/unstable/reactivity/Atom.js +6 -3
  851. package/dist/unstable/reactivity/Atom.js.map +1 -1
  852. package/dist/unstable/reactivity/AtomHttpApi.d.ts +3 -6
  853. package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
  854. package/dist/unstable/reactivity/AtomHttpApi.js +6 -3
  855. package/dist/unstable/reactivity/AtomHttpApi.js.map +1 -1
  856. package/dist/unstable/reactivity/AtomRegistry.js +2 -3
  857. package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
  858. package/dist/unstable/reactivity/AtomRpc.d.ts +2 -2
  859. package/dist/unstable/reactivity/AtomRpc.d.ts.map +1 -1
  860. package/dist/unstable/reactivity/AtomRpc.js +1 -1
  861. package/dist/unstable/reactivity/AtomRpc.js.map +1 -1
  862. package/dist/unstable/reactivity/Hydration.d.ts +6 -4
  863. package/dist/unstable/reactivity/Hydration.d.ts.map +1 -1
  864. package/dist/unstable/reactivity/Hydration.js +20 -6
  865. package/dist/unstable/reactivity/Hydration.js.map +1 -1
  866. package/dist/unstable/reactivity/Reactivity.d.ts +26 -26
  867. package/dist/unstable/reactivity/Reactivity.d.ts.map +1 -1
  868. package/dist/unstable/reactivity/Reactivity.js +10 -12
  869. package/dist/unstable/reactivity/Reactivity.js.map +1 -1
  870. package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
  871. package/dist/unstable/rpc/RpcClient.js +64 -64
  872. package/dist/unstable/rpc/RpcClient.js.map +1 -1
  873. package/dist/unstable/rpc/RpcClientError.d.ts +1 -1
  874. package/dist/unstable/rpc/RpcMessage.d.ts +3 -1
  875. package/dist/unstable/rpc/RpcMessage.d.ts.map +1 -1
  876. package/dist/unstable/rpc/RpcMessage.js +1 -1
  877. package/dist/unstable/rpc/RpcMessage.js.map +1 -1
  878. package/dist/unstable/rpc/RpcMiddleware.d.ts.map +1 -1
  879. package/dist/unstable/rpc/RpcMiddleware.js +7 -5
  880. package/dist/unstable/rpc/RpcMiddleware.js.map +1 -1
  881. package/dist/unstable/rpc/RpcSerialization.d.ts +5 -39
  882. package/dist/unstable/rpc/RpcSerialization.d.ts.map +1 -1
  883. package/dist/unstable/rpc/RpcSerialization.js +22 -115
  884. package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
  885. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  886. package/dist/unstable/rpc/RpcServer.js +104 -57
  887. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  888. package/dist/unstable/schema/Model.d.ts.map +1 -1
  889. package/dist/unstable/schema/Model.js +7 -6
  890. package/dist/unstable/schema/Model.js.map +1 -1
  891. package/dist/unstable/schema/VariantSchema.d.ts.map +1 -1
  892. package/dist/unstable/schema/VariantSchema.js +3 -0
  893. package/dist/unstable/schema/VariantSchema.js.map +1 -1
  894. package/dist/unstable/socket/Socket.d.ts +287 -102
  895. package/dist/unstable/socket/Socket.d.ts.map +1 -1
  896. package/dist/unstable/socket/Socket.js +408 -215
  897. package/dist/unstable/socket/Socket.js.map +1 -1
  898. package/dist/unstable/socket/SocketServer.d.ts +5 -32
  899. package/dist/unstable/socket/SocketServer.d.ts.map +1 -1
  900. package/dist/unstable/socket/SocketServer.js +3 -3
  901. package/dist/unstable/socket/SocketServer.js.map +1 -1
  902. package/dist/unstable/sql/SqlClient.d.ts +20 -0
  903. package/dist/unstable/sql/SqlClient.d.ts.map +1 -1
  904. package/dist/unstable/sql/SqlClient.js +14 -7
  905. package/dist/unstable/sql/SqlClient.js.map +1 -1
  906. package/dist/unstable/sql/SqlConnection.d.ts +15 -0
  907. package/dist/unstable/sql/SqlConnection.d.ts.map +1 -1
  908. package/dist/unstable/sql/SqlConnection.js.map +1 -1
  909. package/dist/unstable/sql/SqlModel.d.ts +1 -1
  910. package/dist/unstable/sql/SqlModel.d.ts.map +1 -1
  911. package/dist/unstable/sql/Statement.d.ts +13 -5
  912. package/dist/unstable/sql/Statement.d.ts.map +1 -1
  913. package/dist/unstable/sql/Statement.js +29 -15
  914. package/dist/unstable/sql/Statement.js.map +1 -1
  915. package/dist/unstable/workers/Transferable.js +1 -1
  916. package/dist/unstable/workers/Worker.d.ts.map +1 -1
  917. package/dist/unstable/workers/Worker.js +3 -1
  918. package/dist/unstable/workers/Worker.js.map +1 -1
  919. package/dist/unstable/workflow/Activity.d.ts +7 -0
  920. package/dist/unstable/workflow/Activity.d.ts.map +1 -1
  921. package/dist/unstable/workflow/Activity.js +14 -4
  922. package/dist/unstable/workflow/Activity.js.map +1 -1
  923. package/dist/unstable/workflow/DurableClock.js +1 -1
  924. package/dist/unstable/workflow/DurableClock.js.map +1 -1
  925. package/dist/unstable/workflow/DurableDeferred.d.ts +2 -2
  926. package/dist/unstable/workflow/DurableDeferred.d.ts.map +1 -1
  927. package/dist/unstable/workflow/DurableDeferred.js.map +1 -1
  928. package/dist/unstable/workflow/DurableQueue.d.ts +8 -0
  929. package/dist/unstable/workflow/DurableQueue.d.ts.map +1 -1
  930. package/dist/unstable/workflow/DurableQueue.js +8 -0
  931. package/dist/unstable/workflow/DurableQueue.js.map +1 -1
  932. package/dist/unstable/workflow/Workflow.d.ts +5 -0
  933. package/dist/unstable/workflow/Workflow.d.ts.map +1 -1
  934. package/dist/unstable/workflow/Workflow.js +70 -12
  935. package/dist/unstable/workflow/Workflow.js.map +1 -1
  936. package/dist/unstable/workflow/WorkflowEngine.d.ts +6 -0
  937. package/dist/unstable/workflow/WorkflowEngine.d.ts.map +1 -1
  938. package/dist/unstable/workflow/WorkflowEngine.js +11 -0
  939. package/dist/unstable/workflow/WorkflowEngine.js.map +1 -1
  940. package/package.json +7 -9
  941. package/src/Array.ts +118 -67
  942. package/src/BigDecimal.ts +50 -28
  943. package/src/ByteSize.ts +838 -0
  944. package/src/Cache.ts +17 -3
  945. package/src/Cause.ts +1 -1
  946. package/src/Channel.ts +183 -48
  947. package/src/Chunk.ts +65 -36
  948. package/src/Config.ts +237 -248
  949. package/src/ConfigProvider.ts +7 -8
  950. package/src/Context.ts +31 -0
  951. package/src/Cron.ts +2 -2
  952. package/src/Crypto.ts +3 -2
  953. package/src/Deferred.ts +7 -6
  954. package/src/Duration.ts +1 -1
  955. package/src/Effect.ts +226 -122
  956. package/src/Effectable.ts +78 -9
  957. package/src/Encoding.ts +101 -11
  958. package/src/Equal.ts +1 -1
  959. package/src/ExecutionPlan.ts +3 -0
  960. package/src/Fiber.ts +33 -8
  961. package/src/FiberHandle.ts +3 -3
  962. package/src/FiberMap.ts +19 -10
  963. package/src/FileSystem.ts +55 -230
  964. package/src/Formatter.ts +21 -3
  965. package/src/Graph.ts +55 -25
  966. package/src/Hash.ts +5 -16
  967. package/src/HashRing.ts +9 -2
  968. package/src/HashSet.ts +56 -0
  969. package/src/Iterable.ts +66 -41
  970. package/src/JsonPatch.ts +4 -4
  971. package/src/JsonPointer.ts +97 -4
  972. package/src/JsonSchema.ts +8 -26
  973. package/src/Layer.ts +59 -18
  974. package/src/LayerMap.ts +8 -6
  975. package/src/LayerRef.ts +7 -5
  976. package/src/Logger.ts +125 -18
  977. package/src/ManagedRuntime.ts +1 -1
  978. package/src/Match.ts +33 -14
  979. package/src/Metric.ts +24 -21
  980. package/src/MutableHashMap.ts +1 -1
  981. package/src/MutableHashSet.ts +1 -1
  982. package/src/MutableList.ts +18 -2
  983. package/src/Number.ts +7 -1
  984. package/src/Optic.ts +8 -4
  985. package/src/Option.ts +1 -1
  986. package/src/Order.ts +3 -1
  987. package/src/PartitionedSemaphore.ts +1 -2
  988. package/src/Path.ts +1 -1
  989. package/src/PlatformError.ts +1 -1
  990. package/src/Pool.ts +125 -8
  991. package/src/PrimaryKey.ts +1 -1
  992. package/src/PubSub.ts +32 -20
  993. package/src/Queue.ts +119 -28
  994. package/src/Random.ts +20 -6
  995. package/src/RcMap.ts +20 -13
  996. package/src/Redacted.ts +1 -1
  997. package/src/RequestResolver.ts +49 -26
  998. package/src/Result.ts +1 -1
  999. package/src/Scheduler.ts +21 -11
  1000. package/src/Schema.ts +4809 -6368
  1001. package/src/SchemaAST.ts +1031 -284
  1002. package/src/SchemaGetter.ts +75 -21
  1003. package/src/SchemaIssue.ts +259 -15
  1004. package/src/SchemaParser.ts +2 -12
  1005. package/src/SchemaRepresentation.ts +1593 -29
  1006. package/src/SchemaTransformation.ts +145 -22
  1007. package/src/ScopedCache.ts +10 -3
  1008. package/src/Sink.ts +31 -15
  1009. package/src/Stream.ts +294 -76
  1010. package/src/String.ts +20 -9
  1011. package/src/Struct.ts +8 -4
  1012. package/src/SynchronizedRef.ts +3 -5
  1013. package/src/Terminal.ts +3 -3
  1014. package/src/Tracer.ts +43 -14
  1015. package/src/Tuple.ts +3 -1
  1016. package/src/TxChunk.ts +1 -1
  1017. package/src/TxDeferred.ts +1 -1
  1018. package/src/TxHashMap.ts +1 -1
  1019. package/src/TxHashSet.ts +92 -11
  1020. package/src/TxPriorityQueue.ts +1 -1
  1021. package/src/TxPubSub.ts +1 -1
  1022. package/src/TxQueue.ts +22 -17
  1023. package/src/TxReentrantLock.ts +29 -1
  1024. package/src/TxRef.ts +1 -1
  1025. package/src/TxSemaphore.ts +1 -1
  1026. package/src/TxSubscriptionRef.ts +1 -1
  1027. package/src/Types.ts +40 -17
  1028. package/src/Utils.ts +3 -1
  1029. package/src/index.ts +5 -0
  1030. package/src/internal/arbitrary/model.ts +701 -0
  1031. package/src/internal/arbitrary/regexp.ts +855 -0
  1032. package/src/internal/arbitrary/runner.ts +608 -0
  1033. package/src/internal/arbitrary/schema.ts +1800 -0
  1034. package/src/internal/core.ts +48 -22
  1035. package/src/internal/count.ts +14 -0
  1036. package/src/internal/dateTime.ts +8 -10
  1037. package/src/internal/effect.ts +475 -295
  1038. package/src/internal/graph.ts +6 -1
  1039. package/src/internal/hashMap.ts +5 -3
  1040. package/src/internal/hashSet.ts +1 -1
  1041. package/src/internal/matcher.ts +34 -19
  1042. package/src/internal/metric.ts +1 -1
  1043. package/src/internal/option.ts +8 -6
  1044. package/src/internal/random.ts +20 -7
  1045. package/src/internal/rcRef.ts +10 -7
  1046. package/src/internal/references.ts +1 -0
  1047. package/src/internal/request.ts +12 -7
  1048. package/src/internal/result.ts +15 -11
  1049. package/src/internal/schema/annotations.ts +3 -3
  1050. package/src/internal/schema/fromJsonSchemaDocument.ts +14 -29
  1051. package/src/internal/schema/fromRepresentation.ts +5 -3
  1052. package/src/internal/schema/{schema.ts → make.ts} +0 -40
  1053. package/src/internal/schema/parser.ts +8 -0
  1054. package/src/internal/schema/standardSchema.ts +125 -0
  1055. package/src/internal/schema/toCodeDocument.ts +58 -44
  1056. package/src/internal/schema/toCodec.ts +312 -0
  1057. package/src/internal/schema/toDifferJsonPatch.ts +24 -0
  1058. package/src/internal/schema/toEncoderXml.ts +110 -0
  1059. package/src/internal/schema/toEquivalence.ts +76 -0
  1060. package/src/internal/schema/toFormatter.ts +230 -0
  1061. package/src/internal/schema/toIso.ts +20 -0
  1062. package/src/internal/schema/toJsonSchemaDocument.ts +125 -67
  1063. package/src/internal/schema/toRepresentation.ts +1 -1
  1064. package/src/internal/schemaError.ts +8 -0
  1065. package/src/internal/stream.ts +14 -6
  1066. package/src/internal/tracer.ts +4 -0
  1067. package/src/internal/trie.ts +5 -2
  1068. package/src/internal/uuid.ts +4 -0
  1069. package/src/testing/TestSchema.ts +36 -30
  1070. package/src/testing/index.ts +0 -5
  1071. package/src/unstable/ai/AiError.ts +69 -5
  1072. package/src/unstable/ai/AnthropicStructuredOutput.ts +4 -1
  1073. package/src/unstable/ai/Chat.ts +35 -36
  1074. package/src/unstable/ai/EmbeddingModel.ts +27 -15
  1075. package/src/unstable/ai/LanguageModel.ts +283 -100
  1076. package/src/unstable/ai/McpServer.ts +36 -20
  1077. package/src/unstable/ai/OpenAiStructuredOutput.ts +4 -1
  1078. package/src/unstable/ai/Prompt.ts +19 -4
  1079. package/src/unstable/ai/Response.ts +107 -189
  1080. package/src/unstable/ai/Tokenizer.ts +4 -5
  1081. package/src/unstable/ai/Tool.ts +58 -35
  1082. package/src/unstable/ai/Toolkit.ts +43 -37
  1083. package/src/unstable/ai/internal/structured-output.ts +5 -5
  1084. package/src/unstable/arbitrary/Arbitrary.ts +715 -0
  1085. package/src/unstable/arbitrary/index.ts +10 -0
  1086. package/src/unstable/cli/Argument.ts +111 -115
  1087. package/src/unstable/cli/CliError.ts +2 -0
  1088. package/src/unstable/cli/CliOutput.ts +40 -3
  1089. package/src/unstable/cli/Command.ts +74 -74
  1090. package/src/unstable/cli/Completions.ts +4 -4
  1091. package/src/unstable/cli/Flag.ts +158 -169
  1092. package/src/unstable/cli/GlobalFlag.ts +13 -13
  1093. package/src/unstable/cli/Param.ts +150 -159
  1094. package/src/unstable/cli/Primitive.ts +75 -76
  1095. package/src/unstable/cli/Prompt.ts +88 -87
  1096. package/src/unstable/cli/internal/completions/descriptor.ts +23 -11
  1097. package/src/unstable/cli/internal/completions/zsh.ts +2 -2
  1098. package/src/unstable/cli/internal/config.ts +4 -4
  1099. package/src/unstable/cli/internal/parser.ts +1 -1
  1100. package/src/unstable/cli/internal/wizard.ts +18 -13
  1101. package/src/unstable/cluster/ClusterSchema.ts +19 -1
  1102. package/src/unstable/cluster/ClusterWorkflowEngine.ts +66 -16
  1103. package/src/unstable/cluster/Entity.ts +3 -0
  1104. package/src/unstable/cluster/EntityProxyServer.ts +6 -2
  1105. package/src/unstable/cluster/Envelope.ts +3 -3
  1106. package/src/unstable/cluster/HttpRunner.ts +7 -2
  1107. package/src/unstable/cluster/MessageStorage.ts +62 -21
  1108. package/src/unstable/cluster/Reply.ts +2 -2
  1109. package/src/unstable/cluster/Sharding.ts +44 -19
  1110. package/src/unstable/cluster/ShardingConfig.ts +23 -23
  1111. package/src/unstable/cluster/SocketRunner.ts +5 -4
  1112. package/src/unstable/cluster/SqlMessageStorage.ts +12 -8
  1113. package/src/unstable/cluster/SqlRunnerStorage.ts +18 -10
  1114. package/src/unstable/cluster/internal/clusterAbandon.ts +14 -0
  1115. package/src/unstable/cluster/internal/entityManager.ts +12 -4
  1116. package/src/unstable/cluster/internal/interruptors.ts +58 -1
  1117. package/src/unstable/cluster/internal/resourceRef.ts +15 -7
  1118. package/src/unstable/devtools/DevToolsClient.ts +13 -3
  1119. package/src/unstable/encoding/Ini.ts +2 -0
  1120. package/src/unstable/encoding/SchemaBinary.ts +150 -44
  1121. package/src/unstable/encoding/Sse.ts +1 -1
  1122. package/src/unstable/encoding/Toml.ts +7 -5
  1123. package/src/unstable/encoding/Yaml.ts +18 -5
  1124. package/src/unstable/encoding/index.ts +0 -5
  1125. package/src/unstable/eventlog/Event.ts +7 -7
  1126. package/src/unstable/eventlog/EventJournal.ts +9 -9
  1127. package/src/unstable/eventlog/EventLog.ts +5 -5
  1128. package/src/unstable/eventlog/EventLogMessage.ts +25 -15
  1129. package/src/unstable/eventlog/EventLogRemote.ts +15 -4
  1130. package/src/unstable/eventlog/EventLogServerUnencrypted.ts +2 -2
  1131. package/src/unstable/eventlog/SqlEventJournal.ts +32 -9
  1132. package/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +11 -1
  1133. package/src/unstable/http/Cookies.ts +2 -102
  1134. package/src/unstable/http/FetchHttpClient.ts +1 -1
  1135. package/src/unstable/http/FindMyWay/internal/router.ts +139 -33
  1136. package/src/unstable/http/FindMyWay.ts +15 -5
  1137. package/src/unstable/http/Headers.ts +3 -48
  1138. package/src/unstable/http/HttpBody.ts +107 -36
  1139. package/src/unstable/http/HttpClient.ts +41 -30
  1140. package/src/unstable/http/HttpClientRequest.ts +32 -18
  1141. package/src/unstable/http/HttpClientResponse.ts +16 -3
  1142. package/src/unstable/http/HttpEffect.ts +145 -73
  1143. package/src/unstable/http/HttpIncomingMessage.ts +4 -4
  1144. package/src/unstable/http/HttpMiddleware.ts +43 -24
  1145. package/src/unstable/http/HttpPlatform.ts +62 -20
  1146. package/src/unstable/http/HttpRouter.ts +27 -16
  1147. package/src/unstable/http/HttpServer.ts +18 -57
  1148. package/src/unstable/http/HttpServerRequest.ts +17 -15
  1149. package/src/unstable/http/HttpServerRespondable.ts +1 -1
  1150. package/src/unstable/http/HttpServerResponse.ts +49 -40
  1151. package/src/unstable/http/HttpStaticServer.ts +30 -28
  1152. package/src/unstable/http/Mime.ts +76 -0
  1153. package/src/unstable/http/Multipart.ts +35 -18
  1154. package/src/unstable/http/MultipartParser/internal/multipart.ts +10 -3
  1155. package/src/unstable/http/MultipartParser.ts +4 -3
  1156. package/src/unstable/http/UrlParams.ts +3 -161
  1157. package/src/unstable/http/index.ts +5 -0
  1158. package/src/unstable/http/internal/compression.ts +11 -12
  1159. package/src/unstable/http/internal/headers.ts +7 -0
  1160. package/src/unstable/http/internal/httpBody.ts +9 -0
  1161. package/src/unstable/http/internal/mimeTypes.ts +395 -0
  1162. package/src/unstable/httpapi/HttpApiBuilder.ts +52 -6
  1163. package/src/unstable/httpapi/HttpApiClient.ts +44 -24
  1164. package/src/unstable/httpapi/HttpApiEndpoint.ts +24 -11
  1165. package/src/unstable/httpapi/HttpApiError.ts +3 -1
  1166. package/src/unstable/httpapi/HttpApiMiddleware.ts +7 -5
  1167. package/src/unstable/httpapi/HttpApiSchema.ts +14 -7
  1168. package/src/unstable/httpapi/HttpApiTest.ts +6 -1
  1169. package/src/unstable/httpapi/OpenApi.ts +32 -22
  1170. package/src/unstable/net/IpInterface.ts +242 -0
  1171. package/src/unstable/net/IpNetwork.ts +357 -0
  1172. package/src/unstable/net/NetAddress.ts +1271 -0
  1173. package/src/unstable/net/index.ts +20 -0
  1174. package/src/unstable/observability/OtlpExporter.ts +5 -6
  1175. package/src/unstable/observability/OtlpLogger.ts +9 -9
  1176. package/src/unstable/observability/OtlpMetrics.ts +9 -8
  1177. package/src/unstable/observability/OtlpResource.ts +5 -4
  1178. package/src/unstable/observability/OtlpTracer.ts +103 -81
  1179. package/src/unstable/observability/internal/otlpEnv.ts +10 -13
  1180. package/src/unstable/persistence/PersistedCache.ts +1 -1
  1181. package/src/unstable/persistence/PersistedQueue.ts +1100 -291
  1182. package/src/unstable/persistence/RateLimiter.ts +64 -58
  1183. package/src/unstable/process/ChildProcess.ts +15 -4
  1184. package/src/unstable/process/ChildProcessSpawner.ts +1 -1
  1185. package/src/unstable/reactivity/Atom.ts +6 -3
  1186. package/src/unstable/reactivity/AtomHttpApi.ts +21 -10
  1187. package/src/unstable/reactivity/AtomRegistry.ts +2 -4
  1188. package/src/unstable/reactivity/AtomRpc.ts +6 -5
  1189. package/src/unstable/reactivity/Hydration.ts +26 -7
  1190. package/src/unstable/reactivity/Reactivity.ts +49 -36
  1191. package/src/unstable/rpc/RpcClient.ts +62 -59
  1192. package/src/unstable/rpc/RpcMessage.ts +3 -1
  1193. package/src/unstable/rpc/RpcMiddleware.ts +7 -5
  1194. package/src/unstable/rpc/RpcSerialization.ts +27 -117
  1195. package/src/unstable/rpc/RpcServer.ts +143 -80
  1196. package/src/unstable/schema/Model.ts +8 -7
  1197. package/src/unstable/schema/VariantSchema.ts +3 -0
  1198. package/src/unstable/socket/Socket.ts +714 -383
  1199. package/src/unstable/socket/SocketServer.ts +5 -35
  1200. package/src/unstable/sql/SqlClient.ts +42 -7
  1201. package/src/unstable/sql/SqlConnection.ts +18 -0
  1202. package/src/unstable/sql/SqlModel.ts +1 -1
  1203. package/src/unstable/sql/Statement.ts +46 -20
  1204. package/src/unstable/workers/Transferable.ts +1 -1
  1205. package/src/unstable/workers/Worker.ts +3 -1
  1206. package/src/unstable/workflow/Activity.ts +20 -9
  1207. package/src/unstable/workflow/DurableClock.ts +1 -1
  1208. package/src/unstable/workflow/DurableDeferred.ts +6 -0
  1209. package/src/unstable/workflow/DurableQueue.ts +8 -0
  1210. package/src/unstable/workflow/Workflow.ts +105 -28
  1211. package/src/unstable/workflow/WorkflowEngine.ts +22 -0
  1212. package/dist/internal/schema/schema.d.ts.map +0 -1
  1213. package/dist/internal/schema/schema.js.map +0 -1
  1214. package/dist/internal/schema/toArbitrary.d.ts +0 -2
  1215. package/dist/internal/schema/toArbitrary.d.ts.map +0 -1
  1216. package/dist/internal/schema/toArbitrary.js +0 -705
  1217. package/dist/internal/schema/toArbitrary.js.map +0 -1
  1218. package/dist/testing/FastCheck.d.ts +0 -90
  1219. package/dist/testing/FastCheck.d.ts.map +0 -1
  1220. package/dist/testing/FastCheck.js +0 -90
  1221. package/dist/testing/FastCheck.js.map +0 -1
  1222. package/dist/unstable/encoding/Msgpack.d.ts +0 -185
  1223. package/dist/unstable/encoding/Msgpack.d.ts.map +0 -1
  1224. package/dist/unstable/encoding/Msgpack.js +0 -227
  1225. package/dist/unstable/encoding/Msgpack.js.map +0 -1
  1226. package/src/internal/schema/toArbitrary.ts +0 -843
  1227. package/src/testing/FastCheck.ts +0 -90
  1228. package/src/unstable/encoding/Msgpack.ts +0 -432
  1229. /package/dist/internal/{schema → arbitrary}/schema.d.ts +0 -0
@@ -8,20 +8,23 @@
8
8
  * This module includes a queue factory, store service, id-based de-duplication,
9
9
  * retry handling, and in-memory, Redis, and SQL-backed store layers.
10
10
  *
11
+ * Delivery is at-least-once: a crash between handler success and the
12
+ * acknowledgement redelivers the element, so handlers must be idempotent.
13
+ *
11
14
  * @since 4.0.0
12
15
  */
13
- import type * as Arr from "../../Array.ts"
14
16
  import * as Cause from "../../Cause.ts"
17
+ import * as Clock from "../../Clock.ts"
15
18
  import * as Context from "../../Context.ts"
16
- import * as Data from "../../Data.ts"
17
19
  import * as Duration from "../../Duration.ts"
18
20
  import * as Effect from "../../Effect.ts"
19
- import * as Exit from "../../Exit.ts"
20
21
  import { flow } from "../../Function.ts"
21
- import * as Iterable from "../../Iterable.ts"
22
+ import { sqlCleanupBatchSize } from "../../internal/persistence.ts"
22
23
  import * as Latch from "../../Latch.ts"
23
24
  import * as Layer from "../../Layer.ts"
24
25
  import * as MutableRef from "../../MutableRef.ts"
26
+ import * as Predicate from "../../Predicate.ts"
27
+ import * as Pull from "../../Pull.ts"
25
28
  import * as Queue from "../../Queue.ts"
26
29
  import * as RcMap from "../../RcMap.ts"
27
30
  import * as Schedule from "../../Schedule.ts"
@@ -54,8 +57,12 @@ export type TypeId = "~effect/persistence/PersistedQueue"
54
57
  * **Details**
55
58
  *
56
59
  * `offer` enqueues values by id, and `take` processes one value at a time,
57
- * marking it complete on success or retrying it until the maximum attempts is
58
- * reached.
60
+ * marking it complete on success or retrying it with the queue's retry
61
+ * schedule until the maximum attempts is reached, after which it is marked as
62
+ * failed.
63
+ *
64
+ * Delivery is at-least-once: a crash between handler success and the
65
+ * acknowledgement redelivers the element, so handlers must be idempotent.
59
66
  *
60
67
  * @category models
61
68
  * @since 4.0.0
@@ -69,7 +76,8 @@ export interface PersistedQueue<in out A, out R = never> {
69
76
  * **Details**
70
77
  *
71
78
  * If an element with the same id already exists in the queue, it will not be
72
- * added again.
79
+ * added again. De-duplication survives completion until the id is removed by
80
+ * `layerCleanup`.
73
81
  */
74
82
  readonly offer: (value: A, options?: {
75
83
  readonly id: string | undefined
@@ -82,18 +90,23 @@ export interface PersistedQueue<in out A, out R = never> {
82
90
  * **Details**
83
91
  *
84
92
  * If the returned effect succeeds, the element is marked as processed;
85
- * otherwise it will be retried according to the provided options. By default,
86
- * max attempts is set to 10.
93
+ * otherwise it will be retried with the queue's retry schedule until the
94
+ * maximum attempts is reached, after which it is marked as failed.
95
+ *
96
+ * An attempt is counted when the element is claimed, so `attempts` in the
97
+ * handler metadata is 1-based ("this is attempt 3"). A handler crash that
98
+ * takes down the process still consumes an attempt.
99
+ *
100
+ * Elements that fail to decode with the queue's schema are marked as failed
101
+ * immediately and the next element is taken instead, so schema decode errors
102
+ * never surface from `take`.
87
103
  */
88
104
  readonly take: <XA, XE, XR>(
89
105
  f: (value: A, metadata: {
90
106
  readonly id: string
91
107
  readonly attempts: number
92
- }) => Effect.Effect<XA, XE, XR>,
93
- options?: {
94
- readonly maxAttempts?: number | undefined
95
- }
96
- ) => Effect.Effect<XA, XE | PersistedQueueError | Schema.SchemaError, R | XR>
108
+ }) => Effect.Effect<XA, XE, XR>
109
+ ) => Effect.Effect<XA, XE | PersistedQueueError, R | XR>
97
110
  }
98
111
 
99
112
  /**
@@ -108,6 +121,8 @@ export class PersistedQueueFactory extends Context.Service<
108
121
  readonly make: <S extends Schema.Constraint>(options: {
109
122
  readonly name: string
110
123
  readonly schema: S
124
+ readonly maxAttempts?: number | undefined
125
+ readonly retrySchedule?: Schedule.Schedule<any, number> | undefined
111
126
  }) => Effect.Effect<PersistedQueue<S["Type"], S["EncodingServices"] | S["DecodingServices"]>>
112
127
  }
113
128
  >()("effect/persistence/PersistedQueue/PersistedQueueFactory") {}
@@ -116,18 +131,65 @@ export class PersistedQueueFactory extends Context.Service<
116
131
  * Accesses `PersistedQueueFactory` to create a named persisted queue for a
117
132
  * schema.
118
133
  *
134
+ * **Details**
135
+ *
136
+ * `maxAttempts` defaults to 10. `retrySchedule` controls the delay before a
137
+ * failed element becomes visible again, and defaults to an exponential delay
138
+ * starting at 1 second and capped at 5 minutes.
139
+ *
140
+ * The schedule's state is the element's persisted attempt count. On each
141
+ * failure the schedule is replayed up to the current attempt, so delays keep
142
+ * progressing even when consecutive retries run in different processes. The
143
+ * schedule input is the attempt number.
144
+ *
145
+ * Replay simulates elapsed time from the sum of the computed delays.
146
+ * Attempt-driven schedules are therefore exact, while wall-clock-anchored
147
+ * schedules observe this idealized time. In particular,
148
+ * `Schedule.upTo({ duration })` caps the summed delays rather than real time
149
+ * since the original failure.
150
+ *
119
151
  * @category accessors
120
152
  * @since 4.0.0
121
153
  */
122
154
  export const make = <S extends Schema.Constraint>(options: {
123
155
  readonly name: string
124
156
  readonly schema: S
157
+ readonly maxAttempts?: number | undefined
158
+ readonly retrySchedule?: Schedule.Schedule<any, number> | undefined
125
159
  }): Effect.Effect<
126
160
  PersistedQueue<S["Type"], S["EncodingServices"] | S["DecodingServices"]>,
127
161
  never,
128
162
  PersistedQueueFactory
129
163
  > => PersistedQueueFactory.use((factory) => factory.make(options))
130
164
 
165
+ const defaultRetrySchedule = Schedule.min([
166
+ Schedule.exponential("1 second"),
167
+ Schedule.spaced("5 minutes")
168
+ ])
169
+
170
+ // The persisted attempt count is the schedule state: consecutive retries of an
171
+ // element can run in different processes, so a fresh schedule step is replayed
172
+ // up to the given attempt on every call. Delays therefore depend only on the
173
+ // attempt count, with elapsed time simulated from the summed delays.
174
+ const retryDelay = Effect.fnUntraced(function*(
175
+ schedule: Schedule.Schedule<any, number>,
176
+ attempts: number
177
+ ): Effect.fn.Return<Duration.Duration> {
178
+ const step = yield* Schedule.toStep(schedule)
179
+ let now = 0
180
+ let delay = Duration.zero
181
+ for (let i = 0; i < attempts; i++) {
182
+ const result = yield* Pull.catchDone(step(now, i + 1), () => Effect.undefined)
183
+ if (result === undefined) {
184
+ // the schedule is done, keep using its final delay
185
+ break
186
+ }
187
+ delay = result[1]
188
+ now += Duration.toMillis(delay)
189
+ }
190
+ return delay
191
+ })
192
+
131
193
  /**
132
194
  * Creates a `PersistedQueueFactory` from the current `PersistedQueueStore`.
133
195
  *
@@ -147,10 +209,18 @@ export const makeFactory = Effect.gen(function*() {
147
209
  make<S extends Schema.Constraint>(options: {
148
210
  readonly name: string
149
211
  readonly schema: S
212
+ readonly maxAttempts?: number | undefined
213
+ readonly retrySchedule?: Schedule.Schedule<any, number> | undefined
150
214
  }) {
151
215
  const jsonSchema = Schema.toCodecJson(options.schema)
152
216
  const encodeUnknown = Schema.encodeUnknownEffect(jsonSchema)
153
217
  const decodeUnknown = Schema.decodeUnknownEffect(jsonSchema)
218
+ const retrySchedule = options.retrySchedule ?? defaultRetrySchedule
219
+ const takeOptions = {
220
+ name: options.name,
221
+ maxAttempts: options.maxAttempts ?? 10,
222
+ retryDelay: (attempts: number) => retryDelay(retrySchedule, attempts)
223
+ }
154
224
 
155
225
  return Effect.succeed<PersistedQueue<S["Type"], S["EncodingServices"] | S["DecodingServices"]>>({
156
226
  [TypeId]: TypeId,
@@ -170,15 +240,38 @@ export const makeFactory = Effect.gen(function*() {
170
240
  )
171
241
  }
172
242
  ),
173
- take: (f, opts) =>
174
- Effect.scopedWith(Effect.fnUntraced(function*(scope) {
175
- const item = yield* store.take({
176
- name: options.name,
177
- maxAttempts: opts?.maxAttempts ?? 10
178
- }).pipe(Scope.provide(scope))
179
- const decoded = yield* decodeUnknown(item.element)
180
- return yield* f(decoded, { id: item.id, attempts: item.attempts })
181
- }))
243
+ take: <XA, XE, XR>(
244
+ f: (value: S["Type"], metadata: {
245
+ readonly id: string
246
+ readonly attempts: number
247
+ }) => Effect.Effect<XA, XE, XR>
248
+ ) => {
249
+ const loop: Effect.Effect<XA, any, any> = Effect.scopedWith((scope) =>
250
+ store.take(takeOptions).pipe(
251
+ Scope.provide(scope),
252
+ Effect.flatMap((item) =>
253
+ decodeUnknown(item.element).pipe(
254
+ Effect.catchCause((cause): Effect.Effect<never, DeadLetter> =>
255
+ Cause.hasInterruptsOnly(cause)
256
+ ? Effect.failCause(cause as Cause.Cause<never>)
257
+ : Effect.fail(new DeadLetter(cause))
258
+ ),
259
+ Effect.flatMap((value) => f(value as S["Type"], { id: item.id, attempts: item.attempts }))
260
+ )
261
+ )
262
+ )
263
+ ).pipe(
264
+ Effect.catchIf(
265
+ isDeadLetter,
266
+ (): Effect.Effect<XA, any, any> => loop
267
+ )
268
+ )
269
+ return loop as Effect.Effect<
270
+ XA,
271
+ XE | PersistedQueueError,
272
+ XR | S["EncodingServices"] | S["DecodingServices"]
273
+ >
274
+ }
182
275
  })
183
276
  }
184
277
  })
@@ -196,13 +289,55 @@ export const layer: Layer.Layer<
196
289
  PersistedQueueStore
197
290
  > = Layer.effect(PersistedQueueFactory, makeFactory)
198
291
 
292
+ /**
293
+ * Runs `PersistedQueueStore.cleanup` on a schedule.
294
+ *
295
+ * **Details**
296
+ *
297
+ * Completed elements are retained for `timeToLive` (default 30 days) so
298
+ * offer de-duplication keeps working across replays, then removed. Failed
299
+ * elements are the dead-letter record and are kept forever unless
300
+ * `failedTimeToLive` is set.
301
+ *
302
+ * Run this layer in one instance of a deployment rather than on every worker;
303
+ * racing instances are harmless since deletes are idempotent, but the work is
304
+ * redundant.
305
+ *
306
+ * @category layers
307
+ * @since 4.0.0
308
+ */
309
+ export const layerCleanup = (options?: {
310
+ readonly interval?: Duration.Input | undefined
311
+ readonly timeToLive?: Duration.Input | undefined
312
+ readonly failedTimeToLive?: Duration.Input | undefined
313
+ }): Layer.Layer<never, never, PersistedQueueStore> =>
314
+ Layer.effectDiscard(Effect.gen(function*() {
315
+ const store = yield* PersistedQueueStore
316
+ const cleanupOptions = {
317
+ timeToLive: Duration.fromInputUnsafe(options?.timeToLive ?? Duration.days(30)),
318
+ failedTimeToLive: options?.failedTimeToLive === undefined
319
+ ? undefined
320
+ : Duration.fromInputUnsafe(options.failedTimeToLive)
321
+ }
322
+ yield* store.cleanup(cleanupOptions).pipe(
323
+ Effect.catchCause((cause) => Effect.logWarning("Failed to clean up persisted queue", cause)),
324
+ Effect.repeat(Schedule.spaced(options?.interval ?? Duration.hours(1))),
325
+ Effect.interruptible,
326
+ Effect.forkScoped,
327
+ Effect.annotateLogs({
328
+ module: "effect/persistence/PersistedQueue",
329
+ fiber: "cleanup"
330
+ })
331
+ )
332
+ }))
333
+
199
334
  /**
200
335
  * Runtime type identifier for `PersistedQueueError`.
201
336
  *
202
337
  * @category type IDs
203
338
  * @since 4.0.0
204
339
  */
205
- export const ErrorTypeId: ErrorTypeId = "~@effect/experimental/PersistedQueue/PersistedQueueError"
340
+ export const ErrorTypeId: ErrorTypeId = "~effect/persistence/PersistedQueue/PersistedQueueError"
206
341
 
207
342
  /**
208
343
  * Type-level identifier used to brand `PersistedQueueError` values.
@@ -210,7 +345,7 @@ export const ErrorTypeId: ErrorTypeId = "~@effect/experimental/PersistedQueue/Pe
210
345
  * @category type IDs
211
346
  * @since 4.0.0
212
347
  */
213
- export type ErrorTypeId = "~@effect/experimental/PersistedQueue/PersistedQueueError"
348
+ export type ErrorTypeId = "~effect/persistence/PersistedQueue/PersistedQueueError"
214
349
 
215
350
  /**
216
351
  * Error raised by persisted queue store operations.
@@ -233,6 +368,64 @@ export class PersistedQueueError extends Schema.Error<PersistedQueueError>(
233
368
  readonly [ErrorTypeId]: ErrorTypeId = ErrorTypeId
234
369
  }
235
370
 
371
+ // Local per-queue state shared between offer, take, and the mailbox lookup in
372
+ // the Redis and SQL stores: take records maxAttempts here before the mailbox
373
+ // is created, and offer opens the nudge latch so local pollers skip the poll
374
+ // interval.
375
+ type QueueState = {
376
+ maxAttempts: number
377
+ readonly nudge: Latch.Latch
378
+ }
379
+
380
+ const makeQueueStates = (): {
381
+ readonly peek: (name: string) => QueueState | undefined
382
+ readonly get: (name: string) => QueueState
383
+ } => {
384
+ const states = new Map<string, QueueState>()
385
+ return {
386
+ peek: (name) => states.get(name),
387
+ get: (name) => {
388
+ let state = states.get(name)
389
+ if (state === undefined) {
390
+ // the placeholder is overwritten by take before any mailbox reads it,
391
+ // and is kept int32-safe for SQL parameters
392
+ state = { maxAttempts: 2147483647, nudge: Latch.makeUnsafe(false) }
393
+ states.set(name, state)
394
+ }
395
+ return state
396
+ }
397
+ }
398
+ }
399
+
400
+ const makeAckRetrySchedule = (lockExpiration: Duration.Input) =>
401
+ Schedule.min([
402
+ Schedule.exponential(200, 1.5),
403
+ Schedule.spaced(5000)
404
+ ]).pipe(Schedule.upTo({ duration: lockExpiration }))
405
+
406
+ // Internal signal used by the factory to tell a store that a taken element
407
+ // cannot ever be processed (its stored payload fails to decode) and must be
408
+ // dead-lettered instead of retried.
409
+ class DeadLetter {
410
+ readonly _tag = "~effect/persistence/PersistedQueue/DeadLetter"
411
+ readonly cause: Cause.Cause<unknown>
412
+ constructor(cause: Cause.Cause<unknown>) {
413
+ this.cause = cause
414
+ }
415
+ }
416
+
417
+ const isDeadLetter = (u: unknown): u is DeadLetter =>
418
+ Predicate.isTagged(u, "~effect/persistence/PersistedQueue/DeadLetter")
419
+
420
+ const deadLetterFromCause = (cause: Cause.Cause<unknown>): DeadLetter | undefined => {
421
+ for (const reason of cause.reasons) {
422
+ if (Cause.isFailReason(reason) && isDeadLetter(reason.error)) {
423
+ return reason.error
424
+ }
425
+ }
426
+ return undefined
427
+ }
428
+
236
429
  /**
237
430
  * Defines the low-level backing store service used by `PersistedQueue`.
238
431
  *
@@ -246,6 +439,12 @@ export class PersistedQueueError extends Schema.Error<PersistedQueueError>(
246
439
  * The store persists offered elements and returns taken elements in a scope so
247
440
  * the finalizer can complete or retry them based on the processing exit.
248
441
  *
442
+ * Claiming an element counts an attempt, so the `attempts` returned by `take`
443
+ * is 1-based. When the take scope closes with a success the element is marked
444
+ * completed; a failure retries it according to `retryDelay` or marks it failed
445
+ * once `maxAttempts` is exhausted; an interruption releases it without
446
+ * counting the attempt.
447
+ *
249
448
  * @category services
250
449
  * @since 4.0.0
251
450
  */
@@ -264,6 +463,7 @@ export class PersistedQueueStore extends Context.Service<
264
463
  readonly take: (options: {
265
464
  readonly name: string
266
465
  readonly maxAttempts: number
466
+ readonly retryDelay: (attempts: number) => Effect.Effect<Duration.Duration>
267
467
  }) => Effect.Effect<
268
468
  {
269
469
  readonly id: string
@@ -273,6 +473,16 @@ export class PersistedQueueStore extends Context.Service<
273
473
  PersistedQueueError,
274
474
  Scope.Scope
275
475
  >
476
+
477
+ /**
478
+ * Removes completed elements older than `timeToLive`, together with their
479
+ * de-duplication records. Failed elements are removed only when
480
+ * `failedTimeToLive` is provided.
481
+ */
482
+ readonly cleanup: (options: {
483
+ readonly timeToLive: Duration.Duration
484
+ readonly failedTimeToLive: Duration.Duration | undefined
485
+ }) => Effect.Effect<void, PersistedQueueError>
276
486
  }
277
487
  >()("effect/persistence/PersistedQueue/PersistedQueueStore") {}
278
488
 
@@ -281,83 +491,156 @@ export class PersistedQueueStore extends Context.Service<
281
491
  *
282
492
  * **Details**
283
493
  *
284
- * The store is process-local and volatile; failed takes are requeued until the
285
- * configured maximum attempts is reached.
494
+ * The store is process-local and volatile; failed takes are requeued with the
495
+ * queue's retry schedule until the configured maximum attempts is reached,
496
+ * after which the element is marked as failed.
286
497
  *
287
498
  * @category layers
288
499
  * @since 4.0.0
289
500
  */
290
501
  export const layerStoreMemory: Layer.Layer<
291
502
  PersistedQueueStore
292
- > = Layer.sync(PersistedQueueStore, () => {
293
- type Entry = {
294
- readonly id: string
295
- attempts: number
296
- readonly element: unknown
297
- }
298
- const queues = new Map<string, {
299
- latch: Latch.Latch
300
- ids: Set<string>
301
- items: Set<Entry>
302
- }>()
303
- const getOrCreateQueue = (name: string) => {
304
- let queue = queues.get(name)
305
- if (!queue) {
306
- queue = {
307
- latch: Latch.makeUnsafe(false),
308
- ids: new Set(),
309
- items: new Set()
503
+ > = Layer.effect(
504
+ PersistedQueueStore,
505
+ Effect.gen(function*() {
506
+ const clock = yield* Clock.Clock
507
+ type Entry = {
508
+ readonly id: string
509
+ readonly element: unknown
510
+ attempts: number
511
+ state: "pending" | "processing" | "completed" | "failed"
512
+ visibleAt: number
513
+ stateChangedAt: number
514
+ }
515
+ const queues = new Map<string, {
516
+ latch: Latch.Latch
517
+ // all entries ever offered, for de-duplication; `pending` holds the
518
+ // deliverable subset so take does not scan the completed backlog
519
+ entries: Map<string, Entry>
520
+ pending: Set<Entry>
521
+ }>()
522
+ const getOrCreateQueue = (name: string) => {
523
+ let queue = queues.get(name)
524
+ if (!queue) {
525
+ queue = {
526
+ latch: Latch.makeUnsafe(false),
527
+ entries: new Map(),
528
+ pending: new Set()
529
+ }
530
+ queues.set(name, queue)
310
531
  }
311
- queues.set(name, queue)
532
+ return queue
312
533
  }
313
- return queue
314
- }
315
534
 
316
- return PersistedQueueStore.of({
317
- offer: (options) =>
318
- Effect.sync(() => {
535
+ return PersistedQueueStore.of({
536
+ offer: (options) =>
537
+ Effect.sync(() => {
538
+ const now = clock.currentTimeMillisUnsafe()
539
+ const queue = getOrCreateQueue(options.name)
540
+ if (queue.entries.has(options.id)) return
541
+ const entry: Entry = {
542
+ id: options.id,
543
+ element: options.element,
544
+ attempts: 0,
545
+ state: "pending",
546
+ visibleAt: now,
547
+ stateChangedAt: now
548
+ }
549
+ queue.entries.set(options.id, entry)
550
+ queue.pending.add(entry)
551
+ queue.latch.openUnsafe()
552
+ }),
553
+ take: Effect.fnUntraced(function*(options) {
319
554
  const queue = getOrCreateQueue(options.name)
320
- if (queue.ids.has(options.id)) return
321
- queue.ids.add(options.id)
322
- queue.items.add({ id: options.id, attempts: 0, element: options.element })
323
- queue.latch.openUnsafe()
324
- }),
325
- take: Effect.fnUntraced(function*(options) {
326
- const queue = getOrCreateQueue(options.name)
327
- while (true) {
328
- yield* queue.latch.await
329
- const item = Iterable.headUnsafe(queue.items)
330
- queue.items.delete(item)
331
- if (queue.items.size === 0) {
555
+ while (true) {
556
+ // close before scanning so a mutation after the scan reopens the latch
332
557
  queue.latch.closeUnsafe()
333
- }
334
- yield* Effect.addFinalizer((exit) => {
335
- if (exit._tag === "Success") {
336
- return Effect.void
337
- } else if (!Exit.hasInterrupts(exit)) {
338
- item.attempts += 1
558
+ const now = clock.currentTimeMillisUnsafe()
559
+ let item: Entry | undefined
560
+ let nextVisibleAt = Infinity
561
+ for (const entry of queue.pending) {
562
+ if (entry.attempts >= options.maxAttempts) continue
563
+ if (entry.visibleAt <= now) {
564
+ item = entry
565
+ break
566
+ }
567
+ nextVisibleAt = Math.min(nextVisibleAt, entry.visibleAt)
339
568
  }
340
- if (item.attempts >= options.maxAttempts) {
341
- return Effect.void
569
+ if (item === undefined) {
570
+ yield* nextVisibleAt === Infinity
571
+ ? queue.latch.await
572
+ : Effect.race(queue.latch.await, Effect.sleep(Duration.millis(nextVisibleAt - now)))
573
+ continue
342
574
  }
343
- queue.items.add(item)
575
+ const entry = item
576
+ entry.state = "processing"
577
+ entry.attempts += 1
578
+ queue.pending.delete(entry)
344
579
  queue.latch.openUnsafe()
345
- return Effect.void
580
+ yield* Effect.addFinalizer(
581
+ Effect.fnUntraced(function*(exit) {
582
+ const now = clock.currentTimeMillisUnsafe()
583
+ if (exit._tag === "Success") {
584
+ entry.state = "completed"
585
+ entry.stateChangedAt = now
586
+ return
587
+ }
588
+ const deadLetter = deadLetterFromCause(exit.cause)
589
+ if (deadLetter !== undefined) {
590
+ entry.state = "failed"
591
+ entry.stateChangedAt = now
592
+ return
593
+ }
594
+ if (Cause.hasInterruptsOnly(exit.cause)) {
595
+ entry.attempts -= 1
596
+ } else if (entry.attempts >= options.maxAttempts) {
597
+ entry.state = "failed"
598
+ entry.stateChangedAt = now
599
+ return
600
+ } else {
601
+ entry.visibleAt = now + Duration.toMillis(yield* options.retryDelay(entry.attempts))
602
+ }
603
+ entry.state = "pending"
604
+ queue.pending.add(entry)
605
+ queue.latch.openUnsafe()
606
+ })
607
+ )
608
+ return { id: entry.id, attempts: entry.attempts, element: entry.element }
609
+ }
610
+ }),
611
+ cleanup: (options) =>
612
+ Effect.sync(() => {
613
+ const now = clock.currentTimeMillisUnsafe()
614
+ const completedCutoff = now - Duration.toMillis(options.timeToLive)
615
+ const failedCutoff = options.failedTimeToLive === undefined
616
+ ? undefined
617
+ : now - Duration.toMillis(options.failedTimeToLive)
618
+ for (const queue of queues.values()) {
619
+ for (const [id, entry] of queue.entries) {
620
+ const cutoff = entry.state === "completed"
621
+ ? completedCutoff
622
+ : entry.state === "failed"
623
+ ? failedCutoff
624
+ : undefined
625
+ if (cutoff !== undefined && entry.stateChangedAt <= cutoff) {
626
+ queue.entries.delete(id)
627
+ }
628
+ }
629
+ }
346
630
  })
347
- return item
348
- }
349
631
  })
350
632
  })
351
- })
633
+ )
352
634
 
353
635
  /**
354
636
  * Creates a Redis-backed `PersistedQueueStore`.
355
637
  *
356
638
  * **Details**
357
639
  *
358
- * The store uses Redis lists and hashes with worker locks, periodically
359
- * refreshes locks while items are being processed, and moves exhausted items
360
- * to a failed queue.
640
+ * The store uses Redis lists, hashes, and sorted sets with worker locks,
641
+ * periodically refreshes locks while items are being processed, delays retried
642
+ * items with the queue's retry schedule, and moves exhausted items to a failed
643
+ * queue.
361
644
  *
362
645
  * @category constructors
363
646
  * @since 4.0.0
@@ -371,6 +654,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
371
654
  }
372
655
  ) {
373
656
  const redis = yield* Redis.Redis
657
+ const clock = yield* Clock.Clock
374
658
 
375
659
  const pollInterval = Duration.max(
376
660
  options?.pollInterval ? Duration.fromInputUnsafe(options.pollInterval) : Duration.seconds(1),
@@ -389,35 +673,47 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
389
673
  1
390
674
  )
391
675
  const prefix = options?.prefix ?? "effectq:"
392
- const keyQueue = (name: string) => `${prefix}${name}`
393
676
  const keyLock = (id: string) => `${prefix}${id}:lock`
394
- const keyPending = (name: string) => `${prefix}${name}:pending`
395
- const keyFailed = (name: string) => `${prefix}${name}:failed`
677
+ const keysFor = (name: string) => ({
678
+ queue: `${prefix}${name}`,
679
+ pending: `${prefix}${name}:pending`,
680
+ failed: `${prefix}${name}:failed`,
681
+ delayed: `${prefix}${name}:delayed`,
682
+ attempts: `${prefix}${name}:attempts`,
683
+ ids: `${prefix}${name}:ids`
684
+ })
396
685
  const workerId = crypto.randomUUID()
397
686
 
687
+ const ackRetrySchedule = makeAckRetrySchedule(lockExpirationMillis)
688
+
398
689
  type Element = {
399
690
  readonly id: string
400
691
  readonly element: unknown
401
- attempts: number
402
- lastFailure?: string
692
+ readonly attempts: number
693
+ // the raw wire payload, so requeue and retry do not re-stringify it
694
+ readonly payload: string
403
695
  }
404
696
 
405
697
  const requeue = redis.eval(requeueRedis)
406
698
  const complete = redis.eval(completeRedis)
407
699
  const failed = redis.eval(failedRedis)
700
+ const retry = redis.eval(retryRedis)
408
701
  const resetQueue = redis.eval(resetQueueRedis)
409
702
  const offer = redis.eval(offerRedis)
410
703
  const take = redis.eval(takeRedis)
411
704
  const expireAll = redis.eval(expireAllRedis)
705
+ const trimFailed = redis.eval(trimFailedRedis)
706
+
707
+ const queueStates = makeQueueStates()
412
708
 
413
709
  const queues = yield* RcMap.make({
414
710
  lookup: Effect.fnUntraced(function*(name: string) {
415
- const queueKey = keyQueue(name)
416
- const pendingKey = keyPending(name)
711
+ const keys = keysFor(name)
417
712
  const queue = yield* Queue.make<Element>()
418
713
  const takers = MutableRef.make(0)
419
714
  const pollLatch = Latch.makeUnsafe()
420
715
  const takenLatch = Latch.makeUnsafe()
716
+ const state = queueStates.get(name)
421
717
 
422
718
  yield* Effect.addFinalizer(() =>
423
719
  Effect.orDie(
@@ -426,47 +722,77 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
426
722
  (elements) =>
427
723
  Effect.forEach(elements, (element) =>
428
724
  requeue(
429
- queueKey,
430
- pendingKey,
725
+ keys.queue,
726
+ keys.pending,
431
727
  keyLock(element.id),
728
+ keys.attempts,
432
729
  element.id,
433
- JSON.stringify(element)
730
+ element.payload
434
731
  ), { concurrency: "unbounded", discard: true })
435
732
  )
436
733
  )
437
734
  )
438
735
 
439
- yield* resetQueue(queueKey, pendingKey, prefix).pipe(
736
+ yield* Effect.suspend(() =>
737
+ resetQueue(
738
+ keys.queue,
739
+ keys.pending,
740
+ keys.attempts,
741
+ keys.failed,
742
+ keys.ids,
743
+ prefix,
744
+ state.maxAttempts,
745
+ clock.currentTimeMillisUnsafe()
746
+ )
747
+ ).pipe(
440
748
  Effect.andThen(Effect.sleep(lockRefreshMillis)),
441
749
  Effect.forever,
442
750
  Effect.forkScoped
443
751
  )
444
752
 
445
753
  const poll = (size: number) =>
446
- take(
447
- queueKey,
448
- pendingKey,
449
- prefix,
450
- workerId,
451
- size,
452
- lockExpirationMillis
754
+ Effect.suspend(() =>
755
+ take(
756
+ keys.queue,
757
+ keys.pending,
758
+ keys.delayed,
759
+ keys.attempts,
760
+ prefix,
761
+ workerId,
762
+ size,
763
+ lockExpirationMillis,
764
+ clock.currentTimeMillisUnsafe()
765
+ )
453
766
  )
454
767
 
455
768
  yield* Effect.gen(function*() {
456
769
  while (true) {
457
770
  yield* pollLatch.await
458
771
  yield* Effect.yieldNow
772
+ state.nudge.closeUnsafe()
459
773
  const results = takers.current === 0 ? null : yield* poll(takers.current)
460
- if (results === null) {
461
- yield* Effect.sleep(pollInterval)
774
+ if (results === null || results.length === 0) {
775
+ yield* Effect.race(Effect.sleep(pollInterval), state.nudge.await)
462
776
  continue
463
777
  }
464
778
  takenLatch.closeUnsafe()
465
- yield* Queue.offerAll(queue, results.map((json) => JSON.parse(json)))
779
+ const elements: Array<Element> = []
780
+ for (let i = 0; i < results.length; i += 2) {
781
+ const payload = results[i] as string
782
+ const parsed = JSON.parse(payload)
783
+ elements.push({
784
+ id: parsed.id,
785
+ element: parsed.element,
786
+ attempts: Number(results[i + 1]),
787
+ payload
788
+ })
789
+ }
790
+ yield* Queue.offerAll(queue, elements)
466
791
  yield* takenLatch.await
467
792
  yield* Effect.yieldNow
468
793
  }
469
794
  }).pipe(
795
+ Effect.tapCause(Effect.logWarning),
470
796
  Effect.sandbox,
471
797
  Effect.retry(Schedule.spaced(500)),
472
798
  Effect.forkScoped,
@@ -494,32 +820,58 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
494
820
  })
495
821
  )
496
822
 
823
+ const scanKeys = (pattern: string) =>
824
+ Effect.gen(function*() {
825
+ const keys: Array<string> = []
826
+ let cursor = "0"
827
+ do {
828
+ const [next, batch] = yield* redis.send<[string, Array<string>]>(
829
+ "SCAN",
830
+ cursor,
831
+ "MATCH",
832
+ pattern,
833
+ "COUNT",
834
+ "100"
835
+ )
836
+ cursor = next
837
+ for (const key of batch) keys.push(key)
838
+ } while (cursor !== "0")
839
+ return keys
840
+ })
841
+
497
842
  return PersistedQueueStore.of({
498
- offer: ({ element, id, isCustomId, name }) =>
499
- Effect.mapError(
500
- isCustomId
501
- ? offer(
502
- `${prefix}${name}`,
503
- `${prefix}${name}:ids`,
504
- id,
505
- JSON.stringify({ id, element, attempts: 0 })
843
+ offer: ({ element, id, isCustomId, name }) => {
844
+ const keys = keysFor(name)
845
+ const payload = JSON.stringify({ id, element })
846
+ return (isCustomId
847
+ ? offer(keys.queue, keys.ids, id, payload)
848
+ : redis.send("RPUSH", keys.queue, payload)).pipe(
849
+ Effect.mapError(({ cause }) =>
850
+ new PersistedQueueError({
851
+ message: "Failed to offer element to persisted queue",
852
+ cause
853
+ })
854
+ ),
855
+ Effect.tap(() =>
856
+ Effect.sync(() => {
857
+ queueStates.peek(name)?.nudge.openUnsafe()
858
+ })
506
859
  )
507
- : redis.send("RPUSH", `${prefix}${name}`, JSON.stringify({ id, element, attempts: 0 })),
508
- ({ cause }) =>
509
- new PersistedQueueError({
510
- message: "Failed to offer element to persisted queue",
511
- cause
512
- })
513
- ),
860
+ )
861
+ },
514
862
  take: (options) =>
515
- Effect.uninterruptibleMask((restore) =>
516
- RcMap.get(queues, options.name).pipe(
863
+ Effect.uninterruptibleMask((restore) => {
864
+ queueStates.get(options.name).maxAttempts = options.maxAttempts
865
+ return RcMap.get(queues, options.name).pipe(
517
866
  Effect.flatMap(({ pollLatch, queue, takenLatch, takers }) => {
518
867
  takers.current++
519
868
  if (takers.current === 1) {
520
869
  pollLatch.openUnsafe()
521
870
  }
522
- return Effect.tap(restore(Queue.take(queue)), () =>
871
+ // onExit so the decrement also runs when a waiting take is
872
+ // interrupted, otherwise the poller keeps fetching for a phantom
873
+ // taker
874
+ return Effect.onExit(restore(Queue.take(queue)), () =>
523
875
  Effect.sync(() => {
524
876
  takers.current--
525
877
  if (takers.current === 0) {
@@ -532,50 +884,89 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
532
884
  }),
533
885
  Effect.scoped,
534
886
  Effect.tap((element) => {
887
+ const keys = keysFor(options.name)
535
888
  const lock = keyLock(element.id)
536
889
  activeLockKeys.add(lock)
537
- return Effect.addFinalizer(Exit.match({
538
- onFailure: (cause) => {
539
- activeLockKeys.delete(lock)
540
- const nextAttempts = element.attempts + 1
541
- if (nextAttempts >= options.maxAttempts) {
542
- return Effect.orDie(failed(
543
- keyPending(options.name),
890
+ const ack = (effect: Effect.Effect<unknown, Redis.RedisError>) =>
891
+ effect.pipe(
892
+ Effect.retry(ackRetrySchedule),
893
+ Effect.orDie,
894
+ Effect.ensuring(Effect.sync(() => activeLockKeys.delete(lock)))
895
+ )
896
+ return Effect.addFinalizer((exit) =>
897
+ Effect.suspend(() => {
898
+ const now = clock.currentTimeMillisUnsafe()
899
+ if (exit._tag === "Success") {
900
+ return ack(complete(keys.pending, lock, keys.attempts, keys.ids, element.id, now))
901
+ }
902
+ const dead = deadLetterFromCause(exit.cause)
903
+ const failElement = (cause: Cause.Cause<unknown>) =>
904
+ ack(failed(
905
+ keys.pending,
544
906
  lock,
545
- keyFailed(options.name),
907
+ keys.failed,
908
+ keys.attempts,
909
+ keys.ids,
546
910
  element.id,
547
911
  JSON.stringify({
548
- ...element,
912
+ id: element.id,
913
+ element: element.element,
914
+ attempts: element.attempts,
549
915
  lastFailure: Cause.pretty(cause),
550
- attempts: nextAttempts
916
+ failedAt: now
551
917
  })
552
918
  ))
919
+ if (dead !== undefined) {
920
+ return failElement(dead.cause)
553
921
  }
554
- return Effect.orDie(requeue(
555
- keyQueue(options.name),
556
- keyPending(options.name),
557
- lock,
558
- element.id,
559
- JSON.stringify(
560
- Cause.hasInterruptsOnly(cause)
561
- ? element
562
- : {
563
- ...element,
564
- lastFailure: Cause.pretty(cause),
565
- attempts: nextAttempts
566
- }
567
- )
568
- ))
569
- },
570
- onSuccess: () => {
571
- activeLockKeys.delete(lock)
572
- return Effect.orDie(complete(
573
- keyPending(options.name),
574
- lock,
575
- element.id
576
- ))
577
- }
578
- }))
922
+ if (Cause.hasInterruptsOnly(exit.cause)) {
923
+ return ack(requeue(keys.queue, keys.pending, lock, keys.attempts, element.id, element.payload))
924
+ }
925
+ if (element.attempts >= options.maxAttempts) {
926
+ return failElement(exit.cause)
927
+ }
928
+ return Effect.flatMap(options.retryDelay(element.attempts), (delay) =>
929
+ ack(retry(
930
+ keys.pending,
931
+ lock,
932
+ keys.delayed,
933
+ element.id,
934
+ element.payload,
935
+ now + Duration.toMillis(delay)
936
+ )))
937
+ })
938
+ )
939
+ })
940
+ )
941
+ }),
942
+ cleanup: ({ failedTimeToLive, timeToLive }) =>
943
+ Effect.gen(function*() {
944
+ const now = clock.currentTimeMillisUnsafe()
945
+ const idsKeys = yield* scanKeys(`${prefix}*:ids`)
946
+ const cutoff = now - Duration.toMillis(timeToLive)
947
+ yield* Effect.forEach(
948
+ idsKeys,
949
+ (key) => redis.send("ZREMRANGEBYSCORE", key, "-inf", `(${cutoff}`),
950
+ { concurrency: 16, discard: true }
951
+ )
952
+ if (failedTimeToLive !== undefined) {
953
+ const failedCutoff = now - Duration.toMillis(failedTimeToLive)
954
+ const failedKeys = yield* scanKeys(`${prefix}*:failed`)
955
+ yield* Effect.forEach(
956
+ failedKeys,
957
+ (key) =>
958
+ trimFailed(key, `${key.slice(0, -":failed".length)}:ids`, failedCutoff).pipe(
959
+ // each call trims at most one batch, so drain until done
960
+ Effect.repeat({ while: (removed) => removed >= trimFailedBatchSize })
961
+ ),
962
+ { concurrency: 16, discard: true }
963
+ )
964
+ }
965
+ }).pipe(
966
+ Effect.mapError(({ cause }) =>
967
+ new PersistedQueueError({
968
+ message: "Failed to clean up persisted queue",
969
+ cause
579
970
  })
580
971
  )
581
972
  )
@@ -591,7 +982,9 @@ local key_ids = KEYS[2]
591
982
  local id = ARGV[1]
592
983
  local payload = ARGV[2]
593
984
 
594
- local result = redis.call("SADD", key_ids, id)
985
+ -- park the dedupe entry outside the timeToLive trim range until the element
986
+ -- completes, so unprocessed elements never lose dedupe protection
987
+ local result = redis.call("ZADD", key_ids, "NX", "+inf", id)
595
988
  if result == 1 then
596
989
  redis.call("RPUSH", key_queue, payload)
597
990
  end
@@ -601,12 +994,28 @@ end
601
994
  )
602
995
 
603
996
  const resetQueueRedis = Redis.script(
604
- (...args: [keyQueue: string, keyPending: string, prefix: string]) => args,
997
+ (
998
+ ...args: [
999
+ keyQueue: string,
1000
+ keyPending: string,
1001
+ keyAttempts: string,
1002
+ keyFailed: string,
1003
+ keyIds: string,
1004
+ prefix: string,
1005
+ maxAttempts: number,
1006
+ now: number
1007
+ ]
1008
+ ) => args,
605
1009
  {
606
1010
  lua: `
607
1011
  local key_queue = KEYS[1]
608
1012
  local key_pending = KEYS[2]
1013
+ local key_attempts = KEYS[3]
1014
+ local key_failed = KEYS[4]
1015
+ local key_ids = KEYS[5]
609
1016
  local prefix = ARGV[1]
1017
+ local max_attempts = tonumber(ARGV[2])
1018
+ local now = ARGV[3]
610
1019
 
611
1020
  local entries = redis.call("HGETALL", key_pending)
612
1021
  for i = 1, #entries, 2 do
@@ -615,96 +1024,184 @@ for i = 1, #entries, 2 do
615
1024
  local lock_key = prefix .. id .. ":lock"
616
1025
  local exists = redis.call("EXISTS", lock_key)
617
1026
  if exists == 0 then
618
- redis.call("RPUSH", key_queue, payload)
1027
+ local attempts = tonumber(redis.call("HGET", key_attempts, id) or "0")
1028
+ if attempts >= max_attempts then
1029
+ -- compose the failed record by hand so the element payload does not go
1030
+ -- through a lossy cjson round-trip
1031
+ local record = string.sub(payload, 1, -2) .. ',"attempts":' .. attempts ..
1032
+ ',"lastFailure":"Lock expired after final attempt","failedAt":' .. now .. '}'
1033
+ redis.call("RPUSH", key_failed, record)
1034
+ redis.call("HDEL", key_attempts, id)
1035
+ -- failed ids keep their dedupe entry until failedTimeToLive removes the
1036
+ -- dead-letter record, so park them outside the timeToLive trim range
1037
+ redis.call("ZADD", key_ids, "XX", "+inf", id)
1038
+ else
1039
+ redis.call("RPUSH", key_queue, payload)
1040
+ end
619
1041
  redis.call("HDEL", key_pending, id)
620
1042
  end
621
1043
  end
622
1044
  `,
623
- numberOfKeys: 2
1045
+ numberOfKeys: 5
624
1046
  }
625
1047
  )
626
1048
 
627
1049
  const requeueRedis = Redis.script(
628
- (...args: [keyQueue: string, keyPending: string, keyLock: string, id: string, payload: string]) => args,
1050
+ (
1051
+ ...args: [keyQueue: string, keyPending: string, keyLock: string, keyAttempts: string, id: string, payload: string]
1052
+ ) => args,
629
1053
  {
630
1054
  lua: `
631
1055
  local key_queue = KEYS[1]
632
1056
  local key_pending = KEYS[2]
633
1057
  local key_lock = KEYS[3]
1058
+ local key_attempts = KEYS[4]
634
1059
  local id = ARGV[1]
635
1060
  local payload = ARGV[2]
636
1061
 
637
1062
  redis.call("DEL", key_lock)
638
1063
  redis.call("HDEL", key_pending, id)
1064
+ local attempts = redis.call("HINCRBY", key_attempts, id, -1)
1065
+ if attempts <= 0 then
1066
+ redis.call("HDEL", key_attempts, id)
1067
+ end
639
1068
  redis.call("RPUSH", key_queue, payload)
640
1069
  `,
641
- numberOfKeys: 3
1070
+ numberOfKeys: 4
642
1071
  }
643
1072
  )
644
1073
 
645
1074
  const completeRedis = Redis.script(
646
- (...args: [keyPending: string, keyLock: string, id: string]) => args,
1075
+ (...args: [keyPending: string, keyLock: string, keyAttempts: string, keyIds: string, id: string, now: number]) =>
1076
+ args,
647
1077
  {
648
1078
  lua: `
649
1079
  local key_pending = KEYS[1]
650
1080
  local key_lock = KEYS[2]
1081
+ local key_attempts = KEYS[3]
1082
+ local key_ids = KEYS[4]
651
1083
  local id = ARGV[1]
1084
+ local now = ARGV[2]
652
1085
 
653
1086
  redis.call("DEL", key_lock)
654
1087
  redis.call("HDEL", key_pending, id)
1088
+ redis.call("HDEL", key_attempts, id)
1089
+ redis.call("ZADD", key_ids, "XX", now, id)
655
1090
  `,
656
- numberOfKeys: 2
1091
+ numberOfKeys: 4
1092
+ }
1093
+ )
1094
+
1095
+ const retryRedis = Redis.script(
1096
+ (
1097
+ ...args: [keyPending: string, keyLock: string, keyDelayed: string, id: string, payload: string, visibleAt: number]
1098
+ ) => args,
1099
+ {
1100
+ lua: `
1101
+ local key_pending = KEYS[1]
1102
+ local key_lock = KEYS[2]
1103
+ local key_delayed = KEYS[3]
1104
+ local id = ARGV[1]
1105
+ local payload = ARGV[2]
1106
+ local visible_at = ARGV[3]
1107
+
1108
+ redis.call("DEL", key_lock)
1109
+ redis.call("HDEL", key_pending, id)
1110
+ redis.call("ZADD", key_delayed, visible_at, payload)
1111
+ `,
1112
+ numberOfKeys: 3
657
1113
  }
658
1114
  )
659
1115
 
660
1116
  const failedRedis = Redis.script(
661
- (...args: [keyPending: string, keyLock: string, keyFailed: string, id: string, payload: string]) => args,
1117
+ (
1118
+ ...args: [
1119
+ keyPending: string,
1120
+ keyLock: string,
1121
+ keyFailed: string,
1122
+ keyAttempts: string,
1123
+ keyIds: string,
1124
+ id: string,
1125
+ payload: string
1126
+ ]
1127
+ ) => args,
662
1128
  {
663
1129
  lua: `
664
1130
  local key_pending = KEYS[1]
665
1131
  local key_lock = KEYS[2]
666
1132
  local key_failed = KEYS[3]
1133
+ local key_attempts = KEYS[4]
1134
+ local key_ids = KEYS[5]
667
1135
  local id = ARGV[1]
668
1136
  local payload = ARGV[2]
669
1137
 
670
1138
  redis.call("DEL", key_lock)
671
1139
  redis.call("HDEL", key_pending, id)
1140
+ redis.call("HDEL", key_attempts, id)
672
1141
  redis.call("RPUSH", key_failed, payload)
1142
+ -- failed ids keep their dedupe entry until failedTimeToLive removes the
1143
+ -- dead-letter record, so park them outside the timeToLive trim range
1144
+ redis.call("ZADD", key_ids, "XX", "+inf", id)
673
1145
  `,
674
- numberOfKeys: 3
1146
+ numberOfKeys: 5
675
1147
  }
676
1148
  )
677
1149
 
678
1150
  const takeRedis = Redis.script(
679
1151
  (
680
- ...args: [keyQueue: string, keyPending: string, prefix: string, workerId: string, batchSize: number, pttl: number]
1152
+ ...args: [
1153
+ keyQueue: string,
1154
+ keyPending: string,
1155
+ keyDelayed: string,
1156
+ keyAttempts: string,
1157
+ prefix: string,
1158
+ workerId: string,
1159
+ batchSize: number,
1160
+ pttl: number,
1161
+ now: number
1162
+ ]
681
1163
  ) => args,
682
1164
  {
683
1165
  lua: `
684
1166
  local key_queue = KEYS[1]
685
1167
  local key_pending = KEYS[2]
1168
+ local key_delayed = KEYS[3]
1169
+ local key_attempts = KEYS[4]
686
1170
  local prefix = ARGV[1]
687
1171
  local worker_id = ARGV[2]
688
1172
  local batch_size = tonumber(ARGV[3])
689
1173
  local pttl = ARGV[4]
1174
+ local now = ARGV[5]
1175
+
1176
+ local due = redis.call("ZRANGEBYSCORE", key_delayed, "-inf", now, "LIMIT", 0, 100)
1177
+ if #due > 0 then
1178
+ for i, payload in ipairs(due) do
1179
+ redis.call("RPUSH", key_queue, payload)
1180
+ end
1181
+ redis.call("ZREM", key_delayed, unpack(due))
1182
+ end
690
1183
 
691
1184
  local payloads = redis.call("LPOP", key_queue, batch_size)
692
1185
  if not payloads then
693
1186
  return nil
694
1187
  end
695
1188
 
1189
+ local result = {}
696
1190
  for i, payload in ipairs(payloads) do
697
1191
  local id = cjson.decode(payload).id
698
1192
  local key_lock = prefix .. id .. ":lock"
699
1193
  redis.call("SET", key_lock, worker_id, "PX", pttl)
700
1194
  redis.call("HSET", key_pending, id, payload)
1195
+ local attempts = redis.call("HINCRBY", key_attempts, id, 1)
1196
+ result[i * 2 - 1] = payload
1197
+ result[i * 2] = attempts
701
1198
  end
702
1199
 
703
- return payloads
1200
+ return result
704
1201
  `,
705
- numberOfKeys: 2
1202
+ numberOfKeys: 4
706
1203
  }
707
- ).withReturnType<Arr.NonEmptyArray<string> | null>()
1204
+ ).withReturnType<ReadonlyArray<string | number> | null>()
708
1205
 
709
1206
  const expireAllRedis = Redis.script(
710
1207
  (keys: ReadonlyArray<string>, ttl: number) => [...keys, ttl],
@@ -719,6 +1216,41 @@ end
719
1216
  }
720
1217
  )
721
1218
 
1219
+ const trimFailedBatchSize = 1000
1220
+
1221
+ const trimFailedRedis = Redis.script(
1222
+ (...args: [keyFailed: string, keyIds: string, cutoff: number]) => args,
1223
+ {
1224
+ lua: `
1225
+ local key_failed = KEYS[1]
1226
+ local key_ids = KEYS[2]
1227
+ local cutoff = tonumber(ARGV[1])
1228
+ local removed = 0
1229
+
1230
+ while removed < ${trimFailedBatchSize} do
1231
+ local head = redis.call("LINDEX", key_failed, 0)
1232
+ if not head then break end
1233
+ local ok, decoded = pcall(cjson.decode, head)
1234
+ if ok then
1235
+ local failed_at = tonumber(decoded.failedAt)
1236
+ if failed_at ~= nil and failed_at >= cutoff then break end
1237
+ redis.call("LPOP", key_failed)
1238
+ if decoded.id ~= nil then
1239
+ redis.call("ZREM", key_ids, decoded.id)
1240
+ end
1241
+ else
1242
+ -- a corrupt head would otherwise block trimming of the whole list
1243
+ redis.call("LPOP", key_failed)
1244
+ end
1245
+ removed = removed + 1
1246
+ end
1247
+
1248
+ return removed
1249
+ `,
1250
+ numberOfKeys: 2
1251
+ }
1252
+ ).withReturnType<number>()
1253
+
722
1254
  /**
723
1255
  * Provides a Redis-backed `PersistedQueueStore` using `makeStoreRedis`.
724
1256
  *
@@ -777,9 +1309,10 @@ export const makeStoreSql: (
777
1309
  options?.lockExpiration ? Duration.fromInputUnsafe(options.lockExpiration) : Duration.minutes(2),
778
1310
  Duration.millis(1)
779
1311
  )
780
- const lockExpirationSql = sql.literal(Math.ceil(Duration.toSeconds(lockExpiration)).toString())
781
1312
  const workerId = crypto.randomUUID()
782
1313
 
1314
+ const ackRetrySchedule = makeAckRetrySchedule(lockExpiration)
1315
+
783
1316
  yield* Effect.orDie(
784
1317
  Migrator.make({})({
785
1318
  loader: sqlMigrations(tableName),
@@ -788,45 +1321,55 @@ export const makeStoreSql: (
788
1321
  )
789
1322
 
790
1323
  const sqlNow = sql.onDialectOrElse({
791
- mssql: () => sql.literal("GETDATE()"),
1324
+ // GETDATE() rounds to 1/300s and can land in the future, hiding freshly
1325
+ // written visible_at values from the poll query
1326
+ mssql: () => sql.literal("SYSDATETIME()"),
792
1327
  mysql: () => sql.literal("NOW()"),
793
1328
  pg: () => sql.literal("NOW()"),
794
1329
  // sqlite
795
1330
  orElse: () => sql.literal("CURRENT_TIMESTAMP")
796
1331
  })
797
1332
 
798
- const expiresAt = sql.onDialectOrElse({
799
- pg: () => sql`${sqlNow} - INTERVAL '${lockExpirationSql} seconds'`,
800
- mysql: () => sql`DATE_SUB(${sqlNow}, INTERVAL ${lockExpirationSql} SECOND)`,
801
- mssql: () => sql`DATEADD(SECOND, -${lockExpirationSql}, ${sqlNow})`,
802
- orElse: () => sql`datetime(${sqlNow}, '-${lockExpirationSql} seconds')`
803
- })
1333
+ // `seconds` is a whole number, possibly negative
1334
+ const secondsOffset = (seconds: number) => {
1335
+ const s = sql.literal(seconds.toString())
1336
+ return sql.onDialectOrElse({
1337
+ pg: () => sql`${sqlNow} + INTERVAL '${s} seconds'`,
1338
+ mysql: () => sql`DATE_ADD(${sqlNow}, INTERVAL ${s} SECOND)`,
1339
+ mssql: () => sql`DATEADD(SECOND, ${s}, ${sqlNow})`,
1340
+ orElse: () => sql`datetime(${sqlNow}, '${s} seconds')`
1341
+ })
1342
+ }
1343
+ const secondsAgo = (seconds: number) => secondsOffset(-Math.max(Math.ceil(seconds), 0))
1344
+ const secondsFromNow = (seconds: number) => secondsOffset(Math.max(Math.ceil(seconds), 0))
1345
+ const expiresAt = secondsAgo(Duration.toSeconds(lockExpiration))
804
1346
 
805
1347
  const offer = sql.onDialectOrElse({
806
1348
  pg: () => (id: string, name: string, element: string) =>
807
1349
  sql`
808
- INSERT INTO ${tableNameSql} (id, queue_name, element, completed, attempts, created_at, updated_at)
809
- VALUES (${id}, ${name}, ${element}, FALSE, 0, ${sqlNow}, ${sqlNow})
1350
+ INSERT INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
1351
+ VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow})
810
1352
  ON CONFLICT (id, queue_name) DO NOTHING
811
1353
  `,
812
1354
  mysql: () => (id: string, name: string, element: string) =>
813
1355
  sql`
814
- INSERT IGNORE INTO ${tableNameSql} (id, queue_name, element, completed, attempts, created_at, updated_at)
815
- VALUES (${id}, ${name}, ${element}, FALSE, 0, ${sqlNow}, ${sqlNow})
1356
+ INSERT IGNORE INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
1357
+ VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow})
816
1358
  `,
817
1359
  mssql: () => (id: string, name: string, element: string) =>
818
1360
  sql`
819
- IF NOT EXISTS (SELECT 1 FROM ${tableNameSql} WHERE id = ${id} AND queue_name = ${name})
820
- BEGIN
821
- INSERT INTO ${tableNameSql} (id, queue_name, element, completed, attempts, created_at, updated_at)
822
- VALUES (${id}, ${name}, ${element}, 0, 0, ${sqlNow}, ${sqlNow})
823
- END
1361
+ MERGE ${tableNameSql} WITH (HOLDLOCK) AS target
1362
+ USING (SELECT ${id} AS id, ${name} AS queue_name) AS source
1363
+ ON target.id = source.id AND target.queue_name = source.queue_name
1364
+ WHEN NOT MATCHED THEN
1365
+ INSERT (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
1366
+ VALUES (source.id, source.queue_name, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow});
824
1367
  `,
825
1368
  // sqlite
826
1369
  orElse: () => (id: string, name: string, element: string) =>
827
1370
  sql`
828
- INSERT OR IGNORE INTO ${tableNameSql} (id, queue_name, element, completed, attempts, created_at, updated_at)
829
- VALUES (${id}, ${name}, ${element}, FALSE, 0, ${sqlNow}, ${sqlNow})
1371
+ INSERT OR IGNORE INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at)
1372
+ VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow})
830
1373
  `
831
1374
  })
832
1375
 
@@ -836,13 +1379,8 @@ export const makeStoreSql: (
836
1379
  })
837
1380
  const stringLiteral = (s: string) => sql.literal(wrapString(s))
838
1381
 
839
- const sqlTrue = sql.onDialectOrElse({
840
- sqlite: () => sql.literal("1"),
841
- orElse: () => sql.literal("TRUE")
842
- })
843
-
844
1382
  const workerIdSql = stringLiteral(workerId)
845
- const elementIds = new Set<number>()
1383
+ const elementIds = new Set<number | string>()
846
1384
  const refreshLocks: Effect.Effect<void, SqlError> = Effect.suspend((): Effect.Effect<void, SqlError> => {
847
1385
  if (elementIds.size === 0) return Effect.void
848
1386
  const ids = Array.from(elementIds)
@@ -853,55 +1391,64 @@ export const makeStoreSql: (
853
1391
  AND acquired_by = ${workerIdSql}
854
1392
  `
855
1393
  })
856
- const complete = (sequence: number, attempts: number) => {
857
- elementIds.delete(sequence)
858
- return sql`
859
- UPDATE ${tableNameSql}
860
- SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, completed = ${sqlTrue}, attempts = ${attempts}
861
- WHERE sequence = ${sequence}
862
- AND acquired_by = ${workerIdSql}
863
- `.pipe(
864
- Effect.retry({
865
- times: 5,
866
- schedule: Schedule.exponential(100, 1.5)
867
- }),
868
- Effect.orDie
1394
+ const ack = (
1395
+ statement: Effect.Effect<unknown, SqlError>,
1396
+ sequences: ReadonlyArray<number | string>
1397
+ ): Effect.Effect<void> =>
1398
+ statement.pipe(
1399
+ Effect.retry(ackRetrySchedule),
1400
+ Effect.orDie,
1401
+ Effect.ensuring(Effect.sync(() => {
1402
+ for (const sequence of sequences) {
1403
+ elementIds.delete(sequence)
1404
+ }
1405
+ })),
1406
+ Effect.asVoid
869
1407
  )
870
- }
871
- const retry = (sequence: number, attempts: number, cause: Cause.Cause<any>) => {
872
- elementIds.delete(sequence)
873
- return sql`
874
- UPDATE ${tableNameSql}
875
- SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, attempts = ${attempts}, last_failure = ${
876
- Cause.pretty(cause)
877
- }
878
- WHERE sequence = ${sequence}
879
- AND acquired_by = ${workerIdSql}
880
- `.pipe(
881
- Effect.retry({
882
- times: 5,
883
- schedule: Schedule.exponential(100, 1.5)
884
- }),
885
- Effect.orDie
1408
+ const complete = (sequence: number | string) =>
1409
+ ack(
1410
+ sql`
1411
+ UPDATE ${tableNameSql}
1412
+ SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, state = 'completed'
1413
+ WHERE sequence = ${sequence}
1414
+ AND acquired_by = ${workerIdSql}
1415
+ `,
1416
+ [sequence]
886
1417
  )
887
- }
888
- const interrupt = (ids: Array<number>) => {
889
- for (const id of ids) {
890
- elementIds.delete(id)
891
- }
892
- return sql`
893
- UPDATE ${tableNameSql}
894
- SET acquired_at = NULL, acquired_by = NULL
895
- WHERE sequence IN (${sql.literal(ids.join(","))})
896
- AND acquired_by = ${workerIdSql}
897
- `.pipe(
898
- Effect.retry({
899
- times: 5,
900
- schedule: Schedule.exponential(100, 1.5)
901
- }),
902
- Effect.orDie
1418
+ const fail = (sequence: number | string, cause: Cause.Cause<any>) =>
1419
+ ack(
1420
+ sql`
1421
+ UPDATE ${tableNameSql}
1422
+ SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, state = 'failed', last_failure = ${
1423
+ Cause.pretty(cause)
1424
+ }
1425
+ WHERE sequence = ${sequence}
1426
+ AND acquired_by = ${workerIdSql}
1427
+ `,
1428
+ [sequence]
1429
+ )
1430
+ const retry = (sequence: number | string, delay: Duration.Duration, cause: Cause.Cause<any>) =>
1431
+ ack(
1432
+ sql`
1433
+ UPDATE ${tableNameSql}
1434
+ SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, visible_at = ${
1435
+ secondsFromNow(Duration.toSeconds(delay))
1436
+ }, last_failure = ${Cause.pretty(cause)}
1437
+ WHERE sequence = ${sequence}
1438
+ AND acquired_by = ${workerIdSql}
1439
+ `,
1440
+ [sequence]
1441
+ )
1442
+ const interrupt = (ids: Array<number | string>) =>
1443
+ ack(
1444
+ sql`
1445
+ UPDATE ${tableNameSql}
1446
+ SET acquired_at = NULL, acquired_by = NULL, attempts = attempts - 1
1447
+ WHERE sequence IN (${sql.literal(ids.join(","))})
1448
+ AND acquired_by = ${workerIdSql}
1449
+ `,
1450
+ ids
903
1451
  )
904
- }
905
1452
 
906
1453
  yield* refreshLocks.pipe(
907
1454
  Effect.tapCause(Effect.logWarning),
@@ -917,17 +1464,20 @@ export const makeStoreSql: (
917
1464
 
918
1465
  type Element = {
919
1466
  readonly id: string
920
- sequence: number
921
- readonly queue_name: string
922
- element: string
923
- readonly attempts: number
1467
+ readonly sequence: number | string
1468
+ readonly element: string
1469
+ attempts: number
924
1470
  }
1471
+ const queueStates = makeQueueStates()
925
1472
  const mailboxes = yield* RcMap.make({
926
- lookup: Effect.fnUntraced(function*({ maxAttempts, name }: QueueKey) {
1473
+ lookup: Effect.fnUntraced(function*(name: string) {
1474
+ const state = queueStates.get(name)
1475
+ const maxAttempts = state.maxAttempts
927
1476
  const queue = yield* Queue.make<Element>()
928
1477
  const takers = MutableRef.make(0)
929
1478
  const pollLatch = Latch.makeUnsafe()
930
1479
  const takenLatch = Latch.makeUnsafe()
1480
+ const nudge = state.nudge
931
1481
 
932
1482
  yield* Effect.addFinalizer(() =>
933
1483
  Effect.flatMap(Queue.clear(queue), (elements) => {
@@ -936,35 +1486,55 @@ export const makeStoreSql: (
936
1486
  })
937
1487
  )
938
1488
 
1489
+ // flip exhausted rows whose lock expired (worker crashed on the final
1490
+ // attempt) to failed, since no finalizer will ever run for them
1491
+ yield* sql`
1492
+ UPDATE ${tableNameSql}
1493
+ SET state = 'failed', acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow},
1494
+ last_failure = COALESCE(last_failure, 'Lock expired after final attempt')
1495
+ WHERE queue_name = ${name}
1496
+ AND state = 'pending'
1497
+ AND attempts >= ${maxAttempts}
1498
+ AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
1499
+ `.pipe(
1500
+ Effect.tapCause(Effect.logWarning),
1501
+ Effect.ignore,
1502
+ Effect.schedule(Schedule.spaced(lockRefreshInterval)),
1503
+ Effect.forkScoped,
1504
+ Effect.interruptible
1505
+ )
1506
+
939
1507
  const poll = sql.onDialectOrElse({
940
1508
  pg: () => (size: number) =>
941
1509
  sql<Element>`
942
1510
  WITH cte AS (
943
1511
  UPDATE ${tableNameSql}
944
- SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
1512
+ SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = attempts + 1
945
1513
  WHERE sequence IN (
946
1514
  SELECT sequence FROM ${tableNameSql}
947
1515
  WHERE queue_name = ${name}
948
- AND completed = FALSE
1516
+ AND state = 'pending'
949
1517
  AND attempts < ${maxAttempts}
1518
+ AND visible_at <= ${sqlNow}
950
1519
  AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
951
- ORDER BY updated_at ASC, sequence ASC
1520
+ ORDER BY visible_at ASC, sequence ASC
952
1521
  FOR UPDATE SKIP LOCKED
953
1522
  LIMIT ${sql.literal(size.toString())}
954
1523
  )
955
- RETURNING sequence, id, queue_name, element, attempts, updated_at
1524
+ RETURNING sequence, id, element, attempts, visible_at
956
1525
  )
957
- SELECT sequence, id, queue_name, element, attempts FROM cte
958
- ORDER BY updated_at ASC, sequence ASC
1526
+ SELECT sequence, id, element, attempts FROM cte
1527
+ ORDER BY visible_at ASC, sequence ASC
959
1528
  `,
960
1529
  mysql: () => (size: number) =>
961
1530
  sql<Element>`
962
- SELECT sequence, id, queue_name, element, attempts FROM ${tableNameSql} q
1531
+ SELECT sequence, id, element, attempts FROM ${tableNameSql} q
963
1532
  WHERE queue_name = ${name}
964
- AND completed = FALSE
1533
+ AND state = 'pending'
965
1534
  AND attempts < ${maxAttempts}
1535
+ AND visible_at <= ${sqlNow}
966
1536
  AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
967
- ORDER BY updated_at ASC, sequence ASC
1537
+ ORDER BY visible_at ASC, sequence ASC
968
1538
  LIMIT ${sql.literal(size.toString())}
969
1539
  FOR UPDATE SKIP LOCKED
970
1540
  `.pipe(
@@ -972,10 +1542,16 @@ export const makeStoreSql: (
972
1542
  if (rows.length === 0) return Effect.void
973
1543
  return sql`
974
1544
  UPDATE ${tableNameSql}
975
- SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
1545
+ SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = attempts + 1
976
1546
  WHERE sequence IN (${sql.literal(rows.map((r) => r.sequence).join(","))})
977
1547
  `.unprepared
978
1548
  }),
1549
+ Effect.map((rows) => {
1550
+ for (const row of rows) {
1551
+ row.attempts = Number(row.attempts) + 1
1552
+ }
1553
+ return rows
1554
+ }),
979
1555
  sql.withTransaction
980
1556
  ),
981
1557
  mssql: () => (size: number) =>
@@ -983,14 +1559,15 @@ export const makeStoreSql: (
983
1559
  WITH cte AS (
984
1560
  SELECT TOP ${sql.literal(size.toString())} sequence FROM ${tableNameSql}
985
1561
  WHERE queue_name = ${name}
986
- AND completed = 0
1562
+ AND state = 'pending'
987
1563
  AND attempts < ${maxAttempts}
1564
+ AND visible_at <= ${sqlNow}
988
1565
  AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
989
- ORDER BY updated_at ASC, sequence ASC
1566
+ ORDER BY visible_at ASC, sequence ASC
990
1567
  )
991
1568
  UPDATE q
992
- SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
993
- OUTPUT inserted.sequence, inserted.id, inserted.queue_name, inserted.element, inserted.attempts
1569
+ SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = q.attempts + 1
1570
+ OUTPUT inserted.sequence, inserted.id, inserted.element, inserted.attempts
994
1571
  FROM ${tableNameSql} AS q
995
1572
  INNER JOIN cte ON q.sequence = cte.sequence
996
1573
  `,
@@ -998,17 +1575,18 @@ export const makeStoreSql: (
998
1575
  orElse: () => (size: number) =>
999
1576
  sql<Element>`
1000
1577
  UPDATE ${tableNameSql}
1001
- SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
1578
+ SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}, attempts = attempts + 1
1002
1579
  WHERE sequence IN (
1003
1580
  SELECT sequence FROM ${tableNameSql}
1004
1581
  WHERE queue_name = ${name}
1005
- AND completed = FALSE
1582
+ AND state = 'pending'
1006
1583
  AND attempts < ${maxAttempts}
1584
+ AND visible_at <= ${sqlNow}
1007
1585
  AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
1008
- ORDER BY updated_at ASC, sequence ASC
1586
+ ORDER BY visible_at ASC, sequence ASC
1009
1587
  LIMIT ${sql.literal(size.toString())}
1010
1588
  )
1011
- RETURNING sequence, id, queue_name, element, attempts
1589
+ RETURNING sequence, id, element, attempts
1012
1590
  `
1013
1591
  })
1014
1592
 
@@ -1016,15 +1594,16 @@ export const makeStoreSql: (
1016
1594
  while (true) {
1017
1595
  yield* pollLatch.await
1018
1596
  yield* Effect.yieldNow
1597
+ nudge.closeUnsafe()
1019
1598
  const results = takers.current === 0 ? [] : yield* poll(takers.current)
1020
1599
  if (results.length === 0) {
1021
- yield* Effect.sleep(pollInterval)
1600
+ yield* Effect.race(Effect.sleep(pollInterval), nudge.await)
1022
1601
  continue
1023
1602
  }
1024
1603
  takenLatch.closeUnsafe()
1025
- for (let i = 0; i < results.length; i++) {
1026
- const element = results[i]
1027
- elementIds.add(element.sequence)
1604
+ for (const row of results) {
1605
+ row.attempts = Number(row.attempts)
1606
+ elementIds.add(row.sequence)
1028
1607
  }
1029
1608
  yield* Queue.offerAll(queue, results)
1030
1609
  yield* takenLatch.await
@@ -1042,6 +1621,62 @@ export const makeStoreSql: (
1042
1621
  idleTimeToLive: Duration.seconds(30)
1043
1622
  })
1044
1623
 
1624
+ const cleanupBatch = sql.onDialectOrElse({
1625
+ pg: () => (state: string, seconds: number) =>
1626
+ sql<{ readonly count: number }>`
1627
+ WITH deleted_entries AS (
1628
+ DELETE FROM ${tableNameSql}
1629
+ WHERE sequence IN (
1630
+ SELECT sequence FROM ${tableNameSql}
1631
+ WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
1632
+ LIMIT ${sql.literal(String(sqlCleanupBatchSize))}
1633
+ )
1634
+ RETURNING 1
1635
+ )
1636
+ SELECT COUNT(*)::INT AS count FROM deleted_entries
1637
+ `.pipe(Effect.map((rows) => rows[0].count)),
1638
+ mysql: () =>
1639
+ Effect.fnUntraced(
1640
+ function*(state: string, seconds: number) {
1641
+ const connection = yield* sql.reserve
1642
+ const [statement, parameters] = sql`
1643
+ DELETE FROM ${tableNameSql}
1644
+ WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
1645
+ LIMIT ${sql.literal(String(sqlCleanupBatchSize))}
1646
+ `.compile()
1647
+ yield* connection.execute(statement, parameters, undefined)
1648
+ const rows = yield* connection.executeValues("SELECT ROW_COUNT()", [])
1649
+ return Number(rows[0][0])
1650
+ },
1651
+ Effect.scoped
1652
+ ),
1653
+ mssql: () => (state: string, seconds: number) =>
1654
+ sql<{ readonly sequence: number }>`
1655
+ DELETE TOP (${sql.literal(String(sqlCleanupBatchSize))}) FROM ${tableNameSql}
1656
+ OUTPUT DELETED.sequence
1657
+ WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
1658
+ `.pipe(Effect.map((rows) => rows.length)),
1659
+ // sqlite
1660
+ orElse: () => (state: string, seconds: number) =>
1661
+ sql<{ readonly deleted: number }>`
1662
+ DELETE FROM ${tableNameSql}
1663
+ WHERE sequence IN (
1664
+ SELECT sequence FROM ${tableNameSql}
1665
+ WHERE state = ${state} AND updated_at <= ${secondsAgo(seconds)}
1666
+ LIMIT ${sql.literal(String(sqlCleanupBatchSize))}
1667
+ )
1668
+ RETURNING 1 AS deleted
1669
+ `.pipe(Effect.map((rows) => rows.length))
1670
+ })
1671
+
1672
+ const cleanupState = (state: string, timeToLive: Duration.Duration) =>
1673
+ cleanupBatch(state, Duration.toSeconds(timeToLive)).pipe(
1674
+ Effect.repeat({
1675
+ while: (deletedCount) => deletedCount === sqlCleanupBatchSize,
1676
+ schedule: Schedule.spaced(Duration.millis(10))
1677
+ })
1678
+ )
1679
+
1045
1680
  return PersistedQueueStore.of({
1046
1681
  offer: ({ element, id, name }) =>
1047
1682
  Effect.catchCause(Effect.suspend(() => offer(id, name, JSON.stringify(element))), (cause) =>
@@ -1050,16 +1685,34 @@ export const makeStoreSql: (
1050
1685
  message: "Failed to offer element to persisted queue",
1051
1686
  cause
1052
1687
  })
1053
- )),
1054
- take: ({ maxAttempts, name }) =>
1055
- Effect.uninterruptibleMask((restore) =>
1056
- RcMap.get(mailboxes, new QueueKey({ name, maxAttempts })).pipe(
1688
+ )).pipe(
1689
+ Effect.tap(() =>
1690
+ Effect.sync(() => {
1691
+ queueStates.peek(name)?.nudge.openUnsafe()
1692
+ })
1693
+ )
1694
+ ),
1695
+ take: (options) => {
1696
+ queueStates.get(options.name).maxAttempts = options.maxAttempts
1697
+ const loop: Effect.Effect<
1698
+ {
1699
+ readonly id: string
1700
+ readonly attempts: number
1701
+ readonly element: unknown
1702
+ },
1703
+ PersistedQueueError,
1704
+ Scope.Scope
1705
+ > = Effect.uninterruptibleMask((restore) =>
1706
+ RcMap.get(mailboxes, options.name).pipe(
1057
1707
  Effect.flatMap(({ pollLatch, queue, takenLatch, takers }) => {
1058
1708
  takers.current++
1059
1709
  if (takers.current === 1) {
1060
1710
  pollLatch.openUnsafe()
1061
1711
  }
1062
- return Effect.tap(restore(Queue.take(queue)), () =>
1712
+ // onExit so the decrement also runs when a waiting take is
1713
+ // interrupted, otherwise the poller keeps fetching for a phantom
1714
+ // taker
1715
+ return Effect.onExit(restore(Queue.take(queue)), () =>
1063
1716
  Effect.sync(() => {
1064
1717
  takers.current--
1065
1718
  if (takers.current === 0) {
@@ -1072,19 +1725,56 @@ export const makeStoreSql: (
1072
1725
  }),
1073
1726
  Effect.scoped,
1074
1727
  restore,
1075
- Effect.tap((element) =>
1076
- Effect.addFinalizer(Exit.match({
1077
- onFailure: (cause) =>
1078
- Cause.hasInterruptsOnly(cause)
1079
- ? interrupt([element.sequence])
1080
- : retry(element.sequence, element.attempts + 1, cause),
1081
- onSuccess: () => complete(element.sequence, element.attempts + 1)
1082
- }))
1083
- ),
1084
- Effect.map((element) => ({
1085
- ...element,
1086
- element: JSON.parse(element.element)
1087
- }))
1728
+ Effect.flatMap((element) => {
1729
+ let parsed: unknown
1730
+ try {
1731
+ parsed = JSON.parse(element.element)
1732
+ } catch (defect) {
1733
+ // a row that cannot be parsed will never succeed, so dead-letter
1734
+ // it and take the next element
1735
+ return Effect.andThen(fail(element.sequence, Cause.die(defect)), loop)
1736
+ }
1737
+ return Effect.as(
1738
+ Effect.addFinalizer((exit) => {
1739
+ if (exit._tag === "Success") {
1740
+ return complete(element.sequence)
1741
+ }
1742
+ const dead = deadLetterFromCause(exit.cause)
1743
+ if (dead !== undefined) {
1744
+ return fail(element.sequence, dead.cause)
1745
+ }
1746
+ if (Cause.hasInterruptsOnly(exit.cause)) {
1747
+ return interrupt([element.sequence])
1748
+ }
1749
+ if (element.attempts >= options.maxAttempts) {
1750
+ return fail(element.sequence, exit.cause)
1751
+ }
1752
+ return Effect.flatMap(
1753
+ options.retryDelay(element.attempts),
1754
+ (delay) => retry(element.sequence, delay, exit.cause)
1755
+ )
1756
+ }),
1757
+ { id: element.id, attempts: element.attempts, element: parsed }
1758
+ )
1759
+ })
1760
+ )
1761
+ )
1762
+ return loop
1763
+ },
1764
+ cleanup: ({ failedTimeToLive, timeToLive }) =>
1765
+ Effect.gen(function*() {
1766
+ yield* cleanupState("completed", timeToLive)
1767
+ if (failedTimeToLive !== undefined) {
1768
+ yield* cleanupState("failed", failedTimeToLive)
1769
+ }
1770
+ }).pipe(
1771
+ Effect.catchCause((cause) =>
1772
+ Effect.fail(
1773
+ new PersistedQueueError({
1774
+ message: "Failed to clean up persisted queue",
1775
+ cause
1776
+ })
1777
+ )
1088
1778
  )
1089
1779
  )
1090
1780
  })
@@ -1126,7 +1816,7 @@ const sqlMigrations = (tableName: string) =>
1126
1816
  updated_at TIMESTAMP NOT NULL
1127
1817
  )`,
1128
1818
  mssql: () =>
1129
- sql`IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=${tableNameSql} AND xtype='U')
1819
+ sql`IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=${tableName} AND xtype='U')
1130
1820
  CREATE TABLE ${tableNameSql} (
1131
1821
  sequence INT IDENTITY(1,1) PRIMARY KEY,
1132
1822
  id NVARCHAR(36) NOT NULL,
@@ -1160,7 +1850,7 @@ const sqlMigrations = (tableName: string) =>
1160
1850
  yield* sql.onDialectOrElse({
1161
1851
  mssql: () =>
1162
1852
  sql`IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = N'idx_${tableName}_id')
1163
- CREATE UNIQUE INDEX idx_${tableNameSql}_id ON ${tableNameSql} (id, queue_name)`,
1853
+ CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`,
1164
1854
  mysql: () =>
1165
1855
  sql`CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`.pipe(
1166
1856
  Effect.ignore
@@ -1172,7 +1862,9 @@ const sqlMigrations = (tableName: string) =>
1172
1862
  yield* sql.onDialectOrElse({
1173
1863
  mssql: () =>
1174
1864
  sql`IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = N'idx_${tableName}_take')
1175
- CREATE INDEX idx_${tableNameSql}_take ON ${tableNameSql} (queue_name, completed, attempts, acquired_at)`,
1865
+ CREATE INDEX ${
1866
+ sql(`idx_${tableName}_take`)
1867
+ } ON ${tableNameSql} (queue_name, completed, attempts, acquired_at)`,
1176
1868
  mysql: () =>
1177
1869
  sql`CREATE INDEX ${
1178
1870
  sql(`idx_${tableName}_take`)
@@ -1195,14 +1887,131 @@ const sqlMigrations = (tableName: string) =>
1195
1887
  orElse: () =>
1196
1888
  sql`CREATE INDEX IF NOT EXISTS ${sql(`idx_${tableName}_update`)} ON ${tableNameSql} (sequence, acquired_by)`
1197
1889
  })
1890
+ }),
1891
+ "0002_upgrade_schema": Effect.gen(function*() {
1892
+ const sql = (yield* SqlClient.SqlClient).withoutTransforms()
1893
+ const tableNameSql = sql(tableName)
1894
+ const takeIndex = sql(`idx_${tableName}_take`)
1895
+
1896
+ yield* sql.onDialectOrElse({
1897
+ pg: () =>
1898
+ Effect.gen(function*() {
1899
+ yield* sql`DROP INDEX IF EXISTS ${takeIndex}`
1900
+ yield* sql`ALTER TABLE ${tableNameSql}
1901
+ ADD COLUMN state VARCHAR(10),
1902
+ ADD COLUMN visible_at TIMESTAMP`
1903
+ yield* sql`UPDATE ${tableNameSql}
1904
+ SET state = CASE WHEN completed THEN 'completed' ELSE 'pending' END,
1905
+ visible_at = updated_at`
1906
+ yield* sql`ALTER TABLE ${tableNameSql}
1907
+ ALTER COLUMN sequence TYPE BIGINT,
1908
+ ALTER COLUMN id TYPE VARCHAR(255),
1909
+ ALTER COLUMN queue_name TYPE VARCHAR(255),
1910
+ ALTER COLUMN state SET NOT NULL,
1911
+ ALTER COLUMN visible_at SET NOT NULL,
1912
+ DROP COLUMN completed`
1913
+ }),
1914
+ mysql: () =>
1915
+ Effect.gen(function*() {
1916
+ yield* sql`DROP INDEX ${takeIndex} ON ${tableNameSql}`
1917
+ yield* sql`ALTER TABLE ${tableNameSql}
1918
+ MODIFY COLUMN id VARCHAR(255) NOT NULL,
1919
+ MODIFY COLUMN queue_name VARCHAR(255) NOT NULL,
1920
+ MODIFY COLUMN element MEDIUMTEXT NOT NULL,
1921
+ MODIFY COLUMN last_failure MEDIUMTEXT NULL,
1922
+ ADD COLUMN state VARCHAR(10) NULL,
1923
+ ADD COLUMN visible_at DATETIME NULL`
1924
+ yield* sql`UPDATE ${tableNameSql}
1925
+ SET state = CASE WHEN completed THEN 'completed' ELSE 'pending' END,
1926
+ visible_at = updated_at`
1927
+ yield* sql`ALTER TABLE ${tableNameSql}
1928
+ MODIFY COLUMN state VARCHAR(10) NOT NULL,
1929
+ MODIFY COLUMN visible_at DATETIME NOT NULL,
1930
+ DROP COLUMN completed`
1931
+ }),
1932
+ mssql: () =>
1933
+ Effect.gen(function*() {
1934
+ const upgradedTableName = `${tableName}_upgrade`
1935
+ const upgradedTable = sql(upgradedTableName)
1936
+ yield* sql`CREATE TABLE ${upgradedTable} (
1937
+ sequence BIGINT IDENTITY(1,1) PRIMARY KEY,
1938
+ id NVARCHAR(255) NOT NULL,
1939
+ queue_name NVARCHAR(255) NOT NULL,
1940
+ element NVARCHAR(MAX) NOT NULL,
1941
+ state NVARCHAR(10) NOT NULL,
1942
+ attempts INT NOT NULL DEFAULT 0,
1943
+ last_failure NVARCHAR(MAX) NULL,
1944
+ visible_at DATETIME2 NOT NULL,
1945
+ acquired_at DATETIME2 NULL,
1946
+ acquired_by UNIQUEIDENTIFIER NULL,
1947
+ created_at DATETIME2 NOT NULL,
1948
+ updated_at DATETIME2 NOT NULL
1949
+ )`
1950
+ yield* sql`SET IDENTITY_INSERT ${upgradedTable} ON;
1951
+ INSERT INTO ${upgradedTable}
1952
+ (sequence, id, queue_name, element, state, attempts, last_failure, visible_at,
1953
+ acquired_at, acquired_by, created_at, updated_at)
1954
+ SELECT sequence, id, queue_name, element,
1955
+ CASE WHEN completed = 1 THEN 'completed' ELSE 'pending' END,
1956
+ attempts, last_failure, updated_at, acquired_at, acquired_by, created_at, updated_at
1957
+ FROM ${tableNameSql};
1958
+ SET IDENTITY_INSERT ${upgradedTable} OFF`
1959
+ yield* sql`DROP TABLE ${tableNameSql}`
1960
+ yield* sql`EXEC sp_rename ${upgradedTableName}, ${tableName}`
1961
+ yield* sql`CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`
1962
+ yield* sql`CREATE INDEX ${sql(`idx_${tableName}_update`)} ON ${tableNameSql} (sequence, acquired_by)`
1963
+ }),
1964
+ // sqlite rebuilds the table because altering or dropping constrained
1965
+ // columns is not portable across supported SQLite versions.
1966
+ orElse: () =>
1967
+ Effect.gen(function*() {
1968
+ const upgradedTableName = `${tableName}_upgrade`
1969
+ const upgradedTable = sql(upgradedTableName)
1970
+ yield* sql`DROP INDEX IF EXISTS ${takeIndex}`
1971
+ yield* sql`CREATE TABLE ${upgradedTable} (
1972
+ sequence INTEGER PRIMARY KEY AUTOINCREMENT,
1973
+ id TEXT NOT NULL,
1974
+ queue_name TEXT NOT NULL,
1975
+ element TEXT NOT NULL,
1976
+ state TEXT NOT NULL,
1977
+ attempts INTEGER NOT NULL DEFAULT 0,
1978
+ last_failure TEXT NULL,
1979
+ visible_at DATETIME NOT NULL,
1980
+ acquired_at DATETIME NULL,
1981
+ acquired_by TEXT NULL,
1982
+ created_at DATETIME NOT NULL,
1983
+ updated_at DATETIME NOT NULL
1984
+ )`
1985
+ yield* sql`INSERT INTO ${upgradedTable}
1986
+ (sequence, id, queue_name, element, state, attempts, last_failure, visible_at,
1987
+ acquired_at, acquired_by, created_at, updated_at)
1988
+ SELECT sequence, id, queue_name, element,
1989
+ CASE WHEN completed THEN 'completed' ELSE 'pending' END,
1990
+ attempts, last_failure, updated_at, acquired_at, acquired_by, created_at, updated_at
1991
+ FROM ${tableNameSql}`
1992
+ yield* sql`DROP TABLE ${tableNameSql}`
1993
+ yield* sql`ALTER TABLE ${upgradedTable} RENAME TO ${tableNameSql}`
1994
+ yield* sql`CREATE UNIQUE INDEX ${sql(`idx_${tableName}_id`)} ON ${tableNameSql} (id, queue_name)`
1995
+ yield* sql`CREATE INDEX ${sql(`idx_${tableName}_update`)} ON ${tableNameSql} (sequence, acquired_by)`
1996
+ })
1997
+ })
1998
+
1999
+ // partial index where supported, so pollers never scan completed rows
2000
+ yield* sql.onDialectOrElse({
2001
+ mssql: () =>
2002
+ sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, visible_at)
2003
+ WHERE state = 'pending'`,
2004
+ mysql: () => sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, state, visible_at)`,
2005
+ pg: () =>
2006
+ sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, visible_at)
2007
+ WHERE state = 'pending'`,
2008
+ orElse: () =>
2009
+ sql`CREATE INDEX ${takeIndex} ON ${tableNameSql} (queue_name, visible_at)
2010
+ WHERE state = 'pending'`
2011
+ })
1198
2012
  })
1199
2013
  })
1200
2014
 
1201
- class QueueKey extends Data.Class<{
1202
- readonly name: string
1203
- readonly maxAttempts: number
1204
- }> {}
1205
-
1206
2015
  /**
1207
2016
  * Provides a SQL-backed `PersistedQueueStore` using `makeStoreSql`.
1208
2017
  *