effect 4.0.6 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (978) hide show
  1. package/README.md +1 -1
  2. package/dist/Arbitrary.d.ts +10 -2
  3. package/dist/Arbitrary.d.ts.map +1 -1
  4. package/dist/Arbitrary.js +2 -1
  5. package/dist/Arbitrary.js.map +1 -1
  6. package/dist/BigDecimal.d.ts.map +1 -1
  7. package/dist/BigDecimal.js +3 -0
  8. package/dist/BigDecimal.js.map +1 -1
  9. package/dist/Channel.d.ts +12 -6
  10. package/dist/Channel.d.ts.map +1 -1
  11. package/dist/Channel.js +30 -9
  12. package/dist/Channel.js.map +1 -1
  13. package/dist/Config.d.ts +7 -1
  14. package/dist/Config.d.ts.map +1 -1
  15. package/dist/Config.js.map +1 -1
  16. package/dist/Context.d.ts +18 -1
  17. package/dist/Context.d.ts.map +1 -1
  18. package/dist/Context.js +27 -8
  19. package/dist/Context.js.map +1 -1
  20. package/dist/Cron.d.ts +81 -11
  21. package/dist/Cron.d.ts.map +1 -1
  22. package/dist/Cron.js +101 -26
  23. package/dist/Cron.js.map +1 -1
  24. package/dist/Data.d.ts +54 -235
  25. package/dist/Data.d.ts.map +1 -1
  26. package/dist/Data.js.map +1 -1
  27. package/dist/Duration.d.ts +78 -0
  28. package/dist/Duration.d.ts.map +1 -1
  29. package/dist/Duration.js +123 -0
  30. package/dist/Duration.js.map +1 -1
  31. package/dist/Effect.d.ts +165 -8
  32. package/dist/Effect.d.ts.map +1 -1
  33. package/dist/Effect.js +12 -2
  34. package/dist/Effect.js.map +1 -1
  35. package/dist/Equal.d.ts.map +1 -1
  36. package/dist/Equal.js +8 -1
  37. package/dist/Equal.js.map +1 -1
  38. package/dist/Exit.d.ts +3 -3
  39. package/dist/Exit.d.ts.map +1 -1
  40. package/dist/Exit.js.map +1 -1
  41. package/dist/FileSystem.d.ts +59 -4
  42. package/dist/FileSystem.d.ts.map +1 -1
  43. package/dist/FileSystem.js +9 -2
  44. package/dist/FileSystem.js.map +1 -1
  45. package/dist/Formatter.d.ts.map +1 -1
  46. package/dist/Formatter.js +27 -13
  47. package/dist/Formatter.js.map +1 -1
  48. package/dist/Glob.d.ts +165 -0
  49. package/dist/Glob.d.ts.map +1 -0
  50. package/dist/Glob.js +127 -0
  51. package/dist/Glob.js.map +1 -0
  52. package/dist/Hash.d.ts.map +1 -1
  53. package/dist/Hash.js +4 -1
  54. package/dist/Hash.js.map +1 -1
  55. package/dist/JsonPatch.js +4 -0
  56. package/dist/JsonPatch.js.map +1 -1
  57. package/dist/JsonSchema.d.ts.map +1 -1
  58. package/dist/JsonSchema.js +13 -40
  59. package/dist/JsonSchema.js.map +1 -1
  60. package/dist/Match.d.ts +86 -0
  61. package/dist/Match.d.ts.map +1 -1
  62. package/dist/Match.js +72 -0
  63. package/dist/Match.js.map +1 -1
  64. package/dist/Metric.d.ts +3 -3
  65. package/dist/MutableList.d.ts +5 -2
  66. package/dist/MutableList.d.ts.map +1 -1
  67. package/dist/MutableList.js +7 -5
  68. package/dist/MutableList.js.map +1 -1
  69. package/dist/MutableRef.d.ts.map +1 -1
  70. package/dist/MutableRef.js +7 -0
  71. package/dist/MutableRef.js.map +1 -1
  72. package/dist/PartitionedSemaphore.d.ts +2 -1
  73. package/dist/PartitionedSemaphore.d.ts.map +1 -1
  74. package/dist/PartitionedSemaphore.js +22 -12
  75. package/dist/PartitionedSemaphore.js.map +1 -1
  76. package/dist/Pool.d.ts +51 -0
  77. package/dist/Pool.d.ts.map +1 -1
  78. package/dist/Pool.js +136 -36
  79. package/dist/Pool.js.map +1 -1
  80. package/dist/PubSub.d.ts.map +1 -1
  81. package/dist/PubSub.js +5 -6
  82. package/dist/PubSub.js.map +1 -1
  83. package/dist/Random.d.ts.map +1 -1
  84. package/dist/Random.js +3 -3
  85. package/dist/Random.js.map +1 -1
  86. package/dist/RcMap.d.ts.map +1 -1
  87. package/dist/RcMap.js +8 -2
  88. package/dist/RcMap.js.map +1 -1
  89. package/dist/Ref.d.ts.map +1 -1
  90. package/dist/Ref.js +8 -0
  91. package/dist/Ref.js.map +1 -1
  92. package/dist/Schedule.d.ts +18 -1
  93. package/dist/Schedule.d.ts.map +1 -1
  94. package/dist/Schedule.js +8 -3
  95. package/dist/Schedule.js.map +1 -1
  96. package/dist/Schema.d.ts +690 -49
  97. package/dist/Schema.d.ts.map +1 -1
  98. package/dist/Schema.js +687 -28
  99. package/dist/Schema.js.map +1 -1
  100. package/dist/SchemaAST.d.ts +4 -2
  101. package/dist/SchemaAST.d.ts.map +1 -1
  102. package/dist/SchemaAST.js +14 -17
  103. package/dist/SchemaAST.js.map +1 -1
  104. package/dist/SchemaGetter.d.ts +6 -1
  105. package/dist/SchemaGetter.d.ts.map +1 -1
  106. package/dist/SchemaGetter.js +22 -11
  107. package/dist/SchemaGetter.js.map +1 -1
  108. package/dist/SchemaParser.d.ts.map +1 -1
  109. package/dist/SchemaParser.js +48 -22
  110. package/dist/SchemaParser.js.map +1 -1
  111. package/dist/SchemaRepresentation.d.ts +33 -10
  112. package/dist/SchemaRepresentation.d.ts.map +1 -1
  113. package/dist/SchemaRepresentation.js +39 -13
  114. package/dist/SchemaRepresentation.js.map +1 -1
  115. package/dist/SchemaTransformation.d.ts +12 -4
  116. package/dist/SchemaTransformation.d.ts.map +1 -1
  117. package/dist/SchemaTransformation.js +15 -5
  118. package/dist/SchemaTransformation.js.map +1 -1
  119. package/dist/ScopedCache.d.ts.map +1 -1
  120. package/dist/ScopedCache.js +5 -7
  121. package/dist/ScopedCache.js.map +1 -1
  122. package/dist/Semaphore.d.ts +130 -0
  123. package/dist/Semaphore.d.ts.map +1 -1
  124. package/dist/Semaphore.js +94 -35
  125. package/dist/Semaphore.js.map +1 -1
  126. package/dist/Sink.d.ts.map +1 -1
  127. package/dist/Sink.js +7 -2
  128. package/dist/Sink.js.map +1 -1
  129. package/dist/Stream.d.ts +13 -4
  130. package/dist/Stream.d.ts.map +1 -1
  131. package/dist/Stream.js +31 -10
  132. package/dist/Stream.js.map +1 -1
  133. package/dist/String.d.ts +3 -0
  134. package/dist/String.d.ts.map +1 -1
  135. package/dist/String.js +3 -0
  136. package/dist/String.js.map +1 -1
  137. package/dist/Terminal.d.ts +18 -1
  138. package/dist/Terminal.d.ts.map +1 -1
  139. package/dist/Terminal.js +1 -1
  140. package/dist/Terminal.js.map +1 -1
  141. package/dist/Tracer.d.ts +11 -1
  142. package/dist/Tracer.d.ts.map +1 -1
  143. package/dist/Tracer.js +6 -1
  144. package/dist/Tracer.js.map +1 -1
  145. package/dist/ai/AiError.d.ts +43 -116
  146. package/dist/ai/AiError.d.ts.map +1 -1
  147. package/dist/ai/AiError.js +53 -72
  148. package/dist/ai/AiError.js.map +1 -1
  149. package/dist/ai/Decision.d.ts +196 -15
  150. package/dist/ai/Decision.d.ts.map +1 -1
  151. package/dist/ai/Decision.js +130 -0
  152. package/dist/ai/Decision.js.map +1 -1
  153. package/dist/ai/DecisionModel.d.ts +54 -13
  154. package/dist/ai/DecisionModel.d.ts.map +1 -1
  155. package/dist/ai/DecisionModel.js +123 -12
  156. package/dist/ai/DecisionModel.js.map +1 -1
  157. package/dist/ai/LanguageModel.d.ts.map +1 -1
  158. package/dist/ai/LanguageModel.js +9 -0
  159. package/dist/ai/LanguageModel.js.map +1 -1
  160. package/dist/ai/McpSchema.d.ts +110 -110
  161. package/dist/ai/McpSchema.d.ts.map +1 -1
  162. package/dist/ai/McpServer.d.ts +630 -29
  163. package/dist/ai/McpServer.d.ts.map +1 -1
  164. package/dist/ai/McpServer.js +583 -131
  165. package/dist/ai/McpServer.js.map +1 -1
  166. package/dist/ai/McpTest.d.ts +130 -0
  167. package/dist/ai/McpTest.d.ts.map +1 -0
  168. package/dist/ai/McpTest.js +237 -0
  169. package/dist/ai/McpTest.js.map +1 -0
  170. package/dist/ai/Prompt.d.ts +52 -0
  171. package/dist/ai/Prompt.d.ts.map +1 -1
  172. package/dist/ai/Prompt.js +38 -0
  173. package/dist/ai/Prompt.js.map +1 -1
  174. package/dist/ai/Response.d.ts +17 -28
  175. package/dist/ai/Response.d.ts.map +1 -1
  176. package/dist/ai/Response.js +5 -10
  177. package/dist/ai/Response.js.map +1 -1
  178. package/dist/ai/Tool.d.ts +43 -1
  179. package/dist/ai/Tool.d.ts.map +1 -1
  180. package/dist/ai/Tool.js +5 -2
  181. package/dist/ai/Tool.js.map +1 -1
  182. package/dist/ai/Toolkit.d.ts.map +1 -1
  183. package/dist/ai/Toolkit.js +8 -1
  184. package/dist/ai/Toolkit.js.map +1 -1
  185. package/dist/ai/index.d.ts +5 -0
  186. package/dist/ai/index.d.ts.map +1 -1
  187. package/dist/ai/index.js +5 -0
  188. package/dist/ai/index.js.map +1 -1
  189. package/dist/ai/internal/mcpCore.js +17 -12
  190. package/dist/ai/internal/mcpCore.js.map +1 -1
  191. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +4 -5
  192. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  193. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +4 -5
  194. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  195. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +4 -5
  196. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  197. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +4 -5
  198. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  199. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +18 -10
  200. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  201. package/dist/ai/internal/mcpProtocol.js +13 -0
  202. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  203. package/dist/ai/internal/mcpRuntime.js +5 -3
  204. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  205. package/dist/ai/internal/mcpStatefulRuntime.js +6 -1
  206. package/dist/ai/internal/mcpStatefulRuntime.js.map +1 -1
  207. package/dist/cli/Argument.d.ts +21 -0
  208. package/dist/cli/Argument.d.ts.map +1 -1
  209. package/dist/cli/Argument.js +7 -0
  210. package/dist/cli/Argument.js.map +1 -1
  211. package/dist/cli/CliError.d.ts +78 -0
  212. package/dist/cli/CliError.d.ts.map +1 -1
  213. package/dist/cli/CliError.js +65 -3
  214. package/dist/cli/CliError.js.map +1 -1
  215. package/dist/cli/CliOutput.d.ts +220 -7
  216. package/dist/cli/CliOutput.d.ts.map +1 -1
  217. package/dist/cli/CliOutput.js +156 -43
  218. package/dist/cli/CliOutput.js.map +1 -1
  219. package/dist/cli/Command.d.ts +47 -16
  220. package/dist/cli/Command.d.ts.map +1 -1
  221. package/dist/cli/Command.js +67 -29
  222. package/dist/cli/Command.js.map +1 -1
  223. package/dist/cli/Completions.d.ts +58 -0
  224. package/dist/cli/Completions.d.ts.map +1 -1
  225. package/dist/cli/Completions.js +49 -9
  226. package/dist/cli/Completions.js.map +1 -1
  227. package/dist/cli/Flag.d.ts +122 -0
  228. package/dist/cli/Flag.d.ts.map +1 -1
  229. package/dist/cli/Flag.js +42 -0
  230. package/dist/cli/Flag.js.map +1 -1
  231. package/dist/cli/GlobalFlag.d.ts.map +1 -1
  232. package/dist/cli/GlobalFlag.js +15 -7
  233. package/dist/cli/GlobalFlag.js.map +1 -1
  234. package/dist/cli/HelpDoc.d.ts +15 -1
  235. package/dist/cli/HelpDoc.d.ts.map +1 -1
  236. package/dist/cli/Param.d.ts +15 -0
  237. package/dist/cli/Param.d.ts.map +1 -1
  238. package/dist/cli/Param.js +23 -2
  239. package/dist/cli/Param.js.map +1 -1
  240. package/dist/cli/Primitive.d.ts +14 -0
  241. package/dist/cli/Primitive.d.ts.map +1 -1
  242. package/dist/cli/Primitive.js +15 -1
  243. package/dist/cli/Primitive.js.map +1 -1
  244. package/dist/cli/Prompt.d.ts +1 -0
  245. package/dist/cli/Prompt.d.ts.map +1 -1
  246. package/dist/cli/Prompt.js +1 -0
  247. package/dist/cli/Prompt.js.map +1 -1
  248. package/dist/cli/TerminalCapabilities.d.ts +3 -2
  249. package/dist/cli/TerminalCapabilities.d.ts.map +1 -1
  250. package/dist/cli/TerminalCapabilities.js +3 -1
  251. package/dist/cli/TerminalCapabilities.js.map +1 -1
  252. package/dist/cli/internal/completions/bash.js +73 -27
  253. package/dist/cli/internal/completions/bash.js.map +1 -1
  254. package/dist/cli/internal/help.d.ts.map +1 -1
  255. package/dist/cli/internal/help.js +17 -10
  256. package/dist/cli/internal/help.js.map +1 -1
  257. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  258. package/dist/cluster/ClusterWorkflowEngine.js +45 -10
  259. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  260. package/dist/cluster/MessageStorage.d.ts +23 -45
  261. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  262. package/dist/cluster/MessageStorage.js +6 -7
  263. package/dist/cluster/MessageStorage.js.map +1 -1
  264. package/dist/cluster/Sharding.d.ts.map +1 -1
  265. package/dist/cluster/Sharding.js +151 -49
  266. package/dist/cluster/Sharding.js.map +1 -1
  267. package/dist/cluster/ShardingConfig.d.ts +76 -0
  268. package/dist/cluster/ShardingConfig.d.ts.map +1 -1
  269. package/dist/cluster/ShardingConfig.js +4 -0
  270. package/dist/cluster/ShardingConfig.js.map +1 -1
  271. package/dist/cluster/SingleRunner.d.ts +3 -3
  272. package/dist/cluster/SingleRunner.d.ts.map +1 -1
  273. package/dist/cluster/SingleRunner.js +1 -1
  274. package/dist/cluster/Snowflake.d.ts.map +1 -1
  275. package/dist/cluster/Snowflake.js +3 -7
  276. package/dist/cluster/Snowflake.js.map +1 -1
  277. package/dist/cluster/SqlMessageStorage.d.ts +37 -6
  278. package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
  279. package/dist/cluster/SqlMessageStorage.js +34 -7
  280. package/dist/cluster/SqlMessageStorage.js.map +1 -1
  281. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  282. package/dist/cluster/SqlRunnerStorage.js +109 -46
  283. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  284. package/dist/cluster/internal/entityManager.js +54 -20
  285. package/dist/cluster/internal/entityManager.js.map +1 -1
  286. package/dist/crypto/Aead.d.ts +322 -0
  287. package/dist/crypto/Aead.d.ts.map +1 -0
  288. package/dist/crypto/Aead.js +331 -0
  289. package/dist/crypto/Aead.js.map +1 -0
  290. package/dist/crypto/Cipher.d.ts +235 -0
  291. package/dist/crypto/Cipher.d.ts.map +1 -0
  292. package/dist/crypto/Cipher.js +233 -0
  293. package/dist/crypto/Cipher.js.map +1 -0
  294. package/dist/crypto/CryptoError.d.ts +212 -0
  295. package/dist/crypto/CryptoError.d.ts.map +1 -0
  296. package/dist/crypto/CryptoError.js +202 -0
  297. package/dist/crypto/CryptoError.js.map +1 -0
  298. package/dist/crypto/Digest.d.ts +143 -0
  299. package/dist/crypto/Digest.d.ts.map +1 -0
  300. package/dist/crypto/Digest.js +146 -0
  301. package/dist/crypto/Digest.js.map +1 -0
  302. package/dist/crypto/Kdf.d.ts +220 -0
  303. package/dist/crypto/Kdf.d.ts.map +1 -0
  304. package/dist/crypto/Kdf.js +212 -0
  305. package/dist/crypto/Kdf.js.map +1 -0
  306. package/dist/crypto/KeyAgreement.d.ts +323 -0
  307. package/dist/crypto/KeyAgreement.d.ts.map +1 -0
  308. package/dist/crypto/KeyAgreement.js +303 -0
  309. package/dist/crypto/KeyAgreement.js.map +1 -0
  310. package/dist/crypto/KeyFormat.d.ts +309 -0
  311. package/dist/crypto/KeyFormat.d.ts.map +1 -0
  312. package/dist/crypto/KeyFormat.js +556 -0
  313. package/dist/crypto/KeyFormat.js.map +1 -0
  314. package/dist/crypto/KeyWrap.d.ts +389 -0
  315. package/dist/crypto/KeyWrap.d.ts.map +1 -0
  316. package/dist/crypto/KeyWrap.js +381 -0
  317. package/dist/crypto/KeyWrap.js.map +1 -0
  318. package/dist/crypto/Mac.d.ts +277 -0
  319. package/dist/crypto/Mac.d.ts.map +1 -0
  320. package/dist/crypto/Mac.js +288 -0
  321. package/dist/crypto/Mac.js.map +1 -0
  322. package/dist/crypto/SecureRandom.d.ts +340 -0
  323. package/dist/crypto/SecureRandom.d.ts.map +1 -0
  324. package/dist/crypto/SecureRandom.js +257 -0
  325. package/dist/crypto/SecureRandom.js.map +1 -0
  326. package/dist/crypto/Signature.d.ts +448 -0
  327. package/dist/crypto/Signature.d.ts.map +1 -0
  328. package/dist/crypto/Signature.js +484 -0
  329. package/dist/crypto/Signature.js.map +1 -0
  330. package/dist/crypto/WebCrypto.d.ts +61 -0
  331. package/dist/crypto/WebCrypto.d.ts.map +1 -0
  332. package/dist/crypto/WebCrypto.js +63 -0
  333. package/dist/crypto/WebCrypto.js.map +1 -0
  334. package/dist/crypto/index.d.ts +65 -0
  335. package/dist/crypto/index.d.ts.map +1 -0
  336. package/dist/crypto/index.js +66 -0
  337. package/dist/crypto/index.js.map +1 -0
  338. package/dist/crypto/internal/asymmetric.d.ts +2 -0
  339. package/dist/crypto/internal/asymmetric.d.ts.map +1 -0
  340. package/dist/crypto/internal/asymmetric.js +210 -0
  341. package/dist/crypto/internal/asymmetric.js.map +1 -0
  342. package/dist/crypto/internal/chacha.d.ts +46 -0
  343. package/dist/crypto/internal/chacha.d.ts.map +1 -0
  344. package/dist/crypto/internal/chacha.js +885 -0
  345. package/dist/crypto/internal/chacha.js.map +1 -0
  346. package/dist/crypto/internal/der.d.ts +6 -0
  347. package/dist/crypto/internal/der.d.ts.map +1 -0
  348. package/dist/crypto/internal/der.js +86 -0
  349. package/dist/crypto/internal/der.js.map +1 -0
  350. package/dist/crypto/internal/digest.d.ts +2 -0
  351. package/dist/crypto/internal/digest.d.ts.map +1 -0
  352. package/dist/crypto/internal/digest.js +15 -0
  353. package/dist/crypto/internal/digest.js.map +1 -0
  354. package/dist/crypto/internal/key.d.ts +2 -0
  355. package/dist/crypto/internal/key.d.ts.map +1 -0
  356. package/dist/crypto/internal/key.js +170 -0
  357. package/dist/crypto/internal/key.js.map +1 -0
  358. package/dist/crypto/internal/md5.d.ts +39 -0
  359. package/dist/crypto/internal/md5.d.ts.map +1 -0
  360. package/dist/crypto/internal/md5.js +179 -0
  361. package/dist/crypto/internal/md5.js.map +1 -0
  362. package/dist/crypto/internal/webCrypto.d.ts +2 -0
  363. package/dist/crypto/internal/webCrypto.d.ts.map +1 -0
  364. package/dist/crypto/internal/webCrypto.js +111 -0
  365. package/dist/crypto/internal/webCrypto.js.map +1 -0
  366. package/dist/devtools/DevToolsClient.d.ts.map +1 -1
  367. package/dist/devtools/DevToolsClient.js +2 -0
  368. package/dist/devtools/DevToolsClient.js.map +1 -1
  369. package/dist/encoding/Base64.js +36 -0
  370. package/dist/encoding/Base64.js.map +1 -1
  371. package/dist/encoding/Base64Url.d.ts.map +1 -1
  372. package/dist/encoding/Base64Url.js +3 -2
  373. package/dist/encoding/Base64Url.js.map +1 -1
  374. package/dist/encoding/Csv.d.ts +608 -0
  375. package/dist/encoding/Csv.d.ts.map +1 -0
  376. package/dist/encoding/Csv.js +718 -0
  377. package/dist/encoding/Csv.js.map +1 -0
  378. package/dist/encoding/Hex.d.ts +3 -2
  379. package/dist/encoding/Hex.d.ts.map +1 -1
  380. package/dist/encoding/Hex.js +3 -2
  381. package/dist/encoding/Hex.js.map +1 -1
  382. package/dist/encoding/SchemaBinary.d.ts.map +1 -1
  383. package/dist/encoding/SchemaBinary.js +42 -4
  384. package/dist/encoding/SchemaBinary.js.map +1 -1
  385. package/dist/encoding/Sse.d.ts.map +1 -1
  386. package/dist/encoding/Sse.js +3 -3
  387. package/dist/encoding/Sse.js.map +1 -1
  388. package/dist/encoding/Toml.d.ts +17 -3
  389. package/dist/encoding/Toml.d.ts.map +1 -1
  390. package/dist/encoding/Toml.js +124 -31
  391. package/dist/encoding/Toml.js.map +1 -1
  392. package/dist/encoding/Yaml.d.ts +8 -1
  393. package/dist/encoding/Yaml.d.ts.map +1 -1
  394. package/dist/encoding/Yaml.js +54 -18
  395. package/dist/encoding/Yaml.js.map +1 -1
  396. package/dist/encoding/index.d.ts +5 -0
  397. package/dist/encoding/index.d.ts.map +1 -1
  398. package/dist/encoding/index.js +5 -0
  399. package/dist/encoding/index.js.map +1 -1
  400. package/dist/eventlog/EventJournal.d.ts.map +1 -1
  401. package/dist/eventlog/EventJournal.js +6 -3
  402. package/dist/eventlog/EventJournal.js.map +1 -1
  403. package/dist/eventlog/EventLog.d.ts +119 -4
  404. package/dist/eventlog/EventLog.d.ts.map +1 -1
  405. package/dist/eventlog/EventLog.js +200 -12
  406. package/dist/eventlog/EventLog.js.map +1 -1
  407. package/dist/eventlog/EventLogEncryption.d.ts +41 -4
  408. package/dist/eventlog/EventLogEncryption.d.ts.map +1 -1
  409. package/dist/eventlog/EventLogEncryption.js +83 -64
  410. package/dist/eventlog/EventLogEncryption.js.map +1 -1
  411. package/dist/eventlog/EventLogMessage.d.ts +279 -0
  412. package/dist/eventlog/EventLogMessage.d.ts.map +1 -1
  413. package/dist/eventlog/EventLogMessage.js +217 -1
  414. package/dist/eventlog/EventLogMessage.js.map +1 -1
  415. package/dist/eventlog/EventLogRemote.d.ts +200 -28
  416. package/dist/eventlog/EventLogRemote.d.ts.map +1 -1
  417. package/dist/eventlog/EventLogRemote.js +186 -16
  418. package/dist/eventlog/EventLogRemote.js.map +1 -1
  419. package/dist/eventlog/EventLogServer.d.ts +3 -0
  420. package/dist/eventlog/EventLogServer.d.ts.map +1 -1
  421. package/dist/eventlog/EventLogServer.js +18 -10
  422. package/dist/eventlog/EventLogServer.js.map +1 -1
  423. package/dist/eventlog/EventLogServerEncrypted.d.ts +326 -5
  424. package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
  425. package/dist/eventlog/EventLogServerEncrypted.js +769 -14
  426. package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
  427. package/dist/eventlog/EventLogSessionAuth.d.ts +5 -3
  428. package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
  429. package/dist/eventlog/EventLogSessionAuth.js +41 -64
  430. package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
  431. package/dist/eventlog/EventRow.d.ts +4 -0
  432. package/dist/eventlog/EventRow.d.ts.map +1 -1
  433. package/dist/eventlog/EventRow.js +12 -2
  434. package/dist/eventlog/EventRow.js.map +1 -1
  435. package/dist/eventlog/SqlEventJournal.d.ts.map +1 -1
  436. package/dist/eventlog/SqlEventJournal.js +47 -27
  437. package/dist/eventlog/SqlEventJournal.js.map +1 -1
  438. package/dist/eventlog/SqlEventLogServerEncrypted.d.ts +21 -5
  439. package/dist/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -1
  440. package/dist/eventlog/SqlEventLogServerEncrypted.js +1048 -113
  441. package/dist/eventlog/SqlEventLogServerEncrypted.js.map +1 -1
  442. package/dist/eventlog/SqlEventLogServerUnencrypted.js +2 -2
  443. package/dist/eventlog/SqlEventLogServerUnencrypted.js.map +1 -1
  444. package/dist/eventlog/internal/batches.d.ts +12 -0
  445. package/dist/eventlog/internal/batches.d.ts.map +1 -0
  446. package/dist/eventlog/internal/batches.js +26 -0
  447. package/dist/eventlog/internal/batches.js.map +1 -0
  448. package/dist/eventlog/internal/compaction.d.ts +38 -0
  449. package/dist/eventlog/internal/compaction.d.ts.map +1 -0
  450. package/dist/eventlog/internal/compaction.js +33 -0
  451. package/dist/eventlog/internal/compaction.js.map +1 -0
  452. package/dist/eventlog/internal/conflicts.d.ts +0 -1
  453. package/dist/eventlog/internal/conflicts.d.ts.map +1 -1
  454. package/dist/eventlog/internal/conflicts.js +0 -3
  455. package/dist/eventlog/internal/conflicts.js.map +1 -1
  456. package/dist/eventlog/internal/identityRootSecretDerivation.js +66 -67
  457. package/dist/eventlog/internal/identityRootSecretDerivation.js.map +1 -1
  458. package/dist/eventlog/internal/remoteCompaction.d.ts +31 -0
  459. package/dist/eventlog/internal/remoteCompaction.d.ts.map +1 -0
  460. package/dist/eventlog/internal/remoteCompaction.js +76 -0
  461. package/dist/eventlog/internal/remoteCompaction.js.map +1 -0
  462. package/dist/http/Cookies.d.ts +172 -0
  463. package/dist/http/Cookies.d.ts.map +1 -1
  464. package/dist/http/Cookies.js +153 -10
  465. package/dist/http/Cookies.js.map +1 -1
  466. package/dist/http/FetchHttpClient.d.ts +26 -2
  467. package/dist/http/FetchHttpClient.d.ts.map +1 -1
  468. package/dist/http/FetchHttpClient.js +78 -15
  469. package/dist/http/FetchHttpClient.js.map +1 -1
  470. package/dist/http/HttpClient.d.ts +502 -8
  471. package/dist/http/HttpClient.d.ts.map +1 -1
  472. package/dist/http/HttpClient.js +296 -12
  473. package/dist/http/HttpClient.js.map +1 -1
  474. package/dist/http/HttpClientError.d.ts +8 -1
  475. package/dist/http/HttpClientError.d.ts.map +1 -1
  476. package/dist/http/HttpClientError.js +37 -1
  477. package/dist/http/HttpClientError.js.map +1 -1
  478. package/dist/http/HttpClientRequest.d.ts +9 -5
  479. package/dist/http/HttpClientRequest.d.ts.map +1 -1
  480. package/dist/http/HttpClientRequest.js +3 -4
  481. package/dist/http/HttpClientRequest.js.map +1 -1
  482. package/dist/http/HttpClientResponse.d.ts.map +1 -1
  483. package/dist/http/HttpClientResponse.js +3 -2
  484. package/dist/http/HttpClientResponse.js.map +1 -1
  485. package/dist/http/HttpMethod.d.ts +7 -1
  486. package/dist/http/HttpMethod.d.ts.map +1 -1
  487. package/dist/http/HttpMethod.js +3 -8
  488. package/dist/http/HttpMethod.js.map +1 -1
  489. package/dist/http/HttpMiddleware.d.ts +1 -1
  490. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  491. package/dist/http/HttpMiddleware.js +14 -16
  492. package/dist/http/HttpMiddleware.js.map +1 -1
  493. package/dist/http/HttpRouter.d.ts.map +1 -1
  494. package/dist/http/HttpRouter.js +2 -1
  495. package/dist/http/HttpRouter.js.map +1 -1
  496. package/dist/http/HttpServerError.js +2 -2
  497. package/dist/http/HttpServerError.js.map +1 -1
  498. package/dist/http/HttpServerRequest.d.ts +11 -3
  499. package/dist/http/HttpServerRequest.d.ts.map +1 -1
  500. package/dist/http/HttpServerRequest.js +77 -25
  501. package/dist/http/HttpServerRequest.js.map +1 -1
  502. package/dist/http/HttpServerResponse.d.ts.map +1 -1
  503. package/dist/http/HttpServerResponse.js +3 -2
  504. package/dist/http/HttpServerResponse.js.map +1 -1
  505. package/dist/http/HttpStaticServer.d.ts.map +1 -1
  506. package/dist/http/HttpStaticServer.js +48 -21
  507. package/dist/http/HttpStaticServer.js.map +1 -1
  508. package/dist/http/HttpTraceContext.d.ts +2 -1
  509. package/dist/http/HttpTraceContext.d.ts.map +1 -1
  510. package/dist/http/HttpTraceContext.js +7 -5
  511. package/dist/http/HttpTraceContext.js.map +1 -1
  512. package/dist/http/MultipartParser/internal/contentType.js +3 -3
  513. package/dist/http/ProblemDetails.d.ts +96 -0
  514. package/dist/http/ProblemDetails.d.ts.map +1 -0
  515. package/dist/http/ProblemDetails.js +98 -0
  516. package/dist/http/ProblemDetails.js.map +1 -0
  517. package/dist/http/UrlParams.d.ts +3 -1
  518. package/dist/http/UrlParams.d.ts.map +1 -1
  519. package/dist/http/UrlParams.js +4 -2
  520. package/dist/http/UrlParams.js.map +1 -1
  521. package/dist/http/index.d.ts +5 -0
  522. package/dist/http/index.d.ts.map +1 -1
  523. package/dist/http/index.js +5 -0
  524. package/dist/http/index.js.map +1 -1
  525. package/dist/http/internal/compression.js +4 -5
  526. package/dist/http/internal/compression.js.map +1 -1
  527. package/dist/http/internal/decompression.js +6 -2
  528. package/dist/http/internal/decompression.js.map +1 -1
  529. package/dist/http/internal/httpBody.js +8 -1
  530. package/dist/http/internal/httpBody.js.map +1 -1
  531. package/dist/http/internal/urlParams.js +33 -0
  532. package/dist/http/internal/urlParams.js.map +1 -1
  533. package/dist/http-api/HttpApi.d.ts +3 -6
  534. package/dist/http-api/HttpApi.d.ts.map +1 -1
  535. package/dist/http-api/HttpApi.js +3 -6
  536. package/dist/http-api/HttpApi.js.map +1 -1
  537. package/dist/http-api/HttpApiBuilder.d.ts.map +1 -1
  538. package/dist/http-api/HttpApiBuilder.js +43 -13
  539. package/dist/http-api/HttpApiBuilder.js.map +1 -1
  540. package/dist/http-api/HttpApiClient.d.ts +140 -7
  541. package/dist/http-api/HttpApiClient.d.ts.map +1 -1
  542. package/dist/http-api/HttpApiClient.js +193 -44
  543. package/dist/http-api/HttpApiClient.js.map +1 -1
  544. package/dist/http-api/HttpApiEndpoint.d.ts +24 -7
  545. package/dist/http-api/HttpApiEndpoint.d.ts.map +1 -1
  546. package/dist/http-api/HttpApiEndpoint.js +123 -32
  547. package/dist/http-api/HttpApiEndpoint.js.map +1 -1
  548. package/dist/http-api/HttpApiError.d.ts +185 -13
  549. package/dist/http-api/HttpApiError.d.ts.map +1 -1
  550. package/dist/http-api/HttpApiError.js +128 -0
  551. package/dist/http-api/HttpApiError.js.map +1 -1
  552. package/dist/http-api/HttpApiMiddleware.d.ts +64 -17
  553. package/dist/http-api/HttpApiMiddleware.d.ts.map +1 -1
  554. package/dist/http-api/HttpApiMiddleware.js +85 -1
  555. package/dist/http-api/HttpApiMiddleware.js.map +1 -1
  556. package/dist/http-api/HttpApiSchema.d.ts +142 -33
  557. package/dist/http-api/HttpApiSchema.d.ts.map +1 -1
  558. package/dist/http-api/HttpApiSchema.js +123 -59
  559. package/dist/http-api/HttpApiSchema.js.map +1 -1
  560. package/dist/http-api/HttpApiTest.d.ts.map +1 -1
  561. package/dist/http-api/HttpApiTest.js +3 -2
  562. package/dist/http-api/HttpApiTest.js.map +1 -1
  563. package/dist/http-api/OpenApi.d.ts.map +1 -1
  564. package/dist/http-api/OpenApi.js +25 -16
  565. package/dist/http-api/OpenApi.js.map +1 -1
  566. package/dist/http-api/internal/toCodecHttpProperties.d.ts +2 -0
  567. package/dist/http-api/internal/toCodecHttpProperties.d.ts.map +1 -0
  568. package/dist/http-api/internal/toCodecHttpProperties.js +38 -0
  569. package/dist/http-api/internal/toCodecHttpProperties.js.map +1 -0
  570. package/dist/index.d.ts +5 -5
  571. package/dist/index.d.ts.map +1 -1
  572. package/dist/index.js +5 -5
  573. package/dist/index.js.map +1 -1
  574. package/dist/internal/arbitrary/runner.d.ts.map +1 -1
  575. package/dist/internal/arbitrary/runner.js +120 -44
  576. package/dist/internal/arbitrary/runner.js.map +1 -1
  577. package/dist/internal/core.js +2 -0
  578. package/dist/internal/core.js.map +1 -1
  579. package/dist/internal/effect.js +46 -27
  580. package/dist/internal/effect.js.map +1 -1
  581. package/dist/internal/equal.js +14 -0
  582. package/dist/internal/equal.js.map +1 -1
  583. package/dist/internal/glob.d.ts +2 -0
  584. package/dist/internal/glob.d.ts.map +1 -0
  585. package/dist/internal/glob.js +630 -0
  586. package/dist/internal/glob.js.map +1 -0
  587. package/dist/internal/jsonSchema.d.ts +2 -0
  588. package/dist/internal/jsonSchema.d.ts.map +1 -0
  589. package/dist/internal/jsonSchema.js +28 -0
  590. package/dist/internal/jsonSchema.js.map +1 -0
  591. package/dist/internal/matcher.js +11 -1
  592. package/dist/internal/matcher.js.map +1 -1
  593. package/dist/internal/persistence.js +9 -0
  594. package/dist/internal/persistence.js.map +1 -1
  595. package/dist/internal/rcRef.js +4 -1
  596. package/dist/internal/rcRef.js.map +1 -1
  597. package/dist/internal/schema/compilerRegistry.js +4 -1
  598. package/dist/internal/schema/compilerRegistry.js.map +1 -1
  599. package/dist/internal/schema/content.d.ts +2 -0
  600. package/dist/internal/schema/content.d.ts.map +1 -0
  601. package/dist/internal/schema/content.js +13 -0
  602. package/dist/internal/schema/content.js.map +1 -0
  603. package/dist/internal/schema/fromJsonSchemaDocument.js +173 -185
  604. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  605. package/dist/internal/schema/fromRepresentation.js +2 -1
  606. package/dist/internal/schema/fromRepresentation.js.map +1 -1
  607. package/dist/internal/schema/standardSchema.js +8 -2
  608. package/dist/internal/schema/standardSchema.js.map +1 -1
  609. package/dist/internal/schema/toCodeDocument.js +9 -6
  610. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  611. package/dist/internal/schema/toCodec.js +21 -24
  612. package/dist/internal/schema/toCodec.js.map +1 -1
  613. package/dist/internal/schema/toJsonSchemaDocument.js +205 -140
  614. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  615. package/dist/internal/schema/toRepresentation.js +107 -129
  616. package/dist/internal/schema/toRepresentation.js.map +1 -1
  617. package/dist/internal/sql.d.ts +2 -0
  618. package/dist/internal/sql.d.ts.map +1 -0
  619. package/dist/internal/sql.js +13 -0
  620. package/dist/internal/sql.js.map +1 -0
  621. package/dist/internal/trie.js +42 -13
  622. package/dist/internal/trie.js.map +1 -1
  623. package/dist/internal/ulid.js +41 -0
  624. package/dist/internal/ulid.js.map +1 -1
  625. package/dist/internal/uuid.js +52 -8
  626. package/dist/internal/uuid.js.map +1 -1
  627. package/dist/internal/version.js +1 -1
  628. package/dist/observability/Otlp.d.ts +15 -4
  629. package/dist/observability/Otlp.d.ts.map +1 -1
  630. package/dist/observability/Otlp.js +10 -0
  631. package/dist/observability/Otlp.js.map +1 -1
  632. package/dist/observability/OtlpProxy.d.ts +154 -0
  633. package/dist/observability/OtlpProxy.d.ts.map +1 -0
  634. package/dist/observability/OtlpProxy.js +235 -0
  635. package/dist/observability/OtlpProxy.js.map +1 -0
  636. package/dist/observability/OtlpResource.d.ts +7 -0
  637. package/dist/observability/OtlpResource.d.ts.map +1 -1
  638. package/dist/observability/OtlpResource.js +11 -1
  639. package/dist/observability/OtlpResource.js.map +1 -1
  640. package/dist/observability/OtlpTracer.d.ts.map +1 -1
  641. package/dist/observability/OtlpTracer.js +4 -0
  642. package/dist/observability/OtlpTracer.js.map +1 -1
  643. package/dist/observability/index.d.ts +5 -0
  644. package/dist/observability/index.d.ts.map +1 -1
  645. package/dist/observability/index.js +5 -0
  646. package/dist/observability/index.js.map +1 -1
  647. package/dist/persistence/CircuitBreaker.d.ts +897 -0
  648. package/dist/persistence/CircuitBreaker.d.ts.map +1 -0
  649. package/dist/persistence/CircuitBreaker.js +1165 -0
  650. package/dist/persistence/CircuitBreaker.js.map +1 -0
  651. package/dist/persistence/KeyValueStore.d.ts +47 -2
  652. package/dist/persistence/KeyValueStore.d.ts.map +1 -1
  653. package/dist/persistence/KeyValueStore.js +129 -18
  654. package/dist/persistence/KeyValueStore.js.map +1 -1
  655. package/dist/persistence/PersistedQueue.d.ts +9 -0
  656. package/dist/persistence/PersistedQueue.d.ts.map +1 -1
  657. package/dist/persistence/PersistedQueue.js +94 -52
  658. package/dist/persistence/PersistedQueue.js.map +1 -1
  659. package/dist/persistence/Persistence.d.ts +10 -4
  660. package/dist/persistence/Persistence.d.ts.map +1 -1
  661. package/dist/persistence/Persistence.js +18 -11
  662. package/dist/persistence/Persistence.js.map +1 -1
  663. package/dist/persistence/Redis.d.ts +78 -2
  664. package/dist/persistence/Redis.d.ts.map +1 -1
  665. package/dist/persistence/Redis.js +62 -7
  666. package/dist/persistence/Redis.js.map +1 -1
  667. package/dist/persistence/index.d.ts +5 -0
  668. package/dist/persistence/index.d.ts.map +1 -1
  669. package/dist/persistence/index.js +5 -0
  670. package/dist/persistence/index.js.map +1 -1
  671. package/dist/process/ChildProcess.d.ts +237 -71
  672. package/dist/process/ChildProcess.d.ts.map +1 -1
  673. package/dist/process/ChildProcess.js +56 -158
  674. package/dist/process/ChildProcess.js.map +1 -1
  675. package/dist/process/ChildProcessSpawner.d.ts +35 -0
  676. package/dist/process/ChildProcessSpawner.d.ts.map +1 -1
  677. package/dist/process/ChildProcessSpawner.js +5 -2
  678. package/dist/process/ChildProcessSpawner.js.map +1 -1
  679. package/dist/process/CommandLine.d.ts +294 -0
  680. package/dist/process/CommandLine.d.ts.map +1 -0
  681. package/dist/process/CommandLine.js +481 -0
  682. package/dist/process/CommandLine.js.map +1 -0
  683. package/dist/process/index.d.ts +5 -0
  684. package/dist/process/index.d.ts.map +1 -1
  685. package/dist/process/index.js +5 -0
  686. package/dist/process/index.js.map +1 -1
  687. package/dist/reactivity/AsyncResult.d.ts +1 -1
  688. package/dist/reactivity/AsyncResult.d.ts.map +1 -1
  689. package/dist/reactivity/Atom.d.ts +2 -1
  690. package/dist/reactivity/Atom.d.ts.map +1 -1
  691. package/dist/reactivity/Atom.js +3 -4
  692. package/dist/reactivity/Atom.js.map +1 -1
  693. package/dist/reactivity/AtomRegistry.js.map +1 -1
  694. package/dist/rpc/RpcClient.d.ts +16 -1
  695. package/dist/rpc/RpcClient.d.ts.map +1 -1
  696. package/dist/rpc/RpcClient.js +59 -24
  697. package/dist/rpc/RpcClient.js.map +1 -1
  698. package/dist/rpc/RpcMessage.d.ts +10 -1
  699. package/dist/rpc/RpcMessage.d.ts.map +1 -1
  700. package/dist/rpc/RpcMessage.js +1 -0
  701. package/dist/rpc/RpcMessage.js.map +1 -1
  702. package/dist/rpc/RpcSerialization.d.ts +14 -0
  703. package/dist/rpc/RpcSerialization.d.ts.map +1 -1
  704. package/dist/rpc/RpcSerialization.js +26 -8
  705. package/dist/rpc/RpcSerialization.js.map +1 -1
  706. package/dist/rpc/RpcServer.d.ts +31 -0
  707. package/dist/rpc/RpcServer.d.ts.map +1 -1
  708. package/dist/rpc/RpcServer.js +139 -56
  709. package/dist/rpc/RpcServer.js.map +1 -1
  710. package/dist/rpc/RpcTest.d.ts +1 -0
  711. package/dist/rpc/RpcTest.d.ts.map +1 -1
  712. package/dist/rpc/RpcTest.js +1 -0
  713. package/dist/rpc/RpcTest.js.map +1 -1
  714. package/dist/schema/Model.d.ts +14 -0
  715. package/dist/schema/Model.d.ts.map +1 -1
  716. package/dist/schema/Model.js +18 -3
  717. package/dist/schema/Model.js.map +1 -1
  718. package/dist/sql/Migrator.d.ts +2 -20
  719. package/dist/sql/Migrator.d.ts.map +1 -1
  720. package/dist/sql/Migrator.js +13 -55
  721. package/dist/sql/Migrator.js.map +1 -1
  722. package/dist/sql/MigratorFileSystem.d.ts +21 -0
  723. package/dist/sql/MigratorFileSystem.d.ts.map +1 -0
  724. package/dist/sql/MigratorFileSystem.js +57 -0
  725. package/dist/sql/MigratorFileSystem.js.map +1 -0
  726. package/dist/sql/SqlClient.d.ts +9 -4
  727. package/dist/sql/SqlClient.d.ts.map +1 -1
  728. package/dist/sql/SqlClient.js +13 -10
  729. package/dist/sql/SqlClient.js.map +1 -1
  730. package/dist/sql/SqlResolver.d.ts +4 -0
  731. package/dist/sql/SqlResolver.d.ts.map +1 -1
  732. package/dist/sql/SqlResolver.js +18 -4
  733. package/dist/sql/SqlResolver.js.map +1 -1
  734. package/dist/sql/Statement.d.ts +27 -6
  735. package/dist/sql/Statement.d.ts.map +1 -1
  736. package/dist/sql/Statement.js +21 -15
  737. package/dist/sql/Statement.js.map +1 -1
  738. package/dist/sql/index.js +1 -1
  739. package/dist/testing/index.d.ts +0 -5
  740. package/dist/testing/index.d.ts.map +1 -1
  741. package/dist/testing/index.js +0 -5
  742. package/dist/testing/index.js.map +1 -1
  743. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  744. package/dist/workflow/DurableDeferred.js +21 -2
  745. package/dist/workflow/DurableDeferred.js.map +1 -1
  746. package/dist/workflow/internal/crypto.js +9 -8
  747. package/dist/workflow/internal/crypto.js.map +1 -1
  748. package/package.json +3 -1
  749. package/src/Arbitrary.ts +10 -2
  750. package/src/BigDecimal.ts +3 -0
  751. package/src/Channel.ts +64 -29
  752. package/src/Config.ts +7 -1
  753. package/src/Context.ts +34 -10
  754. package/src/Cron.ts +178 -29
  755. package/src/Data.ts +60 -265
  756. package/src/Duration.ts +140 -0
  757. package/src/Effect.ts +915 -86
  758. package/src/Equal.ts +8 -1
  759. package/src/Exit.ts +3 -3
  760. package/src/FileSystem.ts +65 -4
  761. package/src/Formatter.ts +28 -13
  762. package/src/Glob.ts +205 -0
  763. package/src/Hash.ts +4 -1
  764. package/src/JsonPatch.ts +4 -0
  765. package/src/JsonSchema.ts +12 -40
  766. package/src/Match.ts +127 -0
  767. package/src/Metric.ts +3 -3
  768. package/src/MutableList.ts +7 -5
  769. package/src/MutableRef.ts +8 -1
  770. package/src/PartitionedSemaphore.ts +24 -13
  771. package/src/Pool.ts +182 -52
  772. package/src/PubSub.ts +5 -6
  773. package/src/Random.ts +3 -3
  774. package/src/RcMap.ts +5 -1
  775. package/src/Ref.ts +8 -0
  776. package/src/Schedule.ts +27 -4
  777. package/src/Schema.ts +1237 -87
  778. package/src/SchemaAST.ts +20 -20
  779. package/src/SchemaGetter.ts +27 -11
  780. package/src/SchemaParser.ts +51 -18
  781. package/src/SchemaRepresentation.ts +50 -34
  782. package/src/SchemaTransformation.ts +17 -5
  783. package/src/ScopedCache.ts +5 -9
  784. package/src/Semaphore.ts +208 -40
  785. package/src/Sink.ts +15 -2
  786. package/src/Stream.ts +48 -22
  787. package/src/String.ts +3 -0
  788. package/src/Terminal.ts +19 -1
  789. package/src/Tracer.ts +15 -1
  790. package/src/ai/AiError.ts +56 -80
  791. package/src/ai/Decision.ts +257 -6
  792. package/src/ai/DecisionModel.ts +171 -28
  793. package/src/ai/LanguageModel.ts +9 -0
  794. package/src/ai/McpServer.ts +1283 -299
  795. package/src/ai/McpTest.ts +380 -0
  796. package/src/ai/Prompt.ts +65 -0
  797. package/src/ai/Response.ts +36 -31
  798. package/src/ai/Tool.ts +65 -10
  799. package/src/ai/Toolkit.ts +4 -1
  800. package/src/ai/index.ts +6 -0
  801. package/src/ai/internal/mcpCore.ts +78 -44
  802. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +4 -7
  803. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +4 -7
  804. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +4 -7
  805. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +4 -7
  806. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +15 -10
  807. package/src/ai/internal/mcpProtocol.ts +29 -0
  808. package/src/ai/internal/mcpRuntime.ts +9 -5
  809. package/src/ai/internal/mcpStatefulRuntime.ts +14 -2
  810. package/src/cli/Argument.ts +21 -0
  811. package/src/cli/CliError.ts +90 -3
  812. package/src/cli/CliOutput.ts +300 -55
  813. package/src/cli/Command.ts +76 -41
  814. package/src/cli/Completions.ts +51 -0
  815. package/src/cli/Flag.ts +122 -0
  816. package/src/cli/GlobalFlag.ts +6 -7
  817. package/src/cli/HelpDoc.ts +18 -1
  818. package/src/cli/Param.ts +35 -6
  819. package/src/cli/Primitive.ts +16 -1
  820. package/src/cli/Prompt.ts +1 -0
  821. package/src/cli/TerminalCapabilities.ts +6 -3
  822. package/src/cli/internal/completions/bash.ts +79 -29
  823. package/src/cli/internal/help.ts +21 -9
  824. package/src/cluster/ClusterWorkflowEngine.ts +46 -16
  825. package/src/cluster/MessageStorage.ts +19 -14
  826. package/src/cluster/Sharding.ts +176 -47
  827. package/src/cluster/ShardingConfig.ts +43 -0
  828. package/src/cluster/SingleRunner.ts +3 -3
  829. package/src/cluster/Snowflake.ts +3 -7
  830. package/src/cluster/SqlMessageStorage.ts +49 -17
  831. package/src/cluster/SqlRunnerStorage.ts +143 -70
  832. package/src/cluster/internal/entityManager.ts +75 -22
  833. package/src/crypto/Aead.ts +517 -0
  834. package/src/crypto/Cipher.ts +326 -0
  835. package/src/crypto/CryptoError.ts +242 -0
  836. package/src/crypto/Digest.ts +167 -0
  837. package/src/crypto/Kdf.ts +315 -0
  838. package/src/crypto/KeyAgreement.ts +489 -0
  839. package/src/crypto/KeyFormat.ts +712 -0
  840. package/src/crypto/KeyWrap.ts +639 -0
  841. package/src/crypto/Mac.ts +386 -0
  842. package/src/crypto/SecureRandom.ts +459 -0
  843. package/src/crypto/Signature.ts +673 -0
  844. package/src/crypto/WebCrypto.ts +64 -0
  845. package/src/crypto/index.ts +78 -0
  846. package/src/crypto/internal/asymmetric.ts +329 -0
  847. package/src/crypto/internal/chacha.ts +1088 -0
  848. package/src/crypto/internal/der.ts +110 -0
  849. package/src/crypto/internal/digest.ts +16 -0
  850. package/src/crypto/internal/key.ts +235 -0
  851. package/src/crypto/internal/md5.ts +170 -0
  852. package/src/crypto/internal/webCrypto.ts +150 -0
  853. package/src/devtools/DevToolsClient.ts +2 -0
  854. package/src/encoding/Base64.ts +38 -0
  855. package/src/encoding/Base64Url.ts +5 -2
  856. package/src/encoding/Csv.ts +1298 -0
  857. package/src/encoding/Hex.ts +3 -2
  858. package/src/encoding/SchemaBinary.ts +40 -4
  859. package/src/encoding/Sse.ts +4 -3
  860. package/src/encoding/Toml.ts +117 -35
  861. package/src/encoding/Yaml.ts +61 -18
  862. package/src/encoding/index.ts +6 -0
  863. package/src/eventlog/EventJournal.ts +4 -3
  864. package/src/eventlog/EventLog.ts +340 -15
  865. package/src/eventlog/EventLogEncryption.ts +118 -110
  866. package/src/eventlog/EventLogMessage.ts +273 -1
  867. package/src/eventlog/EventLogRemote.ts +367 -23
  868. package/src/eventlog/EventLogServer.ts +23 -8
  869. package/src/eventlog/EventLogServerEncrypted.ts +1130 -67
  870. package/src/eventlog/EventLogSessionAuth.ts +56 -70
  871. package/src/eventlog/EventRow.ts +12 -3
  872. package/src/eventlog/SqlEventJournal.ts +68 -45
  873. package/src/eventlog/SqlEventLogServerEncrypted.ts +1244 -136
  874. package/src/eventlog/SqlEventLogServerUnencrypted.ts +2 -2
  875. package/src/eventlog/internal/batches.ts +30 -0
  876. package/src/eventlog/internal/compaction.ts +64 -0
  877. package/src/eventlog/internal/conflicts.ts +0 -4
  878. package/src/eventlog/internal/identityRootSecretDerivation.ts +93 -123
  879. package/src/eventlog/internal/remoteCompaction.ts +92 -0
  880. package/src/http/Cookies.ts +291 -12
  881. package/src/http/FetchHttpClient.ts +88 -16
  882. package/src/http/HttpClient.ts +636 -29
  883. package/src/http/HttpClientError.ts +35 -1
  884. package/src/http/HttpClientRequest.ts +21 -10
  885. package/src/http/HttpClientResponse.ts +3 -2
  886. package/src/http/HttpMethod.ts +10 -2
  887. package/src/http/HttpMiddleware.ts +15 -18
  888. package/src/http/HttpRouter.ts +2 -1
  889. package/src/http/HttpServerError.ts +2 -2
  890. package/src/http/HttpServerRequest.ts +101 -30
  891. package/src/http/HttpServerResponse.ts +3 -2
  892. package/src/http/HttpStaticServer.ts +51 -22
  893. package/src/http/HttpTraceContext.ts +7 -4
  894. package/src/http/MultipartParser/internal/contentType.ts +3 -3
  895. package/src/http/ProblemDetails.ts +149 -0
  896. package/src/http/UrlParams.ts +6 -1
  897. package/src/http/index.ts +6 -0
  898. package/src/http/internal/compression.ts +4 -5
  899. package/src/http/internal/decompression.ts +10 -4
  900. package/src/http/internal/httpBody.ts +10 -2
  901. package/src/http/internal/urlParams.ts +37 -0
  902. package/src/http-api/HttpApi.ts +3 -6
  903. package/src/http-api/HttpApiBuilder.ts +61 -19
  904. package/src/http-api/HttpApiClient.ts +284 -72
  905. package/src/http-api/HttpApiEndpoint.ts +135 -35
  906. package/src/http-api/HttpApiError.ts +250 -0
  907. package/src/http-api/HttpApiMiddleware.ts +95 -1
  908. package/src/http-api/HttpApiSchema.ts +236 -52
  909. package/src/http-api/HttpApiTest.ts +3 -2
  910. package/src/http-api/OpenApi.ts +20 -17
  911. package/src/http-api/internal/toCodecHttpProperties.ts +56 -0
  912. package/src/index.ts +6 -6
  913. package/src/internal/arbitrary/runner.ts +133 -36
  914. package/src/internal/core.ts +2 -0
  915. package/src/internal/effect.ts +58 -34
  916. package/src/internal/equal.ts +17 -0
  917. package/src/internal/glob.ts +736 -0
  918. package/src/internal/jsonSchema.ts +27 -0
  919. package/src/internal/matcher.ts +18 -1
  920. package/src/internal/persistence.ts +11 -0
  921. package/src/internal/rcRef.ts +4 -1
  922. package/src/internal/schema/compilerRegistry.ts +6 -1
  923. package/src/internal/schema/content.ts +18 -0
  924. package/src/internal/schema/fromJsonSchemaDocument.ts +228 -190
  925. package/src/internal/schema/fromRepresentation.ts +2 -1
  926. package/src/internal/schema/standardSchema.ts +2 -2
  927. package/src/internal/schema/toCodeDocument.ts +11 -7
  928. package/src/internal/schema/toCodec.ts +26 -22
  929. package/src/internal/schema/toJsonSchemaDocument.ts +189 -167
  930. package/src/internal/schema/toRepresentation.ts +122 -140
  931. package/src/internal/sql.ts +14 -0
  932. package/src/internal/trie.ts +43 -14
  933. package/src/internal/ulid.ts +44 -0
  934. package/src/internal/uuid.ts +57 -16
  935. package/src/internal/version.ts +1 -1
  936. package/src/observability/Otlp.ts +21 -4
  937. package/src/observability/OtlpProxy.ts +314 -0
  938. package/src/observability/OtlpResource.ts +9 -1
  939. package/src/observability/OtlpTracer.ts +4 -0
  940. package/src/observability/index.ts +6 -0
  941. package/src/persistence/CircuitBreaker.ts +1678 -0
  942. package/src/persistence/KeyValueStore.ts +219 -22
  943. package/src/persistence/PersistedQueue.ts +128 -59
  944. package/src/persistence/Persistence.ts +20 -13
  945. package/src/persistence/Redis.ts +129 -8
  946. package/src/persistence/index.ts +6 -0
  947. package/src/process/ChildProcess.ts +285 -264
  948. package/src/process/ChildProcessSpawner.ts +46 -2
  949. package/src/process/CommandLine.ts +630 -0
  950. package/src/process/index.ts +6 -0
  951. package/src/reactivity/AsyncResult.ts +1 -1
  952. package/src/reactivity/Atom.ts +5 -5
  953. package/src/reactivity/AtomRegistry.ts +1 -1
  954. package/src/rpc/RpcClient.ts +75 -36
  955. package/src/rpc/RpcMessage.ts +10 -1
  956. package/src/rpc/RpcSerialization.ts +35 -9
  957. package/src/rpc/RpcServer.ts +176 -63
  958. package/src/rpc/RpcTest.ts +3 -0
  959. package/src/schema/Model.ts +26 -3
  960. package/src/sql/Migrator.ts +15 -69
  961. package/src/sql/MigratorFileSystem.ts +71 -0
  962. package/src/sql/SqlClient.ts +13 -20
  963. package/src/sql/SqlResolver.ts +23 -9
  964. package/src/sql/Statement.ts +38 -9
  965. package/src/sql/index.ts +1 -1
  966. package/src/testing/index.ts +0 -6
  967. package/src/workflow/DurableDeferred.ts +23 -6
  968. package/src/workflow/internal/crypto.ts +15 -11
  969. package/dist/Crypto.d.ts +0 -244
  970. package/dist/Crypto.d.ts.map +0 -1
  971. package/dist/Crypto.js +0 -139
  972. package/dist/Crypto.js.map +0 -1
  973. package/dist/testing/TestCrypto.d.ts +0 -73
  974. package/dist/testing/TestCrypto.d.ts.map +0 -1
  975. package/dist/testing/TestCrypto.js +0 -87
  976. package/dist/testing/TestCrypto.js.map +0 -1
  977. package/src/Crypto.ts +0 -347
  978. package/src/testing/TestCrypto.ts +0 -94
@@ -12,13 +12,22 @@
12
12
  * @since 4.0.0
13
13
  */
14
14
  import * as Arr from "../Array.ts"
15
+ import * as Cause from "../Cause.ts"
16
+ import * as Clock from "../Clock.ts"
15
17
  import * as Context from "../Context.ts"
18
+ import * as SecureRandom from "../crypto/SecureRandom.ts"
19
+ import * as Data from "../Data.ts"
20
+ import * as Duration from "../Duration.ts"
16
21
  import * as Effect from "../Effect.ts"
17
22
  import * as Equal from "../Equal.ts"
18
23
  import * as Uuid from "../internal/uuid.ts"
24
+ import * as Latch from "../Latch.ts"
19
25
  import * as Layer from "../Layer.ts"
20
26
  import * as PubSub from "../PubSub.ts"
27
+ import * as Pull from "../Pull.ts"
21
28
  import * as RcMap from "../RcMap.ts"
29
+ import type * as Rpc from "../rpc/Rpc.ts"
30
+ import type * as RpcGroup from "../rpc/RpcGroup.ts"
22
31
  import * as RpcServer from "../rpc/RpcServer.ts"
23
32
  import type * as Scope from "../Scope.ts"
24
33
  import * as Stream from "../Stream.ts"
@@ -26,6 +35,13 @@ import { type EntryId, makeRemoteIdUnsafe, type RemoteId } from "./EventJournal.
26
35
  import type { EncryptedBatch, EncryptedRemoteBatch } from "./EventLogEncryption.ts"
27
36
  import {
28
37
  ChangesRpc,
38
+ ChunkedMessage,
39
+ type CompactionInputHeader,
40
+ CompactionInputRpc,
41
+ CompactionPartRpc,
42
+ CompactionStatus,
43
+ type EventLogAuthentication,
44
+ EventLogCompactionRpcs,
29
45
  EventLogProtocolError,
30
46
  EventLogRemoteRpcs,
31
47
  type StoreId,
@@ -33,6 +49,15 @@ import {
33
49
  WriteEntries
34
50
  } from "./EventLogMessage.ts"
35
51
  import * as EventLogServer from "./EventLogServer.ts"
52
+ import {
53
+ hourMillis,
54
+ lagHour,
55
+ logCommit,
56
+ logNoOp,
57
+ logRollback,
58
+ maxSegments,
59
+ positionsPerSegment
60
+ } from "./internal/compaction.ts"
36
61
 
37
62
  /**
38
63
  * Provides RPC handlers for the encrypted event-log server.
@@ -43,72 +68,87 @@ import * as EventLogServer from "./EventLogServer.ts"
43
68
  * sequences. Change streams encode stored batches, excluding this connection's
44
69
  * writes.
45
70
  *
71
+ * It also serves `EventLogCompactionRpcs`. If `Storage` provides `compaction`,
72
+ * it sets `HelloResponse.compaction` and applies the compaction policy;
73
+ * otherwise those handlers fail with `NotFound`.
74
+ *
46
75
  * @stability unstable
47
76
  * @category layers
48
77
  * @since 4.0.0
49
78
  */
50
- export const layerRpcHandlers = Layer.unwrap(Effect.gen(function*() {
79
+ export const layerRpcHandlers: Layer.Layer<
80
+ | Rpc.ToHandler<RpcGroup.Rpcs<typeof EventLogRemoteRpcs>>
81
+ | Rpc.ToHandler<RpcGroup.Rpcs<typeof EventLogCompactionRpcs>>
82
+ | EventLogAuthentication,
83
+ never,
84
+ Storage
85
+ > = Layer.unwrap(Effect.gen(function*() {
51
86
  const storage = yield* Storage
52
87
  const remoteId = yield* storage.getId
53
88
 
54
- return EventLogServer.layerRpcHandlers({
55
- remoteId,
56
- firstSequence: storage.firstSequence,
57
- getOrCreateSessionAuthBinding: (publicKey, signingPublicKey) =>
58
- storage.getOrCreateSessionAuthBinding(publicKey, signingPublicKey),
59
- onWrite: Effect.fnUntraced(function*(data, authenticatedPublicKeys, writes) {
60
- const request = yield* WriteEntries.decode(data).pipe(
61
- Effect.mapError((_) =>
62
- new EventLogProtocolError({
63
- requestTag: "WriteEntries",
64
- publicKey: undefined,
65
- code: "InternalServerError",
66
- message: "Decoding failure"
67
- })
68
- )
69
- )
70
- if (!authenticatedPublicKeys.has(request.publicKey)) {
71
- return yield* new EventLogProtocolError({
72
- requestTag: "WriteEntries",
73
- publicKey: request.publicKey,
74
- code: "Forbidden",
75
- message: "Identity is not authenticated"
76
- })
77
- }
78
- if (request.batches.length === 0) return []
79
- const entryIds = request.batches.flatMap((batch) => batch.entryIds)
80
- return yield* storage.write(request.publicKey, request.storeId, request.batches).pipe(
81
- Effect.flatMap((stored) => toWriteAcks(storage, request.publicKey, request.storeId, request.batches, stored)),
82
- (write) => writes.track({ publicKey: request.publicKey, storeId: request.storeId, entryIds }, write),
83
- Effect.catchCause((_) =>
84
- Effect.fail(
89
+ return Layer.merge(
90
+ EventLogServer.layerRpcHandlers({
91
+ remoteId,
92
+ firstSequence: storage.firstSequence,
93
+ compaction: storage.compaction !== undefined,
94
+ getOrCreateSessionAuthBinding: (publicKey, signingPublicKey) =>
95
+ storage.getOrCreateSessionAuthBinding(publicKey, signingPublicKey),
96
+ onWrite: Effect.fnUntraced(function*(data, authenticatedPublicKeys, writes) {
97
+ const request = yield* WriteEntries.decode(data).pipe(
98
+ Effect.mapError((_) =>
85
99
  new EventLogProtocolError({
86
100
  requestTag: "WriteEntries",
87
- publicKey: request.publicKey,
101
+ publicKey: undefined,
88
102
  code: "InternalServerError",
89
- message: "Persistence failure"
103
+ message: "Decoding failure"
90
104
  })
91
105
  )
92
106
  )
93
- )
107
+ if (!authenticatedPublicKeys.has(request.publicKey)) {
108
+ return yield* new EventLogProtocolError({
109
+ requestTag: "WriteEntries",
110
+ publicKey: request.publicKey,
111
+ code: "Forbidden",
112
+ message: "Identity is not authenticated"
113
+ })
114
+ }
115
+ if (request.batches.length === 0) return []
116
+ const entryIds = request.batches.flatMap((batch) => batch.entryIds)
117
+ return yield* storage.write(request.publicKey, request.storeId, request.batches).pipe(
118
+ Effect.flatMap((stored) => toWriteAcks(storage, request.publicKey, request.storeId, request.batches, stored)),
119
+ (write) => writes.track({ publicKey: request.publicKey, storeId: request.storeId, entryIds }, write),
120
+ Effect.catchCause((_) =>
121
+ Effect.fail(
122
+ new EventLogProtocolError({
123
+ requestTag: "WriteEntries",
124
+ publicKey: request.publicKey,
125
+ code: "InternalServerError",
126
+ message: "Persistence failure"
127
+ })
128
+ )
129
+ )
130
+ )
131
+ }),
132
+ changes: ({ publicKey, startSequence, storeId, writes }) =>
133
+ writes.filter(
134
+ {
135
+ publicKey,
136
+ storeId,
137
+ entryIds: (batch) => batch.entryIds,
138
+ sequence: (batch) => batch.sequence + batch.entryIds.length - 1
139
+ },
140
+ storage.changes(publicKey, storeId, startSequence)
141
+ ).pipe(
142
+ Stream.mapArrayEffect((batches) => Effect.map(ChangesRpc.encodeEncrypted(batches), Arr.of))
143
+ )
94
144
  }),
95
- changes: ({ publicKey, startSequence, storeId, writes }) =>
96
- writes.filter(
97
- {
98
- publicKey,
99
- storeId,
100
- entryIds: (batch) => batch.entryIds,
101
- sequence: (batch) => batch.sequence + batch.entryIds.length - 1
102
- },
103
- storage.changes(publicKey, storeId, startSequence)
104
- ).pipe(
105
- Stream.mapArrayEffect((batches) => Effect.map(ChangesRpc.encodeEncrypted(batches), Arr.of))
106
- )
107
- })
145
+ layerCompactionRpcHandlers(storage)
146
+ )
108
147
  }))
109
148
 
110
149
  // Acknowledges each entry occurrence in request order: at its position in the
111
- // batch this write stored, or, for a skipped batch, where it was first stored.
150
+ // batch this write stored, or, for a skipped batch, where it was first stored
151
+ // if that is above the active compaction base.
112
152
  const toWriteAcks = Effect.fnUntraced(function*(
113
153
  storage: Storage["Service"],
114
154
  publicKey: string,
@@ -125,8 +165,13 @@ const toWriteAcks = Effect.fnUntraced(function*(
125
165
  const skipped = batches.flatMap((batch, i) => storedAt[i] === undefined ? batch.entryIds : [])
126
166
  const firstStored = new Map<string, number>()
127
167
  if (skipped.length > 0) {
168
+ // An id first stored at or below the active base may have been dropped, so
169
+ // acking it would move the client's cursor past kept entries.
170
+ const base = storage.compaction === undefined
171
+ ? -Infinity
172
+ : (yield* storage.compaction.status(publicKey, storeId)).base
128
173
  for (const ack of yield* storage.entrySequences(publicKey, storeId, skipped)) {
129
- firstStored.set(Uuid.stringify(ack.entryId), ack.sequence)
174
+ if (ack.sequence > base) firstStored.set(Uuid.stringify(ack.entryId), ack.sequence)
130
175
  }
131
176
  }
132
177
  const acks: Array<WriteAck> = []
@@ -139,6 +184,260 @@ const toWriteAcks = Effect.fnUntraced(function*(
139
184
  return acks
140
185
  })
141
186
 
187
+ const layerCompactionRpcHandlers = (storage: Storage["Service"]) =>
188
+ EventLogCompactionRpcs.toLayer(Effect.sync(() => {
189
+ const compaction = storage.compaction
190
+ const toFrames = EventLogServer.makeFramer()
191
+
192
+ type Request = { readonly publicKey: string; readonly storeId: StoreId }
193
+ const protocolError = (
194
+ requestTag: string,
195
+ request: Request,
196
+ code: EventLogProtocolError["code"],
197
+ message: string
198
+ ) =>
199
+ new EventLogProtocolError({ requestTag, publicKey: request.publicKey, storeId: request.storeId, code, message })
200
+ const fromCompactionError =
201
+ (requestTag: string, request: Request) => (error: CompactionNotFound | CompactionInvalidRequest) =>
202
+ error._tag === "CompactionNotFound"
203
+ ? protocolError(requestTag, request, "NotFound", "Compaction stage not found")
204
+ : protocolError(requestTag, request, "InvalidRequest", error.message)
205
+
206
+ const withCompaction = <A>(
207
+ requestTag: string,
208
+ request: Request,
209
+ client: Rpc.ServerClient,
210
+ f: (compaction: StorageCompaction) => Effect.Effect<A, CompactionNotFound | CompactionInvalidRequest>
211
+ ): Effect.Effect<A, EventLogProtocolError> => {
212
+ if (!Context.getOrUndefined(client.annotations, EventLogServer.AuthenticatedIdentities)?.has(request.publicKey)) {
213
+ return Effect.fail(protocolError(requestTag, request, "Forbidden", "Identity is not authenticated"))
214
+ }
215
+ if (compaction === undefined) {
216
+ return Effect.fail(protocolError(requestTag, request, "NotFound", "Compaction is not supported"))
217
+ }
218
+ return Effect.mapError(f(compaction), fromCompactionError(requestTag, request))
219
+ }
220
+ const withCompactionStream = <A>(
221
+ requestTag: string,
222
+ request: Request,
223
+ client: Rpc.ServerClient,
224
+ f: (compaction: StorageCompaction) => Stream.Stream<A, CompactionNotFound>
225
+ ): Stream.Stream<A, EventLogProtocolError> =>
226
+ Stream.unwrap(
227
+ withCompaction(
228
+ requestTag,
229
+ request,
230
+ client,
231
+ (compaction) => Effect.succeed(Stream.mapError(f(compaction), fromCompactionError(requestTag, request)))
232
+ )
233
+ )
234
+
235
+ const getStatus = Effect.fnUntraced(function*(compaction: StorageCompaction, publicKey: string, storeId: StoreId) {
236
+ const status = yield* compaction.status(publicKey, storeId)
237
+ return { status, wanted: isCompactionWanted(status, compaction.config.thresholdFloor) }
238
+ })
239
+
240
+ return EventLogCompactionRpcs.of({
241
+ "EventLog.CompactionStatus": (request, { client }) =>
242
+ withCompaction("CompactionStatus", request, client, (compaction) =>
243
+ Effect.map(getStatus(compaction, request.publicKey, request.storeId), ({ status, wanted }) =>
244
+ new CompactionStatus({ wanted, head: status.head }))),
245
+ "EventLog.CompactionBegin": (request, { client }) =>
246
+ withCompaction("CompactionBegin", request, client, (compaction) =>
247
+ Effect.gen(function*() {
248
+ const { status, wanted } = yield* getStatus(compaction, request.publicKey, request.storeId)
249
+ if (!wanted) {
250
+ return { _tag: "NotWanted" } as const
251
+ }
252
+ const result = yield* compaction.begin(request.publicKey, request.storeId, { through: status.lagHead })
253
+ return result._tag === "Busy"
254
+ ? { _tag: "Busy" } as const
255
+ : { _tag: "Started", stageId: result.stageId } as const
256
+ })),
257
+ "EventLog.CompactionInput": (request, { client }) =>
258
+ withCompactionStream("CompactionInput", request, client, (compaction) =>
259
+ compaction.inputHeaders(request.publicKey, request.storeId, request.stageId).pipe(
260
+ Stream.mapArrayEffect((headers) =>
261
+ Effect.map(
262
+ Effect.orDie(
263
+ CompactionInputRpc.encodeHeaders(headers as Arr.NonEmptyReadonlyArray<CompactionInputHeader>)
264
+ ),
265
+ toFrames
266
+ )
267
+ )
268
+ )),
269
+ "EventLog.CompactionFetch": (request, { client }) =>
270
+ withCompactionStream("CompactionFetch", request, client, (compaction) =>
271
+ compaction.fetch(request.publicKey, request.storeId, request.stageId, request.batches).pipe(
272
+ Stream.mapArrayEffect((batches) =>
273
+ Effect.map(Effect.orDie(ChangesRpc.encodeEncrypted(batches)), toFrames)
274
+ )
275
+ )),
276
+ "EventLog.CompactionPart": (request, { client }) =>
277
+ withCompaction("CompactionPart", request, client, (compaction) =>
278
+ Effect.gen(function*() {
279
+ const { publicKey, stageId, storeId } = request
280
+ const data = ChunkedMessage.join(getPendingSegments(client, request), request.chunk)
281
+ if (data === undefined) {
282
+ return
283
+ }
284
+ const batches = yield* CompactionPartRpc.decodeSegment(data).pipe(
285
+ Effect.catch(() =>
286
+ Effect.andThen(
287
+ compaction.abort(publicKey, storeId, stageId),
288
+ Effect.fail(new CompactionInvalidRequest({ stageId, message: "Invalid segment" }))
289
+ )
290
+ )
291
+ )
292
+ yield* compaction.stagePart(publicKey, storeId, stageId, request.chunk.id, batches)
293
+ })),
294
+ "EventLog.CompactionAbort": (request, { client }) =>
295
+ withCompaction("CompactionAbort", request, client, (compaction) => {
296
+ releasePendingSegments(client, request)
297
+ return compaction.abort(request.publicKey, request.storeId, request.stageId)
298
+ }),
299
+ "EventLog.CompactionCommit": (request, { client }) =>
300
+ withCompaction("CompactionCommit", request, client, (compaction) => {
301
+ const { publicKey, stageId, storeId } = request
302
+ releasePendingSegments(client, request)
303
+ if (request.result._tag === "NoOp") {
304
+ return Effect.as(compaction.recordNoOp(publicKey, storeId, stageId), "Committed" as const)
305
+ }
306
+ return checkSnapshot(compaction, publicKey, storeId, stageId, request.result.segmentCount).pipe(
307
+ Effect.tapError((error) =>
308
+ error._tag === "CompactionInvalidRequest" ? compaction.abort(publicKey, storeId, stageId) : Effect.void
309
+ ),
310
+ Effect.flatMap((virtualSequences) =>
311
+ compaction.commit(publicKey, storeId, stageId, virtualSequences)
312
+ )
313
+ )
314
+ })
315
+ })
316
+ }))
317
+
318
+ // The input past the lag must reach the threshold. After `k` consecutive
319
+ // no-ops, the lag head must also have moved `threshold * 2^(k-1)` past the
320
+ // latest one.
321
+ const isCompactionWanted = (status: CompactionStoreStatus, thresholdFloor: number): boolean => {
322
+ const threshold = Math.max(status.activeKeptCount, thresholdFloor)
323
+ const pending = status.lagHead - status.base
324
+ if (pending <= 0 || pending < threshold) return false
325
+ if (status.noOp === undefined) return true
326
+ return status.lagHead - status.noOp.at >= threshold * 2 ** (status.noOp.count - 1)
327
+ }
328
+
329
+ // Checks the staged batches against the stage's input in one pass over both
330
+ // and returns each batch's virtual start sequence. The kept ids, read in
331
+ // segments 0 to segmentCount - 1, must be a non-empty, repeat-free
332
+ // subsequence of the input's ids.
333
+ const checkSnapshot = Effect.fnUntraced(function*(
334
+ compaction: StorageCompaction,
335
+ publicKey: string,
336
+ storeId: StoreId,
337
+ stageId: string,
338
+ segmentCount: number
339
+ ) {
340
+ const invalid = (message: string) => Effect.fail(new CompactionInvalidRequest({ stageId, message }))
341
+ const pullInput = yield* Stream.toPull(compaction.inputHeaders(publicKey, storeId, stageId))
342
+ let headers: ReadonlyArray<CompactionBatchHeader> = []
343
+ let headerIndex = 0
344
+ let entryIndex = 0
345
+ let lastInputSequence = -1
346
+ const nextInput = Effect.gen(function*() {
347
+ while (true) {
348
+ const header = headers[headerIndex]
349
+ if (header === undefined) {
350
+ const chunk = yield* Pull.catchDone(pullInput, () => Effect.succeed(undefined))
351
+ if (chunk === undefined) return undefined
352
+ headers = chunk
353
+ headerIndex = 0
354
+ continue
355
+ }
356
+ if (entryIndex === header.entryIds.length) {
357
+ headerIndex++
358
+ entryIndex = 0
359
+ continue
360
+ }
361
+ const sequence = header.sequence + entryIndex
362
+ const id = Uuid.stringify(header.entryIds[entryIndex++])
363
+ lastInputSequence = sequence
364
+ return { id, sequence }
365
+ }
366
+ })
367
+
368
+ // The input sequence of each batch's last kept entry, and its size.
369
+ const ends: Array<number> = []
370
+ const sizes: Array<number> = []
371
+ let segment = -1
372
+ yield* Stream.runForEach(compaction.stagedHeaders(publicKey, storeId, stageId), (header) =>
373
+ Effect.gen(function*() {
374
+ const headerSegment = Math.floor(header.position / positionsPerSegment)
375
+ if (headerSegment !== segment) {
376
+ if (headerSegment !== segment + 1 || headerSegment >= segmentCount) {
377
+ return yield* invalid("Unexpected segment")
378
+ }
379
+ segment = headerSegment
380
+ }
381
+ let last = -1
382
+ for (const entryId of header.entryIds) {
383
+ const id = Uuid.stringify(entryId)
384
+ let input = yield* nextInput
385
+ while (input !== undefined && input.id !== id) input = yield* nextInput
386
+ if (input === undefined) return yield* invalid("Kept entry is not in the input, or is out of order")
387
+ last = input.sequence
388
+ }
389
+ ends.push(last)
390
+ sizes.push(header.entryIds.length)
391
+ }))
392
+ if (ends.length === 0) return yield* invalid("Snapshot is empty")
393
+ if (segment !== segmentCount - 1) return yield* invalid("Missing segment")
394
+ while ((yield* nextInput) !== undefined) {
395
+ // Drain the input to learn its last sequence.
396
+ }
397
+
398
+ // Each batch ends at its last kept entry's input sequence, and the final one
399
+ // at the end of the input, so no batch starts below its entries' input.
400
+ return ends.map((end, i) => (i === ends.length - 1 ? lastInputSequence : end) - sizes[i] + 1)
401
+ }, Effect.scoped)
402
+
403
+ // One connection's unjoined segment pieces, keyed by identity, store and
404
+ // stage. The JSON tuple key keeps the components unambiguous.
405
+ class PendingSegments extends Context.Service<
406
+ PendingSegments,
407
+ Map<string, ReturnType<typeof ChunkedMessage.initialJoinState>>
408
+ >()("effect/eventlog/EventLogServerEncrypted/PendingSegments") {}
409
+
410
+ const pendingSegmentsKey = (
411
+ request: { readonly publicKey: string; readonly storeId: StoreId; readonly stageId: string }
412
+ ) => JSON.stringify([request.publicKey, request.storeId, request.stageId])
413
+
414
+ const getPendingSegments = (client: Rpc.ServerClient, request: {
415
+ readonly publicKey: string
416
+ readonly storeId: StoreId
417
+ readonly stageId: string
418
+ }) => {
419
+ let stages = Context.getOrUndefined(client.annotations, PendingSegments)
420
+ if (stages === undefined) {
421
+ stages = new Map()
422
+ client.annotate(PendingSegments, stages)
423
+ }
424
+ const key = pendingSegmentsKey(request)
425
+ let pending = stages.get(key)
426
+ if (pending === undefined) {
427
+ pending = ChunkedMessage.initialJoinState()
428
+ stages.set(key, pending)
429
+ }
430
+ return pending
431
+ }
432
+
433
+ const releasePendingSegments = (client: Rpc.ServerClient, request: {
434
+ readonly publicKey: string
435
+ readonly storeId: StoreId
436
+ readonly stageId: string
437
+ }) => {
438
+ Context.getOrUndefined(client.annotations, PendingSegments)?.delete(pendingSegmentsKey(request))
439
+ }
440
+
142
441
  /**
143
442
  * Provides an encrypted event-log RPC server using `EventLogRemoteRpcs` and the
144
443
  * encrypted server RPC handlers.
@@ -153,7 +452,8 @@ const toWriteAcks = Effect.fnUntraced(function*(
153
452
  * This layer installs `EventLogRemoteRpcs` on the provided RPC server protocol
154
453
  * and wires those RPCs to `layerRpcHandlers`. Encrypted batches, session
155
454
  * authentication bindings, remote ids, and change streams are delegated to
156
- * `Storage`.
455
+ * `Storage`. It also installs `EventLogCompactionRpcs`, which clients call
456
+ * only when `Storage` provides `compaction`.
157
457
  *
158
458
  * @see {@link layerRpcHandlers} for the encrypted handler layer without installing an RPC server protocol
159
459
  * @see {@link Storage} for the storage service required by this layer
@@ -163,9 +463,9 @@ const toWriteAcks = Effect.fnUntraced(function*(
163
463
  * @category layers
164
464
  * @since 4.0.0
165
465
  */
166
- export const layer: Layer.Layer<never, never, RpcServer.Protocol | Storage> = RpcServer.layer(EventLogRemoteRpcs).pipe(
167
- Layer.provide(layerRpcHandlers)
168
- )
466
+ export const layer: Layer.Layer<never, never, RpcServer.Protocol | Storage> = RpcServer.layer(
467
+ EventLogRemoteRpcs.merge(EventLogCompactionRpcs)
468
+ ).pipe(Layer.provide(layerRpcHandlers))
169
469
 
170
470
  /**
171
471
  * Defines the backing store service used by the encrypted event-log server.
@@ -189,6 +489,10 @@ export const layer: Layer.Layer<never, never, RpcServer.Protocol | Storage> = Rp
189
489
  * whose last sequence is at or after `startSequence`, followed by newly stored
190
490
  * batches.
191
491
  *
492
+ * Storages that support remote compaction provide `compaction`. With an active
493
+ * snapshot, `changes` serves snapshot batches by their virtual sequences for a
494
+ * start sequence at or below the snapshot base, then the originals above it.
495
+ *
192
496
  * @stability unstable
193
497
  * @category services
194
498
  * @since 4.0.0
@@ -218,33 +522,372 @@ export class Storage extends Context.Service<Storage, {
218
522
  storeId: StoreId,
219
523
  startSequence: number
220
524
  ) => Stream.Stream<EncryptedRemoteBatch>
525
+ readonly compaction?: StorageCompaction | undefined
221
526
  }>()("effect/eventlog/EventLogServer/Storage") {}
222
527
 
223
528
  /**
224
- * Creates an in-memory encrypted server `Storage`.
529
+ * Storage primitives for encrypted remote compaction.
530
+ *
531
+ * **Details**
532
+ *
533
+ * The server owns the policy: trigger threshold, lag, no-op backoff, commit
534
+ * checks and virtual sequences. Each primitive is atomic per store.
535
+ *
536
+ * A store always has one active activation. Before the first commit it is the
537
+ * originals, with base `firstSequence - 1`. `begin` opens a stage pinned to the
538
+ * active activation and to `through`. The stage claim expires `stageTtl` after
539
+ * begin or the latest stored Part, and `begin` takes over an expired claim.
540
+ * Stage-scoped primitives fail with `CompactionNotFound` once the stage has
541
+ * expired, been aborted or committed, or when it belongs to another store, so
542
+ * a commit is answered only once.
543
+ *
544
+ * `stagePart` stores one segment of encrypted snapshot batches, or answers
545
+ * `AlreadyStored` for a segment it already has. It doesn't check entry ids:
546
+ * the server's commit checks reject repeats, and readers dedupe by id.
547
+ * `commit` takes every staged batch's virtual
548
+ * start sequence in position order, and activates the staged set only if the
549
+ * pinned activation is still active.
550
+ *
551
+ * `rollback` reactivates the previous generation under a new activation id
552
+ * and re-appends, at the head, the input of the stage that built the withdrawn
553
+ * one: the previous batch set, then the originals between the two bases.
554
+ * Clients that read the withdrawn snapshot get what it dropped through
555
+ * `changes`. A sweep on `Clock` prunes a generation, and the originals it
556
+ * replaced, `grace` after it was superseded or withdrawn.
557
+ *
558
+ * @stability unstable
559
+ * @category models
560
+ * @since 4.0.0
561
+ */
562
+ export interface StorageCompaction {
563
+ readonly config: CompactionConfig
564
+ readonly status: (publicKey: string, storeId: StoreId) => Effect.Effect<CompactionStoreStatus>
565
+ readonly begin: (
566
+ publicKey: string,
567
+ storeId: StoreId,
568
+ options: { readonly through: number }
569
+ ) => Effect.Effect<CompactionBeginResult>
570
+ /**
571
+ * Batch headers in input order: the pinned batch set, then the originals in
572
+ * `(base, through]`.
573
+ */
574
+ readonly inputHeaders: (
575
+ publicKey: string,
576
+ storeId: StoreId,
577
+ stageId: string
578
+ ) => Stream.Stream<CompactionBatchHeader, CompactionNotFound>
579
+ /**
580
+ * Input batches by their input sequence: snapshot batches at or below the
581
+ * pinned base, originals above it.
582
+ */
583
+ readonly fetch: (
584
+ publicKey: string,
585
+ storeId: StoreId,
586
+ stageId: string,
587
+ sequences: ReadonlyArray<number>
588
+ ) => Stream.Stream<EncryptedRemoteBatch, CompactionNotFound>
589
+ readonly stagePart: (
590
+ publicKey: string,
591
+ storeId: StoreId,
592
+ stageId: string,
593
+ segment: number,
594
+ batches: Arr.NonEmptyReadonlyArray<EncryptedBatch>
595
+ ) => Effect.Effect<"Stored" | "AlreadyStored", CompactionInvalidRequest | CompactionNotFound>
596
+ /**
597
+ * Staged batch headers in position order (segment, then batch index).
598
+ */
599
+ readonly stagedHeaders: (
600
+ publicKey: string,
601
+ storeId: StoreId,
602
+ stageId: string
603
+ ) => Stream.Stream<{ readonly position: number; readonly entryIds: ReadonlyArray<EntryId> }, CompactionNotFound>
604
+ /**
605
+ * Logs the outcome with this commit's own activation id, not the one active
606
+ * when the log runs.
607
+ */
608
+ readonly commit: (
609
+ publicKey: string,
610
+ storeId: StoreId,
611
+ stageId: string,
612
+ virtualSequences: ReadonlyArray<number>
613
+ ) => Effect.Effect<"Committed" | "Superseded", CompactionNotFound>
614
+ /**
615
+ * Records a no-op for the stage and logs it.
616
+ */
617
+ readonly recordNoOp: (publicKey: string, storeId: StoreId, stageId: string) => Effect.Effect<void, CompactionNotFound>
618
+ /**
619
+ * Releases an open stage and discards what it staged. Aborting a stage that
620
+ * has left `open` is a no-op.
621
+ */
622
+ readonly abort: (publicKey: string, storeId: StoreId, stageId: string) => Effect.Effect<void>
623
+ readonly rollback: (
624
+ publicKey: string,
625
+ storeId: StoreId,
626
+ expectedActivationId: string
627
+ ) => Effect.Effect<string, CompactionRollbackError>
628
+ /**
629
+ * Every activation of the store, oldest first.
630
+ */
631
+ readonly history: (publicKey: string, storeId: StoreId) => Effect.Effect<ReadonlyArray<CompactionActivation>>
632
+ }
633
+
634
+ /**
635
+ * The compaction state of one store.
636
+ *
637
+ * **Details**
638
+ *
639
+ * `base` is `firstSequence - 1` while the originals are active. `lagHead` is
640
+ * the highest last sequence in a storage-time hour that ended before
641
+ * `now - lag`.
642
+ *
643
+ * @stability unstable
644
+ * @category models
645
+ * @since 4.0.0
646
+ */
647
+ export interface CompactionStoreStatus {
648
+ readonly head: number
649
+ readonly base: number
650
+ readonly activeKeptCount: number
651
+ readonly lagHead: number
652
+ readonly noOp: { readonly at: number; readonly count: number } | undefined
653
+ }
654
+
655
+ /**
656
+ * The result of opening a compaction stage.
657
+ *
658
+ * @stability unstable
659
+ * @category models
660
+ * @since 4.0.0
661
+ */
662
+ export type CompactionBeginResult = {
663
+ readonly _tag: "Started"
664
+ readonly stageId: string
665
+ } | {
666
+ readonly _tag: "Busy"
667
+ readonly expiresAt: number
668
+ }
669
+
670
+ /**
671
+ * The header of one compaction input batch.
672
+ *
673
+ * @stability unstable
674
+ * @category models
675
+ * @since 4.0.0
676
+ */
677
+ export interface CompactionBatchHeader {
678
+ readonly sequence: number
679
+ readonly entryIds: ReadonlyArray<EntryId>
680
+ }
681
+
682
+ /**
683
+ * One entry of a store's activation log.
684
+ *
685
+ * **Details**
686
+ *
687
+ * `keptCount` is `undefined` for the originals. `restorable` is `false` once
688
+ * the generation's prune has started.
689
+ *
690
+ * @stability unstable
691
+ * @category models
692
+ * @since 4.0.0
693
+ */
694
+ export interface CompactionActivation {
695
+ readonly activationId: string
696
+ readonly base: number
697
+ readonly keptCount: number | undefined
698
+ readonly active: boolean
699
+ readonly supersededAt: number | undefined
700
+ readonly restorable: boolean
701
+ }
702
+
703
+ /**
704
+ * Error raised when a compaction stage has expired, was aborted or committed,
705
+ * or is unknown to the store.
706
+ *
707
+ * @stability unstable
708
+ * @category errors
709
+ * @since 4.0.0
710
+ */
711
+ export class CompactionNotFound extends Data.TaggedError("CompactionNotFound")<{
712
+ readonly stageId: string
713
+ }> {}
714
+
715
+ /**
716
+ * Error raised when a compaction Part fails validation.
717
+ *
718
+ * @stability unstable
719
+ * @category errors
720
+ * @since 4.0.0
721
+ */
722
+ export class CompactionInvalidRequest extends Data.TaggedError("CompactionInvalidRequest")<{
723
+ readonly stageId: string
724
+ readonly message: string
725
+ }> {}
726
+
727
+ /**
728
+ * Error raised when a rollback is refused.
729
+ *
730
+ * **Details**
731
+ *
732
+ * `NotActive` means the expected activation is not the active one,
733
+ * `NoPrevious` that there is no earlier generation, and `PruneStarted` that
734
+ * the previous generation has started to be pruned.
735
+ *
736
+ * @stability unstable
737
+ * @category errors
738
+ * @since 4.0.0
739
+ */
740
+ export class CompactionRollbackError extends Data.TaggedError("CompactionRollbackError")<{
741
+ readonly reason: "NotActive" | "NoPrevious" | "PruneStarted"
742
+ }> {}
743
+
744
+ /**
745
+ * Tuning for encrypted remote compaction.
746
+ *
747
+ * **Details**
748
+ *
749
+ * `grace` is how long a superseded or withdrawn generation is kept before it
750
+ * is pruned (default 30 days). `lag` is how old data must be, by storage time,
751
+ * before it can be compacted (default 24 hours). `stageTtl` is how long a stage
752
+ * claim lasts after its latest Part (default 10 minutes). `thresholdFloor` is
753
+ * the smallest number of new entries that makes compaction wanted (default
754
+ * 1,000). `sweepInterval` is how often pruning runs (default 1 hour).
755
+ *
756
+ * @stability unstable
757
+ * @category models
758
+ * @since 4.0.0
759
+ */
760
+ export interface CompactionOptions {
761
+ readonly grace?: Duration.Input | undefined
762
+ readonly lag?: Duration.Input | undefined
763
+ readonly stageTtl?: Duration.Input | undefined
764
+ readonly thresholdFloor?: number | undefined
765
+ readonly sweepInterval?: Duration.Input | undefined
766
+ }
767
+
768
+ /**
769
+ * Resolved compaction tuning, shared by a storage and the server using it.
770
+ *
771
+ * @stability unstable
772
+ * @category models
773
+ * @since 4.0.0
774
+ */
775
+ export interface CompactionConfig {
776
+ readonly grace: Duration.Duration
777
+ readonly lag: Duration.Duration
778
+ readonly stageTtl: Duration.Duration
779
+ readonly thresholdFloor: number
780
+ readonly sweepInterval: Duration.Duration
781
+ }
782
+
783
+ /**
784
+ * Resolves compaction options against their defaults.
785
+ *
786
+ * **Details**
787
+ *
788
+ * Dies with an `IllegalArgumentError` unless `grace` is longer than both `lag`
789
+ * and `stageTtl`, `stageTtl` and `sweepInterval` are positive, and
790
+ * `thresholdFloor` is a non-negative integer.
791
+ *
792
+ * @stability unstable
793
+ * @category constructors
794
+ * @since 4.0.0
795
+ */
796
+ export const makeCompactionConfig = (options?: CompactionOptions | undefined): Effect.Effect<CompactionConfig> =>
797
+ Effect.suspend(() => {
798
+ const config: CompactionConfig = {
799
+ grace: Duration.fromInputUnsafe(options?.grace ?? "30 days"),
800
+ lag: Duration.fromInputUnsafe(options?.lag ?? "24 hours"),
801
+ stageTtl: Duration.fromInputUnsafe(options?.stageTtl ?? "10 minutes"),
802
+ thresholdFloor: options?.thresholdFloor ?? 1000,
803
+ sweepInterval: Duration.fromInputUnsafe(options?.sweepInterval ?? "1 hour")
804
+ }
805
+ if (
806
+ !Duration.isGreaterThan(config.grace, config.lag) ||
807
+ !Duration.isGreaterThan(config.grace, config.stageTtl)
808
+ ) {
809
+ return Effect.die(new Cause.IllegalArgumentError("Compaction grace must be longer than lag and stageTtl"))
810
+ }
811
+ if (!Duration.isGreaterThan(config.stageTtl, Duration.zero)) {
812
+ return Effect.die(new Cause.IllegalArgumentError("Compaction stageTtl must be positive"))
813
+ }
814
+ if (!Duration.isGreaterThan(config.sweepInterval, Duration.zero)) {
815
+ return Effect.die(new Cause.IllegalArgumentError("Compaction sweepInterval must be positive"))
816
+ }
817
+ if (!Number.isSafeInteger(config.thresholdFloor) || config.thresholdFloor < 0) {
818
+ return Effect.die(new Cause.IllegalArgumentError("Compaction thresholdFloor must be a non-negative integer"))
819
+ }
820
+ return Effect.succeed(config)
821
+ })
822
+
823
+ /**
824
+ * Creates an in-memory encrypted server `Storage` with default compaction
825
+ * tuning.
225
826
  *
226
827
  * **Details**
227
828
  *
228
829
  * Data, session authentication bindings, and streams are process-local and are
229
830
  * released with the surrounding scope.
230
831
  *
832
+ * @see {@link makeStorageMemoryWith} to configure compaction
833
+ *
834
+ * @stability unstable
835
+ * @category constructors
836
+ * @since 4.0.0
837
+ */
838
+ export const makeStorageMemory: Effect.Effect<Storage["Service"], never, Scope.Scope> = Effect.suspend(() =>
839
+ makeStorageMemoryWith({})
840
+ )
841
+
842
+ /**
843
+ * Creates an in-memory encrypted server `Storage`.
844
+ *
845
+ * **Details**
846
+ *
847
+ * Data, session authentication bindings, and streams are process-local and are
848
+ * released with the surrounding scope. The storage always provides
849
+ * `compaction`. Its sweep runs on `Clock` only while some store has a
850
+ * generation to prune or a stage to expire.
851
+ *
852
+ * Dies when the compaction options are invalid (see {@link makeCompactionConfig}).
853
+ *
231
854
  * @stability unstable
232
855
  * @category constructors
233
856
  * @since 4.0.0
234
857
  */
235
- export const makeStorageMemory: Effect.Effect<Storage["Service"], never, Scope.Scope> = Effect.gen(function*() {
236
- const journals = new Map<string, {
237
- // Entry id to the sequence it was first stored at.
238
- readonly knownIds: Map<string, number>
239
- readonly batches: Array<EncryptedRemoteBatch>
240
- nextSequence: number
241
- }>()
858
+ export const makeStorageMemoryWith = Effect.fnUntraced(function*(options: {
859
+ readonly compaction?: CompactionOptions | undefined
860
+ }): Effect.fn.Return<Storage["Service"], never, Scope.Scope> {
861
+ const config = yield* makeCompactionConfig(options.compaction)
862
+ const graceMillis = Duration.toMillis(config.grace)
863
+ const lagMillis = Duration.toMillis(config.lag)
864
+ const stageTtlMillis = Duration.toMillis(config.stageTtl)
865
+ const journals = new Map<string, MemoryJournal>()
242
866
  const sessionAuthBindings = new Map<string, Uint8Array<ArrayBuffer>>()
243
867
  const remoteId = makeRemoteIdUnsafe()
244
868
  const ensureJournal = (scopeKey: string) => {
245
869
  let journal = journals.get(scopeKey)
246
870
  if (journal) return journal
247
- journal = { knownIds: new Map(), batches: [], nextSequence: 0 }
871
+ const originals: MemoryActivation = {
872
+ id: SecureRandom.uuidV4Unsafe(),
873
+ set: undefined,
874
+ base: -1,
875
+ keptCount: undefined,
876
+ previous: undefined,
877
+ supersededAt: undefined,
878
+ pruneStartedAt: undefined
879
+ }
880
+ journal = {
881
+ knownIds: new Map(),
882
+ batches: [],
883
+ nextSequence: 0,
884
+ buckets: new Map(),
885
+ activations: [originals],
886
+ active: originals,
887
+ noOp: undefined,
888
+ openStage: undefined,
889
+ nextSweepAt: undefined
890
+ }
248
891
  journals.set(scopeKey, journal)
249
892
  return journal
250
893
  }
@@ -257,6 +900,346 @@ export const makeStorageMemory: Effect.Effect<Storage["Service"], never, Scope.S
257
900
  idleTimeToLive: 60000
258
901
  })
259
902
 
903
+ // Open while any store has something for the sweep to do, so an idle
904
+ // storage never sleeps on the clock.
905
+ const sweepPending = Latch.makeUnsafe(false)
906
+ const schedulePrune = (journal: MemoryJournal, at: number) => {
907
+ journal.nextSweepAt = journal.nextSweepAt === undefined ? at : Math.min(journal.nextSweepAt, at)
908
+ sweepPending.openUnsafe()
909
+ }
910
+ const closeStage = (journal: MemoryJournal, stage: MemoryStage) => {
911
+ if (journal.openStage === stage) journal.openStage = undefined
912
+ }
913
+ const sweep = (now: number) => {
914
+ let pending = false
915
+ for (const journal of journals.values()) {
916
+ if (journal.openStage !== undefined && journal.openStage.expiresAt <= now) closeStage(journal, journal.openStage)
917
+ if (journal.nextSweepAt !== undefined && journal.nextSweepAt <= now) prune(journal, now)
918
+ pending ||= journal.openStage !== undefined || journal.nextSweepAt !== undefined
919
+ }
920
+ if (!pending) sweepPending.closeUnsafe()
921
+ }
922
+ // Keeps what the active generation and every generation still within grace
923
+ // need, which is their batch sets and the originals above their bases.
924
+ const prune = (journal: MemoryJournal, now: number) => {
925
+ const kept = journal.activations.filter((activation) =>
926
+ activation === journal.active ||
927
+ (activation.pruneStartedAt === undefined && activation.supersededAt! + graceMillis > now)
928
+ )
929
+ const keptSets = new Set(kept.map((activation) => activation.set))
930
+ for (const activation of journal.activations) {
931
+ if (kept.includes(activation) || activation.pruneStartedAt !== undefined) continue
932
+ activation.pruneStartedAt = now
933
+ if (activation.set !== undefined && !keptSets.has(activation.set)) activation.set.batches = []
934
+ }
935
+ const keptBase = Math.min(...kept.map((activation) => activation.base))
936
+ const first = journal.batches.findIndex((batch) => batch.sequence + batch.entryIds.length - 1 > keptBase)
937
+ journal.batches.splice(0, first === -1 ? journal.batches.length : first)
938
+ journal.nextSweepAt = undefined
939
+ for (const activation of journal.activations) {
940
+ if (activation !== journal.active && activation.pruneStartedAt === undefined) {
941
+ schedulePrune(journal, activation.supersededAt! + graceMillis)
942
+ }
943
+ }
944
+ }
945
+ yield* Effect.forkScoped(
946
+ Effect.forever(
947
+ sweepPending.await.pipe(
948
+ Effect.andThen(Effect.sleep(config.sweepInterval)),
949
+ Effect.andThen(Clock.currentTimeMillis),
950
+ Effect.flatMap((now) => Effect.sync(() => sweep(now)))
951
+ )
952
+ )
953
+ )
954
+
955
+ // The highest last sequence stored in an hour that ended before now - lag.
956
+ const lagHead = (journal: MemoryJournal, now: number) => {
957
+ const pastLag = lagHour(now, lagMillis)
958
+ let head = -1
959
+ for (const [hour, lastSequence] of journal.buckets) {
960
+ if (hour <= pastLag) head = Math.max(head, lastSequence)
961
+ }
962
+ return head
963
+ }
964
+ const recordBucket = (journal: MemoryJournal, now: number, lastSequence: number) => {
965
+ const hour = Math.floor(now / hourMillis)
966
+ const existing = journal.buckets.get(hour)
967
+ if (existing !== undefined) {
968
+ journal.buckets.set(hour, Math.max(existing, lastSequence))
969
+ return
970
+ }
971
+ journal.buckets.set(hour, lastSequence)
972
+ // Only the newest bucket past the lag matters for the lag head.
973
+ const pastLag = Array.from(journal.buckets.keys()).filter((bucket) => bucket <= lagHour(now, lagMillis))
974
+ const newest = Math.max(...pastLag)
975
+ for (const bucket of pastLag) {
976
+ if (bucket < newest) journal.buckets.delete(bucket)
977
+ }
978
+ }
979
+ // The input of a stage: its pinned batch set, then originals in (base, through].
980
+ const inputBatches = (journal: MemoryJournal, stage: MemoryStage): ReadonlyArray<EncryptedRemoteBatch> => [
981
+ ...(stage.pinned.set?.batches ?? []),
982
+ ...journal.batches.filter((batch) =>
983
+ batch.sequence > stage.pinned.base && batch.sequence + batch.entryIds.length - 1 <= stage.through
984
+ )
985
+ ]
986
+ // A stage that Part, NoOp and Commit can act on.
987
+ const findOpenStage = (publicKey: string, storeId: StoreId, stageId: string, now: number) => {
988
+ const journal = journals.get(makeEncryptedScopeKey({ publicKey, storeId }))
989
+ const stage = journal?.openStage
990
+ if (journal === undefined || stage === undefined || stage.id !== stageId) return undefined
991
+ if (stage.expiresAt <= now) {
992
+ closeStage(journal, stage)
993
+ return undefined
994
+ }
995
+ return { stage, journal }
996
+ }
997
+ // Call within the same execution-time block that captures the input rows.
998
+ const findInputStage = (
999
+ publicKey: string,
1000
+ storeId: StoreId,
1001
+ stageId: string,
1002
+ now: number
1003
+ ) => {
1004
+ const found = findOpenStage(publicKey, storeId, stageId, now)
1005
+ return found?.stage.pinned.pruneStartedAt === undefined ? found : undefined
1006
+ }
1007
+
1008
+ const compaction: StorageCompaction = {
1009
+ config,
1010
+ status: (publicKey, storeId) =>
1011
+ Effect.map(Clock.currentTimeMillis, (now) => {
1012
+ const journal = ensureJournal(makeEncryptedScopeKey({ publicKey, storeId }))
1013
+ return {
1014
+ head: journal.nextSequence - 1,
1015
+ base: journal.active.base,
1016
+ activeKeptCount: journal.active.keptCount ?? 0,
1017
+ lagHead: lagHead(journal, now),
1018
+ noOp: journal.noOp
1019
+ }
1020
+ }),
1021
+ begin: (publicKey, storeId, { through }) =>
1022
+ Effect.map(Clock.currentTimeMillis, (now): CompactionBeginResult => {
1023
+ const scopeKey = makeEncryptedScopeKey({ publicKey, storeId })
1024
+ const journal = ensureJournal(scopeKey)
1025
+ const open = journal.openStage
1026
+ if (open !== undefined) {
1027
+ if (open.expiresAt > now) return { _tag: "Busy", expiresAt: open.expiresAt }
1028
+ closeStage(journal, open)
1029
+ }
1030
+ const stage: MemoryStage = {
1031
+ id: SecureRandom.uuidV4Unsafe(),
1032
+ pinned: journal.active,
1033
+ through,
1034
+ expiresAt: now + stageTtlMillis,
1035
+ staged: new Map()
1036
+ }
1037
+ journal.openStage = stage
1038
+ sweepPending.openUnsafe()
1039
+ return { _tag: "Started", stageId: stage.id }
1040
+ }),
1041
+ inputHeaders: (publicKey, storeId, stageId) =>
1042
+ Clock.currentTimeMillis.pipe(
1043
+ Effect.flatMap((now) => {
1044
+ const found = findInputStage(publicKey, storeId, stageId, now)
1045
+ if (found === undefined) return Effect.fail(new CompactionNotFound({ stageId }))
1046
+ const { journal, stage } = found
1047
+ return Effect.succeed(Stream.fromArray(
1048
+ inputBatches(journal, stage).map((batch): CompactionBatchHeader => ({
1049
+ sequence: batch.sequence,
1050
+ entryIds: batch.entryIds
1051
+ }))
1052
+ ))
1053
+ }),
1054
+ Stream.unwrap
1055
+ ),
1056
+ fetch: (publicKey, storeId, stageId, sequences) =>
1057
+ Clock.currentTimeMillis.pipe(
1058
+ Effect.flatMap((now) => {
1059
+ const found = findInputStage(publicKey, storeId, stageId, now)
1060
+ if (found === undefined) return Effect.fail(new CompactionNotFound({ stageId }))
1061
+ const { journal, stage } = found
1062
+ const fetched: Array<EncryptedRemoteBatch> = []
1063
+ for (const sequence of sequences) {
1064
+ const batch = sequence <= stage.pinned.base
1065
+ ? findBatch(stage.pinned.set?.batches ?? [], sequence)
1066
+ : sequence <= stage.through
1067
+ ? findBatch(journal.batches, sequence)
1068
+ : undefined
1069
+ if (batch !== undefined) fetched.push(batch)
1070
+ }
1071
+ return Effect.succeed(Stream.fromArray(fetched))
1072
+ }),
1073
+ Stream.unwrap
1074
+ ),
1075
+ stagePart: (publicKey, storeId, stageId, segment, batches) =>
1076
+ Effect.flatMap(Clock.currentTimeMillis, (now): ReturnType<StorageCompaction["stagePart"]> => {
1077
+ const found = findOpenStage(publicKey, storeId, stageId, now)
1078
+ if (found === undefined) return Effect.fail(new CompactionNotFound({ stageId }))
1079
+ const { journal, stage } = found
1080
+ const reject = (message: string) => {
1081
+ closeStage(journal, stage)
1082
+ return Effect.fail(new CompactionInvalidRequest({ stageId, message }))
1083
+ }
1084
+ if (!Number.isSafeInteger(segment) || segment < 0 || segment >= maxSegments) {
1085
+ return reject("Invalid segment index")
1086
+ }
1087
+ if (batches.length > positionsPerSegment) return reject("Too many batches in a segment")
1088
+ // Positions are the staged rows' key, so a retried segment inserts nothing.
1089
+ let inserted = 0
1090
+ batches.forEach((batch, i) => {
1091
+ const position = segment * positionsPerSegment + i
1092
+ if (stage.staged.has(position)) return
1093
+ stage.staged.set(position, batch)
1094
+ inserted++
1095
+ })
1096
+ if (inserted === 0) return Effect.succeed("AlreadyStored" as const)
1097
+ stage.expiresAt = now + stageTtlMillis
1098
+ return Effect.succeed("Stored" as const)
1099
+ }),
1100
+ stagedHeaders: (publicKey, storeId, stageId) =>
1101
+ Effect.flatMap(Clock.currentTimeMillis, (now) => {
1102
+ const found = findOpenStage(publicKey, storeId, stageId, now)
1103
+ if (found === undefined) {
1104
+ return Effect.fail(new CompactionNotFound({ stageId }))
1105
+ }
1106
+ return Effect.succeed(Stream.fromArray(
1107
+ stagedBatches(found.stage).map(({ batch, position }) => ({
1108
+ position,
1109
+ entryIds: batch.entryIds
1110
+ }))
1111
+ ))
1112
+ }).pipe(Stream.unwrap),
1113
+ commit: (publicKey, storeId, stageId, virtualSequences) =>
1114
+ Effect.flatMap(Clock.currentTimeMillis, (now) => {
1115
+ const found = findOpenStage(publicKey, storeId, stageId, now)
1116
+ if (found === undefined) return Effect.fail(new CompactionNotFound({ stageId }))
1117
+ const { journal, stage } = found
1118
+ if (stage.pinned !== journal.active) {
1119
+ const keptCount = stagedBatches(stage).reduce((count, { batch }) => count + batch.entryIds.length, 0)
1120
+ closeStage(journal, stage)
1121
+ return Effect.as(
1122
+ logCommit({
1123
+ publicKey,
1124
+ storeId,
1125
+ result: "Superseded",
1126
+ through: stage.through,
1127
+ keptCount,
1128
+ activationId: undefined
1129
+ }),
1130
+ "Superseded" as const
1131
+ )
1132
+ }
1133
+ const staged = stagedBatches(stage)
1134
+ if (staged.length !== virtualSequences.length) {
1135
+ return Effect.die(new Cause.IllegalArgumentError("Expected one virtual sequence per staged batch"))
1136
+ }
1137
+ const keptCount = staged.reduce((count, { batch }) => count + batch.entryIds.length, 0)
1138
+ const activation: MemoryActivation = {
1139
+ id: SecureRandom.uuidV4Unsafe(),
1140
+ set: {
1141
+ batches: staged.map(({ batch }, i): EncryptedRemoteBatch => ({ ...batch, sequence: virtualSequences[i] }))
1142
+ },
1143
+ base: stage.through,
1144
+ keptCount,
1145
+ previous: journal.active,
1146
+ supersededAt: undefined,
1147
+ pruneStartedAt: undefined
1148
+ }
1149
+ journal.active.supersededAt = now
1150
+ journal.activations.push(activation)
1151
+ journal.active = activation
1152
+ journal.noOp = undefined
1153
+ schedulePrune(journal, now + graceMillis)
1154
+ closeStage(journal, stage)
1155
+ // Log this commit's activation id. A concurrent rollback could change
1156
+ // the active one before the logger runs.
1157
+ return Effect.as(
1158
+ logCommit({
1159
+ publicKey,
1160
+ storeId,
1161
+ result: "Committed",
1162
+ through: stage.through,
1163
+ keptCount,
1164
+ activationId: activation.id
1165
+ }),
1166
+ "Committed" as const
1167
+ )
1168
+ }),
1169
+ recordNoOp: (publicKey, storeId, stageId) =>
1170
+ Effect.flatMap(Clock.currentTimeMillis, (now) => {
1171
+ const found = findOpenStage(publicKey, storeId, stageId, now)
1172
+ if (found === undefined) return Effect.fail(new CompactionNotFound({ stageId }))
1173
+ const { journal, stage } = found
1174
+ // A rollback since begin cleared the no-op state, so a stale run leaves it.
1175
+ if (stage.pinned === journal.active) {
1176
+ journal.noOp = { at: stage.through, count: (journal.noOp?.count ?? 0) + 1 }
1177
+ }
1178
+ closeStage(journal, stage)
1179
+ return logNoOp({ publicKey, storeId, stageId, through: stage.through })
1180
+ }),
1181
+ abort: (publicKey, storeId, stageId) =>
1182
+ Effect.sync(() => {
1183
+ const journal = journals.get(makeEncryptedScopeKey({ publicKey, storeId }))
1184
+ if (journal?.openStage?.id === stageId) closeStage(journal, journal.openStage)
1185
+ }),
1186
+ rollback: Effect.fnUntraced(function*(publicKey, storeId, expectedActivationId) {
1187
+ const scopeKey = makeEncryptedScopeKey({ publicKey, storeId })
1188
+ const pubsub = yield* RcMap.get(pubsubs, scopeKey)
1189
+ const now = yield* Clock.currentTimeMillis
1190
+ return yield* Effect.suspend(() => {
1191
+ const journal = ensureJournal(scopeKey)
1192
+ const withdrawn = journal.active
1193
+ if (withdrawn.id !== expectedActivationId) {
1194
+ return Effect.fail(new CompactionRollbackError({ reason: "NotActive" }))
1195
+ }
1196
+ const target = withdrawn.previous
1197
+ if (target === undefined) return Effect.fail(new CompactionRollbackError({ reason: "NoPrevious" }))
1198
+ if (target.pruneStartedAt !== undefined) {
1199
+ return Effect.fail(new CompactionRollbackError({ reason: "PruneStarted" }))
1200
+ }
1201
+ const activation: MemoryActivation = {
1202
+ ...target,
1203
+ id: SecureRandom.uuidV4Unsafe(),
1204
+ supersededAt: undefined,
1205
+ pruneStartedAt: undefined
1206
+ }
1207
+ // The input of the stage that built the withdrawn generation.
1208
+ const input = [
1209
+ ...(target.set?.batches ?? []),
1210
+ ...journal.batches.filter((batch) =>
1211
+ batch.sequence > target.base && batch.sequence + batch.entryIds.length - 1 <= withdrawn.base
1212
+ )
1213
+ ]
1214
+ withdrawn.supersededAt = now
1215
+ journal.activations.push(activation)
1216
+ journal.active = activation
1217
+ journal.noOp = undefined
1218
+ schedulePrune(journal, now + graceMillis)
1219
+ for (const { encryptedBatch, entryIds, iv } of input) {
1220
+ const batch: EncryptedRemoteBatch = { sequence: journal.nextSequence, entryIds, iv, encryptedBatch }
1221
+ journal.nextSequence += entryIds.length
1222
+ journal.batches.push(batch)
1223
+ PubSub.publishUnsafe(pubsub, batch)
1224
+ }
1225
+ if (input.length > 0) recordBucket(journal, now, journal.nextSequence - 1)
1226
+ return Effect.as(logRollback({ publicKey, storeId, activation, withdrawn }), activation.id)
1227
+ })
1228
+ }, Effect.scoped),
1229
+ history: (publicKey, storeId) =>
1230
+ Effect.sync(() => {
1231
+ const journal = ensureJournal(makeEncryptedScopeKey({ publicKey, storeId }))
1232
+ return journal.activations.map((activation): CompactionActivation => ({
1233
+ activationId: activation.id,
1234
+ base: activation.base,
1235
+ keptCount: activation.keptCount,
1236
+ active: activation === journal.active,
1237
+ supersededAt: activation === journal.active ? undefined : activation.supersededAt,
1238
+ restorable: activation.pruneStartedAt === undefined
1239
+ }))
1240
+ })
1241
+ }
1242
+
260
1243
  return Storage.of({
261
1244
  getId: Effect.succeed(remoteId),
262
1245
  getOrCreateSessionAuthBinding: (publicKey, signingPublicKey) =>
@@ -269,6 +1252,7 @@ export const makeStorageMemory: Effect.Effect<Storage["Service"], never, Scope.S
269
1252
  write: Effect.fnUntraced(function*(publicKey, storeId, batches) {
270
1253
  const scopeKey = makeEncryptedScopeKey({ publicKey, storeId })
271
1254
  const pubsub = yield* RcMap.get(pubsubs, scopeKey)
1255
+ const now = yield* Clock.currentTimeMillis
272
1256
  const journal = ensureJournal(scopeKey)
273
1257
  const stored: Array<EncryptedRemoteBatch> = []
274
1258
  for (const batch of batches) {
@@ -283,6 +1267,7 @@ export const makeStorageMemory: Effect.Effect<Storage["Service"], never, Scope.S
283
1267
  stored.push(remoteBatch)
284
1268
  PubSub.publishUnsafe(pubsub, remoteBatch)
285
1269
  }
1270
+ if (stored.length > 0) recordBucket(journal, now, journal.nextSequence - 1)
286
1271
  return stored
287
1272
  }, Effect.scoped),
288
1273
  entrySequences: (publicKey, storeId, entryIds) =>
@@ -299,17 +1284,35 @@ export const makeStorageMemory: Effect.Effect<Storage["Service"], never, Scope.S
299
1284
  const scopeKey = makeEncryptedScopeKey({ publicKey, storeId })
300
1285
  const pubsub = yield* RcMap.get(pubsubs, scopeKey)
301
1286
  const subscription = yield* PubSub.subscribe(pubsub)
302
- const batches = ensureJournal(scopeKey).batches
1287
+ const journal = ensureJournal(scopeKey)
1288
+ const { base, set } = journal.active
1289
+ const live = Stream.fromSubscription(subscription)
1290
+ // Below the base an active snapshot replaces the originals.
1291
+ if (set !== undefined && startSequence <= base) {
1292
+ if (set.batches.length === 0) {
1293
+ return Stream.die(new Error(`The active compaction snapshot of store ${storeId} has no batches`))
1294
+ }
1295
+ const tail = journal.batches.filter((batch) => batch.sequence > base)
1296
+ return Stream.fromArray([
1297
+ ...set.batches.filter((batch) => batch.sequence + batch.entryIds.length > startSequence),
1298
+ ...tail
1299
+ ]).pipe(Stream.concat(live))
1300
+ }
1301
+ const batches = journal.batches
303
1302
  const first = batches.findIndex((batch) => batch.sequence + batch.entryIds.length > startSequence)
304
1303
  return Stream.fromArray(first === -1 ? [] : batches.slice(first)).pipe(
305
- Stream.concat(Stream.fromSubscription(subscription))
1304
+ Stream.concat(live)
306
1305
  )
307
- }, Stream.unwrap)
1306
+ }, Stream.unwrap),
1307
+ compaction
308
1308
  })
309
1309
  })
310
1310
 
311
1311
  /**
312
- * Provides encrypted server `Storage` using the in-memory implementation.
1312
+ * Provides encrypted server `Storage` using the in-memory implementation with
1313
+ * default compaction tuning.
1314
+ *
1315
+ * @see {@link layerStorageMemoryWith} to configure compaction
313
1316
  *
314
1317
  * @stability unstable
315
1318
  * @category layers
@@ -317,6 +1320,66 @@ export const makeStorageMemory: Effect.Effect<Storage["Service"], never, Scope.S
317
1320
  */
318
1321
  export const layerStorageMemory: Layer.Layer<Storage> = Layer.effect(Storage)(makeStorageMemory)
319
1322
 
1323
+ /**
1324
+ * Provides encrypted server `Storage` using the in-memory implementation.
1325
+ *
1326
+ * @stability unstable
1327
+ * @category layers
1328
+ * @since 4.0.0
1329
+ */
1330
+ export const layerStorageMemoryWith = (options: {
1331
+ readonly compaction?: CompactionOptions | undefined
1332
+ }): Layer.Layer<Storage> => Layer.effect(Storage)(makeStorageMemoryWith(options))
1333
+
1334
+ interface MemoryActivation {
1335
+ readonly id: string
1336
+ // undefined while the originals are active.
1337
+ readonly set: { batches: ReadonlyArray<EncryptedRemoteBatch> } | undefined
1338
+ readonly base: number
1339
+ readonly keptCount: number | undefined
1340
+ readonly previous: MemoryActivation | undefined
1341
+ supersededAt: number | undefined
1342
+ pruneStartedAt: number | undefined
1343
+ }
1344
+
1345
+ interface MemoryStage {
1346
+ readonly id: string
1347
+ readonly pinned: MemoryActivation
1348
+ readonly through: number
1349
+ expiresAt: number
1350
+ // Staged batches by position.
1351
+ readonly staged: Map<number, EncryptedBatch>
1352
+ }
1353
+
1354
+ interface MemoryJournal {
1355
+ // Entry id to the sequence it was first stored at. Never pruned.
1356
+ readonly knownIds: Map<string, number>
1357
+ // Originals, in sequence order.
1358
+ readonly batches: Array<EncryptedRemoteBatch>
1359
+ nextSequence: number
1360
+ // Storage-time hour to the highest last sequence stored in it.
1361
+ readonly buckets: Map<number, number>
1362
+ readonly activations: Array<MemoryActivation>
1363
+ active: MemoryActivation
1364
+ noOp: { readonly at: number; readonly count: number } | undefined
1365
+ openStage: MemoryStage | undefined
1366
+ nextSweepAt: number | undefined
1367
+ }
1368
+
1369
+ const stagedBatches = (stage: MemoryStage) =>
1370
+ Array.from(stage.staged, ([position, batch]) => ({ batch, position })).sort((a, b) => a.position - b.position)
1371
+
1372
+ const findBatch = (batches: ReadonlyArray<EncryptedRemoteBatch>, sequence: number) => {
1373
+ let low = 0
1374
+ let high = batches.length
1375
+ while (low < high) {
1376
+ const mid = (low + high) >>> 1
1377
+ if (batches[mid].sequence < sequence) low = mid + 1
1378
+ else high = mid
1379
+ }
1380
+ return batches[low]?.sequence === sequence ? batches[low] : undefined
1381
+ }
1382
+
320
1383
  const makeEncryptedScopeKey = ({ publicKey, storeId }: {
321
1384
  readonly publicKey: string
322
1385
  readonly storeId: StoreId