effect 4.0.5 → 4.0.7

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 (1177) hide show
  1. package/ai-docs/package.json +1 -1
  2. package/ai-docs/src/06_schedule/10_schedules.ts +5 -4
  3. package/ai-docs/tsconfig.json +3 -1
  4. package/dist/Arbitrary.d.ts +10 -2
  5. package/dist/Arbitrary.d.ts.map +1 -1
  6. package/dist/Arbitrary.js +2 -1
  7. package/dist/Arbitrary.js.map +1 -1
  8. package/dist/Array.d.ts +117 -52
  9. package/dist/Array.d.ts.map +1 -1
  10. package/dist/Array.js +41 -20
  11. package/dist/Array.js.map +1 -1
  12. package/dist/Bytes.d.ts +79 -0
  13. package/dist/Bytes.d.ts.map +1 -0
  14. package/dist/Bytes.js +109 -0
  15. package/dist/Bytes.js.map +1 -0
  16. package/dist/Channel.d.ts +181 -13
  17. package/dist/Channel.d.ts.map +1 -1
  18. package/dist/Channel.js +144 -34
  19. package/dist/Channel.js.map +1 -1
  20. package/dist/ChannelSchema.d.ts.map +1 -1
  21. package/dist/ChannelSchema.js.map +1 -1
  22. package/dist/Chunk.d.ts +39 -21
  23. package/dist/Chunk.d.ts.map +1 -1
  24. package/dist/Chunk.js +26 -17
  25. package/dist/Chunk.js.map +1 -1
  26. package/dist/Config.d.ts.map +1 -1
  27. package/dist/Config.js +8 -3
  28. package/dist/Config.js.map +1 -1
  29. package/dist/ConfigProvider.d.ts.map +1 -1
  30. package/dist/ConfigProvider.js +2 -1
  31. package/dist/ConfigProvider.js.map +1 -1
  32. package/dist/Cron.d.ts +2 -2
  33. package/dist/Cron.d.ts.map +1 -1
  34. package/dist/Cron.js +6 -3
  35. package/dist/Cron.js.map +1 -1
  36. package/dist/Data.d.ts +54 -235
  37. package/dist/Data.d.ts.map +1 -1
  38. package/dist/Data.js.map +1 -1
  39. package/dist/Duration.d.ts.map +1 -1
  40. package/dist/Duration.js +56 -16
  41. package/dist/Duration.js.map +1 -1
  42. package/dist/Effect.d.ts +488 -164
  43. package/dist/Effect.d.ts.map +1 -1
  44. package/dist/Effect.js +204 -38
  45. package/dist/Effect.js.map +1 -1
  46. package/dist/Equal.d.ts.map +1 -1
  47. package/dist/Equal.js +8 -1
  48. package/dist/Equal.js.map +1 -1
  49. package/dist/Equivalence.d.ts +2 -1
  50. package/dist/Equivalence.d.ts.map +1 -1
  51. package/dist/Equivalence.js +12 -8
  52. package/dist/Equivalence.js.map +1 -1
  53. package/dist/Exit.d.ts +27 -12
  54. package/dist/Exit.d.ts.map +1 -1
  55. package/dist/Exit.js +8 -3
  56. package/dist/Exit.js.map +1 -1
  57. package/dist/FiberHandle.d.ts +2 -0
  58. package/dist/FiberHandle.d.ts.map +1 -1
  59. package/dist/FiberHandle.js +9 -4
  60. package/dist/FiberHandle.js.map +1 -1
  61. package/dist/FiberMap.d.ts +2 -0
  62. package/dist/FiberMap.d.ts.map +1 -1
  63. package/dist/FiberMap.js +5 -2
  64. package/dist/FiberMap.js.map +1 -1
  65. package/dist/FileSystem.d.ts +5 -0
  66. package/dist/FileSystem.d.ts.map +1 -1
  67. package/dist/FileSystem.js +3 -0
  68. package/dist/FileSystem.js.map +1 -1
  69. package/dist/Formatter.d.ts.map +1 -1
  70. package/dist/Formatter.js +27 -13
  71. package/dist/Formatter.js.map +1 -1
  72. package/dist/Graph.d.ts.map +1 -1
  73. package/dist/Graph.js.map +1 -1
  74. package/dist/Hash.d.ts.map +1 -1
  75. package/dist/Hash.js +4 -1
  76. package/dist/Hash.js.map +1 -1
  77. package/dist/HashSet.d.ts.map +1 -1
  78. package/dist/HashSet.js.map +1 -1
  79. package/dist/Iterable.d.ts.map +1 -1
  80. package/dist/Iterable.js +18 -17
  81. package/dist/Iterable.js.map +1 -1
  82. package/dist/JsonPatch.js +4 -0
  83. package/dist/JsonPatch.js.map +1 -1
  84. package/dist/JsonSchema.d.ts +9 -3
  85. package/dist/JsonSchema.d.ts.map +1 -1
  86. package/dist/JsonSchema.js +32 -43
  87. package/dist/JsonSchema.js.map +1 -1
  88. package/dist/Layer.d.ts.map +1 -1
  89. package/dist/Layer.js +2 -2
  90. package/dist/Layer.js.map +1 -1
  91. package/dist/ManagedRuntime.d.ts +19 -1
  92. package/dist/ManagedRuntime.d.ts.map +1 -1
  93. package/dist/ManagedRuntime.js +62 -6
  94. package/dist/ManagedRuntime.js.map +1 -1
  95. package/dist/Metric.d.ts +135 -3
  96. package/dist/Metric.d.ts.map +1 -1
  97. package/dist/Metric.js +197 -25
  98. package/dist/Metric.js.map +1 -1
  99. package/dist/MutableHashMap.d.ts.map +1 -1
  100. package/dist/MutableHashMap.js.map +1 -1
  101. package/dist/MutableList.d.ts +5 -2
  102. package/dist/MutableList.d.ts.map +1 -1
  103. package/dist/MutableList.js +7 -5
  104. package/dist/MutableList.js.map +1 -1
  105. package/dist/MutableRef.d.ts.map +1 -1
  106. package/dist/MutableRef.js +7 -0
  107. package/dist/MutableRef.js.map +1 -1
  108. package/dist/Number.js +2 -2
  109. package/dist/Number.js.map +1 -1
  110. package/dist/Optic.d.ts.map +1 -1
  111. package/dist/Optic.js.map +1 -1
  112. package/dist/PartitionedSemaphore.d.ts +2 -1
  113. package/dist/PartitionedSemaphore.d.ts.map +1 -1
  114. package/dist/PartitionedSemaphore.js +22 -12
  115. package/dist/PartitionedSemaphore.js.map +1 -1
  116. package/dist/Path.d.ts.map +1 -1
  117. package/dist/Path.js +8 -1
  118. package/dist/Path.js.map +1 -1
  119. package/dist/Pool.d.ts.map +1 -1
  120. package/dist/Pool.js +77 -27
  121. package/dist/Pool.js.map +1 -1
  122. package/dist/PubSub.d.ts.map +1 -1
  123. package/dist/PubSub.js +17 -13
  124. package/dist/PubSub.js.map +1 -1
  125. package/dist/Queue.d.ts +4 -1
  126. package/dist/Queue.d.ts.map +1 -1
  127. package/dist/Queue.js +4 -1
  128. package/dist/Queue.js.map +1 -1
  129. package/dist/Random.d.ts.map +1 -1
  130. package/dist/Random.js +1 -1
  131. package/dist/Random.js.map +1 -1
  132. package/dist/RcMap.d.ts +12 -7
  133. package/dist/RcMap.d.ts.map +1 -1
  134. package/dist/RcMap.js +9 -6
  135. package/dist/RcMap.js.map +1 -1
  136. package/dist/Record.d.ts +51 -0
  137. package/dist/Record.d.ts.map +1 -1
  138. package/dist/Record.js +17 -0
  139. package/dist/Record.js.map +1 -1
  140. package/dist/Ref.d.ts.map +1 -1
  141. package/dist/Ref.js +8 -0
  142. package/dist/Ref.js.map +1 -1
  143. package/dist/References.d.ts +5 -4
  144. package/dist/References.d.ts.map +1 -1
  145. package/dist/References.js +5 -4
  146. package/dist/References.js.map +1 -1
  147. package/dist/RequestResolver.d.ts +31 -1
  148. package/dist/RequestResolver.d.ts.map +1 -1
  149. package/dist/RequestResolver.js +39 -4
  150. package/dist/RequestResolver.js.map +1 -1
  151. package/dist/Resource.js +1 -1
  152. package/dist/Resource.js.map +1 -1
  153. package/dist/Runtime.d.ts +3 -3
  154. package/dist/Runtime.d.ts.map +1 -1
  155. package/dist/Runtime.js +18 -15
  156. package/dist/Runtime.js.map +1 -1
  157. package/dist/Schedule.d.ts.map +1 -1
  158. package/dist/Schedule.js +1 -1
  159. package/dist/Schedule.js.map +1 -1
  160. package/dist/Schema.d.ts +408 -340
  161. package/dist/Schema.d.ts.map +1 -1
  162. package/dist/Schema.js +370 -47
  163. package/dist/Schema.js.map +1 -1
  164. package/dist/SchemaAST.d.ts +9 -7
  165. package/dist/SchemaAST.d.ts.map +1 -1
  166. package/dist/SchemaAST.js +38 -25
  167. package/dist/SchemaAST.js.map +1 -1
  168. package/dist/SchemaGetter.d.ts +8 -3
  169. package/dist/SchemaGetter.d.ts.map +1 -1
  170. package/dist/SchemaGetter.js +15 -5
  171. package/dist/SchemaGetter.js.map +1 -1
  172. package/dist/SchemaIssue.d.ts +4 -4
  173. package/dist/SchemaIssue.d.ts.map +1 -1
  174. package/dist/SchemaIssue.js +29 -51
  175. package/dist/SchemaIssue.js.map +1 -1
  176. package/dist/SchemaParser.d.ts.map +1 -1
  177. package/dist/SchemaParser.js +48 -22
  178. package/dist/SchemaParser.js.map +1 -1
  179. package/dist/SchemaRepresentation.d.ts +75 -11
  180. package/dist/SchemaRepresentation.d.ts.map +1 -1
  181. package/dist/SchemaRepresentation.js +41 -13
  182. package/dist/SchemaRepresentation.js.map +1 -1
  183. package/dist/SchemaTransformation.d.ts +5 -2
  184. package/dist/SchemaTransformation.d.ts.map +1 -1
  185. package/dist/SchemaTransformation.js +10 -3
  186. package/dist/SchemaTransformation.js.map +1 -1
  187. package/dist/Scope.d.ts +12 -2
  188. package/dist/Scope.d.ts.map +1 -1
  189. package/dist/Scope.js +5 -0
  190. package/dist/Scope.js.map +1 -1
  191. package/dist/ScopedCache.d.ts +10 -7
  192. package/dist/ScopedCache.d.ts.map +1 -1
  193. package/dist/ScopedCache.js +9 -5
  194. package/dist/ScopedCache.js.map +1 -1
  195. package/dist/SemVer.d.ts +1196 -0
  196. package/dist/SemVer.d.ts.map +1 -0
  197. package/dist/SemVer.js +756 -0
  198. package/dist/SemVer.js.map +1 -0
  199. package/dist/SemVerRange.d.ts +559 -0
  200. package/dist/SemVerRange.d.ts.map +1 -0
  201. package/dist/SemVerRange.js +551 -0
  202. package/dist/SemVerRange.js.map +1 -0
  203. package/dist/Semaphore.d.ts.map +1 -1
  204. package/dist/Semaphore.js +54 -35
  205. package/dist/Semaphore.js.map +1 -1
  206. package/dist/Sink.d.ts.map +1 -1
  207. package/dist/Sink.js +13 -4
  208. package/dist/Sink.js.map +1 -1
  209. package/dist/Stream.d.ts +235 -8
  210. package/dist/Stream.d.ts.map +1 -1
  211. package/dist/Stream.js +167 -70
  212. package/dist/Stream.js.map +1 -1
  213. package/dist/String.d.ts +7 -2
  214. package/dist/String.d.ts.map +1 -1
  215. package/dist/String.js +11 -6
  216. package/dist/String.js.map +1 -1
  217. package/dist/TextDiff.d.ts +399 -0
  218. package/dist/TextDiff.d.ts.map +1 -0
  219. package/dist/TextDiff.js +634 -0
  220. package/dist/TextDiff.js.map +1 -0
  221. package/dist/Tracer.d.ts +11 -1
  222. package/dist/Tracer.d.ts.map +1 -1
  223. package/dist/Tracer.js +6 -1
  224. package/dist/Tracer.js.map +1 -1
  225. package/dist/TxHashMap.d.ts.map +1 -1
  226. package/dist/TxHashMap.js.map +1 -1
  227. package/dist/TxHashSet.d.ts.map +1 -1
  228. package/dist/TxHashSet.js.map +1 -1
  229. package/dist/ai/AiError.d.ts +68 -4
  230. package/dist/ai/AiError.d.ts.map +1 -1
  231. package/dist/ai/AiError.js +50 -2
  232. package/dist/ai/AiError.js.map +1 -1
  233. package/dist/ai/Chat.d.ts +1 -2
  234. package/dist/ai/Chat.d.ts.map +1 -1
  235. package/dist/ai/Chat.js +6 -3
  236. package/dist/ai/Chat.js.map +1 -1
  237. package/dist/ai/Decision.d.ts +46 -1
  238. package/dist/ai/Decision.d.ts.map +1 -1
  239. package/dist/ai/Decision.js.map +1 -1
  240. package/dist/ai/DecisionModel.d.ts +129 -11
  241. package/dist/ai/DecisionModel.d.ts.map +1 -1
  242. package/dist/ai/DecisionModel.js +182 -94
  243. package/dist/ai/DecisionModel.js.map +1 -1
  244. package/dist/ai/EmbeddingModel.d.ts +10 -0
  245. package/dist/ai/EmbeddingModel.d.ts.map +1 -1
  246. package/dist/ai/EmbeddingModel.js +21 -7
  247. package/dist/ai/EmbeddingModel.js.map +1 -1
  248. package/dist/ai/LanguageModel.d.ts.map +1 -1
  249. package/dist/ai/LanguageModel.js +9 -0
  250. package/dist/ai/LanguageModel.js.map +1 -1
  251. package/dist/ai/McpClient.d.ts +289 -0
  252. package/dist/ai/McpClient.d.ts.map +1 -0
  253. package/dist/ai/McpClient.js +819 -0
  254. package/dist/ai/McpClient.js.map +1 -0
  255. package/dist/ai/McpClientError.d.ts +254 -0
  256. package/dist/ai/McpClientError.d.ts.map +1 -0
  257. package/dist/ai/McpClientError.js +260 -0
  258. package/dist/ai/McpClientError.js.map +1 -0
  259. package/dist/ai/McpProtocol.d.ts +2 -2
  260. package/dist/ai/McpProtocol.d.ts.map +1 -1
  261. package/dist/ai/McpSchema.d.ts +258 -125
  262. package/dist/ai/McpSchema.d.ts.map +1 -1
  263. package/dist/ai/McpSchema.js +94 -24
  264. package/dist/ai/McpSchema.js.map +1 -1
  265. package/dist/ai/McpServer.d.ts.map +1 -1
  266. package/dist/ai/McpServer.js +46 -37
  267. package/dist/ai/McpServer.js.map +1 -1
  268. package/dist/ai/Prompt.d.ts +74 -20
  269. package/dist/ai/Prompt.d.ts.map +1 -1
  270. package/dist/ai/Prompt.js +61 -1
  271. package/dist/ai/Prompt.js.map +1 -1
  272. package/dist/ai/Response.d.ts +160 -3
  273. package/dist/ai/Response.d.ts.map +1 -1
  274. package/dist/ai/Response.js +39 -37
  275. package/dist/ai/Response.js.map +1 -1
  276. package/dist/ai/Tool.d.ts +36 -4
  277. package/dist/ai/Tool.d.ts.map +1 -1
  278. package/dist/ai/Tool.js +5 -2
  279. package/dist/ai/Tool.js.map +1 -1
  280. package/dist/ai/Toolkit.d.ts +48 -1
  281. package/dist/ai/Toolkit.d.ts.map +1 -1
  282. package/dist/ai/Toolkit.js +73 -13
  283. package/dist/ai/Toolkit.js.map +1 -1
  284. package/dist/ai/index.d.ts +10 -0
  285. package/dist/ai/index.d.ts.map +1 -1
  286. package/dist/ai/index.js +10 -0
  287. package/dist/ai/index.js.map +1 -1
  288. package/dist/ai/internal/mcpCore.js.map +1 -1
  289. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +1 -1
  290. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  291. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +1 -1
  292. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  293. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +2 -2
  294. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  295. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +2 -2
  296. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  297. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +4 -7
  298. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  299. package/dist/ai/internal/mcpProtocol.js +16 -2
  300. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  301. package/dist/ai/internal/mcpRuntime.js +3 -4
  302. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  303. package/dist/ai/internal/mcpSchema/v2024_11_05.js +19 -13
  304. package/dist/ai/internal/mcpSchema/v2024_11_05.js.map +1 -1
  305. package/dist/ai/internal/mcpSchema/v2025_03_26.js +3 -2
  306. package/dist/ai/internal/mcpSchema/v2025_03_26.js.map +1 -1
  307. package/dist/ai/internal/mcpSchema/v2025_06_18.js +7 -7
  308. package/dist/ai/internal/mcpSchema/v2025_06_18.js.map +1 -1
  309. package/dist/ai/internal/mcpSchema/v2025_11_25.js +8 -8
  310. package/dist/ai/internal/mcpSchema/v2025_11_25.js.map +1 -1
  311. package/dist/ai/internal/mcpSchema/v2026_07_28.js +43 -42
  312. package/dist/ai/internal/mcpSchema/v2026_07_28.js.map +1 -1
  313. package/dist/cli/Argument.d.ts +15 -0
  314. package/dist/cli/Argument.d.ts.map +1 -1
  315. package/dist/cli/Argument.js +5 -0
  316. package/dist/cli/Argument.js.map +1 -1
  317. package/dist/cli/CliError.d.ts +120 -2
  318. package/dist/cli/CliError.d.ts.map +1 -1
  319. package/dist/cli/CliError.js +106 -5
  320. package/dist/cli/CliError.js.map +1 -1
  321. package/dist/cli/CliOutput.d.ts +155 -6
  322. package/dist/cli/CliOutput.d.ts.map +1 -1
  323. package/dist/cli/CliOutput.js +62 -6
  324. package/dist/cli/CliOutput.js.map +1 -1
  325. package/dist/cli/Command.d.ts +310 -18
  326. package/dist/cli/Command.d.ts.map +1 -1
  327. package/dist/cli/Command.js +164 -33
  328. package/dist/cli/Command.js.map +1 -1
  329. package/dist/cli/Completions.d.ts +63 -0
  330. package/dist/cli/Completions.d.ts.map +1 -1
  331. package/dist/cli/Completions.js +49 -9
  332. package/dist/cli/Completions.js.map +1 -1
  333. package/dist/cli/Flag.d.ts +130 -0
  334. package/dist/cli/Flag.d.ts.map +1 -1
  335. package/dist/cli/Flag.js +61 -0
  336. package/dist/cli/Flag.js.map +1 -1
  337. package/dist/cli/GlobalFlag.d.ts.map +1 -1
  338. package/dist/cli/GlobalFlag.js +14 -6
  339. package/dist/cli/GlobalFlag.js.map +1 -1
  340. package/dist/cli/Param.d.ts +116 -0
  341. package/dist/cli/Param.d.ts.map +1 -1
  342. package/dist/cli/Param.js +77 -2
  343. package/dist/cli/Param.js.map +1 -1
  344. package/dist/cli/TerminalCapabilities.d.ts +85 -0
  345. package/dist/cli/TerminalCapabilities.d.ts.map +1 -0
  346. package/dist/cli/TerminalCapabilities.js +124 -0
  347. package/dist/cli/TerminalCapabilities.js.map +1 -0
  348. package/dist/cli/index.d.ts +5 -0
  349. package/dist/cli/index.d.ts.map +1 -1
  350. package/dist/cli/index.js +5 -0
  351. package/dist/cli/index.js.map +1 -1
  352. package/dist/cli/internal/command.d.ts +2 -1
  353. package/dist/cli/internal/command.d.ts.map +1 -1
  354. package/dist/cli/internal/command.js +5 -2
  355. package/dist/cli/internal/command.js.map +1 -1
  356. package/dist/cli/internal/completions/bash.js +77 -30
  357. package/dist/cli/internal/completions/bash.js.map +1 -1
  358. package/dist/cli/internal/completions/descriptor.js +9 -1
  359. package/dist/cli/internal/completions/descriptor.js.map +1 -1
  360. package/dist/cli/internal/completions/fish.js +10 -8
  361. package/dist/cli/internal/completions/fish.js.map +1 -1
  362. package/dist/cli/internal/completions/powershell.js +18 -9
  363. package/dist/cli/internal/completions/powershell.js.map +1 -1
  364. package/dist/cli/internal/completions/zsh.js +8 -5
  365. package/dist/cli/internal/completions/zsh.js.map +1 -1
  366. package/dist/cli/internal/formatter.d.ts +2 -0
  367. package/dist/cli/internal/formatter.d.ts.map +1 -0
  368. package/dist/cli/internal/formatter.js +15 -0
  369. package/dist/cli/internal/formatter.js.map +1 -0
  370. package/dist/cli/internal/lexer.js +24 -10
  371. package/dist/cli/internal/lexer.js.map +1 -1
  372. package/dist/cli/internal/parser.js +24 -12
  373. package/dist/cli/internal/parser.js.map +1 -1
  374. package/dist/cluster/ClusterCron.d.ts +29 -1
  375. package/dist/cluster/ClusterCron.d.ts.map +1 -1
  376. package/dist/cluster/ClusterCron.js +64 -32
  377. package/dist/cluster/ClusterCron.js.map +1 -1
  378. package/dist/cluster/ClusterMetrics.d.ts +143 -7
  379. package/dist/cluster/ClusterMetrics.d.ts.map +1 -1
  380. package/dist/cluster/ClusterMetrics.js +151 -7
  381. package/dist/cluster/ClusterMetrics.js.map +1 -1
  382. package/dist/cluster/ClusterWorkflowEngine.d.ts +1 -1
  383. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  384. package/dist/cluster/ClusterWorkflowEngine.js +82 -21
  385. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  386. package/dist/cluster/Entity.d.ts +2 -0
  387. package/dist/cluster/Entity.d.ts.map +1 -1
  388. package/dist/cluster/Entity.js.map +1 -1
  389. package/dist/cluster/MessageStorage.d.ts +44 -45
  390. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  391. package/dist/cluster/MessageStorage.js +6 -7
  392. package/dist/cluster/MessageStorage.js.map +1 -1
  393. package/dist/cluster/Runners.d.ts.map +1 -1
  394. package/dist/cluster/Runners.js +54 -15
  395. package/dist/cluster/Runners.js.map +1 -1
  396. package/dist/cluster/Sharding.d.ts +1 -0
  397. package/dist/cluster/Sharding.d.ts.map +1 -1
  398. package/dist/cluster/Sharding.js +155 -52
  399. package/dist/cluster/Sharding.js.map +1 -1
  400. package/dist/cluster/ShardingConfig.d.ts +30 -0
  401. package/dist/cluster/ShardingConfig.d.ts.map +1 -1
  402. package/dist/cluster/ShardingConfig.js +4 -0
  403. package/dist/cluster/ShardingConfig.js.map +1 -1
  404. package/dist/cluster/Snowflake.d.ts.map +1 -1
  405. package/dist/cluster/Snowflake.js +3 -7
  406. package/dist/cluster/Snowflake.js.map +1 -1
  407. package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
  408. package/dist/cluster/SqlMessageStorage.js +40 -50
  409. package/dist/cluster/SqlMessageStorage.js.map +1 -1
  410. package/dist/cluster/SqlRunnerStorage.d.ts +2 -2
  411. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  412. package/dist/cluster/SqlRunnerStorage.js +97 -43
  413. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  414. package/dist/cluster/internal/entityManager.js +119 -28
  415. package/dist/cluster/internal/entityManager.js.map +1 -1
  416. package/dist/devtools/DevTools.d.ts +3 -2
  417. package/dist/devtools/DevTools.d.ts.map +1 -1
  418. package/dist/devtools/DevTools.js +3 -2
  419. package/dist/devtools/DevTools.js.map +1 -1
  420. package/dist/devtools/DevToolsClient.d.ts +11 -0
  421. package/dist/devtools/DevToolsClient.d.ts.map +1 -1
  422. package/dist/devtools/DevToolsClient.js +33 -4
  423. package/dist/devtools/DevToolsClient.js.map +1 -1
  424. package/dist/encoding/Base64.js +36 -0
  425. package/dist/encoding/Base64.js.map +1 -1
  426. package/dist/encoding/Base64Url.d.ts +29 -0
  427. package/dist/encoding/Base64Url.d.ts.map +1 -1
  428. package/dist/encoding/Base64Url.js +29 -0
  429. package/dist/encoding/Base64Url.js.map +1 -1
  430. package/dist/encoding/Ini.d.ts.map +1 -1
  431. package/dist/encoding/Ini.js +3 -0
  432. package/dist/encoding/Ini.js.map +1 -1
  433. package/dist/encoding/JsonArray.d.ts +270 -0
  434. package/dist/encoding/JsonArray.d.ts.map +1 -0
  435. package/dist/encoding/JsonArray.js +691 -0
  436. package/dist/encoding/JsonArray.js.map +1 -0
  437. package/dist/encoding/Ndjson.d.ts +43 -4
  438. package/dist/encoding/Ndjson.d.ts.map +1 -1
  439. package/dist/encoding/Ndjson.js +169 -18
  440. package/dist/encoding/Ndjson.js.map +1 -1
  441. package/dist/encoding/SchemaBinary.d.ts +9 -1
  442. package/dist/encoding/SchemaBinary.d.ts.map +1 -1
  443. package/dist/encoding/SchemaBinary.js +12 -19
  444. package/dist/encoding/SchemaBinary.js.map +1 -1
  445. package/dist/encoding/Sse.d.ts.map +1 -1
  446. package/dist/encoding/Sse.js +3 -3
  447. package/dist/encoding/Sse.js.map +1 -1
  448. package/dist/encoding/Tar.d.ts +309 -0
  449. package/dist/encoding/Tar.d.ts.map +1 -0
  450. package/dist/encoding/Tar.js +1182 -0
  451. package/dist/encoding/Tar.js.map +1 -0
  452. package/dist/encoding/Toml.d.ts.map +1 -1
  453. package/dist/encoding/Toml.js +24 -19
  454. package/dist/encoding/Toml.js.map +1 -1
  455. package/dist/encoding/Xml.d.ts +464 -0
  456. package/dist/encoding/Xml.d.ts.map +1 -0
  457. package/dist/encoding/Xml.js +1619 -0
  458. package/dist/encoding/Xml.js.map +1 -0
  459. package/dist/encoding/Yaml.d.ts +6 -2
  460. package/dist/encoding/Yaml.d.ts.map +1 -1
  461. package/dist/encoding/Yaml.js +97 -34
  462. package/dist/encoding/Yaml.js.map +1 -1
  463. package/dist/encoding/Zip.d.ts +413 -0
  464. package/dist/encoding/Zip.d.ts.map +1 -0
  465. package/dist/encoding/Zip.js +1255 -0
  466. package/dist/encoding/Zip.js.map +1 -0
  467. package/dist/encoding/index.d.ts +20 -0
  468. package/dist/encoding/index.d.ts.map +1 -1
  469. package/dist/encoding/index.js +20 -0
  470. package/dist/encoding/index.js.map +1 -1
  471. package/dist/eventlog/EventJournal.d.ts +90 -5
  472. package/dist/eventlog/EventJournal.d.ts.map +1 -1
  473. package/dist/eventlog/EventJournal.js +488 -228
  474. package/dist/eventlog/EventJournal.js.map +1 -1
  475. package/dist/eventlog/EventLog.d.ts +180 -12
  476. package/dist/eventlog/EventLog.d.ts.map +1 -1
  477. package/dist/eventlog/EventLog.js +394 -146
  478. package/dist/eventlog/EventLog.js.map +1 -1
  479. package/dist/eventlog/EventLogEncryption.d.ts +36 -30
  480. package/dist/eventlog/EventLogEncryption.d.ts.map +1 -1
  481. package/dist/eventlog/EventLogEncryption.js +97 -38
  482. package/dist/eventlog/EventLogEncryption.js.map +1 -1
  483. package/dist/eventlog/EventLogMessage.d.ts +74 -34
  484. package/dist/eventlog/EventLogMessage.d.ts.map +1 -1
  485. package/dist/eventlog/EventLogMessage.js +45 -11
  486. package/dist/eventlog/EventLogMessage.js.map +1 -1
  487. package/dist/eventlog/EventLogRemote.d.ts +45 -8
  488. package/dist/eventlog/EventLogRemote.d.ts.map +1 -1
  489. package/dist/eventlog/EventLogRemote.js +51 -14
  490. package/dist/eventlog/EventLogRemote.js.map +1 -1
  491. package/dist/eventlog/EventLogServer.d.ts +46 -3
  492. package/dist/eventlog/EventLogServer.d.ts.map +1 -1
  493. package/dist/eventlog/EventLogServer.js +119 -19
  494. package/dist/eventlog/EventLogServer.js.map +1 -1
  495. package/dist/eventlog/EventLogServerEncrypted.d.ts +23 -33
  496. package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
  497. package/dist/eventlog/EventLogServerEncrypted.js +97 -70
  498. package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
  499. package/dist/eventlog/EventLogServerUnencrypted.d.ts +39 -5
  500. package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
  501. package/dist/eventlog/EventLogServerUnencrypted.js +151 -69
  502. package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
  503. package/dist/eventlog/EventRow.d.ts +288 -0
  504. package/dist/eventlog/EventRow.d.ts.map +1 -0
  505. package/dist/eventlog/EventRow.js +435 -0
  506. package/dist/eventlog/EventRow.js.map +1 -0
  507. package/dist/eventlog/SqlEventJournal.d.ts +18 -18
  508. package/dist/eventlog/SqlEventJournal.d.ts.map +1 -1
  509. package/dist/eventlog/SqlEventJournal.js +417 -169
  510. package/dist/eventlog/SqlEventJournal.js.map +1 -1
  511. package/dist/eventlog/SqlEventLogServerEncrypted.d.ts +10 -18
  512. package/dist/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -1
  513. package/dist/eventlog/SqlEventLogServerEncrypted.js +179 -69
  514. package/dist/eventlog/SqlEventLogServerEncrypted.js.map +1 -1
  515. package/dist/eventlog/SqlEventLogServerUnencrypted.d.ts +23 -3
  516. package/dist/eventlog/SqlEventLogServerUnencrypted.d.ts.map +1 -1
  517. package/dist/eventlog/SqlEventLogServerUnencrypted.js +102 -51
  518. package/dist/eventlog/SqlEventLogServerUnencrypted.js.map +1 -1
  519. package/dist/eventlog/index.d.ts +5 -0
  520. package/dist/eventlog/index.d.ts.map +1 -1
  521. package/dist/eventlog/index.js +5 -0
  522. package/dist/eventlog/index.js.map +1 -1
  523. package/dist/eventlog/internal/conflicts.d.ts +46 -0
  524. package/dist/eventlog/internal/conflicts.d.ts.map +1 -0
  525. package/dist/eventlog/internal/conflicts.js +89 -0
  526. package/dist/eventlog/internal/conflicts.js.map +1 -0
  527. package/dist/graphql/GraphQLClientError.d.ts +2 -2
  528. package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
  529. package/dist/graphql/GraphQLClientError.js +4 -18
  530. package/dist/graphql/GraphQLClientError.js.map +1 -1
  531. package/dist/graphql/GraphQLProtocol.d.ts +12 -9
  532. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
  533. package/dist/graphql/GraphQLProtocol.js +4 -3
  534. package/dist/graphql/GraphQLProtocol.js.map +1 -1
  535. package/dist/graphql/GraphQLResolver.d.ts +2 -0
  536. package/dist/graphql/GraphQLResolver.d.ts.map +1 -1
  537. package/dist/graphql/GraphQLResolver.js +74 -32
  538. package/dist/graphql/GraphQLResolver.js.map +1 -1
  539. package/dist/http/Cookies.d.ts.map +1 -1
  540. package/dist/http/Cookies.js +10 -9
  541. package/dist/http/Cookies.js.map +1 -1
  542. package/dist/http/FetchHttpClient.d.ts +8 -1
  543. package/dist/http/FetchHttpClient.d.ts.map +1 -1
  544. package/dist/http/FetchHttpClient.js +51 -4
  545. package/dist/http/FetchHttpClient.js.map +1 -1
  546. package/dist/http/FindMyWay/internal/router.js +10 -0
  547. package/dist/http/FindMyWay/internal/router.js.map +1 -1
  548. package/dist/http/Headers.d.ts +227 -0
  549. package/dist/http/Headers.d.ts.map +1 -1
  550. package/dist/http/Headers.js +261 -0
  551. package/dist/http/Headers.js.map +1 -1
  552. package/dist/http/HttpClient.d.ts +516 -0
  553. package/dist/http/HttpClient.d.ts.map +1 -1
  554. package/dist/http/HttpClient.js +286 -23
  555. package/dist/http/HttpClient.js.map +1 -1
  556. package/dist/http/HttpClientError.d.ts +8 -1
  557. package/dist/http/HttpClientError.d.ts.map +1 -1
  558. package/dist/http/HttpClientError.js +37 -1
  559. package/dist/http/HttpClientError.js.map +1 -1
  560. package/dist/http/HttpClientRequest.d.ts +127 -3
  561. package/dist/http/HttpClientRequest.d.ts.map +1 -1
  562. package/dist/http/HttpClientRequest.js +45 -3
  563. package/dist/http/HttpClientRequest.js.map +1 -1
  564. package/dist/http/HttpClientResponse.d.ts +53 -0
  565. package/dist/http/HttpClientResponse.d.ts.map +1 -1
  566. package/dist/http/HttpClientResponse.js +47 -0
  567. package/dist/http/HttpClientResponse.js.map +1 -1
  568. package/dist/http/HttpEffect.d.ts +6 -2
  569. package/dist/http/HttpEffect.d.ts.map +1 -1
  570. package/dist/http/HttpEffect.js +21 -17
  571. package/dist/http/HttpEffect.js.map +1 -1
  572. package/dist/http/HttpIncomingMessage.d.ts +2 -1
  573. package/dist/http/HttpIncomingMessage.d.ts.map +1 -1
  574. package/dist/http/HttpIncomingMessage.js +3 -23
  575. package/dist/http/HttpIncomingMessage.js.map +1 -1
  576. package/dist/http/HttpMiddleware.d.ts +119 -2
  577. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  578. package/dist/http/HttpMiddleware.js +173 -29
  579. package/dist/http/HttpMiddleware.js.map +1 -1
  580. package/dist/http/HttpRouter.d.ts.map +1 -1
  581. package/dist/http/HttpRouter.js +9 -3
  582. package/dist/http/HttpRouter.js.map +1 -1
  583. package/dist/http/HttpServerError.d.ts +5 -2
  584. package/dist/http/HttpServerError.d.ts.map +1 -1
  585. package/dist/http/HttpServerError.js +7 -5
  586. package/dist/http/HttpServerError.js.map +1 -1
  587. package/dist/http/HttpServerRequest.d.ts +30 -5
  588. package/dist/http/HttpServerRequest.d.ts.map +1 -1
  589. package/dist/http/HttpServerRequest.js +109 -20
  590. package/dist/http/HttpServerRequest.js.map +1 -1
  591. package/dist/http/HttpServerResponse.d.ts +17 -8
  592. package/dist/http/HttpServerResponse.d.ts.map +1 -1
  593. package/dist/http/HttpServerResponse.js +37 -21
  594. package/dist/http/HttpServerResponse.js.map +1 -1
  595. package/dist/http/HttpStaticServer.d.ts +0 -12
  596. package/dist/http/HttpStaticServer.d.ts.map +1 -1
  597. package/dist/http/HttpStaticServer.js +65 -24
  598. package/dist/http/HttpStaticServer.js.map +1 -1
  599. package/dist/http/MultipartParser/internal/contentType.d.ts +15 -1
  600. package/dist/http/MultipartParser/internal/contentType.d.ts.map +1 -1
  601. package/dist/http/MultipartParser/internal/contentType.js +89 -7
  602. package/dist/http/MultipartParser/internal/contentType.js.map +1 -1
  603. package/dist/http/MultipartParser/internal/headers.d.ts.map +1 -1
  604. package/dist/http/MultipartParser/internal/headers.js +4 -12
  605. package/dist/http/MultipartParser/internal/headers.js.map +1 -1
  606. package/dist/http/MultipartParser/internal/multipart.d.ts.map +1 -1
  607. package/dist/http/MultipartParser/internal/multipart.js +6 -27
  608. package/dist/http/MultipartParser/internal/multipart.js.map +1 -1
  609. package/dist/http/Url.d.ts.map +1 -1
  610. package/dist/http/Url.js +6 -1
  611. package/dist/http/Url.js.map +1 -1
  612. package/dist/http/UrlParams.d.ts +3 -1
  613. package/dist/http/UrlParams.d.ts.map +1 -1
  614. package/dist/http/UrlParams.js +4 -2
  615. package/dist/http/UrlParams.js.map +1 -1
  616. package/dist/http/internal/compression.js +4 -5
  617. package/dist/http/internal/compression.js.map +1 -1
  618. package/dist/http/internal/decompression.js +20 -11
  619. package/dist/http/internal/decompression.js.map +1 -1
  620. package/dist/http/internal/httpBody.js +8 -1
  621. package/dist/http/internal/httpBody.js.map +1 -1
  622. package/dist/http/internal/lazyStream.d.ts +2 -0
  623. package/dist/http/internal/lazyStream.d.ts.map +1 -0
  624. package/dist/http/internal/lazyStream.js +18 -0
  625. package/dist/http/internal/lazyStream.js.map +1 -0
  626. package/dist/http/internal/requestState.d.ts +2 -0
  627. package/dist/http/internal/requestState.d.ts.map +1 -0
  628. package/dist/http/internal/requestState.js +28 -0
  629. package/dist/http/internal/requestState.js.map +1 -0
  630. package/dist/http/internal/urlParams.js +25 -0
  631. package/dist/http/internal/urlParams.js.map +1 -1
  632. package/dist/http/internal/webResponse.d.ts +2 -0
  633. package/dist/http/internal/webResponse.d.ts.map +1 -0
  634. package/dist/http/internal/webResponse.js +12 -0
  635. package/dist/http/internal/webResponse.js.map +1 -0
  636. package/dist/http-api/HttpApi.d.ts +3 -6
  637. package/dist/http-api/HttpApi.d.ts.map +1 -1
  638. package/dist/http-api/HttpApi.js +4 -7
  639. package/dist/http-api/HttpApi.js.map +1 -1
  640. package/dist/http-api/HttpApiBuilder.d.ts.map +1 -1
  641. package/dist/http-api/HttpApiBuilder.js +142 -23
  642. package/dist/http-api/HttpApiBuilder.js.map +1 -1
  643. package/dist/http-api/HttpApiClient.d.ts +99 -4
  644. package/dist/http-api/HttpApiClient.d.ts.map +1 -1
  645. package/dist/http-api/HttpApiClient.js +218 -60
  646. package/dist/http-api/HttpApiClient.js.map +1 -1
  647. package/dist/http-api/HttpApiEndpoint.d.ts +72 -29
  648. package/dist/http-api/HttpApiEndpoint.d.ts.map +1 -1
  649. package/dist/http-api/HttpApiEndpoint.js +140 -14
  650. package/dist/http-api/HttpApiEndpoint.js.map +1 -1
  651. package/dist/http-api/HttpApiError.d.ts +58 -0
  652. package/dist/http-api/HttpApiError.d.ts.map +1 -1
  653. package/dist/http-api/HttpApiError.js +38 -12
  654. package/dist/http-api/HttpApiError.js.map +1 -1
  655. package/dist/http-api/HttpApiScalar.d.ts +2 -1
  656. package/dist/http-api/HttpApiScalar.d.ts.map +1 -1
  657. package/dist/http-api/HttpApiScalar.js +6 -3
  658. package/dist/http-api/HttpApiScalar.js.map +1 -1
  659. package/dist/http-api/HttpApiSchema.d.ts +242 -13
  660. package/dist/http-api/HttpApiSchema.d.ts.map +1 -1
  661. package/dist/http-api/HttpApiSchema.js +149 -5
  662. package/dist/http-api/HttpApiSchema.js.map +1 -1
  663. package/dist/http-api/HttpApiSwagger.d.ts +5 -0
  664. package/dist/http-api/HttpApiSwagger.d.ts.map +1 -1
  665. package/dist/http-api/HttpApiSwagger.js +14 -4
  666. package/dist/http-api/HttpApiSwagger.js.map +1 -1
  667. package/dist/http-api/HttpApiTest.d.ts +13 -2
  668. package/dist/http-api/HttpApiTest.d.ts.map +1 -1
  669. package/dist/http-api/HttpApiTest.js +33 -11
  670. package/dist/http-api/HttpApiTest.js.map +1 -1
  671. package/dist/http-api/OpenApi.d.ts +17 -3
  672. package/dist/http-api/OpenApi.d.ts.map +1 -1
  673. package/dist/http-api/OpenApi.js +81 -47
  674. package/dist/http-api/OpenApi.js.map +1 -1
  675. package/dist/http-api/internal/path.js +35 -10
  676. package/dist/http-api/internal/path.js.map +1 -1
  677. package/dist/http-api/internal/toCodecHttpProperties.d.ts +2 -0
  678. package/dist/http-api/internal/toCodecHttpProperties.d.ts.map +1 -0
  679. package/dist/http-api/internal/toCodecHttpProperties.js +38 -0
  680. package/dist/http-api/internal/toCodecHttpProperties.js.map +1 -0
  681. package/dist/index.d.ts +20 -0
  682. package/dist/index.d.ts.map +1 -1
  683. package/dist/index.js +20 -0
  684. package/dist/index.js.map +1 -1
  685. package/dist/internal/arbitrary/runner.d.ts.map +1 -1
  686. package/dist/internal/arbitrary/runner.js +120 -44
  687. package/dist/internal/arbitrary/runner.js.map +1 -1
  688. package/dist/internal/core.js +3 -1
  689. package/dist/internal/core.js.map +1 -1
  690. package/dist/internal/dateTime.js +19 -7
  691. package/dist/internal/dateTime.js.map +1 -1
  692. package/dist/internal/effect.js +106 -87
  693. package/dist/internal/effect.js.map +1 -1
  694. package/dist/internal/equal.js +14 -0
  695. package/dist/internal/equal.js.map +1 -1
  696. package/dist/internal/hashMap.js +2 -2
  697. package/dist/internal/hashMap.js.map +1 -1
  698. package/dist/internal/hydration.d.ts +2 -0
  699. package/dist/internal/hydration.d.ts.map +1 -0
  700. package/dist/internal/hydration.js +15 -0
  701. package/dist/internal/hydration.js.map +1 -0
  702. package/dist/internal/inflate.d.ts +2 -0
  703. package/dist/internal/inflate.d.ts.map +1 -0
  704. package/dist/internal/inflate.js +680 -0
  705. package/dist/internal/inflate.js.map +1 -0
  706. package/dist/internal/jsonSchema.d.ts +2 -0
  707. package/dist/internal/jsonSchema.d.ts.map +1 -0
  708. package/dist/internal/jsonSchema.js +28 -0
  709. package/dist/internal/jsonSchema.js.map +1 -0
  710. package/dist/internal/matcher.js +1 -1
  711. package/dist/internal/matcher.js.map +1 -1
  712. package/dist/internal/record.js +6 -0
  713. package/dist/internal/record.js.map +1 -1
  714. package/dist/internal/request.js +20 -7
  715. package/dist/internal/request.js.map +1 -1
  716. package/dist/internal/schedule.js +17 -22
  717. package/dist/internal/schedule.js.map +1 -1
  718. package/dist/internal/schema/compilerRegistry.js +4 -1
  719. package/dist/internal/schema/compilerRegistry.js.map +1 -1
  720. package/dist/internal/schema/content.d.ts +2 -0
  721. package/dist/internal/schema/content.d.ts.map +1 -0
  722. package/dist/internal/schema/content.js +13 -0
  723. package/dist/internal/schema/content.js.map +1 -0
  724. package/dist/internal/schema/fromJsonSchemaDocument.js +253 -255
  725. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  726. package/dist/internal/schema/fromRepresentation.js +2 -1
  727. package/dist/internal/schema/fromRepresentation.js.map +1 -1
  728. package/dist/internal/schema/interpreter.js +51 -36
  729. package/dist/internal/schema/interpreter.js.map +1 -1
  730. package/dist/internal/schema/standardSchema.js +42 -35
  731. package/dist/internal/schema/standardSchema.js.map +1 -1
  732. package/dist/internal/schema/toCodeDocument.js +10 -5
  733. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  734. package/dist/internal/schema/toCodec.js +99 -17
  735. package/dist/internal/schema/toCodec.js.map +1 -1
  736. package/dist/internal/schema/toFormatter.js +5 -5
  737. package/dist/internal/schema/toFormatter.js.map +1 -1
  738. package/dist/internal/schema/toJsonSchemaDocument.js +257 -170
  739. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  740. package/dist/internal/schema/toRepresentation.js +107 -129
  741. package/dist/internal/schema/toRepresentation.js.map +1 -1
  742. package/dist/internal/sql.d.ts +2 -0
  743. package/dist/internal/sql.d.ts.map +1 -0
  744. package/dist/internal/sql.js +13 -0
  745. package/dist/internal/sql.js.map +1 -0
  746. package/dist/internal/trie.js +1 -3
  747. package/dist/internal/trie.js.map +1 -1
  748. package/dist/internal/uuid.js +7 -3
  749. package/dist/internal/uuid.js.map +1 -1
  750. package/dist/internal/version.js +1 -1
  751. package/dist/net/NetAddress.d.ts.map +1 -1
  752. package/dist/net/NetAddress.js +2 -8
  753. package/dist/net/NetAddress.js.map +1 -1
  754. package/dist/observability/OtlpExporter.d.ts.map +1 -1
  755. package/dist/observability/OtlpExporter.js +5 -12
  756. package/dist/observability/OtlpExporter.js.map +1 -1
  757. package/dist/observability/OtlpMetrics.d.ts.map +1 -1
  758. package/dist/observability/OtlpMetrics.js +5 -3
  759. package/dist/observability/OtlpMetrics.js.map +1 -1
  760. package/dist/observability/OtlpProxy.d.ts +154 -0
  761. package/dist/observability/OtlpProxy.d.ts.map +1 -0
  762. package/dist/observability/OtlpProxy.js +235 -0
  763. package/dist/observability/OtlpProxy.js.map +1 -0
  764. package/dist/observability/OtlpResource.d.ts +3 -1
  765. package/dist/observability/OtlpResource.d.ts.map +1 -1
  766. package/dist/observability/OtlpResource.js +11 -4
  767. package/dist/observability/OtlpResource.js.map +1 -1
  768. package/dist/observability/OtlpSerialization.d.ts +5 -0
  769. package/dist/observability/OtlpSerialization.d.ts.map +1 -1
  770. package/dist/observability/OtlpSerialization.js +14 -3
  771. package/dist/observability/OtlpSerialization.js.map +1 -1
  772. package/dist/observability/OtlpTracer.d.ts.map +1 -1
  773. package/dist/observability/OtlpTracer.js +4 -0
  774. package/dist/observability/OtlpTracer.js.map +1 -1
  775. package/dist/observability/index.d.ts +5 -0
  776. package/dist/observability/index.d.ts.map +1 -1
  777. package/dist/observability/index.js +5 -0
  778. package/dist/observability/index.js.map +1 -1
  779. package/dist/observability/internal/protobuf.d.ts +0 -9
  780. package/dist/observability/internal/protobuf.d.ts.map +1 -1
  781. package/dist/observability/internal/protobuf.js +2 -10
  782. package/dist/observability/internal/protobuf.js.map +1 -1
  783. package/dist/persistence/CircuitBreaker.d.ts +897 -0
  784. package/dist/persistence/CircuitBreaker.d.ts.map +1 -0
  785. package/dist/persistence/CircuitBreaker.js +1165 -0
  786. package/dist/persistence/CircuitBreaker.js.map +1 -0
  787. package/dist/persistence/PersistedCache.d.ts.map +1 -1
  788. package/dist/persistence/PersistedCache.js +1 -1
  789. package/dist/persistence/PersistedCache.js.map +1 -1
  790. package/dist/persistence/PersistedQueue.d.ts +6 -2
  791. package/dist/persistence/PersistedQueue.d.ts.map +1 -1
  792. package/dist/persistence/PersistedQueue.js +162 -84
  793. package/dist/persistence/PersistedQueue.js.map +1 -1
  794. package/dist/persistence/Persistence.d.ts +28 -0
  795. package/dist/persistence/Persistence.d.ts.map +1 -1
  796. package/dist/persistence/Persistence.js +36 -8
  797. package/dist/persistence/Persistence.js.map +1 -1
  798. package/dist/persistence/RateLimiter.d.ts.map +1 -1
  799. package/dist/persistence/RateLimiter.js +8 -12
  800. package/dist/persistence/RateLimiter.js.map +1 -1
  801. package/dist/persistence/Redis.d.ts +78 -2
  802. package/dist/persistence/Redis.d.ts.map +1 -1
  803. package/dist/persistence/Redis.js +62 -7
  804. package/dist/persistence/Redis.js.map +1 -1
  805. package/dist/persistence/UserDirectories.d.ts +129 -0
  806. package/dist/persistence/UserDirectories.d.ts.map +1 -0
  807. package/dist/persistence/UserDirectories.js +178 -0
  808. package/dist/persistence/UserDirectories.js.map +1 -0
  809. package/dist/persistence/index.d.ts +10 -0
  810. package/dist/persistence/index.d.ts.map +1 -1
  811. package/dist/persistence/index.js +10 -0
  812. package/dist/persistence/index.js.map +1 -1
  813. package/dist/process/ChildProcess.d.ts +32 -5
  814. package/dist/process/ChildProcess.d.ts.map +1 -1
  815. package/dist/process/ChildProcess.js.map +1 -1
  816. package/dist/process/ChildProcessSpawner.d.ts +30 -0
  817. package/dist/process/ChildProcessSpawner.d.ts.map +1 -1
  818. package/dist/process/ChildProcessSpawner.js +5 -2
  819. package/dist/process/ChildProcessSpawner.js.map +1 -1
  820. package/dist/reactivity/AsyncResult.d.ts +1 -1
  821. package/dist/reactivity/AsyncResult.d.ts.map +1 -1
  822. package/dist/reactivity/AsyncResult.js +1 -1
  823. package/dist/reactivity/AsyncResult.js.map +1 -1
  824. package/dist/reactivity/Atom.d.ts +69 -30
  825. package/dist/reactivity/Atom.d.ts.map +1 -1
  826. package/dist/reactivity/Atom.js +40 -15
  827. package/dist/reactivity/Atom.js.map +1 -1
  828. package/dist/reactivity/AtomHttpApi.d.ts +5 -5
  829. package/dist/reactivity/AtomHttpApi.d.ts.map +1 -1
  830. package/dist/reactivity/AtomHttpApi.js +1 -1
  831. package/dist/reactivity/AtomHttpApi.js.map +1 -1
  832. package/dist/reactivity/AtomRegistry.d.ts +2 -2
  833. package/dist/reactivity/AtomRegistry.d.ts.map +1 -1
  834. package/dist/reactivity/AtomRegistry.js +44 -9
  835. package/dist/reactivity/AtomRegistry.js.map +1 -1
  836. package/dist/reactivity/AtomRpc.d.ts +3 -3
  837. package/dist/reactivity/AtomRpc.d.ts.map +1 -1
  838. package/dist/reactivity/AtomRpc.js +1 -1
  839. package/dist/reactivity/AtomRpc.js.map +1 -1
  840. package/dist/reactivity/Hydration.d.ts.map +1 -1
  841. package/dist/reactivity/Hydration.js +46 -12
  842. package/dist/reactivity/Hydration.js.map +1 -1
  843. package/dist/reactivity/Reactivity.d.ts +71 -7
  844. package/dist/reactivity/Reactivity.d.ts.map +1 -1
  845. package/dist/reactivity/Reactivity.js +21 -12
  846. package/dist/reactivity/Reactivity.js.map +1 -1
  847. package/dist/rpc/Rpc.d.ts +18 -0
  848. package/dist/rpc/Rpc.d.ts.map +1 -1
  849. package/dist/rpc/Rpc.js +20 -0
  850. package/dist/rpc/Rpc.js.map +1 -1
  851. package/dist/rpc/RpcClient.d.ts +26 -2
  852. package/dist/rpc/RpcClient.d.ts.map +1 -1
  853. package/dist/rpc/RpcClient.js +198 -61
  854. package/dist/rpc/RpcClient.js.map +1 -1
  855. package/dist/rpc/RpcSerialization.d.ts +29 -0
  856. package/dist/rpc/RpcSerialization.d.ts.map +1 -1
  857. package/dist/rpc/RpcSerialization.js +41 -13
  858. package/dist/rpc/RpcSerialization.js.map +1 -1
  859. package/dist/rpc/RpcServer.d.ts.map +1 -1
  860. package/dist/rpc/RpcServer.js +20 -19
  861. package/dist/rpc/RpcServer.js.map +1 -1
  862. package/dist/rpc/RpcTest.d.ts +1 -0
  863. package/dist/rpc/RpcTest.d.ts.map +1 -1
  864. package/dist/rpc/RpcTest.js +1 -0
  865. package/dist/rpc/RpcTest.js.map +1 -1
  866. package/dist/schema/Model.d.ts +1 -1
  867. package/dist/schema/VariantSchema.d.ts +22 -3
  868. package/dist/schema/VariantSchema.d.ts.map +1 -1
  869. package/dist/schema/VariantSchema.js +32 -18
  870. package/dist/schema/VariantSchema.js.map +1 -1
  871. package/dist/socket/Socket.d.ts +23 -1
  872. package/dist/socket/Socket.d.ts.map +1 -1
  873. package/dist/socket/Socket.js +36 -3
  874. package/dist/socket/Socket.js.map +1 -1
  875. package/dist/sql/Migrator.d.ts +2 -1
  876. package/dist/sql/Migrator.d.ts.map +1 -1
  877. package/dist/sql/Migrator.js +21 -6
  878. package/dist/sql/Migrator.js.map +1 -1
  879. package/dist/sql/SqlClient.d.ts +148 -5
  880. package/dist/sql/SqlClient.d.ts.map +1 -1
  881. package/dist/sql/SqlClient.js +155 -27
  882. package/dist/sql/SqlClient.js.map +1 -1
  883. package/dist/sql/SqlResolver.d.ts +13 -0
  884. package/dist/sql/SqlResolver.d.ts.map +1 -1
  885. package/dist/sql/SqlResolver.js +27 -4
  886. package/dist/sql/SqlResolver.js.map +1 -1
  887. package/dist/sql/Statement.d.ts +10 -1
  888. package/dist/sql/Statement.d.ts.map +1 -1
  889. package/dist/sql/Statement.js +45 -7
  890. package/dist/sql/Statement.js.map +1 -1
  891. package/dist/testing/TestClock.d.ts.map +1 -1
  892. package/dist/testing/TestClock.js +4 -2
  893. package/dist/testing/TestClock.js.map +1 -1
  894. package/dist/testing/TestSchema.d.ts +7 -1
  895. package/dist/testing/TestSchema.d.ts.map +1 -1
  896. package/dist/testing/TestSchema.js +9 -3
  897. package/dist/testing/TestSchema.js.map +1 -1
  898. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  899. package/dist/workflow/DurableDeferred.js +21 -2
  900. package/dist/workflow/DurableDeferred.js.map +1 -1
  901. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  902. package/dist/workflow/WorkflowEngine.js +18 -4
  903. package/dist/workflow/WorkflowEngine.js.map +1 -1
  904. package/package.json +13 -3
  905. package/src/Arbitrary.ts +10 -2
  906. package/src/Array.ts +128 -55
  907. package/src/Bytes.ts +111 -0
  908. package/src/Channel.ts +308 -452
  909. package/src/ChannelSchema.ts +1 -49
  910. package/src/Chunk.ts +55 -61
  911. package/src/Config.ts +12 -86
  912. package/src/ConfigProvider.ts +2 -1
  913. package/src/Cron.ts +6 -3
  914. package/src/Data.ts +60 -265
  915. package/src/Duration.ts +66 -16
  916. package/src/Effect.ts +503 -168
  917. package/src/Equal.ts +8 -1
  918. package/src/Equivalence.ts +7 -4
  919. package/src/Exit.ts +27 -12
  920. package/src/FiberHandle.ts +10 -9
  921. package/src/FiberMap.ts +13 -66
  922. package/src/FileSystem.ts +10 -0
  923. package/src/Formatter.ts +28 -13
  924. package/src/Graph.ts +171 -1263
  925. package/src/Hash.ts +4 -1
  926. package/src/HashSet.ts +0 -60
  927. package/src/Iterable.ts +15 -14
  928. package/src/JsonPatch.ts +4 -0
  929. package/src/JsonSchema.ts +40 -43
  930. package/src/Layer.ts +2 -2
  931. package/src/ManagedRuntime.ts +72 -6
  932. package/src/Metric.ts +252 -510
  933. package/src/MutableHashMap.ts +4 -473
  934. package/src/MutableList.ts +7 -5
  935. package/src/MutableRef.ts +8 -1
  936. package/src/Number.ts +2 -2
  937. package/src/Optic.ts +17 -332
  938. package/src/PartitionedSemaphore.ts +24 -13
  939. package/src/Path.ts +9 -1
  940. package/src/Pool.ts +109 -37
  941. package/src/PubSub.ts +17 -13
  942. package/src/Queue.ts +5 -37
  943. package/src/Random.ts +1 -1
  944. package/src/RcMap.ts +14 -8
  945. package/src/Record.ts +51 -0
  946. package/src/Ref.ts +15 -104
  947. package/src/References.ts +5 -4
  948. package/src/RequestResolver.ts +46 -87
  949. package/src/Resource.ts +1 -1
  950. package/src/Runtime.ts +19 -18
  951. package/src/Schedule.ts +3 -95
  952. package/src/Schema.ts +639 -381
  953. package/src/SchemaAST.ts +40 -29
  954. package/src/SchemaGetter.ts +22 -5
  955. package/src/SchemaIssue.ts +44 -53
  956. package/src/SchemaParser.ts +51 -18
  957. package/src/SchemaRepresentation.ts +95 -35
  958. package/src/SchemaTransformation.ts +10 -3
  959. package/src/Scope.ts +12 -2
  960. package/src/ScopedCache.ts +17 -142
  961. package/src/SemVer.ts +1528 -0
  962. package/src/SemVerRange.ts +883 -0
  963. package/src/Semaphore.ts +67 -40
  964. package/src/Sink.ts +21 -4
  965. package/src/Stream.ts +367 -1027
  966. package/src/String.ts +11 -8
  967. package/src/TextDiff.ts +766 -0
  968. package/src/Tracer.ts +15 -1
  969. package/src/TxHashMap.ts +1 -103
  970. package/src/TxHashSet.ts +48 -753
  971. package/src/ai/AiError.ts +66 -4
  972. package/src/ai/Chat.ts +10 -5
  973. package/src/ai/Decision.ts +49 -1
  974. package/src/ai/DecisionModel.ts +264 -108
  975. package/src/ai/EmbeddingModel.ts +25 -13
  976. package/src/ai/LanguageModel.ts +9 -0
  977. package/src/ai/McpClient.ts +1211 -0
  978. package/src/ai/McpClientError.ts +329 -0
  979. package/src/ai/McpProtocol.ts +2 -2
  980. package/src/ai/McpSchema.ts +153 -35
  981. package/src/ai/McpServer.ts +231 -233
  982. package/src/ai/Prompt.ts +114 -35
  983. package/src/ai/Response.ts +220 -65
  984. package/src/ai/Tool.ts +50 -6
  985. package/src/ai/Toolkit.ts +82 -11
  986. package/src/ai/index.ts +12 -0
  987. package/src/ai/internal/mcpCore.ts +13 -14
  988. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +1 -1
  989. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +1 -1
  990. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -3
  991. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -3
  992. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +12 -14
  993. package/src/ai/internal/mcpProtocol.ts +22 -2
  994. package/src/ai/internal/mcpRuntime.ts +3 -4
  995. package/src/ai/internal/mcpSchema/v2024_11_05.ts +20 -13
  996. package/src/ai/internal/mcpSchema/v2025_03_26.ts +4 -2
  997. package/src/ai/internal/mcpSchema/v2025_06_18.ts +7 -7
  998. package/src/ai/internal/mcpSchema/v2025_11_25.ts +8 -8
  999. package/src/ai/internal/mcpSchema/v2026_07_28.ts +44 -42
  1000. package/src/cli/Argument.ts +15 -0
  1001. package/src/cli/CliError.ts +136 -4
  1002. package/src/cli/CliOutput.ts +183 -6
  1003. package/src/cli/Command.ts +367 -48
  1004. package/src/cli/Completions.ts +56 -0
  1005. package/src/cli/Flag.ts +132 -0
  1006. package/src/cli/GlobalFlag.ts +5 -6
  1007. package/src/cli/Param.ts +151 -6
  1008. package/src/cli/TerminalCapabilities.ts +159 -0
  1009. package/src/cli/index.ts +6 -0
  1010. package/src/cli/internal/command.ts +8 -3
  1011. package/src/cli/internal/completions/bash.ts +83 -32
  1012. package/src/cli/internal/completions/descriptor.ts +11 -1
  1013. package/src/cli/internal/completions/fish.ts +10 -8
  1014. package/src/cli/internal/completions/powershell.ts +23 -14
  1015. package/src/cli/internal/completions/zsh.ts +8 -5
  1016. package/src/cli/internal/formatter.ts +18 -0
  1017. package/src/cli/internal/lexer.ts +12 -5
  1018. package/src/cli/internal/parser.ts +31 -11
  1019. package/src/cluster/ClusterCron.ts +94 -35
  1020. package/src/cluster/ClusterMetrics.ts +149 -7
  1021. package/src/cluster/ClusterWorkflowEngine.ts +130 -57
  1022. package/src/cluster/Entity.ts +4 -0
  1023. package/src/cluster/MessageStorage.ts +26 -14
  1024. package/src/cluster/Runners.ts +76 -25
  1025. package/src/cluster/Sharding.ts +200 -58
  1026. package/src/cluster/ShardingConfig.ts +20 -0
  1027. package/src/cluster/Snowflake.ts +3 -7
  1028. package/src/cluster/SqlMessageStorage.ts +58 -55
  1029. package/src/cluster/SqlRunnerStorage.ts +120 -51
  1030. package/src/cluster/internal/entityManager.ts +155 -32
  1031. package/src/devtools/DevTools.ts +3 -2
  1032. package/src/devtools/DevToolsClient.ts +29 -10
  1033. package/src/encoding/Base64.ts +38 -0
  1034. package/src/encoding/Base64Url.ts +31 -0
  1035. package/src/encoding/Ini.ts +3 -0
  1036. package/src/encoding/JsonArray.ts +849 -0
  1037. package/src/encoding/Ndjson.ts +211 -20
  1038. package/src/encoding/SchemaBinary.ts +14 -48
  1039. package/src/encoding/Sse.ts +4 -3
  1040. package/src/encoding/Tar.ts +1417 -0
  1041. package/src/encoding/Toml.ts +36 -19
  1042. package/src/encoding/Xml.ts +2037 -0
  1043. package/src/encoding/Yaml.ts +108 -36
  1044. package/src/encoding/Zip.ts +1558 -0
  1045. package/src/encoding/index.ts +24 -0
  1046. package/src/eventlog/EventJournal.ts +611 -227
  1047. package/src/eventlog/EventLog.ts +496 -196
  1048. package/src/eventlog/EventLogEncryption.ts +128 -43
  1049. package/src/eventlog/EventLogMessage.ts +62 -11
  1050. package/src/eventlog/EventLogRemote.ts +77 -21
  1051. package/src/eventlog/EventLogServer.ts +162 -20
  1052. package/src/eventlog/EventLogServerEncrypted.ts +121 -80
  1053. package/src/eventlog/EventLogServerUnencrypted.ts +180 -77
  1054. package/src/eventlog/EventRow.ts +706 -0
  1055. package/src/eventlog/SqlEventJournal.ts +496 -193
  1056. package/src/eventlog/SqlEventLogServerEncrypted.ts +212 -83
  1057. package/src/eventlog/SqlEventLogServerUnencrypted.ts +141 -62
  1058. package/src/eventlog/index.ts +6 -0
  1059. package/src/eventlog/internal/conflicts.ts +119 -0
  1060. package/src/graphql/GraphQLClientError.ts +5 -12
  1061. package/src/graphql/GraphQLProtocol.ts +12 -9
  1062. package/src/graphql/GraphQLResolver.ts +90 -239
  1063. package/src/http/Cookies.ts +10 -11
  1064. package/src/http/FetchHttpClient.ts +56 -6
  1065. package/src/http/FindMyWay/internal/router.ts +11 -0
  1066. package/src/http/Headers.ts +337 -154
  1067. package/src/http/HttpClient.ts +673 -142
  1068. package/src/http/HttpClientError.ts +35 -1
  1069. package/src/http/HttpClientRequest.ts +151 -5
  1070. package/src/http/HttpClientResponse.ts +71 -0
  1071. package/src/http/HttpEffect.ts +25 -33
  1072. package/src/http/HttpIncomingMessage.ts +3 -23
  1073. package/src/http/HttpMiddleware.ts +194 -40
  1074. package/src/http/HttpRouter.ts +10 -3
  1075. package/src/http/HttpServerError.ts +8 -5
  1076. package/src/http/HttpServerRequest.ts +172 -27
  1077. package/src/http/HttpServerResponse.ts +49 -23
  1078. package/src/http/HttpStaticServer.ts +71 -26
  1079. package/src/http/MultipartParser/internal/contentType.ts +109 -8
  1080. package/src/http/MultipartParser/internal/headers.ts +4 -16
  1081. package/src/http/MultipartParser/internal/multipart.ts +10 -32
  1082. package/src/http/Url.ts +6 -1
  1083. package/src/http/UrlParams.ts +6 -1
  1084. package/src/http/internal/compression.ts +4 -5
  1085. package/src/http/internal/decompression.ts +32 -17
  1086. package/src/http/internal/httpBody.ts +10 -2
  1087. package/src/http/internal/lazyStream.ts +25 -0
  1088. package/src/http/internal/requestState.ts +33 -0
  1089. package/src/http/internal/urlParams.ts +29 -0
  1090. package/src/http/internal/webResponse.ts +20 -0
  1091. package/src/http-api/HttpApi.ts +4 -7
  1092. package/src/http-api/HttpApiBuilder.ts +190 -32
  1093. package/src/http-api/HttpApiClient.ts +364 -90
  1094. package/src/http-api/HttpApiEndpoint.ts +248 -46
  1095. package/src/http-api/HttpApiError.ts +55 -0
  1096. package/src/http-api/HttpApiScalar.ts +4 -3
  1097. package/src/http-api/HttpApiSchema.ts +373 -18
  1098. package/src/http-api/HttpApiSwagger.ts +11 -4
  1099. package/src/http-api/HttpApiTest.ts +36 -11
  1100. package/src/http-api/OpenApi.ts +103 -58
  1101. package/src/http-api/internal/path.ts +40 -12
  1102. package/src/http-api/internal/toCodecHttpProperties.ts +56 -0
  1103. package/src/index.ts +24 -0
  1104. package/src/internal/arbitrary/runner.ts +133 -36
  1105. package/src/internal/core.ts +3 -1
  1106. package/src/internal/dateTime.ts +20 -7
  1107. package/src/internal/effect.ts +144 -100
  1108. package/src/internal/equal.ts +17 -0
  1109. package/src/internal/hashMap.ts +2 -2
  1110. package/src/internal/hydration.ts +15 -0
  1111. package/src/internal/inflate.ts +710 -0
  1112. package/src/internal/jsonSchema.ts +27 -0
  1113. package/src/internal/matcher.ts +1 -1
  1114. package/src/internal/record.ts +6 -0
  1115. package/src/internal/request.ts +21 -6
  1116. package/src/internal/schedule.ts +20 -39
  1117. package/src/internal/schema/compilerRegistry.ts +6 -1
  1118. package/src/internal/schema/content.ts +18 -0
  1119. package/src/internal/schema/fromJsonSchemaDocument.ts +458 -351
  1120. package/src/internal/schema/fromRepresentation.ts +2 -1
  1121. package/src/internal/schema/interpreter.ts +58 -45
  1122. package/src/internal/schema/standardSchema.ts +34 -32
  1123. package/src/internal/schema/toCodeDocument.ts +12 -7
  1124. package/src/internal/schema/toCodec.ts +134 -15
  1125. package/src/internal/schema/toFormatter.ts +6 -6
  1126. package/src/internal/schema/toJsonSchemaDocument.ts +246 -194
  1127. package/src/internal/schema/toRepresentation.ts +122 -140
  1128. package/src/internal/sql.ts +14 -0
  1129. package/src/internal/trie.ts +1 -3
  1130. package/src/internal/uuid.ts +15 -10
  1131. package/src/internal/version.ts +1 -1
  1132. package/src/net/NetAddress.ts +2 -9
  1133. package/src/observability/OtlpExporter.ts +5 -12
  1134. package/src/observability/OtlpMetrics.ts +5 -3
  1135. package/src/observability/OtlpProxy.ts +314 -0
  1136. package/src/observability/OtlpResource.ts +16 -6
  1137. package/src/observability/OtlpSerialization.ts +17 -3
  1138. package/src/observability/OtlpTracer.ts +4 -0
  1139. package/src/observability/index.ts +6 -0
  1140. package/src/observability/internal/protobuf.ts +2 -10
  1141. package/src/persistence/CircuitBreaker.ts +1678 -0
  1142. package/src/persistence/PersistedCache.ts +3 -1
  1143. package/src/persistence/PersistedQueue.ts +223 -153
  1144. package/src/persistence/Persistence.ts +64 -30
  1145. package/src/persistence/RateLimiter.ts +18 -21
  1146. package/src/persistence/Redis.ts +129 -8
  1147. package/src/persistence/UserDirectories.ts +273 -0
  1148. package/src/persistence/index.ts +12 -0
  1149. package/src/process/ChildProcess.ts +32 -5
  1150. package/src/process/ChildProcessSpawner.ts +41 -2
  1151. package/src/reactivity/AsyncResult.ts +5 -39
  1152. package/src/reactivity/Atom.ts +134 -166
  1153. package/src/reactivity/AtomHttpApi.ts +10 -16
  1154. package/src/reactivity/AtomRegistry.ts +50 -29
  1155. package/src/reactivity/AtomRpc.ts +6 -22
  1156. package/src/reactivity/Hydration.ts +53 -12
  1157. package/src/reactivity/Reactivity.ts +97 -39
  1158. package/src/rpc/Rpc.ts +21 -0
  1159. package/src/rpc/RpcClient.ts +283 -104
  1160. package/src/rpc/RpcSerialization.ts +53 -14
  1161. package/src/rpc/RpcServer.ts +27 -21
  1162. package/src/rpc/RpcTest.ts +3 -0
  1163. package/src/schema/VariantSchema.ts +86 -22
  1164. package/src/socket/Socket.ts +45 -20
  1165. package/src/sql/Migrator.ts +26 -11
  1166. package/src/sql/SqlClient.ts +273 -29
  1167. package/src/sql/SqlResolver.ts +32 -9
  1168. package/src/sql/Statement.ts +65 -10
  1169. package/src/testing/TestClock.ts +4 -1
  1170. package/src/testing/TestSchema.ts +9 -3
  1171. package/src/workflow/DurableDeferred.ts +23 -6
  1172. package/src/workflow/WorkflowEngine.ts +34 -10
  1173. package/dist/http/internal/preResponseHandler.d.ts +0 -2
  1174. package/dist/http/internal/preResponseHandler.d.ts.map +0 -1
  1175. package/dist/http/internal/preResponseHandler.js +0 -14
  1176. package/dist/http/internal/preResponseHandler.js.map +0 -1
  1177. package/src/http/internal/preResponseHandler.ts +0 -19
@@ -0,0 +1,1678 @@
1
+ /**
2
+ * Keyed circuit breakers for calls to unreliable dependencies.
3
+ *
4
+ * A `CircuitBreaker` holds one circuit per key, so one policy can guard many
5
+ * hosts or tenants. State lives in a `CircuitBreakerStore`: `layerStoreMemory`
6
+ * keeps it in the process, `layerStoreRedis` shares it between processes.
7
+ *
8
+ * **Details**
9
+ *
10
+ * - Closed: calls run, and the trip policy decides when the circuit opens.
11
+ * - Open: calls fail with `CircuitOpen` until the open time ends.
12
+ * - HalfOpen: the next call is the probe, and other calls fail with
13
+ * `CircuitHalfOpen` while it runs. A successful probe closes the circuit and
14
+ * a failed one reopens it. An interrupted or `Ignored` probe frees the slot.
15
+ * - The backoff count survives a close. It resets once the circuit has stayed
16
+ * closed as long as it was last open.
17
+ *
18
+ * The breaker never waits. Retry rejections outside it, as in "Retrying
19
+ * rejections" below. Put `Effect.retry` and `Effect.timeout` inside it, so it
20
+ * sees one outcome per call. With `ExecutionPlan`, put a breaker inside each
21
+ * step under its own key.
22
+ *
23
+ * The breaker emits no metrics or spans. Observe it through `state(key)`,
24
+ * `transitions` and the typed error.
25
+ *
26
+ * **Gotchas**
27
+ *
28
+ * - Use one policy per key. Breakers that share a key and a store share one
29
+ * circuit, and nothing checks that their policies match. Separate them with
30
+ * distinct keys or a Redis `prefix`.
31
+ * - Calls admitted before a trip still run to completion, but their outcomes
32
+ * change nothing.
33
+ * - "Consecutive" counts in order of completion, not of start.
34
+ * - `state(key)` reads the shared store. `transitions` only shows what this
35
+ * breaker instance did.
36
+ * - Store errors the breaker swallows (a fail-open admit, a failed record, a
37
+ * failed Redis lease renewal) are reported with their cause at
38
+ * `References.UnhandledLogLevel`. Set it to `undefined` to silence them.
39
+ * - Cap an exponential `openDuration`, with `Schedule.upTo` or with
40
+ * `Schedule.min` and a `Schedule.spaced`, or it keeps growing for a
41
+ * dependency that flaps.
42
+ *
43
+ * **Example** (Rejecting calls once a circuit opens)
44
+ *
45
+ * ```ts import.meta.vitest
46
+ * import { Data, Effect } from "effect"
47
+ * import { CircuitBreaker } from "effect/persistence"
48
+ *
49
+ * class PaymentsError extends Data.TaggedError("PaymentsError") {}
50
+ *
51
+ * const program = Effect.gen(function*() {
52
+ * const breaker = yield* CircuitBreaker.make({
53
+ * tripPolicy: CircuitBreaker.consecutive(2),
54
+ * openDuration: "30 seconds"
55
+ * })
56
+ * const charge = Effect.fail(new PaymentsError()).pipe(
57
+ * breaker.execute({ key: "payments" })
58
+ * )
59
+ *
60
+ * // Two failures in a row open the circuit.
61
+ * yield* Effect.flip(charge)
62
+ * yield* Effect.flip(charge)
63
+ *
64
+ * // The third call is rejected without running.
65
+ * return yield* charge.pipe(
66
+ * Effect.catchReason("CircuitBreakerError", "CircuitOpen", (reason) => Effect.succeed(`${reason.key} is open`))
67
+ * )
68
+ * }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
69
+ *
70
+ * await Effect.runPromise(program) // => "payments is open"
71
+ * ```
72
+ *
73
+ * **Example** (Retrying rejections)
74
+ *
75
+ * For `CircuitOpen`, wait at least the rejection's `retryAfter`. For
76
+ * `CircuitHalfOpen`, the jittered backoff keeps waiting callers from retrying
77
+ * at the same moment. Store errors and the call's own errors are not retried.
78
+ *
79
+ * ```ts import.meta.vitest
80
+ * import { Data, Duration, Effect, Ref, Schedule } from "effect"
81
+ * import { CircuitBreaker } from "effect/persistence"
82
+ *
83
+ * const isRejection = (error: unknown): error is CircuitBreaker.CircuitBreakerError =>
84
+ * error instanceof CircuitBreaker.CircuitBreakerError && error.reason._tag !== "CircuitBreakerStoreError"
85
+ *
86
+ * const retryRejections = Schedule.exponential("20 millis").pipe(
87
+ * Schedule.jittered,
88
+ * Schedule.modifyDelay(({ duration, input }) =>
89
+ * Effect.succeed(
90
+ * isRejection(input) && input.reason._tag === "CircuitOpen"
91
+ * ? Duration.max(duration, input.reason.retryAfter)
92
+ * : duration
93
+ * )
94
+ * )
95
+ * )
96
+ *
97
+ * class QuoteError extends Data.TaggedError("QuoteError") {}
98
+ *
99
+ * const program = Effect.gen(function*() {
100
+ * const breaker = yield* CircuitBreaker.make({
101
+ * tripPolicy: CircuitBreaker.consecutive(1),
102
+ * openDuration: "50 millis"
103
+ * })
104
+ * const healthy = yield* Ref.make(false)
105
+ * const quote = Effect.flatMap(Ref.get(healthy), (ok) => ok ? Effect.succeed(42) : Effect.fail(new QuoteError()))
106
+ *
107
+ * // One failure opens the circuit, then the dependency recovers.
108
+ * yield* Effect.flip(quote.pipe(breaker.execute({ key: "quotes" })))
109
+ * yield* Ref.set(healthy, true)
110
+ *
111
+ * // Rejected while open, then retried once the circuit lets a probe through.
112
+ * return yield* quote.pipe(
113
+ * breaker.execute({ key: "quotes" }),
114
+ * Effect.retry({ schedule: retryRejections, while: isRejection, times: 5 })
115
+ * )
116
+ * }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
117
+ *
118
+ * await Effect.runPromise(program) // => 42
119
+ * ```
120
+ *
121
+ * @stability experimental
122
+ * @since 4.0.6
123
+ */
124
+ import * as Cause from "../Cause.ts"
125
+ import * as Clock from "../Clock.ts"
126
+ import * as Config from "../Config.ts"
127
+ import * as Context from "../Context.ts"
128
+ import * as Duration from "../Duration.ts"
129
+ import * as Effect from "../Effect.ts"
130
+ import * as Exit from "../Exit.ts"
131
+ import { dual } from "../Function.ts"
132
+ import { findRecoverableError } from "../internal/effect.ts"
133
+ import * as Layer from "../Layer.ts"
134
+ import * as PubSub from "../PubSub.ts"
135
+ import { UnhandledLogLevel } from "../References.ts"
136
+ import * as Schedule from "../Schedule.ts"
137
+ import * as Schema from "../Schema.ts"
138
+ import * as Scope from "../Scope.ts"
139
+ import * as Stream from "../Stream.ts"
140
+ import type * as Take from "../Take.ts"
141
+ import * as Redis from "./Redis.ts"
142
+
143
+ /**
144
+ * Runtime type identifier for `CircuitBreaker` values.
145
+ *
146
+ * @stability experimental
147
+ * @category type IDs
148
+ * @since 4.0.6
149
+ */
150
+ export const TypeId: TypeId = "~effect/persistence/CircuitBreaker"
151
+
152
+ /**
153
+ * Type-level identifier used to brand `CircuitBreaker` values.
154
+ *
155
+ * @stability experimental
156
+ * @category type IDs
157
+ * @since 4.0.6
158
+ */
159
+ export type TypeId = "~effect/persistence/CircuitBreaker"
160
+
161
+ /**
162
+ * Service that runs effects through keyed circuits and reports their state.
163
+ *
164
+ * **Details**
165
+ *
166
+ * - `execute` runs the call through the circuit for `key` and records its
167
+ * outcome. `classify` maps the call's `Exit` to an `Outcome`, directly or as
168
+ * an effect. By default a success is a `Success` and any failure, defects
169
+ * included, is a `Failure`, since a rejected `Effect.promise` only surfaces
170
+ * as a defect. An exit that is only interruption is always `Ignored`.
171
+ * - `state` reads the shared state of a circuit.
172
+ * - `transitions` emits this breaker's transitions and ends when its scope
173
+ * closes.
174
+ *
175
+ * **Gotchas**
176
+ *
177
+ * - If `classify` throws or dies, the outcome is `Ignored` and the call dies.
178
+ * - Bound an effectful `classify` with `Effect.timeoutOrElse` or
179
+ * `Effect.timeoutOption`, since `Effect.timeout` adds a typed error. A call
180
+ * to the same key from inside `classify` is rejected while its call is the
181
+ * probe.
182
+ *
183
+ * **Example** (Timing out calls inside the breaker)
184
+ *
185
+ * A timeout inside the breaker counts as a `Failure`. Outside, it interrupts
186
+ * the call, which is `Ignored`.
187
+ *
188
+ * ```ts import.meta.vitest
189
+ * import { Effect } from "effect"
190
+ * import { CircuitBreaker } from "effect/persistence"
191
+ *
192
+ * const program = Effect.gen(function*() {
193
+ * const breaker = yield* CircuitBreaker.make({
194
+ * tripPolicy: CircuitBreaker.consecutive(1),
195
+ * openDuration: "1 minute"
196
+ * })
197
+ *
198
+ * yield* Effect.never.pipe(
199
+ * Effect.timeout("10 millis"),
200
+ * breaker.execute({ key: "inside" }),
201
+ * Effect.ignore
202
+ * )
203
+ * yield* Effect.never.pipe(
204
+ * breaker.execute({ key: "outside" }),
205
+ * Effect.timeout("10 millis"),
206
+ * Effect.ignore
207
+ * )
208
+ *
209
+ * const inside = yield* breaker.state("inside")
210
+ * const outside = yield* breaker.state("outside")
211
+ * return [inside._tag, outside._tag]
212
+ * }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
213
+ *
214
+ * await Effect.runPromise(program) // => ["Open", "Closed"]
215
+ * ```
216
+ *
217
+ * **Example** (Retrying inside the breaker)
218
+ *
219
+ * The breaker sees one outcome per call, so a call a retry rescued counts as
220
+ * one `Success`.
221
+ *
222
+ * ```ts import.meta.vitest
223
+ * import { Data, Effect, Ref, Schedule } from "effect"
224
+ * import { CircuitBreaker } from "effect/persistence"
225
+ *
226
+ * class InventoryError extends Data.TaggedError("InventoryError") {}
227
+ *
228
+ * const program = Effect.gen(function*() {
229
+ * const breaker = yield* CircuitBreaker.make({
230
+ * tripPolicy: CircuitBreaker.consecutive(1),
231
+ * openDuration: "1 minute"
232
+ * })
233
+ * const attempts = yield* Ref.make(0)
234
+ * // Fails twice, then succeeds.
235
+ * const stock = Effect.flatMap(
236
+ * Ref.updateAndGet(attempts, (n) => n + 1),
237
+ * (n) => n <= 2 ? Effect.fail(new InventoryError()) : Effect.succeed(7)
238
+ * )
239
+ *
240
+ * const result = yield* stock.pipe(
241
+ * Effect.retry({ schedule: Schedule.exponential("1 millis"), times: 3 }),
242
+ * breaker.execute({ key: "inventory" })
243
+ * )
244
+ * const state = yield* breaker.state("inventory")
245
+ * return [result, state._tag]
246
+ * }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
247
+ *
248
+ * await Effect.runPromise(program) // => [7, "Closed"]
249
+ * ```
250
+ *
251
+ * **Example** (Falling back to another provider with ExecutionPlan)
252
+ *
253
+ * Each provider guards its calls under its own key, so once the primary's
254
+ * circuit opens its step fails fast and the plan moves to the fallback.
255
+ *
256
+ * ```ts import.meta.vitest
257
+ * import { Context, Data, Effect, ExecutionPlan, Layer } from "effect"
258
+ * import { CircuitBreaker } from "effect/persistence"
259
+ *
260
+ * class GeocoderError extends Data.TaggedError("GeocoderError") {}
261
+ *
262
+ * class Geocoder extends Context.Service<Geocoder, {
263
+ * readonly lookup: (address: string) => Effect.Effect<string, GeocoderError | CircuitBreaker.CircuitBreakerError>
264
+ * }>()("app/Geocoder") {}
265
+ *
266
+ * const provider = (name: string, lookup: (address: string) => Effect.Effect<string, GeocoderError>) =>
267
+ * Layer.effect(
268
+ * Geocoder,
269
+ * Effect.gen(function*() {
270
+ * const breaker = yield* CircuitBreaker.CircuitBreaker
271
+ * return Geocoder.of({
272
+ * lookup: (address) => lookup(address).pipe(breaker.execute({ key: `geocoder:${name}` }))
273
+ * })
274
+ * })
275
+ * )
276
+ *
277
+ * const Primary = provider("primary", () => Effect.fail(new GeocoderError()))
278
+ * const Fallback = provider("fallback", (address) => Effect.succeed(`${address} (fallback)`))
279
+ *
280
+ * const plan = ExecutionPlan.make({ provide: Primary }, { provide: Fallback })
281
+ *
282
+ * const program = Effect.gen(function*() {
283
+ * const geocoder = yield* Geocoder
284
+ * return yield* geocoder.lookup("1 Main St")
285
+ * }).pipe(
286
+ * Effect.withExecutionPlan(plan),
287
+ * Effect.provide(
288
+ * CircuitBreaker.layer({ tripPolicy: CircuitBreaker.consecutive(3), openDuration: "30 seconds" }).pipe(
289
+ * Layer.provide(CircuitBreaker.layerStoreMemory())
290
+ * )
291
+ * )
292
+ * )
293
+ *
294
+ * await Effect.runPromise(program) // => "1 Main St (fallback)"
295
+ * ```
296
+ *
297
+ * **Example** (Counting transitions)
298
+ *
299
+ * Label by `from` and `to`, not `key`, to keep the number of series fixed.
300
+ *
301
+ * ```ts import.meta.vitest
302
+ * import { Effect, Metric, Stream } from "effect"
303
+ * import { CircuitBreaker } from "effect/persistence"
304
+ *
305
+ * const transitionsTotal = Metric.counter("circuit_breaker_transitions_total", { incremental: true })
306
+ *
307
+ * const countTransitions = (breaker: CircuitBreaker.CircuitBreaker) =>
308
+ * breaker.transitions.pipe(
309
+ * Stream.runForEach((transition) =>
310
+ * Metric.update(Metric.withAttributes(transitionsTotal, { from: transition.from, to: transition.to }), 1)
311
+ * ),
312
+ * Effect.forkScoped({ startImmediately: true })
313
+ * )
314
+ *
315
+ * const program = Effect.gen(function*() {
316
+ * const breaker = yield* CircuitBreaker.make({
317
+ * tripPolicy: CircuitBreaker.consecutive(1),
318
+ * openDuration: "1 minute"
319
+ * })
320
+ * yield* countTransitions(breaker)
321
+ *
322
+ * yield* Effect.fail("unavailable").pipe(breaker.execute({ key: "a" }), Effect.ignore)
323
+ * yield* Effect.fail("unavailable").pipe(breaker.execute({ key: "b" }), Effect.ignore)
324
+ * yield* Effect.yieldNow
325
+ *
326
+ * const opened = yield* Metric.value(Metric.withAttributes(transitionsTotal, { from: "Closed", to: "Open" }))
327
+ * return opened.count
328
+ * }).pipe(
329
+ * Effect.scoped,
330
+ * Effect.provide(CircuitBreaker.layerStoreMemory()),
331
+ * Effect.provideService(Metric.MetricRegistry, new Map())
332
+ * )
333
+ *
334
+ * await Effect.runPromise(program) // => 2
335
+ * ```
336
+ *
337
+ * @stability experimental
338
+ * @category models
339
+ * @since 4.0.6
340
+ */
341
+ export interface CircuitBreaker {
342
+ readonly [TypeId]: TypeId
343
+
344
+ readonly execute: {
345
+ <A, E, R2 = never>(options: {
346
+ readonly key: string
347
+ readonly classify?: ((exit: Exit.Exit<A, E>) => Outcome | Effect.Effect<Outcome, never, R2>) | undefined
348
+ }): <R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E | CircuitBreakerError, R | R2>
349
+ <A, E, R, R2 = never>(
350
+ self: Effect.Effect<A, E, R>,
351
+ options: {
352
+ readonly key: string
353
+ readonly classify?: ((exit: Exit.Exit<A, E>) => Outcome | Effect.Effect<Outcome, never, R2>) | undefined
354
+ }
355
+ ): Effect.Effect<A, E | CircuitBreakerError, R | R2>
356
+ }
357
+
358
+ readonly state: (key: string) => Effect.Effect<State, CircuitBreakerError>
359
+
360
+ readonly transitions: Stream.Stream<Transition>
361
+ }
362
+
363
+ /**
364
+ * Service tag for the default `CircuitBreaker`.
365
+ *
366
+ * **When to use**
367
+ *
368
+ * Use when one breaker policy serves the whole context. For several policies,
369
+ * use a `LayerMap` keyed by policy, with every entry sharing one store, or
370
+ * define your own tags.
371
+ *
372
+ * **Example** (Several breaker policies with LayerMap)
373
+ *
374
+ * ```ts import.meta.vitest
375
+ * import { Effect, LayerMap, Schedule } from "effect"
376
+ * import { CircuitBreaker } from "effect/persistence"
377
+ *
378
+ * class Breakers extends LayerMap.Service<Breakers>()("app/Breakers", {
379
+ * lookup: (dependency: "payments" | "search") =>
380
+ * dependency === "payments"
381
+ * ? CircuitBreaker.layer({ tripPolicy: CircuitBreaker.consecutive(5), openDuration: "30 seconds" })
382
+ * : CircuitBreaker.layer({
383
+ * tripPolicy: CircuitBreaker.rate({ threshold: 0.5, size: 20 }),
384
+ * openDuration: Schedule.exponential("1 second")
385
+ * }),
386
+ * // One store shared by every entry.
387
+ * dependencies: [CircuitBreaker.layerStoreMemory()],
388
+ * idleTimeToLive: "10 minutes"
389
+ * }) {}
390
+ *
391
+ * const search = Effect.gen(function*() {
392
+ * const breaker = yield* CircuitBreaker.CircuitBreaker
393
+ * return yield* Effect.succeed(["result"]).pipe(breaker.execute({ key: "search:eu" }))
394
+ * }).pipe(Effect.provide(Breakers.get("search")))
395
+ *
396
+ * await Effect.runPromise(search.pipe(Effect.provide(Breakers.layer))) // => ["result"]
397
+ * ```
398
+ *
399
+ * @stability experimental
400
+ * @category services
401
+ * @since 4.0.6
402
+ */
403
+ export const CircuitBreaker: Context.Service<CircuitBreaker, CircuitBreaker> = Context.Service<CircuitBreaker>(
404
+ TypeId
405
+ )
406
+
407
+ /**
408
+ * Creates a `CircuitBreaker` from the current `CircuitBreakerStore`.
409
+ *
410
+ * **Details**
411
+ *
412
+ * - `tripPolicy` decides when a Closed circuit opens.
413
+ * - `openDuration` is a duration or a `Schedule`. The n-th consecutive opening
414
+ * lasts the schedule's n-th delay, and the last delay repeats once the
415
+ * schedule ends. Elapsed-time combinators such as `upTo` and `during`
416
+ * measure the time spent open, not wall time. Schedules that read the
417
+ * current date, such as `cron`, are not supported.
418
+ * - `maxOpenDuration` (default one hour) caps a `Retry-After` hint. It never
419
+ * caps `openDuration`.
420
+ * - `onStoreError` decides what a call does when the store cannot admit it:
421
+ * `"allow"` (the default) runs it unrecorded, `"fail"` fails it with
422
+ * `CircuitBreakerStoreError`.
423
+ *
424
+ * The backoff count resets once the circuit has stayed closed as long as its
425
+ * last opening, `Retry-After` hint included.
426
+ *
427
+ * **Example** (Capping backoff growth by time spent open)
428
+ *
429
+ * ```ts import.meta.vitest
430
+ * import { Effect, Schedule } from "effect"
431
+ * import { CircuitBreaker } from "effect/persistence"
432
+ *
433
+ * // Openings last 1, 2, 4, 8, 16 and 32 seconds. By then 63 seconds have been
434
+ * // spent open, past the one-minute cap, so later openings stay at 32 seconds.
435
+ * const program = Effect.gen(function*() {
436
+ * const breaker = yield* CircuitBreaker.make({
437
+ * tripPolicy: CircuitBreaker.consecutive(5),
438
+ * openDuration: Schedule.exponential("1 second").pipe(
439
+ * Schedule.upTo({ duration: "1 minute" })
440
+ * )
441
+ * })
442
+ * return yield* Effect.succeed("ok").pipe(breaker.execute({ key: "payments" }))
443
+ * }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
444
+ *
445
+ * await Effect.runPromise(program) // => "ok"
446
+ * ```
447
+ *
448
+ * @stability experimental
449
+ * @category constructors
450
+ * @since 4.0.6
451
+ */
452
+ export const make: (options: {
453
+ readonly tripPolicy: TripPolicy
454
+ readonly openDuration: Duration.Input | Schedule.Schedule<unknown, number, never, never>
455
+ readonly maxOpenDuration?: Duration.Input | undefined
456
+ readonly onStoreError?: "allow" | "fail" | undefined
457
+ }) => Effect.Effect<CircuitBreaker, never, CircuitBreakerStore | Scope.Scope> = Effect.fnUntraced(
458
+ function*(options: Parameters<typeof make>[0]) {
459
+ const store = yield* CircuitBreakerStore
460
+ const scope = yield* Effect.scope
461
+ const pubsub = yield* PubSub.sliding<Take.Take<Transition>>(128)
462
+ yield* Scope.addFinalizer(scope, PubSub.end(pubsub, Exit.void))
463
+
464
+ const maxOpenDuration = Duration.fromInputUnsafe(options.maxOpenDuration ?? "1 hour")
465
+ const onStoreError = options.onStoreError ?? "allow"
466
+ const openDurationFor = makeOpenDuration(options.openDuration)
467
+
468
+ const publish = (transition: Transition | undefined) =>
469
+ transition === undefined ? Effect.void : PubSub.publish(pubsub, [transition])
470
+
471
+ // Only an outcome that can open the circuit needs the schedule replayed.
472
+ // An Ignored outcome never opens it, and neither does a Success unless it
473
+ // can trip a rate policy: a probe's Success closes the circuit. Only a
474
+ // Failure carries a hint.
475
+ const openFor = (outcome: Outcome, admission: Admission): Effect.Effect<Duration.Duration> =>
476
+ outcome._tag === "Ignored" ||
477
+ (outcome._tag === "Success" && (admission.probe || options.tripPolicy._tag === "Consecutive"))
478
+ ? Effect.succeed(Duration.zero)
479
+ : Effect.map(
480
+ openDurationFor(admission.consecutiveOpenings + 1),
481
+ (delay) =>
482
+ outcome._tag === "Failure" && outcome.retryAfter !== undefined
483
+ ? Duration.max(delay, Duration.min(outcome.retryAfter, maxOpenDuration))
484
+ : delay
485
+ )
486
+
487
+ const execute = <A, E, R, R2>(
488
+ self: Effect.Effect<A, E, R>,
489
+ { classify, key }: {
490
+ readonly key: string
491
+ readonly classify?: ((exit: Exit.Exit<A, E>) => Outcome | Effect.Effect<Outcome, never, R2>) | undefined
492
+ }
493
+ ): Effect.Effect<A, E | CircuitBreakerError, R | R2> =>
494
+ Effect.uninterruptibleMask((restore) =>
495
+ Effect.scopedWith(Effect.fnUntraced(function*(admitScope) {
496
+ const admitted = yield* Effect.exit(Scope.provide(store.admit(key), admitScope))
497
+ if (Exit.isFailure(admitted)) {
498
+ const error = Cause.findErrorOption(admitted.cause)
499
+ if (
500
+ onStoreError === "fail" || error._tag === "None" ||
501
+ error.value.reason._tag !== "CircuitBreakerStoreError"
502
+ ) {
503
+ return yield* Effect.failCause(admitted.cause)
504
+ }
505
+ yield* reportUnhandled("CircuitBreaker store unavailable, running the call unrecorded", admitted.cause)
506
+ return yield* restore(self)
507
+ }
508
+ const admission = admitted.value
509
+ yield* publish(admission.transition)
510
+ const exit = yield* Effect.exit(restore(self))
511
+ const classified = yield* classifyExit(exit, classify, restore)
512
+ const outcome = Exit.isSuccess(classified) ? classified.value : ignored
513
+ yield* store.record({
514
+ key,
515
+ generation: admission.generation,
516
+ outcome: outcome._tag,
517
+ tripPolicy: options.tripPolicy,
518
+ openFor: yield* openFor(outcome, admission)
519
+ }).pipe(
520
+ Effect.flatMap(publish),
521
+ Effect.catchCauseFilter(
522
+ findRecoverableError,
523
+ (_, cause) => reportUnhandled("CircuitBreaker failed to record an outcome", cause)
524
+ )
525
+ )
526
+ if (Exit.isSuccess(classified)) return yield* exit
527
+ return yield* Effect.failCause(
528
+ Exit.isFailure(exit) ? Cause.combine(exit.cause, classified.cause) : classified.cause
529
+ )
530
+ }))
531
+ )
532
+
533
+ return CircuitBreaker.of({
534
+ [TypeId]: TypeId,
535
+ execute: dual(2, execute),
536
+ state: store.state,
537
+ transitions: Stream.fromPubSubTake(pubsub)
538
+ })
539
+ }
540
+ )
541
+
542
+ /**
543
+ * Provides `CircuitBreaker` using the current `CircuitBreakerStore`.
544
+ *
545
+ * **Details**
546
+ *
547
+ * Takes the same options as `make`.
548
+ *
549
+ * @stability experimental
550
+ * @category layers
551
+ * @since 4.0.6
552
+ */
553
+ export const layer = (options: {
554
+ readonly tripPolicy: TripPolicy
555
+ readonly openDuration: Duration.Input | Schedule.Schedule<unknown, number, never, never>
556
+ readonly maxOpenDuration?: Duration.Input | undefined
557
+ readonly onStoreError?: "allow" | "fail" | undefined
558
+ }): Layer.Layer<CircuitBreaker, never, CircuitBreakerStore> => Layer.effect(CircuitBreaker, make(options))
559
+
560
+ // Reports an error that has no caller to receive it at `UnhandledLogLevel`, as
561
+ // `Pool` and `ScopedCache` do. An undefined level silences it. Callers recover
562
+ // with `findRecoverableError`, as `Effect.catch` does, so a cause that holds a
563
+ // defect still propagates.
564
+ const reportUnhandled = (message: string, cause: Cause.Cause<unknown>): Effect.Effect<void> =>
565
+ Effect.withFiber((fiber) => {
566
+ const level = fiber.getRef(UnhandledLogLevel)
567
+ return level === undefined ? Effect.void : Effect.logWithLevel(level)(message, cause)
568
+ })
569
+
570
+ const classifyExit = <A, E, R2>(
571
+ exit: Exit.Exit<A, E>,
572
+ classify: ((exit: Exit.Exit<A, E>) => Outcome | Effect.Effect<Outcome, never, R2>) | undefined,
573
+ restore: <AX, EX, RX>(effect: Effect.Effect<AX, EX, RX>) => Effect.Effect<AX, EX, RX>
574
+ ): Effect.Effect<Exit.Exit<Outcome>, never, R2> => {
575
+ if (Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)) {
576
+ return Effect.succeed(Exit.succeed(ignored))
577
+ }
578
+ if (classify === undefined) {
579
+ return Effect.succeed(Exit.succeed(Exit.isSuccess(exit) ? success : failure()))
580
+ }
581
+ return Effect.exit(restore(Effect.suspend(() => {
582
+ const outcome = classify(exit)
583
+ return Effect.isEffect(outcome) ? outcome : Effect.succeed(outcome)
584
+ })))
585
+ }
586
+
587
+ const makeOpenDuration = (
588
+ openDuration: Duration.Input | Schedule.Schedule<unknown, number, never, never>
589
+ ): (consecutiveOpenings: number) => Effect.Effect<Duration.Duration> => {
590
+ if (!Schedule.isSchedule(openDuration)) {
591
+ const duration = Effect.succeed(Duration.fromInputUnsafe(openDuration))
592
+ return () => duration
593
+ }
594
+ // Replays the schedule against a simulated clock, so `upTo` and `during`
595
+ // measure the time spent open rather than wall time.
596
+ return (consecutiveOpenings) =>
597
+ Effect.gen(function*() {
598
+ const step = yield* Schedule.toStep(openDuration)
599
+ let now = yield* Clock.currentTimeMillis
600
+ let delay = Duration.zero
601
+ for (let attempt = 1; attempt <= consecutiveOpenings; attempt++) {
602
+ const result = yield* Effect.exit(step(now, attempt))
603
+ if (Exit.isFailure(result)) break
604
+ delay = result.value[1]
605
+ now += Duration.toMillis(delay)
606
+ }
607
+ return delay
608
+ })
609
+ }
610
+
611
+ /**
612
+ * Policy that decides when a Closed circuit opens.
613
+ *
614
+ * **Details**
615
+ *
616
+ * - `Consecutive` opens after `failures` failures in a row, in order of
617
+ * completion. Only a `Success` resets the streak.
618
+ * - `Rate` keeps the last `size` recorded outcomes and opens when the
619
+ * fraction of failures reaches `threshold`, once at least `minimumCalls`
620
+ * outcomes are recorded.
621
+ *
622
+ * `Ignored` outcomes never count towards either policy.
623
+ *
624
+ * @stability experimental
625
+ * @category models
626
+ * @since 4.0.6
627
+ */
628
+ export type TripPolicy =
629
+ | { readonly _tag: "Consecutive"; readonly failures: number }
630
+ | { readonly _tag: "Rate"; readonly threshold: number; readonly minimumCalls: number; readonly size: number }
631
+
632
+ /**
633
+ * Creates a trip policy that opens after `failures` consecutive failures.
634
+ *
635
+ * **Gotchas**
636
+ *
637
+ * Throws a `RangeError` when `failures` is not an integer of at least 1.
638
+ *
639
+ * @stability experimental
640
+ * @category constructors
641
+ * @since 4.0.6
642
+ */
643
+ export const consecutive = (failures: number): TripPolicy => {
644
+ if (!Number.isInteger(failures) || failures < 1) {
645
+ throw new RangeError(`CircuitBreaker.consecutive: failures must be an integer >= 1, got ${failures}`)
646
+ }
647
+ return { _tag: "Consecutive", failures }
648
+ }
649
+
650
+ /**
651
+ * Creates a trip policy that opens when the failure rate over the last
652
+ * recorded calls reaches a threshold.
653
+ *
654
+ * **Details**
655
+ *
656
+ * `threshold` is a fraction in `[0, 1]` and defaults to `0.5`. `size` is the
657
+ * number of recorded calls kept and defaults to `100`. `minimumCalls`
658
+ * defaults to `size`. A ring with no failures never opens the circuit.
659
+ *
660
+ * **Gotchas**
661
+ *
662
+ * Throws a `RangeError` when `threshold` is outside `[0, 1]`, when `size` is
663
+ * not an integer of at least 1, or when `minimumCalls` is not an integer
664
+ * between 1 and `size`.
665
+ *
666
+ * @stability experimental
667
+ * @category constructors
668
+ * @since 4.0.6
669
+ */
670
+ export const rate = (options: {
671
+ readonly threshold?: number | undefined
672
+ readonly size?: number | undefined
673
+ readonly minimumCalls?: number | undefined
674
+ }): TripPolicy => {
675
+ const threshold = options.threshold ?? 0.5
676
+ const size = options.size ?? 100
677
+ const minimumCalls = options.minimumCalls ?? size
678
+ if (!(threshold >= 0 && threshold <= 1)) {
679
+ throw new RangeError(`CircuitBreaker.rate: threshold must be in [0, 1], got ${threshold}`)
680
+ }
681
+ if (!Number.isInteger(size) || size < 1) {
682
+ throw new RangeError(`CircuitBreaker.rate: size must be an integer >= 1, got ${size}`)
683
+ }
684
+ if (!Number.isInteger(minimumCalls) || minimumCalls < 1 || minimumCalls > size) {
685
+ throw new RangeError(`CircuitBreaker.rate: minimumCalls must be an integer in [1, size], got ${minimumCalls}`)
686
+ }
687
+ return { _tag: "Rate", threshold, minimumCalls, size }
688
+ }
689
+
690
+ /**
691
+ * Classification of a call's exit, as returned by a `classify` function.
692
+ *
693
+ * @stability experimental
694
+ * @category models
695
+ * @since 4.0.6
696
+ */
697
+ export type Outcome = Outcome.Success | Outcome.Ignored | Outcome.Failure
698
+
699
+ /**
700
+ * Members of the `Outcome` union.
701
+ *
702
+ * @stability experimental
703
+ * @category models
704
+ * @since 4.0.6
705
+ */
706
+ export declare namespace Outcome {
707
+ /**
708
+ * Outcome for a call that shows the dependency is healthy.
709
+ *
710
+ * @stability experimental
711
+ * @category models
712
+ * @since 4.0.6
713
+ */
714
+ export interface Success {
715
+ readonly _tag: "Success"
716
+ }
717
+
718
+ /**
719
+ * Outcome for a call that says nothing about the dependency's health.
720
+ *
721
+ * @stability experimental
722
+ * @category models
723
+ * @since 4.0.6
724
+ */
725
+ export interface Ignored {
726
+ readonly _tag: "Ignored"
727
+ }
728
+
729
+ /**
730
+ * Outcome for a call that counts against the dependency, with an optional
731
+ * hint for how long to stay open.
732
+ *
733
+ * @stability experimental
734
+ * @category models
735
+ * @since 4.0.6
736
+ */
737
+ export interface Failure {
738
+ readonly _tag: "Failure"
739
+ readonly retryAfter?: Duration.Duration | undefined
740
+ }
741
+ }
742
+
743
+ /**
744
+ * Outcome that records a success and resets a consecutive failure streak.
745
+ *
746
+ * @stability experimental
747
+ * @category constructors
748
+ * @since 4.0.6
749
+ */
750
+ export const success: Outcome = { _tag: "Success" }
751
+
752
+ /**
753
+ * Outcome that records nothing.
754
+ *
755
+ * @stability experimental
756
+ * @category constructors
757
+ * @since 4.0.6
758
+ */
759
+ export const ignored: Outcome = { _tag: "Ignored" }
760
+
761
+ /**
762
+ * Creates an outcome that records a failure.
763
+ *
764
+ * **Details**
765
+ *
766
+ * `retryAfter` is a hint, such as an HTTP `Retry-After` header. It is used
767
+ * only when this failure opens the circuit, can only lengthen the opening,
768
+ * and is capped at `maxOpenDuration`.
769
+ *
770
+ * @stability experimental
771
+ * @category constructors
772
+ * @since 4.0.6
773
+ */
774
+ export const failure = (options?: { readonly retryAfter?: Duration.Input | undefined }): Outcome => ({
775
+ _tag: "Failure",
776
+ retryAfter: options?.retryAfter === undefined ? undefined : Duration.fromInputUnsafe(options.retryAfter)
777
+ })
778
+
779
+ /**
780
+ * State of one circuit, as read from the store.
781
+ *
782
+ * **Details**
783
+ *
784
+ * - `Open.retryAfter` is the remaining open time, clamped to zero. A circuit
785
+ * past its open time reads as Open until the next call moves it to
786
+ * HalfOpen.
787
+ * - `HalfOpen.probing` is `false` when no probe currently holds the slot.
788
+ * - `consecutiveOpenings` on Open and HalfOpen counts the openings since the
789
+ * backoff last reset, not since the circuit last closed.
790
+ *
791
+ * @stability experimental
792
+ * @category models
793
+ * @since 4.0.6
794
+ */
795
+ export type State =
796
+ | { readonly _tag: "Closed"; readonly key: string }
797
+ | {
798
+ readonly _tag: "Open"
799
+ readonly key: string
800
+ readonly retryAfter: Duration.Duration
801
+ readonly consecutiveOpenings: number
802
+ }
803
+ | { readonly _tag: "HalfOpen"; readonly key: string; readonly probing: boolean; readonly consecutiveOpenings: number }
804
+
805
+ /**
806
+ * Transition between circuit states made by a breaker.
807
+ *
808
+ * **Details**
809
+ *
810
+ * Only the legal edges appear. `from` tells a failed probe (`"HalfOpen"`)
811
+ * apart from a fresh trip (`"Closed"`). On an opening, `consecutiveOpenings`
812
+ * counts the openings since the backoff last reset, including this one.
813
+ *
814
+ * @stability experimental
815
+ * @category models
816
+ * @since 4.0.6
817
+ */
818
+ export type Transition =
819
+ | {
820
+ readonly key: string
821
+ readonly from: "Closed" | "HalfOpen"
822
+ readonly to: "Open"
823
+ readonly retryAfter: Duration.Duration
824
+ readonly consecutiveOpenings: number
825
+ }
826
+ | { readonly key: string; readonly from: "Open"; readonly to: "HalfOpen" }
827
+ | { readonly key: string; readonly from: "HalfOpen"; readonly to: "Closed" }
828
+
829
+ /**
830
+ * Error reason for a call rejected by an Open circuit.
831
+ *
832
+ * @stability experimental
833
+ * @category errors
834
+ * @since 4.0.6
835
+ */
836
+ export class CircuitOpen extends Schema.Error<CircuitOpen>("effect/persistence/CircuitBreaker/CircuitOpen")({
837
+ _tag: Schema.tag("CircuitOpen"),
838
+ key: Schema.String,
839
+ retryAfter: Schema.DurationFromMillis
840
+ }) {
841
+ /**
842
+ * Public message used when an Open circuit rejects a call.
843
+ *
844
+ * @stability experimental
845
+ * @since 4.0.6
846
+ */
847
+ override get message(): string {
848
+ return `Circuit open`
849
+ }
850
+ }
851
+
852
+ /**
853
+ * Error reason for a call rejected while a HalfOpen circuit's probe runs.
854
+ *
855
+ * @stability experimental
856
+ * @category errors
857
+ * @since 4.0.6
858
+ */
859
+ export class CircuitHalfOpen extends Schema.Error<CircuitHalfOpen>(
860
+ "effect/persistence/CircuitBreaker/CircuitHalfOpen"
861
+ )({
862
+ _tag: Schema.tag("CircuitHalfOpen"),
863
+ key: Schema.String
864
+ }) {
865
+ /**
866
+ * Public message used when a HalfOpen circuit rejects a call.
867
+ *
868
+ * @stability experimental
869
+ * @since 4.0.6
870
+ */
871
+ override get message(): string {
872
+ return `Circuit half-open`
873
+ }
874
+ }
875
+
876
+ /**
877
+ * Error reason for failures in the backing `CircuitBreakerStore`.
878
+ *
879
+ * @stability experimental
880
+ * @category errors
881
+ * @since 4.0.6
882
+ */
883
+ export class CircuitBreakerStoreError extends Schema.Error<CircuitBreakerStoreError>(
884
+ "effect/persistence/CircuitBreaker/CircuitBreakerStoreError"
885
+ )({
886
+ _tag: Schema.tag("CircuitBreakerStoreError"),
887
+ message: Schema.String,
888
+ cause: Schema.optional(Schema.Defect())
889
+ }) {}
890
+
891
+ /**
892
+ * Union of reasons carried by `CircuitBreakerError`.
893
+ *
894
+ * @stability experimental
895
+ * @category errors
896
+ * @since 4.0.6
897
+ */
898
+ export type CircuitBreakerErrorReason = CircuitOpen | CircuitHalfOpen | CircuitBreakerStoreError
899
+
900
+ /**
901
+ * Schema for all reasons that can be carried by `CircuitBreakerError`.
902
+ *
903
+ * @stability experimental
904
+ * @category errors
905
+ * @since 4.0.6
906
+ */
907
+ export const CircuitBreakerErrorReason: Schema.Union<[
908
+ typeof CircuitOpen,
909
+ typeof CircuitHalfOpen,
910
+ typeof CircuitBreakerStoreError
911
+ ]> = Schema.Union([CircuitOpen, CircuitHalfOpen, CircuitBreakerStoreError])
912
+
913
+ /**
914
+ * Runtime type identifier for `CircuitBreakerError`.
915
+ *
916
+ * @stability experimental
917
+ * @category type IDs
918
+ * @since 4.0.6
919
+ */
920
+ export const ErrorTypeId: ErrorTypeId = "~effect/persistence/CircuitBreaker/CircuitBreakerError"
921
+
922
+ /**
923
+ * Type-level identifier used to brand `CircuitBreakerError` values.
924
+ *
925
+ * @stability experimental
926
+ * @category type IDs
927
+ * @since 4.0.6
928
+ */
929
+ export type ErrorTypeId = "~effect/persistence/CircuitBreaker/CircuitBreakerError"
930
+
931
+ /**
932
+ * Error raised by circuit breaker operations, wrapping a concrete failure
933
+ * `reason`.
934
+ *
935
+ * @stability experimental
936
+ * @category errors
937
+ * @since 4.0.6
938
+ */
939
+ export class CircuitBreakerError extends Schema.Error<CircuitBreakerError>(ErrorTypeId)({
940
+ _tag: Schema.tag("CircuitBreakerError"),
941
+ reason: CircuitBreakerErrorReason
942
+ }) {
943
+ // @effect-diagnostics-next-line overriddenSchemaConstructor:off
944
+ constructor(props: {
945
+ readonly reason: CircuitBreakerErrorReason
946
+ }) {
947
+ if ("cause" in props.reason) {
948
+ super({
949
+ ...props,
950
+ cause: props.reason.cause
951
+ } as any)
952
+ } else {
953
+ super(props)
954
+ }
955
+ }
956
+
957
+ /**
958
+ * Marks this value as a circuit breaker error for runtime guards.
959
+ *
960
+ * @stability experimental
961
+ * @since 4.0.6
962
+ */
963
+ readonly [ErrorTypeId]: ErrorTypeId = ErrorTypeId
964
+
965
+ override get message(): string {
966
+ return this.reason.message
967
+ }
968
+ }
969
+
970
+ /**
971
+ * Store that holds circuit state for a `CircuitBreaker`.
972
+ *
973
+ * **Details**
974
+ *
975
+ * - `admit` returns an `Admission`, or fails with `CircuitOpen` or
976
+ * `CircuitHalfOpen`. Its scope stays open while the call runs.
977
+ * - `record` applies an outcome atomically and opens the circuit for
978
+ * `openFor` if it trips. An outcome stamped with an older generation
979
+ * changes nothing.
980
+ * - `state` reads a key without changing it.
981
+ *
982
+ * Each operation reads the current time inside the store. A close keeps
983
+ * `consecutiveOpenings` and sets a reset deadline of the close time plus the
984
+ * last `openFor`. The first `admit` at or after the deadline resets the count
985
+ * to 0. `record` never reads the deadline, so a call admitted before a reset
986
+ * still opens with the `openFor` it computed.
987
+ *
988
+ * @stability experimental
989
+ * @category services
990
+ * @since 4.0.6
991
+ */
992
+ export class CircuitBreakerStore extends Context.Service<CircuitBreakerStore, {
993
+ readonly admit: (key: string) => Effect.Effect<Admission, CircuitBreakerError, Scope.Scope>
994
+ readonly record: (options: {
995
+ readonly key: string
996
+ readonly generation: number
997
+ readonly outcome: "Success" | "Failure" | "Ignored"
998
+ readonly tripPolicy: TripPolicy
999
+ readonly openFor: Duration.Duration
1000
+ }) => Effect.Effect<Transition | undefined, CircuitBreakerError>
1001
+ readonly state: (key: string) => Effect.Effect<State, CircuitBreakerError>
1002
+ }>()("effect/persistence/CircuitBreaker/CircuitBreakerStore") {}
1003
+
1004
+ /**
1005
+ * Permit returned by `CircuitBreakerStore.admit`.
1006
+ *
1007
+ * **Details**
1008
+ *
1009
+ * - `generation` is an opaque fencing token to pass back to `record`.
1010
+ * - `probe` is `true` when this call is the HalfOpen probe.
1011
+ * - `consecutiveOpenings` counts the openings since the backoff last reset.
1012
+ * - `transition` is the Open to HalfOpen transition this admission made, if
1013
+ * any.
1014
+ *
1015
+ * @stability experimental
1016
+ * @category models
1017
+ * @since 4.0.6
1018
+ */
1019
+ export interface Admission {
1020
+ readonly generation: number
1021
+ readonly probe: boolean
1022
+ readonly consecutiveOpenings: number
1023
+ readonly transition: Transition | undefined
1024
+ }
1025
+
1026
+ interface MemoryEntry {
1027
+ state: "Closed" | "Open" | "HalfOpen"
1028
+ generation: number
1029
+ lastActivity: number
1030
+ streak: number
1031
+ ring: Array<boolean>
1032
+ ringFailures: number
1033
+ openUntil: number
1034
+ consecutiveOpenings: number
1035
+ lastOpenFor: number
1036
+ resetAt: number
1037
+ probing: boolean
1038
+ }
1039
+
1040
+ /**
1041
+ * Creates an in-memory `CircuitBreakerStore` layer.
1042
+ *
1043
+ * **Details**
1044
+ *
1045
+ * State is local to the layer and uses `Clock`. `idleTimeToLive` (default
1046
+ * one hour) drops a key once it has been idle that long. An Open key is kept
1047
+ * that long past its open time, and a key whose probe is running is never
1048
+ * dropped. A dropped key starts again as Closed with a backoff count of 0.
1049
+ *
1050
+ * @stability experimental
1051
+ * @category layers
1052
+ * @since 4.0.6
1053
+ */
1054
+ export const layerStoreMemory = (options?: {
1055
+ readonly idleTimeToLive?: Duration.Input | undefined
1056
+ }): Layer.Layer<CircuitBreakerStore> =>
1057
+ Layer.sync(CircuitBreakerStore, () => {
1058
+ const idleTimeToLive = Duration.toMillis(Duration.fromInputUnsafe(options?.idleTimeToLive ?? "1 hour"))
1059
+ const entries = new Map<string, MemoryEntry>()
1060
+ let nextGeneration = 0
1061
+ let lastSweep = 0
1062
+
1063
+ const isExpired = (entry: MemoryEntry, now: number) => {
1064
+ switch (entry.state) {
1065
+ case "Closed":
1066
+ return now - entry.lastActivity >= idleTimeToLive
1067
+ case "Open":
1068
+ return now - Math.max(entry.lastActivity, entry.openUntil) >= idleTimeToLive
1069
+ case "HalfOpen":
1070
+ return !entry.probing && now - entry.lastActivity >= idleTimeToLive
1071
+ }
1072
+ }
1073
+
1074
+ const sweep = (now: number) => {
1075
+ if (now - lastSweep < idleTimeToLive) return
1076
+ lastSweep = now
1077
+ for (const [key, entry] of entries) {
1078
+ if (isExpired(entry, now)) entries.delete(key)
1079
+ }
1080
+ }
1081
+
1082
+ const lookup = (key: string, now: number): MemoryEntry | undefined => {
1083
+ const entry = entries.get(key)
1084
+ return entry !== undefined && !isExpired(entry, now) ? entry : undefined
1085
+ }
1086
+
1087
+ const resetCounters = (entry: MemoryEntry) => {
1088
+ entry.streak = 0
1089
+ entry.ring = []
1090
+ entry.ringFailures = 0
1091
+ }
1092
+
1093
+ const open = (
1094
+ key: string,
1095
+ entry: MemoryEntry,
1096
+ from: "Closed" | "HalfOpen",
1097
+ openFor: Duration.Duration,
1098
+ now: number
1099
+ ): Transition => {
1100
+ entry.state = "Open"
1101
+ entry.generation = nextGeneration++
1102
+ entry.lastOpenFor = Duration.toMillis(openFor)
1103
+ entry.openUntil = now + entry.lastOpenFor
1104
+ entry.consecutiveOpenings++
1105
+ entry.probing = false
1106
+ resetCounters(entry)
1107
+ return { key, from, to: "Open", retryAfter: openFor, consecutiveOpenings: entry.consecutiveOpenings }
1108
+ }
1109
+
1110
+ const trips = (entry: MemoryEntry, policy: TripPolicy) => {
1111
+ if (policy._tag === "Consecutive") return entry.streak >= policy.failures
1112
+ return entry.ring.length >= policy.minimumCalls && entry.ringFailures > 0 &&
1113
+ entry.ringFailures / entry.ring.length >= policy.threshold
1114
+ }
1115
+
1116
+ return CircuitBreakerStore.of({
1117
+ admit: (key) =>
1118
+ Effect.flatMap(Clock.currentTimeMillis, (now) => {
1119
+ sweep(now)
1120
+ let entry = lookup(key, now)
1121
+ if (entry === undefined) {
1122
+ entry = {
1123
+ state: "Closed",
1124
+ generation: nextGeneration++,
1125
+ lastActivity: now,
1126
+ streak: 0,
1127
+ ring: [],
1128
+ ringFailures: 0,
1129
+ openUntil: 0,
1130
+ consecutiveOpenings: 0,
1131
+ lastOpenFor: 0,
1132
+ resetAt: 0,
1133
+ probing: false
1134
+ }
1135
+ entries.set(key, entry)
1136
+ }
1137
+ let transition: Transition | undefined
1138
+ switch (entry.state) {
1139
+ case "Closed": {
1140
+ // A call admitted before this reset can still trip the circuit.
1141
+ // It opens with the openFor it computed from its older count,
1142
+ // and the stored count restarts at 1.
1143
+ if (entry.consecutiveOpenings > 0 && now >= entry.resetAt) {
1144
+ entry.consecutiveOpenings = 0
1145
+ }
1146
+ break
1147
+ }
1148
+ case "Open": {
1149
+ if (now < entry.openUntil) {
1150
+ return Effect.fail(
1151
+ new CircuitBreakerError({
1152
+ reason: new CircuitOpen({ key, retryAfter: Duration.millis(entry.openUntil - now) })
1153
+ })
1154
+ )
1155
+ }
1156
+ entry.state = "HalfOpen"
1157
+ transition = { key, from: "Open", to: "HalfOpen" }
1158
+ break
1159
+ }
1160
+ case "HalfOpen": {
1161
+ if (entry.probing) {
1162
+ return Effect.fail(new CircuitBreakerError({ reason: new CircuitHalfOpen({ key }) }))
1163
+ }
1164
+ break
1165
+ }
1166
+ }
1167
+ entry.lastActivity = now
1168
+ if (entry.state === "HalfOpen") {
1169
+ entry.probing = true
1170
+ entry.generation = nextGeneration++
1171
+ }
1172
+ return Effect.succeed<Admission>({
1173
+ generation: entry.generation,
1174
+ probe: entry.state === "HalfOpen",
1175
+ consecutiveOpenings: entry.consecutiveOpenings,
1176
+ transition
1177
+ })
1178
+ }),
1179
+ record: ({ generation, key, openFor, outcome, tripPolicy }) =>
1180
+ Effect.map(Clock.currentTimeMillis, (now): Transition | undefined => {
1181
+ const entry = lookup(key, now)
1182
+ if (entry === undefined || entry.generation !== generation) return undefined
1183
+ entry.lastActivity = now
1184
+ if (entry.state === "HalfOpen") {
1185
+ switch (outcome) {
1186
+ case "Ignored": {
1187
+ entry.probing = false
1188
+ return undefined
1189
+ }
1190
+ case "Failure":
1191
+ return open(key, entry, "HalfOpen", openFor, now)
1192
+ case "Success": {
1193
+ entry.state = "Closed"
1194
+ entry.generation = nextGeneration++
1195
+ entry.resetAt = now + entry.lastOpenFor
1196
+ entry.probing = false
1197
+ resetCounters(entry)
1198
+ return { key, from: "HalfOpen", to: "Closed" }
1199
+ }
1200
+ }
1201
+ }
1202
+ if (entry.state !== "Closed" || outcome === "Ignored") return undefined
1203
+ const failed = outcome === "Failure"
1204
+ entry.streak = failed ? entry.streak + 1 : 0
1205
+ if (tripPolicy._tag === "Rate") {
1206
+ entry.ring.push(failed)
1207
+ if (failed) entry.ringFailures++
1208
+ while (entry.ring.length > tripPolicy.size) {
1209
+ if (entry.ring.shift()) entry.ringFailures--
1210
+ }
1211
+ }
1212
+ return trips(entry, tripPolicy) ? open(key, entry, "Closed", openFor, now) : undefined
1213
+ }),
1214
+ state: (key) =>
1215
+ Effect.map(Clock.currentTimeMillis, (now): State => {
1216
+ const entry = lookup(key, now)
1217
+ if (entry === undefined) return { _tag: "Closed", key }
1218
+ switch (entry.state) {
1219
+ case "Closed":
1220
+ return { _tag: "Closed", key }
1221
+ case "Open":
1222
+ return {
1223
+ _tag: "Open",
1224
+ key,
1225
+ retryAfter: Duration.millis(Math.max(0, entry.openUntil - now)),
1226
+ consecutiveOpenings: entry.consecutiveOpenings
1227
+ }
1228
+ case "HalfOpen":
1229
+ return { _tag: "HalfOpen", key, probing: entry.probing, consecutiveOpenings: entry.consecutiveOpenings }
1230
+ }
1231
+ })
1232
+ })
1233
+ })
1234
+
1235
+ /**
1236
+ * Creates a Redis-backed `CircuitBreakerStore` layer.
1237
+ *
1238
+ * **Details**
1239
+ *
1240
+ * - Each circuit is one hash at `${prefix}${key}`, and `prefix` defaults to
1241
+ * `"circuitbreaker:"`. Times come from Redis `TIME`, so client clock skew
1242
+ * never matters. Requires Redis 5 or later.
1243
+ * - The probe holds a lease of `probeLease` (default 10 seconds), renewed
1244
+ * while it runs. If the lease lapses, another caller takes over and the old
1245
+ * probe's outcome is discarded.
1246
+ * - `idleTimeToLive` (default one hour) expires idle keys. An Open key, or one
1247
+ * whose probe holds the lease, is kept that long past its open time or
1248
+ * lease, so expiry never cuts a probe short or resets the backoff early. An
1249
+ * expired key starts again as Closed with a backoff count of 0.
1250
+ *
1251
+ * **Gotchas**
1252
+ *
1253
+ * - If renewals cannot reach Redis, the lease lapses and a second probe can
1254
+ * start.
1255
+ * - Durations above 2^50 milliseconds cannot be stored. An operation that
1256
+ * needs one fails with `CircuitBreakerStoreError`.
1257
+ *
1258
+ * @stability experimental
1259
+ * @category layers
1260
+ * @since 4.0.6
1261
+ */
1262
+ export const layerStoreRedis = (options?: {
1263
+ readonly prefix?: string | undefined
1264
+ readonly probeLease?: Duration.Input | undefined
1265
+ readonly idleTimeToLive?: Duration.Input | undefined
1266
+ }): Layer.Layer<CircuitBreakerStore, never, Redis.Redis> => Layer.effect(CircuitBreakerStore, makeStoreRedis(options))
1267
+
1268
+ /**
1269
+ * Creates a Redis-backed `CircuitBreakerStore` layer from wrapped
1270
+ * configuration options.
1271
+ *
1272
+ * **Details**
1273
+ *
1274
+ * Takes the same options as `layerStoreRedis`.
1275
+ *
1276
+ * @stability experimental
1277
+ * @category layers
1278
+ * @since 4.0.6
1279
+ */
1280
+ export const layerStoreRedisConfig = (
1281
+ options: Config.Wrap<{
1282
+ readonly prefix?: string | undefined
1283
+ readonly probeLease?: Duration.Input | undefined
1284
+ readonly idleTimeToLive?: Duration.Input | undefined
1285
+ }>
1286
+ ): Layer.Layer<CircuitBreakerStore, Config.ConfigError, Redis.Redis> =>
1287
+ Layer.effect(CircuitBreakerStore, Effect.flatMap(Config.unwrap(options), makeStoreRedis))
1288
+
1289
+ const makeStoreRedis = Effect.fnUntraced(function*(options?: {
1290
+ readonly prefix?: string | undefined
1291
+ readonly probeLease?: Duration.Input | undefined
1292
+ readonly idleTimeToLive?: Duration.Input | undefined
1293
+ }) {
1294
+ const redis = yield* Redis.Redis
1295
+ const prefix = options?.prefix ?? "circuitbreaker:"
1296
+ const leaseMillis = toRedisMillis(options?.probeLease ?? "10 seconds")
1297
+ const idleMillis = toRedisMillis(options?.idleTimeToLive ?? "1 hour")
1298
+ const renewInterval = Duration.millis(leaseMillis / 3)
1299
+
1300
+ const admitRedis = redis.eval(admitScript)
1301
+ const recordRedis = redis.eval(recordScript)
1302
+ const stateRedis = redis.eval(stateScript)
1303
+ const renewRedis = redis.eval(renewScript)
1304
+
1305
+ const storeError = (message: string) => (error: Redis.RedisError) =>
1306
+ new CircuitBreakerError({ reason: new CircuitBreakerStoreError({ message, cause: error.cause }) })
1307
+
1308
+ // Stops once the lease is gone, because a renewal can never win it back.
1309
+ const renew = Effect.fnUntraced(function*(redisKey: string, generation: string) {
1310
+ while (true) {
1311
+ yield* Effect.sleep(renewInterval)
1312
+ const renewed = yield* renewRedis(redisKey, generation, leaseMillis, idleMillis).pipe(
1313
+ Effect.catchCauseFilter(
1314
+ findRecoverableError,
1315
+ (_, cause) => Effect.as(reportUnhandled("CircuitBreaker failed to renew a probe lease, retrying", cause), 1)
1316
+ )
1317
+ )
1318
+ if (renewed !== 1) return
1319
+ }
1320
+ })
1321
+
1322
+ return CircuitBreakerStore.of({
1323
+ admit: Effect.fnUntraced(function*(key) {
1324
+ const redisKey = `${prefix}${key}`
1325
+ const result = yield* admitRedis(redisKey, leaseMillis, idleMillis).pipe(
1326
+ Effect.mapError(storeError("Failed to admit a call through the circuit"))
1327
+ )
1328
+ switch (result[0]) {
1329
+ case "open":
1330
+ return yield* new CircuitBreakerError({
1331
+ reason: new CircuitOpen({ key, retryAfter: Duration.millis(Math.max(0, Number(result[1]))) })
1332
+ })
1333
+ case "halfopen":
1334
+ return yield* new CircuitBreakerError({ reason: new CircuitHalfOpen({ key }) })
1335
+ }
1336
+ const [, generation, probe, consecutiveOpenings, moved] = result
1337
+ if (probe === 1) {
1338
+ yield* renew(redisKey, generation).pipe(Effect.interruptible, Effect.forkScoped)
1339
+ }
1340
+ return {
1341
+ generation: Number(generation),
1342
+ probe: probe === 1,
1343
+ consecutiveOpenings: Number(consecutiveOpenings),
1344
+ transition: moved === 1 ? { key, from: "Open", to: "HalfOpen" } : undefined
1345
+ } satisfies Admission
1346
+ }),
1347
+ record: ({ generation, key, openFor, outcome, tripPolicy }) =>
1348
+ recordRedis(
1349
+ `${prefix}${key}`,
1350
+ String(generation),
1351
+ outcome,
1352
+ tripPolicy,
1353
+ Math.max(0, Math.ceil(Duration.toMillis(openFor))),
1354
+ idleMillis
1355
+ ).pipe(
1356
+ Effect.mapError(storeError("Failed to record an outcome")),
1357
+ Effect.map(([moved, consecutiveOpenings]): Transition | undefined => {
1358
+ switch (moved) {
1359
+ case "closed-open":
1360
+ case "halfopen-open":
1361
+ return {
1362
+ key,
1363
+ from: moved === "closed-open" ? "Closed" : "HalfOpen",
1364
+ to: "Open",
1365
+ retryAfter: openFor,
1366
+ consecutiveOpenings: Number(consecutiveOpenings)
1367
+ }
1368
+ case "halfopen-closed":
1369
+ return { key, from: "HalfOpen", to: "Closed" }
1370
+ default:
1371
+ return undefined
1372
+ }
1373
+ })
1374
+ ),
1375
+ state: (key) =>
1376
+ stateRedis(`${prefix}${key}`).pipe(
1377
+ Effect.mapError(storeError("Failed to read the circuit state")),
1378
+ Effect.map(([state, remaining, consecutiveOpenings, probing]): State => {
1379
+ switch (state) {
1380
+ case "Open":
1381
+ return {
1382
+ _tag: "Open",
1383
+ key,
1384
+ retryAfter: Duration.millis(Math.max(0, Number(remaining))),
1385
+ consecutiveOpenings: Number(consecutiveOpenings)
1386
+ }
1387
+ case "HalfOpen":
1388
+ return { _tag: "HalfOpen", key, probing: probing === 1, consecutiveOpenings: Number(consecutiveOpenings) }
1389
+ default:
1390
+ return { _tag: "Closed", key }
1391
+ }
1392
+ })
1393
+ )
1394
+ })
1395
+ })
1396
+
1397
+ const toRedisMillis = (input: Duration.Input) =>
1398
+ Math.max(1, Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(input))))
1399
+
1400
+ // Shared Lua prelude.
1401
+ //
1402
+ // Numeric arguments are capped at 2^50 (about 35,000 years in milliseconds),
1403
+ // so every TTL and timestamp derived from them stays an exact integer. Redis
1404
+ // formats a Lua number above 1e17 with an exponent, which PEXPIRE rejects
1405
+ // after earlier writes have already happened. `int` formats every number
1406
+ // written to Redis without one.
1407
+ //
1408
+ // Generations are the larger of the previous generation plus one and the
1409
+ // current Redis time in microseconds, so a key that expired and was recreated
1410
+ // never reissues a generation an old permit still holds. They are kept as
1411
+ // integer strings so they never pass through a float format.
1412
+ const luaPrelude = `
1413
+ local maxValue = 1125899906842624
1414
+ local function bounded(value, min)
1415
+ local n = tonumber(value)
1416
+ if n == nil or n ~= n or n < min or n > maxValue then return nil end
1417
+ return n
1418
+ end
1419
+ local function positive(value) return bounded(value, 1) end
1420
+ local function nonNegative(value) return bounded(value, 0) end
1421
+ local function int(n) return string.format("%.0f", n) end
1422
+ local time = redis.call("TIME")
1423
+ local nowMicros = tonumber(time[1]) * 1000000 + tonumber(time[2])
1424
+ local now = math.floor(nowMicros / 1000)
1425
+ local function nextGeneration(current)
1426
+ return int(math.max((tonumber(current) or 0) + 1, nowMicros))
1427
+ end
1428
+ `
1429
+
1430
+ // Returns { "ok", generation, probe, consecutiveOpenings, movedToHalfOpen },
1431
+ // { "open", remainingMillis } or { "halfopen" }.
1432
+ const admitScript = Redis.script(
1433
+ (key: string, leaseMillis: number, idleMillis: number) => [key, leaseMillis, idleMillis],
1434
+ {
1435
+ numberOfKeys: 1,
1436
+ lua: `${luaPrelude}
1437
+ local key = KEYS[1]
1438
+ local lease = positive(ARGV[1])
1439
+ local idle = positive(ARGV[2])
1440
+ if not lease or not idle then
1441
+ return redis.error_reply("CircuitBreaker admit: invalid arguments")
1442
+ end
1443
+
1444
+ local fields = redis.call("HMGET", key, "state", "generation", "openUntil", "openings", "leaseUntil", "resetAt")
1445
+ local state = fields[1]
1446
+ local generation = fields[2]
1447
+ local openings = tonumber(fields[4]) or 0
1448
+
1449
+ if not state then
1450
+ generation = nextGeneration(0)
1451
+ redis.call("HSET", key, "state", "Closed", "generation", generation)
1452
+ redis.call("PEXPIRE", key, int(idle))
1453
+ return { "ok", generation, 0, 0, 0 }
1454
+ end
1455
+
1456
+ if state == "Closed" then
1457
+ -- A call admitted before this reset can still trip the circuit. It opens
1458
+ -- with the openFor it computed from its older count, and the stored count
1459
+ -- restarts at 1.
1460
+ if openings > 0 and now >= (tonumber(fields[6]) or 0) then
1461
+ openings = 0
1462
+ redis.call("HSET", key, "openings", 0)
1463
+ end
1464
+ redis.call("PEXPIRE", key, int(idle))
1465
+ return { "ok", generation, 0, openings, 0 }
1466
+ end
1467
+
1468
+ local moved = 0
1469
+ if state == "Open" then
1470
+ local openUntil = tonumber(fields[3]) or 0
1471
+ if now < openUntil then
1472
+ return { "open", openUntil - now }
1473
+ end
1474
+ moved = 1
1475
+ elseif (tonumber(fields[5]) or 0) > now then
1476
+ return { "halfopen" }
1477
+ end
1478
+
1479
+ -- Every probe admission, including a takeover of a lapsed lease, starts a
1480
+ -- new generation.
1481
+ generation = nextGeneration(generation)
1482
+ redis.call("HSET", key, "state", "HalfOpen", "generation", generation, "leaseUntil", int(now + lease))
1483
+ redis.call("PEXPIRE", key, int(lease + idle))
1484
+ return { "ok", generation, 1, openings, moved }
1485
+ `
1486
+ }
1487
+ ).withReturnType<
1488
+ | readonly [tag: "ok", generation: string, probe: number, consecutiveOpenings: number, moved: number]
1489
+ | readonly [tag: "open", remainingMillis: number]
1490
+ | readonly [tag: "halfopen"]
1491
+ >()
1492
+
1493
+ // Returns { transition, consecutiveOpenings }, where transition is "none",
1494
+ // "closed-open", "halfopen-open" or "halfopen-closed".
1495
+ //
1496
+ // ARGV holds the generation, the outcome, the policy tag, the policy's
1497
+ // failures (Consecutive) or threshold (Rate), minimumCalls and size (0 under
1498
+ // Consecutive), then the open time and idle time in milliseconds.
1499
+ const recordScript = Redis.script(
1500
+ (
1501
+ key: string,
1502
+ generation: string,
1503
+ outcome: "Success" | "Failure" | "Ignored",
1504
+ tripPolicy: TripPolicy,
1505
+ openForMillis: number,
1506
+ idleMillis: number
1507
+ ) => [
1508
+ key,
1509
+ generation,
1510
+ outcome,
1511
+ tripPolicy._tag,
1512
+ ...(tripPolicy._tag === "Consecutive"
1513
+ ? [tripPolicy.failures, 0, 0]
1514
+ : [tripPolicy.threshold, tripPolicy.minimumCalls, tripPolicy.size]),
1515
+ openForMillis,
1516
+ idleMillis
1517
+ ],
1518
+ {
1519
+ numberOfKeys: 1,
1520
+ lua: `${luaPrelude}
1521
+ local key = KEYS[1]
1522
+ local expected = ARGV[1]
1523
+ local outcome = ARGV[2]
1524
+ local policy = ARGV[3]
1525
+ local openFor = nonNegative(ARGV[7])
1526
+ local idle = positive(ARGV[8])
1527
+ local valid = openFor ~= nil and idle ~= nil and
1528
+ (outcome == "Success" or outcome == "Failure" or outcome == "Ignored")
1529
+ local failuresNeeded, threshold, minimumCalls, size
1530
+ if policy == "Consecutive" then
1531
+ failuresNeeded = positive(ARGV[4])
1532
+ valid = valid and failuresNeeded ~= nil
1533
+ elseif policy == "Rate" then
1534
+ threshold = nonNegative(ARGV[4])
1535
+ minimumCalls = positive(ARGV[5])
1536
+ size = positive(ARGV[6])
1537
+ valid = valid and threshold ~= nil and threshold <= 1 and minimumCalls ~= nil and size ~= nil and
1538
+ minimumCalls <= size
1539
+ else
1540
+ valid = false
1541
+ end
1542
+ if not valid then
1543
+ return redis.error_reply("CircuitBreaker record: invalid arguments")
1544
+ end
1545
+
1546
+ local fields = redis.call("HMGET", key, "state", "generation", "streak", "ring", "failures", "openings", "lastOpenFor")
1547
+ local state = fields[1]
1548
+ if not state or fields[2] ~= expected then
1549
+ return { "none", 0 }
1550
+ end
1551
+ local openings = tonumber(fields[6]) or 0
1552
+
1553
+ local function open()
1554
+ openings = openings + 1
1555
+ redis.call(
1556
+ "HSET", key,
1557
+ "state", "Open",
1558
+ "generation", nextGeneration(fields[2]),
1559
+ "openUntil", int(now + openFor),
1560
+ "openings", openings,
1561
+ "lastOpenFor", int(openFor),
1562
+ "streak", 0,
1563
+ "ring", "",
1564
+ "failures", 0,
1565
+ "leaseUntil", 0
1566
+ )
1567
+ redis.call("PEXPIRE", key, int(openFor + idle))
1568
+ end
1569
+
1570
+ if state == "HalfOpen" then
1571
+ if outcome == "Ignored" then
1572
+ -- A new generation fences a renewal already on its way, so the freed slot
1573
+ -- stays free.
1574
+ redis.call("HSET", key, "generation", nextGeneration(fields[2]), "leaseUntil", 0)
1575
+ redis.call("PEXPIRE", key, int(idle))
1576
+ return { "none", openings }
1577
+ end
1578
+ if outcome == "Failure" then
1579
+ open()
1580
+ return { "halfopen-open", openings }
1581
+ end
1582
+ redis.call(
1583
+ "HSET", key,
1584
+ "state", "Closed",
1585
+ "generation", nextGeneration(fields[2]),
1586
+ "resetAt", int(now + (tonumber(fields[7]) or 0)),
1587
+ "streak", 0,
1588
+ "ring", "",
1589
+ "failures", 0,
1590
+ "leaseUntil", 0
1591
+ )
1592
+ redis.call("PEXPIRE", key, int(idle))
1593
+ return { "halfopen-closed", openings }
1594
+ end
1595
+
1596
+ if state ~= "Closed" then
1597
+ return { "none", openings }
1598
+ end
1599
+ if outcome == "Ignored" then
1600
+ redis.call("PEXPIRE", key, int(idle))
1601
+ return { "none", openings }
1602
+ end
1603
+
1604
+ local failed = outcome == "Failure"
1605
+ local streak = failed and (tonumber(fields[3]) or 0) + 1 or 0
1606
+ local trips
1607
+ local ring = fields[4] or ""
1608
+ local failures = tonumber(fields[5]) or 0
1609
+ if policy == "Consecutive" then
1610
+ trips = streak >= failuresNeeded
1611
+ else
1612
+ ring = ring .. (failed and "1" or "0")
1613
+ if failed then failures = failures + 1 end
1614
+ if #ring > size then
1615
+ local _, dropped = string.gsub(string.sub(ring, 1, #ring - size), "1", "")
1616
+ failures = failures - dropped
1617
+ ring = string.sub(ring, #ring - size + 1)
1618
+ end
1619
+ trips = #ring >= minimumCalls and failures > 0 and failures / #ring >= threshold
1620
+ end
1621
+
1622
+ if trips then
1623
+ open()
1624
+ return { "closed-open", openings }
1625
+ end
1626
+ redis.call("HSET", key, "streak", streak, "ring", ring, "failures", failures)
1627
+ redis.call("PEXPIRE", key, int(idle))
1628
+ return { "none", openings }
1629
+ `
1630
+ }
1631
+ ).withReturnType<readonly [transition: string, consecutiveOpenings: number]>()
1632
+
1633
+ // Read-only. Returns { state, remainingOpenMillis, consecutiveOpenings, probing }.
1634
+ const stateScript = Redis.script(
1635
+ (key: string) => [key],
1636
+ {
1637
+ numberOfKeys: 1,
1638
+ lua: `${luaPrelude}
1639
+ local fields = redis.call("HMGET", KEYS[1], "state", "openUntil", "openings", "leaseUntil")
1640
+ local state = fields[1]
1641
+ if not state then
1642
+ return { "Closed", 0, 0, 0 }
1643
+ end
1644
+ local remaining = math.max(0, (tonumber(fields[2]) or 0) - now)
1645
+ local probing = (tonumber(fields[4]) or 0) > now and 1 or 0
1646
+ return { state, remaining, tonumber(fields[3]) or 0, probing }
1647
+ `
1648
+ }
1649
+ ).withReturnType<readonly [state: string, remainingMillis: number, consecutiveOpenings: number, probing: number]>()
1650
+
1651
+ // Extends the probe lease only while the generation still holds it. Returns 1
1652
+ // when renewed, 0 when the lease is gone.
1653
+ const renewScript = Redis.script(
1654
+ (key: string, generation: string, leaseMillis: number, idleMillis: number) => [
1655
+ key,
1656
+ generation,
1657
+ leaseMillis,
1658
+ idleMillis
1659
+ ],
1660
+ {
1661
+ numberOfKeys: 1,
1662
+ lua: `${luaPrelude}
1663
+ local key = KEYS[1]
1664
+ local lease = positive(ARGV[2])
1665
+ local idle = positive(ARGV[3])
1666
+ if not lease or not idle then
1667
+ return redis.error_reply("CircuitBreaker renew: invalid arguments")
1668
+ end
1669
+ local fields = redis.call("HMGET", key, "state", "generation")
1670
+ if fields[1] ~= "HalfOpen" or fields[2] ~= ARGV[1] then
1671
+ return 0
1672
+ end
1673
+ redis.call("HSET", key, "leaseUntil", int(now + lease))
1674
+ redis.call("PEXPIRE", key, int(lease + idle))
1675
+ return 1
1676
+ `
1677
+ }
1678
+ ).withReturnType<number>()