effect 4.0.7 → 4.1.1

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