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
@@ -5,24 +5,32 @@
5
5
  * `EventLogServerEncrypted` when entries should be stored without exposing
6
6
  * plaintext event data to the database. It persists the server remote id,
7
7
  * session authentication bindings, and encrypted batch tables, assigns stable
8
- * sequence numbers, and streams changes. Clients remain responsible for
9
- * encrypting writes and decrypting reads.
8
+ * sequence numbers, streams changes, and stores encrypted compaction snapshots.
9
+ * Clients remain responsible for encrypting writes and decrypting reads.
10
10
  *
11
11
  * @stability unstable
12
12
  * @since 4.0.0
13
13
  */
14
14
  import * as Arr from "../Array.js";
15
+ import * as Cause from "../Cause.js";
16
+ import * as Clock from "../Clock.js";
17
+ import * as Digest from "../crypto/Digest.js";
18
+ import * as SecureRandom from "../crypto/SecureRandom.js";
19
+ import * as Duration from "../Duration.js";
15
20
  import * as Effect from "../Effect.js";
16
21
  import * as Layer from "../Layer.js";
22
+ import * as Option from "../Option.js";
17
23
  import * as PubSub from "../PubSub.js";
18
24
  import * as RcMap from "../RcMap.js";
25
+ import * as Schedule from "../Schedule.js";
19
26
  import * as Schema from "../Schema.js";
20
27
  import * as Semaphore from "../Semaphore.js";
21
28
  import * as SqlClient from "../sql/SqlClient.js";
29
+ import * as SqlError from "../sql/SqlError.js";
22
30
  import * as Stream from "../Stream.js";
23
31
  import { makeRemoteIdUnsafe } from "./EventJournal.js";
24
- import * as EventLogEncryption from "./EventLogEncryption.js";
25
32
  import * as EventLogServerEncrypted from "./EventLogServerEncrypted.js";
33
+ import { hourMillis, lagHour, logCommit, logNoOp, logRollback, maxSegments, positionsPerSegment } from "./internal/compaction.js";
26
34
  /**
27
35
  * Creates encrypted event-log server `Storage` backed by SQL.
28
36
  *
@@ -32,6 +40,11 @@ import * as EventLogServerEncrypted from "./EventLogServerEncrypted.js";
32
40
  * batches in dialect-specific tables, creating per-identity/store batch and entry
33
41
  * id tables as needed.
34
42
  *
43
+ * The storage always provides `compaction`. Its tables are created on startup
44
+ * and when a store is first opened. A sweep in the surrounding scope prunes
45
+ * superseded generations every `sweepInterval`. Startup dies on invalid
46
+ * compaction options (see `EventLogServerEncrypted.makeCompactionConfig`).
47
+ *
35
48
  * **Gotchas**
36
49
  *
37
50
  * Storage does not retry sequence conflicts between competing instances.
@@ -40,13 +53,26 @@ import * as EventLogServerEncrypted from "./EventLogServerEncrypted.js";
40
53
  * are local to each storage instance; clients on other instances must reconnect
41
54
  * to read those writes.
42
55
  *
56
+ * Inside a caller's transaction, `write` and `compaction.rollback` publish to
57
+ * live readers after the outermost commit, and not at all if it rolls back.
58
+ * Concurrent caller transactions may reach live readers out of sequence order.
59
+ * `changes` always reads in committed order.
60
+ *
61
+ * Instances sharing a database should share compaction options. The instance
62
+ * that writes a generation or stage fixes its prune time or expiry.
63
+ *
43
64
  * @stability unstable
44
65
  * @category constructors
45
66
  * @since 4.0.0
46
67
  */
47
68
  export const makeStorage = options => Effect.gen(function* () {
48
- const encryptions = yield* EventLogEncryption.EventLogEncryption;
69
+ const digest = yield* Digest.Digest;
49
70
  const sql = (yield* SqlClient.SqlClient).withoutTransforms();
71
+ const config = yield* EventLogServerEncrypted.makeCompactionConfig(options?.compaction);
72
+ const graceMillis = Duration.toMillis(config.grace);
73
+ const lagMillis = Duration.toMillis(config.lag);
74
+ const stageTtlMillis = Duration.toMillis(config.stageTtl);
75
+ const sweepIntervalMillis = Duration.toMillis(config.sweepInterval);
50
76
  const tablePrefix = options?.entryTablePrefix ?? "effect_events";
51
77
  const remoteIdTable = options?.remoteIdTable ?? "effect_remote_id";
52
78
  const sessionAuthBindingsTable = `${tablePrefix}_session_auth_bindings`;
@@ -94,6 +120,110 @@ export const makeStorage = options => Effect.gen(function* () {
94
120
  signing_public_key BLOB NOT NULL
95
121
  )`
96
122
  });
123
+ const types = sql.onDialectOrElse({
124
+ pg: () => ({
125
+ id: "TEXT",
126
+ int: "INTEGER",
127
+ bigint: "BIGINT",
128
+ bytes: "BYTEA",
129
+ entryId: "BYTEA",
130
+ iv: "BYTEA"
131
+ }),
132
+ mysql: () => ({
133
+ id: "VARCHAR(36)",
134
+ int: "INT",
135
+ bigint: "BIGINT",
136
+ bytes: "LONGBLOB",
137
+ entryId: "BINARY(16)",
138
+ iv: "BINARY(12)"
139
+ }),
140
+ mssql: () => ({
141
+ id: "NVARCHAR(36)",
142
+ int: "INT",
143
+ bigint: "BIGINT",
144
+ bytes: "VARBINARY(MAX)",
145
+ entryId: "VARBINARY(16)",
146
+ iv: "VARBINARY(12)"
147
+ }),
148
+ orElse: () => ({
149
+ id: "TEXT",
150
+ int: "INTEGER",
151
+ bigint: "INTEGER",
152
+ bytes: "BLOB",
153
+ entryId: "BLOB",
154
+ iv: "BLOB"
155
+ })
156
+ });
157
+ // Two instances can open the same table at once, and PostgreSQL and SQL
158
+ // Server can fail the loser of a concurrent create, so creation retries.
159
+ const createTable = (name, columns, indexes = []) => sql.onDialectOrElse({
160
+ mysql: () => {
161
+ const definitions = [columns, ...indexes.map(index => `INDEX (${index})`)].join(", ");
162
+ return Effect.asVoid(sql`CREATE TABLE IF NOT EXISTS ${sql(name)} (${sql.literal(definitions)})`);
163
+ },
164
+ mssql: () => sql`IF OBJECT_ID(${name}, N'U') IS NULL CREATE TABLE ${sql(name)} (${sql.literal(columns)})`.pipe(Effect.andThen(Effect.forEach(indexes, (index, i) => {
165
+ const indexName = `${name}_i${i}`;
166
+ return sql`
167
+ IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = ${indexName} AND object_id = OBJECT_ID(${name}))
168
+ CREATE INDEX ${sql(indexName)} ON ${sql(name)} (${sql.literal(index)})`;
169
+ }, {
170
+ discard: true
171
+ }))),
172
+ orElse: () => sql`CREATE TABLE IF NOT EXISTS ${sql(name)} (${sql.literal(columns)})`.pipe(Effect.andThen(Effect.forEach(indexes, (index, i) => sql`CREATE INDEX IF NOT EXISTS ${sql(`${name}_i${i}`)} ON ${sql(name)} (${sql.literal(index)})`, {
173
+ discard: true
174
+ })))
175
+ }).pipe(Effect.retry({
176
+ times: 3
177
+ }));
178
+ const storesTable = `${tablePrefix}_compaction_stores`;
179
+ const bucketsTable = `${tablePrefix}_compaction_buckets`;
180
+ const activationsTable = `${tablePrefix}_compaction_activations`;
181
+ const stagesTable = `${tablePrefix}_compaction_stages`;
182
+ const storesSql = sql(storesTable);
183
+ const bucketsSql = sql(bucketsTable);
184
+ const activationsSql = sql(activationsTable);
185
+ const stagesSql = sql(stagesTable);
186
+ // One row per store, keyed by the scope hash its tables are named after.
187
+ // "Nothing" is 0 for the base, `pruned_through` and the lag head.
188
+ yield* createTable(storesTable, `scope ${types.id} PRIMARY KEY,
189
+ next_sequence ${types.int} NOT NULL,
190
+ active_activation ${types.id} NOT NULL,
191
+ active_batch_set ${types.id} NULL,
192
+ base_sequence ${types.int} NOT NULL,
193
+ active_kept_count ${types.int} NOT NULL,
194
+ no_op_at ${types.int} NULL,
195
+ no_op_count ${types.int} NOT NULL,
196
+ open_stage ${types.id} NULL,
197
+ next_sweep_at ${types.bigint} NULL,
198
+ sweep_lease_until ${types.bigint} NULL,
199
+ sweep_lease_owner ${types.id} NULL,
200
+ pruned_through ${types.int} NOT NULL`, ["next_sweep_at"]);
201
+ // The highest last sequence stored in each storage-time hour.
202
+ yield* createTable(bucketsTable, `scope ${types.id} NOT NULL,
203
+ bucket_hour ${types.bigint} NOT NULL,
204
+ last_sequence ${types.int} NOT NULL,
205
+ PRIMARY KEY (scope, bucket_hour)`);
206
+ // A `batch_set` of null means the originals only. `prunable_at` is set when
207
+ // the activation is superseded or withdrawn.
208
+ yield* createTable(activationsTable, `id ${types.id} PRIMARY KEY,
209
+ scope ${types.id} NOT NULL,
210
+ ordinal ${types.int} NOT NULL,
211
+ batch_set ${types.id} NULL,
212
+ base_sequence ${types.int} NOT NULL,
213
+ kept_count ${types.int} NULL,
214
+ previous ${types.id} NULL,
215
+ superseded_at ${types.bigint} NULL,
216
+ prunable_at ${types.bigint} NULL,
217
+ prune_started_at ${types.bigint} NULL`, ["scope, ordinal"]);
218
+ // A stage's pending batch set is its own id. A committed stage's row is
219
+ // deleted at commit. Other stages go from `open` to `closed`, and the
220
+ // sweep deletes what they staged, then the row.
221
+ yield* createTable(stagesTable, `id ${types.id} PRIMARY KEY,
222
+ scope ${types.id} NOT NULL,
223
+ state ${types.id} NOT NULL,
224
+ pinned ${types.id} NOT NULL,
225
+ through_sequence ${types.int} NOT NULL,
226
+ expires_at ${types.bigint} NOT NULL`, ["scope, state", "state, expires_at"]);
97
227
  const remoteId = yield* sql`SELECT remote_id FROM ${remoteIdTableSql}`.pipe(Effect.flatMap(results => {
98
228
  if (results.length > 0) {
99
229
  return Effect.succeed(results[0].remote_id);
@@ -101,96 +231,260 @@ export const makeStorage = options => Effect.gen(function* () {
101
231
  const created = makeRemoteIdUnsafe();
102
232
  return Effect.as(sql`INSERT INTO ${remoteIdTableSql} (remote_id) VALUES (${created})`, created);
103
233
  }));
234
+ const scopeTables = scope => {
235
+ const table = `${tablePrefix}_${scope}`;
236
+ return {
237
+ tableSql: sql(table),
238
+ idsTableSql: sql(`${table}_ids`),
239
+ snapshotTableSql: sql(`${table}_snapshot`)
240
+ };
241
+ };
242
+ const ensureTables = Effect.fnUntraced(function* (scope, tables) {
243
+ const table = `${tablePrefix}_${scope}`;
244
+ const {
245
+ idsTableSql,
246
+ tableSql
247
+ } = tables;
248
+ const indexSql = sql(`${table}_last_sequence`);
249
+ yield* sql.onDialectOrElse({
250
+ pg: () => sql`
251
+ CREATE TABLE IF NOT EXISTS ${tableSql} (
252
+ sequence INTEGER PRIMARY KEY,
253
+ last_sequence INTEGER NOT NULL,
254
+ iv BYTEA NOT NULL,
255
+ entry_ids BYTEA NOT NULL,
256
+ encrypted_batch BYTEA NOT NULL
257
+ )`.pipe(Effect.andThen(sql`CREATE INDEX IF NOT EXISTS ${indexSql} ON ${tableSql} (last_sequence)`), Effect.andThen(sql`
258
+ CREATE TABLE IF NOT EXISTS ${idsTableSql} (
259
+ entry_id BYTEA PRIMARY KEY,
260
+ sequence INTEGER NOT NULL
261
+ )`)),
262
+ mysql: () => sql`
263
+ CREATE TABLE IF NOT EXISTS ${tableSql} (
264
+ sequence INT PRIMARY KEY,
265
+ last_sequence INT NOT NULL,
266
+ iv BINARY(12) NOT NULL,
267
+ entry_ids LONGBLOB NOT NULL,
268
+ encrypted_batch LONGBLOB NOT NULL,
269
+ INDEX (last_sequence)
270
+ )`.pipe(Effect.andThen(sql`
271
+ CREATE TABLE IF NOT EXISTS ${idsTableSql} (
272
+ entry_id BINARY(16) PRIMARY KEY,
273
+ sequence INT NOT NULL
274
+ )`)),
275
+ mssql: () => sql`
276
+ IF OBJECT_ID(${table}, N'U') IS NULL
277
+ CREATE TABLE ${tableSql} (
278
+ sequence INT PRIMARY KEY,
279
+ last_sequence INT NOT NULL,
280
+ iv VARBINARY(12) NOT NULL,
281
+ entry_ids VARBINARY(MAX) NOT NULL,
282
+ encrypted_batch VARBINARY(MAX) NOT NULL,
283
+ INDEX ${indexSql} NONCLUSTERED (last_sequence)
284
+ )`.pipe(Effect.andThen(sql`
285
+ IF OBJECT_ID(${`${table}_ids`}, N'U') IS NULL
286
+ CREATE TABLE ${idsTableSql} (
287
+ entry_id VARBINARY(16) PRIMARY KEY,
288
+ sequence INT NOT NULL
289
+ )`)),
290
+ orElse: () => sql`
291
+ CREATE TABLE IF NOT EXISTS ${tableSql} (
292
+ sequence INTEGER PRIMARY KEY,
293
+ last_sequence INTEGER NOT NULL,
294
+ iv BLOB NOT NULL,
295
+ entry_ids BLOB NOT NULL,
296
+ encrypted_batch BLOB NOT NULL
297
+ )`.pipe(Effect.andThen(sql`CREATE INDEX IF NOT EXISTS ${indexSql} ON ${tableSql} (last_sequence)`), Effect.andThen(sql`
298
+ CREATE TABLE IF NOT EXISTS ${idsTableSql} (
299
+ entry_id BLOB PRIMARY KEY,
300
+ sequence INTEGER NOT NULL
301
+ )`))
302
+ }).pipe(Effect.retry({
303
+ times: 3
304
+ }));
305
+ // Staged and committed snapshot batches. `sequence` and
306
+ // `last_sequence` stay null until commit.
307
+ yield* createTable(`${table}_snapshot`, `batch_set ${types.id} NOT NULL,
308
+ batch_position ${types.bigint} NOT NULL,
309
+ sequence ${types.int} NULL,
310
+ last_sequence ${types.int} NULL,
311
+ entry_count ${types.int} NOT NULL,
312
+ iv ${types.iv} NOT NULL,
313
+ entry_ids ${types.bytes} NOT NULL,
314
+ encrypted_batch ${types.bytes} NOT NULL,
315
+ PRIMARY KEY (batch_set, batch_position)`, ["batch_set, last_sequence"]);
316
+ yield* ensureStore(scope, tables);
317
+ });
318
+ // Stores whose tables and row this instance has ensured, so a store
319
+ // reopened after idling skips the DDL.
320
+ const ensured = new Set();
321
+ const lookup = Effect.fnUntraced(function* (scopeKey) {
322
+ const scope = (yield* digest.digestHex(Digest.sha256, new TextEncoder().encode(scopeKey))).slice(0, 16);
323
+ const tables = scopeTables(scope);
324
+ if (!ensured.has(scope)) {
325
+ yield* ensureTables(scope, tables);
326
+ ensured.add(scope);
327
+ }
328
+ const pubsub = yield* Effect.acquireRelease(PubSub.unbounded(), PubSub.shutdown);
329
+ const semaphore = yield* Semaphore.make(1);
330
+ return {
331
+ pubsub,
332
+ semaphore,
333
+ scope,
334
+ ...tables
335
+ };
336
+ }, withTracerDisabled);
104
337
  const resources = yield* RcMap.make({
105
- lookup: Effect.fnUntraced(function* (scopeKey) {
106
- const scopeHash = (yield* encryptions.sha256String(new TextEncoder().encode(scopeKey))).slice(0, 16);
107
- const table = `${tablePrefix}_${scopeHash}`;
108
- const tableSql = sql(table);
109
- const idsTableSql = sql(`${table}_ids`);
110
- const indexSql = sql(`${table}_last_sequence`);
111
- yield* sql.onDialectOrElse({
112
- pg: () => sql`
113
- CREATE TABLE IF NOT EXISTS ${tableSql} (
114
- sequence INTEGER PRIMARY KEY,
115
- last_sequence INTEGER NOT NULL,
116
- iv BYTEA NOT NULL,
117
- entry_ids BYTEA NOT NULL,
118
- encrypted_batch BYTEA NOT NULL
119
- )`.pipe(Effect.andThen(sql`CREATE INDEX IF NOT EXISTS ${indexSql} ON ${tableSql} (last_sequence)`), Effect.andThen(sql`
120
- CREATE TABLE IF NOT EXISTS ${idsTableSql} (
121
- entry_id BYTEA PRIMARY KEY,
122
- sequence INTEGER NOT NULL
123
- )`)),
124
- mysql: () => sql`
125
- CREATE TABLE IF NOT EXISTS ${tableSql} (
126
- sequence INT PRIMARY KEY,
127
- last_sequence INT NOT NULL,
128
- iv BINARY(12) NOT NULL,
129
- entry_ids LONGBLOB NOT NULL,
130
- encrypted_batch LONGBLOB NOT NULL,
131
- INDEX (last_sequence)
132
- )`.pipe(Effect.andThen(sql`
133
- CREATE TABLE IF NOT EXISTS ${idsTableSql} (
134
- entry_id BINARY(16) PRIMARY KEY,
135
- sequence INT NOT NULL
136
- )`)),
137
- mssql: () => sql`
138
- IF OBJECT_ID(${table}, N'U') IS NULL
139
- CREATE TABLE ${tableSql} (
140
- sequence INT PRIMARY KEY,
141
- last_sequence INT NOT NULL,
142
- iv VARBINARY(12) NOT NULL,
143
- entry_ids VARBINARY(MAX) NOT NULL,
144
- encrypted_batch VARBINARY(MAX) NOT NULL,
145
- INDEX ${indexSql} NONCLUSTERED (last_sequence)
146
- )`.pipe(Effect.andThen(sql`
147
- IF OBJECT_ID(${`${table}_ids`}, N'U') IS NULL
148
- CREATE TABLE ${idsTableSql} (
149
- entry_id VARBINARY(16) PRIMARY KEY,
150
- sequence INT NOT NULL
151
- )`)),
152
- orElse: () => sql`
153
- CREATE TABLE IF NOT EXISTS ${tableSql} (
154
- sequence INTEGER PRIMARY KEY,
155
- last_sequence INTEGER NOT NULL,
156
- iv BLOB NOT NULL,
157
- entry_ids BLOB NOT NULL,
158
- encrypted_batch BLOB NOT NULL
159
- )`.pipe(Effect.andThen(sql`CREATE INDEX IF NOT EXISTS ${indexSql} ON ${tableSql} (last_sequence)`), Effect.andThen(sql`
160
- CREATE TABLE IF NOT EXISTS ${idsTableSql} (
161
- entry_id BLOB PRIMARY KEY,
162
- sequence INTEGER NOT NULL
163
- )`))
164
- });
165
- const pubsub = yield* Effect.acquireRelease(PubSub.unbounded(), PubSub.shutdown);
166
- // Serializes writes within this storage instance.
167
- const semaphore = yield* Semaphore.make(1);
168
- return {
169
- pubsub,
170
- semaphore,
171
- tableSql,
172
- idsTableSql
173
- };
174
- }, withTracerDisabled),
338
+ lookup,
175
339
  idleTimeToLive: "5 minutes"
176
340
  });
177
- // Returns how many of the ids were not stored yet. Stored ids keep their
178
- // first sequence.
179
- const insertNewIds = (idsTableSql, rows) => sql.onDialectOrElse({
180
- mysql: () => sql`INSERT IGNORE INTO ${idsTableSql} ${sql.insert(rows)}`.raw.pipe(Effect.map(result => result.affectedRows)),
341
+ // Statements that return rows the caller decodes.
342
+ const query = statement => statement;
343
+ // Inserts the rows whose `keys` are not stored yet, leaving stored rows as
344
+ // they are, and returns how many were inserted. Every row has the same
345
+ // columns, and SQL Server's MERGE needs them unique by key.
346
+ const insertNew = (tableSql, rows, keys) => sql.onDialectOrElse({
347
+ mysql: () => sql`INSERT IGNORE INTO ${tableSql} ${sql.insert(rows)}`.raw.pipe(Effect.map(result => result.affectedRows)),
348
+ mssql: () => {
349
+ const columns = Object.keys(rows[0]);
350
+ const values = sql.join(", ", false)(rows.map(row => sql`(${sql.join(", ", false)(columns.map(column => sql`${row[column]}`))})`));
351
+ return sql`
352
+ MERGE ${tableSql} WITH (HOLDLOCK) AS target
353
+ USING (VALUES ${values}) AS source (${sql.literal(columns.join(", "))})
354
+ ON ${sql.literal(keys.map(key => `target.${key} = source.${key}`).join(" AND "))}
355
+ WHEN NOT MATCHED THEN
356
+ INSERT (${sql.literal(columns.join(", "))})
357
+ VALUES (${sql.literal(columns.map(column => `source.${column}`).join(", "))})
358
+ OUTPUT inserted.${sql.literal(keys[0])};
359
+ `.pipe(Effect.map(inserted => inserted.length));
360
+ },
361
+ orElse: () => sql`INSERT INTO ${tableSql} ${sql.insert(rows)} ON CONFLICT DO NOTHING RETURNING ${sql(keys[0])}`.pipe(Effect.map(inserted => inserted.length))
362
+ });
363
+ const upsertBucket = (scope, hour, lastSequence) => sql.onDialectOrElse({
364
+ pg: () => sql`
365
+ INSERT INTO ${bucketsSql} (scope, bucket_hour, last_sequence) VALUES (${scope}, ${hour}, ${lastSequence})
366
+ ON CONFLICT (scope, bucket_hour)
367
+ DO UPDATE SET last_sequence = GREATEST(${bucketsSql}.last_sequence, EXCLUDED.last_sequence)`,
368
+ mysql: () => sql`
369
+ INSERT INTO ${bucketsSql} (scope, bucket_hour, last_sequence) VALUES (${scope}, ${hour}, ${lastSequence})
370
+ ON DUPLICATE KEY UPDATE last_sequence = GREATEST(last_sequence, VALUES(last_sequence))`,
181
371
  mssql: () => sql`
182
- MERGE ${idsTableSql} WITH (HOLDLOCK) AS target
183
- USING (VALUES ${sql.join(",", false)(rows.map(row => sql`(${row.entry_id}, ${row.sequence})`))}) AS source (entry_id, sequence)
184
- ON target.entry_id = source.entry_id
372
+ MERGE ${bucketsSql} WITH (HOLDLOCK) AS target
373
+ USING (VALUES (${scope}, ${hour}, ${lastSequence})) AS source (scope, bucket_hour, last_sequence)
374
+ ON target.scope = source.scope AND target.bucket_hour = source.bucket_hour
375
+ WHEN MATCHED AND source.last_sequence > target.last_sequence THEN
376
+ UPDATE SET last_sequence = source.last_sequence
185
377
  WHEN NOT MATCHED THEN
186
- INSERT (entry_id, sequence) VALUES (source.entry_id, source.sequence)
187
- OUTPUT inserted.entry_id;
188
- `.pipe(Effect.map(inserted => inserted.length)),
378
+ INSERT (scope, bucket_hour, last_sequence)
379
+ VALUES (source.scope, source.bucket_hour, source.last_sequence);`,
189
380
  orElse: () => sql`
190
- INSERT INTO ${idsTableSql} ${sql.insert(rows)}
191
- ON CONFLICT DO NOTHING RETURNING entry_id
192
- `.pipe(Effect.map(inserted => inserted.length))
381
+ INSERT INTO ${bucketsSql} (scope, bucket_hour, last_sequence) VALUES (${scope}, ${hour}, ${lastSequence})
382
+ ON CONFLICT (scope, bucket_hour)
383
+ DO UPDATE SET last_sequence = MAX(${bucketsSql}.last_sequence, excluded.last_sequence)`
384
+ });
385
+ const maxLastSequence = tableSql => Effect.map(query(sql`SELECT MAX(last_sequence) AS last_sequence FROM ${tableSql}`), ([row]) => Number(row?.last_sequence ?? 0));
386
+ // Creates the store row on first open. The first instance to insert it
387
+ // also records the originals activation and seeds a bucket at the current
388
+ // last sequence, so data written before the upgrade becomes compactable.
389
+ const ensureStore = Effect.fnUntraced(function* (scope, tables) {
390
+ const existing = yield* query(sql`SELECT scope FROM ${storesSql} WHERE scope = ${scope}`);
391
+ if (existing.length > 0) return;
392
+ const now = yield* Clock.currentTimeMillis;
393
+ yield* sql.withTransaction(Effect.gen(function* () {
394
+ const lastSequence = yield* maxLastSequence(tables.tableSql);
395
+ const originals = SecureRandom.uuidV4Unsafe();
396
+ const inserted = yield* insertNew(storesSql, [{
397
+ scope,
398
+ next_sequence: lastSequence + 1,
399
+ active_activation: originals,
400
+ active_batch_set: null,
401
+ base_sequence: 0,
402
+ active_kept_count: 0,
403
+ no_op_at: null,
404
+ no_op_count: 0,
405
+ open_stage: null,
406
+ next_sweep_at: null,
407
+ sweep_lease_until: null,
408
+ sweep_lease_owner: null,
409
+ pruned_through: 0
410
+ }], ["scope"]);
411
+ if (inserted === 0) return;
412
+ yield* sql`INSERT INTO ${activationsSql} ${sql.insert({
413
+ id: originals,
414
+ scope,
415
+ ordinal: 0,
416
+ batch_set: null,
417
+ base_sequence: 0,
418
+ kept_count: null,
419
+ previous: null,
420
+ superseded_at: null,
421
+ prunable_at: null,
422
+ prune_started_at: null
423
+ })}`;
424
+ if (lastSequence > 0) yield* upsertBucket(scope, Math.floor(now / hourMillis), lastSequence);
425
+ }));
426
+ });
427
+ const storeColumns = sql.literal("next_sequence, active_activation, active_batch_set, base_sequence, active_kept_count, no_op_at, " + "no_op_count, open_stage, next_sweep_at, sweep_lease_until, sweep_lease_owner, pruned_through");
428
+ const selectStore = scope => Effect.map(query(sql`SELECT ${storeColumns} FROM ${storesSql} WHERE scope = ${scope}`), ([row]) => row === undefined ? undefined : toStoreRow(row));
429
+ // Row locks held for the rest of the transaction. SQLite transactions take
430
+ // the database write lock instead.
431
+ const lockHint = sql.onDialectOrElse({
432
+ mssql: () => sql.literal("WITH (UPDLOCK, ROWLOCK, HOLDLOCK)"),
433
+ orElse: () => sql.literal("")
193
434
  });
435
+ const forUpdate = sql.onDialectOrElse({
436
+ pg: () => sql.literal("FOR UPDATE"),
437
+ mysql: () => sql.literal("FOR UPDATE"),
438
+ orElse: () => sql.literal("")
439
+ });
440
+ const noClause = sql.literal("");
441
+ // Locks the store row for the rest of the transaction. Lock order is store
442
+ // row, then stage row, then snapshot rows.
443
+ const lockStore = scope => query(sql`SELECT ${storeColumns} FROM ${storesSql} ${lockHint} WHERE scope = ${scope} ${forUpdate}`).pipe(Effect.flatMap(([row]) => row === undefined ? Effect.die(new Cause.NoSuchElementError(`No compaction store row for scope ${scope}`)) : Effect.succeed(toStoreRow(row))));
444
+ const stageColumns = sql.literal("id, scope, state, pinned, through_sequence, expires_at");
445
+ const selectStage = (scope, stageId, lock) => query(sql`
446
+ SELECT ${stageColumns} FROM ${stagesSql} ${lock ? lockHint : noClause}
447
+ WHERE id = ${stageId} ${lock ? forUpdate : noClause}`).pipe(Effect.map(([row]) => {
448
+ if (row === undefined) return undefined;
449
+ const stage = toStageRow(row);
450
+ return stage.scope === scope ? stage : undefined;
451
+ }));
452
+ const selectActivation = id => Effect.map(query(sql`SELECT ${activationColumns} FROM ${activationsSql} WHERE id = ${id}`), ([row]) => row === undefined ? undefined : toActivationRow(row));
453
+ const selectActivations = scope => Effect.map(query(sql`SELECT ${activationColumns} FROM ${activationsSql} WHERE scope = ${scope} ORDER BY ordinal`), rows => rows.map(toActivationRow));
454
+ const activationColumns = sql.literal("id, ordinal, batch_set, base_sequence, kept_count, previous, superseded_at, prunable_at, prune_started_at");
455
+ const releaseClaim = (scope, stageId) => sql`UPDATE ${storesSql} SET open_stage = NULL WHERE scope = ${scope} AND open_stage = ${stageId}`;
456
+ // Closes an open stage, leaving what it staged to the sweep, and releases
457
+ // the store's busy claim.
458
+ const closeStage = (scope, stageId) => sql`UPDATE ${stagesSql} SET state = ${"closed"} WHERE id = ${stageId} AND state = ${"open"}`.pipe(Effect.andThen(releaseClaim(scope, stageId)));
459
+ const scheduleSweep = (scope, at) => sql`
460
+ UPDATE ${storesSql}
461
+ SET next_sweep_at = CASE WHEN next_sweep_at IS NULL OR next_sweep_at > ${at} THEN ${at} ELSE next_sweep_at END
462
+ WHERE scope = ${scope}`;
463
+ // The clauses that cap a statement at `rows` rows, for `SELECT ${top} ... ${limit}`.
464
+ const rowCap = rows => ({
465
+ top: sql.onDialectOrElse({
466
+ mssql: () => sql.literal(`TOP (${rows})`),
467
+ orElse: () => noClause
468
+ }),
469
+ limit: sql.onDialectOrElse({
470
+ mssql: () => noClause,
471
+ orElse: () => sql.literal(`LIMIT ${rows}`)
472
+ })
473
+ });
474
+ const {
475
+ limit,
476
+ top
477
+ } = rowCap(pageSize);
478
+ const {
479
+ limit: chunkLimit,
480
+ top: chunkTop
481
+ } = rowCap(pruneChunkRows);
482
+ // Streams rows in key order, one page per statement.
483
+ const paged = (first, page, key) => Stream.paginate(first, after => Effect.map(page(after), rows => [rows, rows.length < pageSize ? Option.none() : Option.some(key(rows[rows.length - 1]))]));
484
+ const setRows = (snapshotTableSql, batchSet) => paged(-1, after => query(sql`
485
+ SELECT ${top} batch_position, sequence, entry_ids FROM ${snapshotTableSql}
486
+ WHERE batch_set = ${batchSet} AND batch_position > ${after}
487
+ ORDER BY batch_position ${limit}`), row => Number(row.batch_position));
194
488
  const getSessionAuthBinding = publicKey => sql`
195
489
  SELECT public_key, signing_public_key
196
490
  FROM ${sessionAuthBindingsTableSql}
@@ -199,6 +493,571 @@ export const makeStorage = options => Effect.gen(function* () {
199
493
  const row = rows[0];
200
494
  return row === undefined ? undefined : row.signing_public_key;
201
495
  }), Effect.orDie);
496
+ // Runs `f` with the store's resources and the time, read before any
497
+ // transaction starts. SQL failures are defects.
498
+ const withStore = (publicKey, storeId, f) => Effect.zip(RcMap.get(resources, makeEncryptedScopeKey(publicKey, storeId)), Clock.currentTimeMillis).pipe(Effect.flatMap(([store, now]) => f(store, now)), dieOnSqlError, Effect.scoped, withTracerDisabled);
499
+ const notFound = stageId => Effect.fail(new EventLogServerEncrypted.CompactionNotFound({
500
+ stageId
501
+ }));
502
+ // An open stage whose pin isn't being pruned yet, for Input and Fetch.
503
+ const findInputStage = Effect.fnUntraced(function* (scope, stageId, now) {
504
+ const stage = yield* selectStage(scope, stageId, false);
505
+ if (stage === undefined || stage.state !== "open" || stage.expiresAt <= now) return undefined;
506
+ const pinned = yield* selectActivation(stage.pinned);
507
+ if (pinned === undefined || pinned.pruneStartedAt !== undefined) return undefined;
508
+ return {
509
+ stage,
510
+ pinned
511
+ };
512
+ });
513
+ // Fails a read that may have been truncated by a prune of its pin.
514
+ const recheckPin = (stageId, pinned) => Effect.flatMap(selectActivation(pinned), activation => activation === undefined || activation.pruneStartedAt !== undefined ? notFound(stageId) : Effect.void);
515
+ const status = Effect.fnUntraced(function* ({
516
+ scope,
517
+ tableSql
518
+ }, now) {
519
+ const store = yield* selectStore(scope);
520
+ const lastSequence = yield* maxLastSequence(tableSql);
521
+ const [lag] = yield* query(sql`
522
+ SELECT MAX(last_sequence) AS last_sequence FROM ${bucketsSql}
523
+ WHERE scope = ${scope} AND bucket_hour <= ${lagHour(now, lagMillis)}`);
524
+ return {
525
+ head: Math.max(store.nextSequence - 1, lastSequence),
526
+ base: store.base,
527
+ activeKeptCount: store.activeKeptCount,
528
+ lagHead: Number(lag?.last_sequence ?? 0),
529
+ noOp: store.noOpAt === undefined ? undefined : {
530
+ at: store.noOpAt,
531
+ count: store.noOpCount
532
+ }
533
+ };
534
+ });
535
+ const begin = Effect.fnUntraced(function* ({
536
+ scope
537
+ }, now, through) {
538
+ const store = yield* lockStore(scope);
539
+ if (store.openStage !== undefined) {
540
+ const open = yield* selectStage(scope, store.openStage, true);
541
+ if (open !== undefined && open.state === "open") {
542
+ if (open.expiresAt > now) {
543
+ return {
544
+ _tag: "Busy",
545
+ expiresAt: open.expiresAt
546
+ };
547
+ }
548
+ yield* closeStage(scope, open.id);
549
+ }
550
+ }
551
+ const stageId = SecureRandom.uuidV4Unsafe();
552
+ yield* sql`INSERT INTO ${stagesSql} ${sql.insert({
553
+ id: stageId,
554
+ scope,
555
+ state: "open",
556
+ pinned: store.activeActivation,
557
+ through_sequence: through,
558
+ expires_at: now + stageTtlMillis
559
+ })}`;
560
+ yield* sql`UPDATE ${storesSql} SET open_stage = ${stageId} WHERE scope = ${scope}`;
561
+ return {
562
+ _tag: "Started",
563
+ stageId
564
+ };
565
+ }, sql.withTransaction);
566
+ const inputHeaders = Effect.fnUntraced(function* ({
567
+ scope,
568
+ snapshotTableSql,
569
+ tableSql
570
+ }, now, stageId) {
571
+ const found = yield* findInputStage(scope, stageId, now);
572
+ if (found === undefined) {
573
+ return yield* notFound(stageId);
574
+ }
575
+ const {
576
+ pinned,
577
+ stage
578
+ } = found;
579
+ // Membership comes from the batch rows, never from the id table.
580
+ const snapshot = pinned.batchSet === undefined ? Stream.empty : setRows(snapshotTableSql, pinned.batchSet);
581
+ const tail = paged(pinned.base, after => query(sql`
582
+ SELECT ${top} sequence, entry_ids FROM ${tableSql}
583
+ WHERE sequence > ${after} AND last_sequence <= ${stage.through}
584
+ ORDER BY sequence ${limit}`), row => Number(row.sequence));
585
+ return snapshot.pipe(Stream.concat(tail), Stream.map(row => ({
586
+ sequence: Number(row.sequence),
587
+ entryIds: toEntryIds(row.entry_ids)
588
+ })), Stream.orDie, Stream.concat(Stream.fromEffectDrain(dieOnSqlError(recheckPin(stageId, pinned.id)))));
589
+ });
590
+ const fetch = Effect.fnUntraced(function* ({
591
+ scope,
592
+ snapshotTableSql,
593
+ tableSql
594
+ }, now, stageId, sequences) {
595
+ const found = yield* findInputStage(scope, stageId, now);
596
+ if (found === undefined) {
597
+ return yield* notFound(stageId);
598
+ }
599
+ const {
600
+ pinned,
601
+ stage
602
+ } = found;
603
+ const bySequence = new Map();
604
+ if (pinned.batchSet !== undefined) {
605
+ for (const chunk of Arr.chunksOf(sequences.filter(sequence => sequence <= pinned.base), 200)) {
606
+ const rows = yield* sql`
607
+ SELECT sequence, iv, entry_ids, encrypted_batch FROM ${snapshotTableSql}
608
+ WHERE batch_set = ${pinned.batchSet} AND ${sql.in("sequence", chunk)}
609
+ `.pipe(Effect.flatMap(decodeBatches), Effect.orDie);
610
+ for (const batch of rows) bySequence.set(batch.sequence, batch);
611
+ }
612
+ }
613
+ const originals = sequences.filter(sequence => sequence > pinned.base && sequence <= stage.through);
614
+ for (const chunk of Arr.chunksOf(originals, 200)) {
615
+ const rows = yield* sql`
616
+ SELECT sequence, iv, entry_ids, encrypted_batch FROM ${tableSql}
617
+ WHERE ${sql.in("sequence", chunk)}
618
+ `.pipe(Effect.flatMap(decodeBatches), Effect.orDie);
619
+ for (const batch of rows) bySequence.set(batch.sequence, batch);
620
+ }
621
+ yield* recheckPin(stageId, pinned.id);
622
+ return Stream.fromArray(sequences.flatMap(sequence => {
623
+ const batch = bySequence.get(sequence);
624
+ return batch === undefined ? [] : [batch];
625
+ }));
626
+ });
627
+ const stagePart = (store, now, stageId, segment, batches) => {
628
+ const invalid = !Number.isSafeInteger(segment) || segment < 0 || segment >= maxSegments ? "Invalid segment index" : batches.length > positionsPerSegment ? "Too many batches in a segment" : undefined;
629
+ // A Part that fails validation closes the stage.
630
+ if (invalid !== undefined) {
631
+ return Effect.andThen(abort(store, stageId), Effect.fail(new EventLogServerEncrypted.CompactionInvalidRequest({
632
+ stageId,
633
+ message: invalid
634
+ })));
635
+ }
636
+ const {
637
+ scope,
638
+ snapshotTableSql
639
+ } = store;
640
+ return sql.withTransaction(Effect.gen(function* () {
641
+ yield* lockStore(scope);
642
+ const stage = yield* selectStage(scope, stageId, true);
643
+ if (stage === undefined || stage.state !== "open" || stage.expiresAt <= now) {
644
+ return yield* notFound(stageId);
645
+ }
646
+ // The snapshot table's key makes a retried segment insert nothing.
647
+ // `sequence` and `last_sequence` stay null until commit.
648
+ const rows = batches.map((batch, i) => ({
649
+ batch_set: stageId,
650
+ batch_position: segment * positionsPerSegment + i,
651
+ entry_count: batch.entryIds.length,
652
+ iv: batch.iv,
653
+ entry_ids: concatEntryIds(batch.entryIds),
654
+ encrypted_batch: batch.encryptedBatch
655
+ }));
656
+ let inserted = 0;
657
+ for (const chunk of Arr.chunksOf(rows, 200)) {
658
+ inserted += yield* insertNew(snapshotTableSql, chunk, ["batch_set", "batch_position"]);
659
+ }
660
+ if (inserted === 0) return "AlreadyStored";
661
+ yield* sql`UPDATE ${stagesSql} SET expires_at = ${now + stageTtlMillis} WHERE id = ${stageId}`;
662
+ return "Stored";
663
+ }));
664
+ };
665
+ const stagedHeaders = Effect.fnUntraced(function* ({
666
+ scope,
667
+ snapshotTableSql
668
+ }, now, stageId) {
669
+ const stage = yield* selectStage(scope, stageId, false);
670
+ if (stage === undefined || stage.state !== "open" || stage.expiresAt <= now) {
671
+ return yield* notFound(stageId);
672
+ }
673
+ return setRows(snapshotTableSql, stageId).pipe(Stream.map(row => ({
674
+ position: Number(row.batch_position),
675
+ entryIds: toEntryIds(row.entry_ids)
676
+ })), Stream.orDie);
677
+ });
678
+ const commit = Effect.fnUntraced(function* ({
679
+ scope,
680
+ snapshotTableSql
681
+ }, now, stageId, virtualSequences) {
682
+ const store = yield* lockStore(scope);
683
+ const stage = yield* selectStage(scope, stageId, true);
684
+ if (stage === undefined || stage.state !== "open" || stage.expiresAt <= now) {
685
+ return yield* notFound(stageId);
686
+ }
687
+ const staged = yield* query(sql`
688
+ SELECT batch_position, entry_count FROM ${snapshotTableSql}
689
+ WHERE batch_set = ${stageId} ORDER BY batch_position`);
690
+ let keptCount = 0;
691
+ for (const row of staged) keptCount += Number(row.entry_count);
692
+ const audit = {
693
+ through: stage.through,
694
+ keptCount
695
+ };
696
+ if (stage.pinned !== store.activeActivation) {
697
+ yield* closeStage(scope, stageId);
698
+ return {
699
+ result: "Superseded",
700
+ activationId: undefined,
701
+ ...audit
702
+ };
703
+ }
704
+ if (staged.length !== virtualSequences.length) {
705
+ return yield* Effect.die(new Cause.IllegalArgumentError("Expected one virtual sequence per staged batch"));
706
+ }
707
+ for (let i = 0; i < staged.length; i++) {
708
+ const sequence = virtualSequences[i];
709
+ const lastSequence = sequence + Number(staged[i].entry_count) - 1;
710
+ yield* sql`
711
+ UPDATE ${snapshotTableSql} SET sequence = ${sequence}, last_sequence = ${lastSequence}
712
+ WHERE batch_set = ${stageId} AND batch_position = ${Number(staged[i].batch_position)}`;
713
+ }
714
+ const activationId = SecureRandom.uuidV4Unsafe();
715
+ yield* insertActivation(scope, {
716
+ id: activationId,
717
+ batch_set: stageId,
718
+ base_sequence: stage.through,
719
+ kept_count: keptCount,
720
+ previous: store.activeActivation
721
+ });
722
+ yield* supersede(store.activeActivation, now);
723
+ yield* sql`
724
+ UPDATE ${storesSql}
725
+ SET active_activation = ${activationId}, active_batch_set = ${stageId},
726
+ base_sequence = ${stage.through}, active_kept_count = ${keptCount},
727
+ no_op_at = NULL, no_op_count = 0
728
+ WHERE scope = ${scope}`;
729
+ yield* scheduleSweep(scope, now + graceMillis);
730
+ // The staged rows are now the active batch set.
731
+ yield* sql`DELETE FROM ${stagesSql} WHERE id = ${stageId}`;
732
+ yield* releaseClaim(scope, stageId);
733
+ return {
734
+ result: "Committed",
735
+ activationId,
736
+ ...audit
737
+ };
738
+ }, sql.withTransaction);
739
+ const recordNoOp = Effect.fnUntraced(function* ({
740
+ scope
741
+ }, now, stageId) {
742
+ const store = yield* lockStore(scope);
743
+ const stage = yield* selectStage(scope, stageId, true);
744
+ if (stage === undefined || stage.state !== "open" || stage.expiresAt <= now) {
745
+ return yield* notFound(stageId);
746
+ }
747
+ // A rollback since begin cleared the no-op state, so a stale run leaves it.
748
+ if (stage.pinned === store.activeActivation) {
749
+ yield* sql`
750
+ UPDATE ${storesSql} SET no_op_at = ${stage.through}, no_op_count = ${store.noOpCount + 1}
751
+ WHERE scope = ${scope}`;
752
+ }
753
+ yield* closeStage(scope, stageId);
754
+ return stage.through;
755
+ }, sql.withTransaction);
756
+ const rollbackTransaction = Effect.fnUntraced(function* ({
757
+ pubsub,
758
+ scope,
759
+ snapshotTableSql,
760
+ tableSql
761
+ }, now, expectedActivationId) {
762
+ const refuse = reason => Effect.fail(new EventLogServerEncrypted.CompactionRollbackError({
763
+ reason
764
+ }));
765
+ const store = yield* lockStore(scope);
766
+ if (store.activeActivation !== expectedActivationId) return yield* refuse("NotActive");
767
+ const withdrawn = yield* selectActivation(store.activeActivation);
768
+ const target = withdrawn.previous === undefined ? undefined : yield* selectActivation(withdrawn.previous);
769
+ if (target === undefined) return yield* refuse("NoPrevious");
770
+ if (target.pruneStartedAt !== undefined) return yield* refuse("PruneStarted");
771
+ const activationId = SecureRandom.uuidV4Unsafe();
772
+ yield* insertActivation(scope, {
773
+ id: activationId,
774
+ batch_set: target.batchSet ?? null,
775
+ base_sequence: target.base,
776
+ kept_count: target.keptCount ?? null,
777
+ previous: target.previous ?? null
778
+ });
779
+ yield* supersede(withdrawn.id, now);
780
+ yield* sql`
781
+ UPDATE ${storesSql}
782
+ SET active_activation = ${activationId}, active_batch_set = ${target.batchSet ?? null},
783
+ base_sequence = ${target.base}, active_kept_count = ${target.keptCount ?? 0},
784
+ no_op_at = NULL, no_op_count = 0
785
+ WHERE scope = ${scope}`;
786
+ yield* scheduleSweep(scope, now + graceMillis);
787
+ // Re-appends the input of the stage that built the withdrawn generation,
788
+ // the target's batch set and then the originals between the two bases,
789
+ // at sequences taken as `write` takes them.
790
+ let nextSequence = Math.max(store.nextSequence, (yield* maxLastSequence(tableSql)) + 1);
791
+ const reappended = [];
792
+ const reappend = rows => {
793
+ const inserted = [];
794
+ for (const row of rows) {
795
+ const entry_ids = row.entry_ids;
796
+ const batch = {
797
+ sequence: nextSequence,
798
+ entryIds: toEntryIds(entry_ids),
799
+ iv: row.iv,
800
+ encryptedBatch: row.encrypted_batch
801
+ };
802
+ nextSequence += batch.entryIds.length;
803
+ reappended.push(batch);
804
+ inserted.push({
805
+ sequence: batch.sequence,
806
+ last_sequence: nextSequence - 1,
807
+ iv: batch.iv,
808
+ entry_ids,
809
+ encrypted_batch: batch.encryptedBatch
810
+ });
811
+ }
812
+ return Effect.forEach(Arr.chunksOf(inserted, 200), chunk => sql`INSERT INTO ${tableSql} ${sql.insert(chunk)}`, {
813
+ discard: true
814
+ });
815
+ };
816
+ if (target.batchSet !== undefined) {
817
+ yield* Stream.runForEachArray(paged(-1, after => query(sql`
818
+ SELECT ${top} batch_position, iv, entry_ids, encrypted_batch FROM ${snapshotTableSql}
819
+ WHERE batch_set = ${target.batchSet} AND batch_position > ${after}
820
+ ORDER BY batch_position ${limit}`), row => Number(row.batch_position)), reappend);
821
+ }
822
+ // The copies land above `withdrawn.base`, so the pages never reach them.
823
+ yield* Stream.runForEachArray(paged(target.base, after => query(sql`
824
+ SELECT ${top} sequence, iv, entry_ids, encrypted_batch FROM ${tableSql}
825
+ WHERE sequence > ${after} AND last_sequence <= ${withdrawn.base}
826
+ ORDER BY sequence ${limit}`), row => Number(row.sequence)), reappend);
827
+ if (reappended.length > 0) {
828
+ yield* sql`UPDATE ${storesSql} SET next_sequence = ${nextSequence} WHERE scope = ${scope}`;
829
+ yield* upsertBucket(scope, Math.floor(now / hourMillis), nextSequence - 1);
830
+ yield* sql.afterCommit(Effect.asVoid(PubSub.publishAll(pubsub, reappended)));
831
+ }
832
+ return {
833
+ activationId,
834
+ target,
835
+ withdrawn
836
+ };
837
+ }, sql.withTransaction);
838
+ // The re-appended batches are published after the outermost commit. When
839
+ // the storage owns the transaction, that happens before the semaphore is
840
+ // released, so live readers get them in sequence order.
841
+ const rollback = (store, now, expectedActivationId) => store.semaphore.withPermits(1)(rollbackTransaction(store, now, expectedActivationId));
842
+ const history = Effect.fnUntraced(function* ({
843
+ scope
844
+ }) {
845
+ const store = yield* selectStore(scope);
846
+ const activations = yield* selectActivations(scope);
847
+ return activations.map(activation => {
848
+ const active = activation.id === store.activeActivation;
849
+ return {
850
+ activationId: activation.id,
851
+ base: activation.base,
852
+ keptCount: activation.keptCount,
853
+ active,
854
+ supersededAt: active ? undefined : activation.supersededAt,
855
+ restorable: activation.pruneStartedAt === undefined
856
+ };
857
+ });
858
+ });
859
+ const abort = Effect.fnUntraced(function* ({
860
+ scope
861
+ }, stageId) {
862
+ yield* lockStore(scope);
863
+ const stage = yield* selectStage(scope, stageId, true);
864
+ if (stage?.state === "open") yield* closeStage(scope, stageId);
865
+ }, sql.withTransaction);
866
+ const compaction = {
867
+ config,
868
+ status: (publicKey, storeId) => withStore(publicKey, storeId, status),
869
+ begin: (publicKey, storeId, {
870
+ through
871
+ }) => withStore(publicKey, storeId, (store, now) => begin(store, now, through)),
872
+ inputHeaders: (publicKey, storeId, stageId) => Stream.unwrap(withStore(publicKey, storeId, (store, now) => inputHeaders(store, now, stageId))),
873
+ fetch: (publicKey, storeId, stageId, sequences) => Stream.unwrap(withStore(publicKey, storeId, (store, now) => fetch(store, now, stageId, sequences))),
874
+ stagePart: (publicKey, storeId, stageId, segment, batches) => withStore(publicKey, storeId, (store, now) => stagePart(store, now, stageId, segment, batches)),
875
+ stagedHeaders: (publicKey, storeId, stageId) => Stream.unwrap(withStore(publicKey, storeId, (store, now) => stagedHeaders(store, now, stageId))),
876
+ commit: (publicKey, storeId, stageId, virtualSequences) => withStore(publicKey, storeId, (store, now) => commit(store, now, stageId, virtualSequences)).pipe(Effect.tap(outcome => logCommit({
877
+ publicKey,
878
+ storeId,
879
+ ...outcome
880
+ })), Effect.map(outcome => outcome.result)),
881
+ recordNoOp: (publicKey, storeId, stageId) => withStore(publicKey, storeId, (store, now) => recordNoOp(store, now, stageId)).pipe(Effect.flatMap(through => logNoOp({
882
+ publicKey,
883
+ storeId,
884
+ stageId,
885
+ through
886
+ }))),
887
+ abort: (publicKey, storeId, stageId) => withStore(publicKey, storeId, store => abort(store, stageId)),
888
+ rollback: (publicKey, storeId, expectedActivationId) => withStore(publicKey, storeId, (store, now) => rollback(store, now, expectedActivationId)).pipe(Effect.tap(({
889
+ activationId,
890
+ target,
891
+ withdrawn
892
+ }) => logRollback({
893
+ publicKey,
894
+ storeId,
895
+ activation: {
896
+ ...target,
897
+ id: activationId
898
+ },
899
+ withdrawn
900
+ })), Effect.map(outcome => outcome.activationId)),
901
+ history: (publicKey, storeId) => withStore(publicKey, storeId, store => history(store))
902
+ };
903
+ const insertActivation = Effect.fnUntraced(function* (scope, activation) {
904
+ const [last] = yield* query(sql`SELECT MAX(ordinal) AS ordinal FROM ${activationsSql} WHERE scope = ${scope}`);
905
+ yield* sql`INSERT INTO ${activationsSql} ${sql.insert({
906
+ ...activation,
907
+ scope,
908
+ ordinal: Number(last?.ordinal ?? 0) + 1,
909
+ superseded_at: null,
910
+ prunable_at: null,
911
+ prune_started_at: null
912
+ })}`;
913
+ });
914
+ // Supersedes or withdraws an activation, prunable `grace` later.
915
+ const supersede = (activationId, now) => sql`
916
+ UPDATE ${activationsSql} SET superseded_at = ${now}, prunable_at = ${now + graceMillis}
917
+ WHERE id = ${activationId}`;
918
+ // Deletes up to `pruneChunkRows` rows matching `where` in key order and
919
+ // returns whether it found any. No new rows match `where`, so a later call
920
+ // resumes an interrupted one.
921
+ const deleteChunk = Effect.fnUntraced(function* (tableSql, key, where) {
922
+ const keys = yield* query(sql`
923
+ SELECT ${chunkTop} ${sql(key)} AS chunk_key FROM ${tableSql}
924
+ WHERE ${where} ORDER BY ${sql(key)} ${chunkLimit}`);
925
+ if (keys.length === 0) return false;
926
+ yield* sql`DELETE FROM ${tableSql} WHERE ${where} AND ${sql(key)} <= ${keys[keys.length - 1].chunk_key}`;
927
+ return true;
928
+ });
929
+ // Runs one chunk of a prune under the store row lock, if this sweeper
930
+ // still holds the lease and the active activation hasn't changed.
931
+ const pruneChunk = (scope, now, lease, f) => sql.withTransaction(Effect.gen(function* () {
932
+ const store = yield* lockStore(scope);
933
+ if (store.sweepLeaseOwner !== lease.owner || store.activeActivation !== lease.activeActivation) {
934
+ return Option.none();
935
+ }
936
+ const value = yield* f;
937
+ yield* sql`UPDATE ${storesSql} SET sweep_lease_until = ${now + sweepIntervalMillis} WHERE scope = ${scope}`;
938
+ return Option.some(value);
939
+ }));
940
+ // Prunes what no activation still needs: the batch sets of generations
941
+ // past their grace, and the originals at or below the lowest base still
942
+ // kept. Deletes are idempotent, so a later sweep finishes one that died.
943
+ const pruneStore = Effect.fnUntraced(function* (scope, now) {
944
+ const {
945
+ snapshotTableSql,
946
+ tableSql
947
+ } = scopeTables(scope);
948
+ const owner = SecureRandom.uuidV4Unsafe();
949
+ // Claim the store and mark what this prune removes before deleting anything.
950
+ const plan = yield* sql.withTransaction(Effect.gen(function* () {
951
+ const store = yield* lockStore(scope);
952
+ if (store.nextSweepAt === undefined || store.nextSweepAt > now) return undefined;
953
+ if (store.sweepLeaseUntil !== undefined && store.sweepLeaseUntil > now) return undefined;
954
+ const activations = yield* selectActivations(scope);
955
+ const isKept = activation => activation.id === store.activeActivation || activation.pruneStartedAt === undefined && (activation.prunableAt ?? Infinity) > now;
956
+ const kept = activations.filter(isKept);
957
+ const pruned = activations.filter(activation => !isKept(activation));
958
+ const starting = pruned.filter(activation => activation.pruneStartedAt === undefined);
959
+ for (const chunk of Arr.chunksOf(starting.map(activation => activation.id), 200)) {
960
+ yield* sql`UPDATE ${activationsSql} SET prune_started_at = ${now} WHERE ${sql.in("id", chunk)}`;
961
+ }
962
+ const keptSets = new Set(kept.map(activation => activation.batchSet));
963
+ yield* sql`
964
+ UPDATE ${storesSql} SET sweep_lease_until = ${now + sweepIntervalMillis}, sweep_lease_owner = ${owner}
965
+ WHERE scope = ${scope}`;
966
+ return {
967
+ lease: {
968
+ owner,
969
+ activeActivation: store.activeActivation
970
+ },
971
+ sets: Array.from(new Set(pruned.flatMap(activation => activation.batchSet === undefined || keptSets.has(activation.batchSet) ? [] : [activation.batchSet]))),
972
+ keptBase: kept.reduce((base, activation) => Math.min(base, activation.base), Infinity),
973
+ prunedThrough: store.prunedThrough
974
+ };
975
+ }));
976
+ if (plan === undefined) return;
977
+ for (const set of plan.sets) {
978
+ while (true) {
979
+ const deleted = yield* pruneChunk(scope, now, plan.lease, deleteChunk(snapshotTableSql, "batch_position", sql`batch_set = ${set}`));
980
+ if (Option.isNone(deleted)) return;
981
+ if (!deleted.value) break;
982
+ }
983
+ }
984
+ for (let start = plan.prunedThrough; start < plan.keptBase; start += pruneChunkSequences) {
985
+ const end = Math.min(start + pruneChunkSequences, plan.keptBase);
986
+ const held = yield* pruneChunk(scope, now, plan.lease, sql`DELETE FROM ${tableSql} WHERE last_sequence <= ${end}`.pipe(Effect.andThen(sql`
987
+ UPDATE ${storesSql}
988
+ SET pruned_through = CASE WHEN pruned_through < ${end} THEN ${end} ELSE pruned_through END
989
+ WHERE scope = ${scope}`)));
990
+ if (Option.isNone(held)) return;
991
+ }
992
+ yield* sql.withTransaction(Effect.gen(function* () {
993
+ const store = yield* lockStore(scope);
994
+ if (store.sweepLeaseOwner !== owner) return;
995
+ const [next] = yield* query(sql`
996
+ SELECT MIN(prunable_at) AS next_sweep_at FROM ${activationsSql}
997
+ WHERE scope = ${scope} AND id <> ${store.activeActivation} AND prune_started_at IS NULL`);
998
+ yield* sql`
999
+ UPDATE ${storesSql}
1000
+ SET next_sweep_at = ${next?.next_sweep_at ?? null}, sweep_lease_until = NULL, sweep_lease_owner = NULL
1001
+ WHERE scope = ${scope}`;
1002
+ }));
1003
+ });
1004
+ // Deletes a closed stage's rows matching `where`, one chunk per
1005
+ // transaction, each under the store row lock to keep the lock order.
1006
+ const deleteStaged = Effect.fnUntraced(function* (scope, tableSql, key, where) {
1007
+ while (yield* sql.withTransaction(Effect.andThen(lockStore(scope), deleteChunk(tableSql, key, where)))) {
1008
+ // Each chunk commits on its own.
1009
+ }
1010
+ });
1011
+ // Closes stages past their claim, then deletes closed stages. A closed
1012
+ // stage's staged rows are read by nothing, so they go first in short
1013
+ // chunks, and the stage row last.
1014
+ const sweepStages = Effect.fnUntraced(function* (now) {
1015
+ const expired = yield* query(sql`
1016
+ SELECT id, scope FROM ${stagesSql} WHERE state = ${"open"} AND expires_at <= ${now}`);
1017
+ for (const row of expired) {
1018
+ const scope = String(row.scope);
1019
+ const stageId = String(row.id);
1020
+ yield* sql.withTransaction(Effect.gen(function* () {
1021
+ yield* lockStore(scope);
1022
+ const stage = yield* selectStage(scope, stageId, true);
1023
+ if (stage?.state === "open" && stage.expiresAt <= now) {
1024
+ yield* closeStage(scope, stageId);
1025
+ }
1026
+ }));
1027
+ }
1028
+ const closed = yield* query(sql`SELECT id, scope FROM ${stagesSql} WHERE state = ${"closed"}`);
1029
+ for (const row of closed) {
1030
+ const scope = String(row.scope);
1031
+ const stageId = String(row.id);
1032
+ yield* deleteStaged(scope, scopeTables(scope).snapshotTableSql, "batch_position", sql`batch_set = ${stageId}`);
1033
+ yield* sql.withTransaction(Effect.andThen(lockStore(scope), sql`DELETE FROM ${stagesSql} WHERE id = ${stageId}`));
1034
+ }
1035
+ });
1036
+ // Only the newest bucket past the lag matters for the lag head.
1037
+ const sweepBuckets = Effect.fnUntraced(function* (now) {
1038
+ const rows = yield* query(sql`
1039
+ SELECT scope, MAX(bucket_hour) AS newest FROM ${bucketsSql}
1040
+ WHERE bucket_hour <= ${lagHour(now, lagMillis)}
1041
+ GROUP BY scope HAVING COUNT(*) > 1`);
1042
+ for (const row of rows) {
1043
+ yield* sql`DELETE FROM ${bucketsSql} WHERE scope = ${String(row.scope)} AND bucket_hour < ${Number(row.newest)}`;
1044
+ }
1045
+ });
1046
+ const logSweepFailure = cause => Effect.logWarning("Compaction sweep failed", cause).pipe(Effect.annotateLogs({
1047
+ service: "SqlEventLogServerEncrypted"
1048
+ }));
1049
+ const sweep = Effect.gen(function* () {
1050
+ const now = yield* Clock.currentTimeMillis;
1051
+ yield* Effect.catchCause(sweepStages(now), logSweepFailure);
1052
+ const due = yield* query(sql`SELECT scope FROM ${storesSql} WHERE next_sweep_at <= ${now}`);
1053
+ for (const row of due) {
1054
+ yield* Effect.catchCause(pruneStore(String(row.scope), now), logSweepFailure);
1055
+ }
1056
+ yield* Effect.catchCause(sweepBuckets(now), logSweepFailure);
1057
+ }).pipe(Effect.catchCause(logSweepFailure), withTracerDisabled);
1058
+ // A fixed schedule starts an overdue sweep straight away, so slow queries
1059
+ // don't push sweeps back.
1060
+ yield* Effect.forkScoped(Effect.delay(Effect.repeat(sweep, Schedule.fixed(config.sweepInterval)), config.sweepInterval));
202
1061
  return EventLogServerEncrypted.Storage.of({
203
1062
  getId: Effect.succeed(remoteId),
204
1063
  // Auto-increment sequences start at 1.
@@ -219,16 +1078,19 @@ export const makeStorage = options => Effect.gen(function* () {
219
1078
  const {
220
1079
  idsTableSql,
221
1080
  pubsub,
1081
+ scope,
222
1082
  semaphore,
223
1083
  tableSql
224
1084
  } = yield* RcMap.get(resources, scopeKey);
1085
+ const now = yield* Clock.currentTimeMillis;
225
1086
  const writeBatches = Effect.gen(function* () {
226
1087
  const stored = [];
1088
+ // The store row lock makes sequence order commit order. The row's
1089
+ // `next_sequence` survives pruning, and `MAX(last_sequence)` covers
1090
+ // instances that predate the row.
1091
+ const store = yield* lockStore(scope);
227
1092
  // Read before inserting ids, which record the sequence they are stored at.
228
- const [last] = yield* sql`
229
- SELECT MAX(last_sequence) AS last_sequence FROM ${tableSql}
230
- `;
231
- let nextSequence = Number(last?.last_sequence ?? 0) + 1;
1093
+ let nextSequence = Math.max(store.nextSequence, (yield* maxLastSequence(tableSql)) + 1);
232
1094
  for (const batch of batches) {
233
1095
  // Inserting the ids decides whether the batch is new. SQL Server's
234
1096
  // MERGE also requires unique source ids.
@@ -243,7 +1105,8 @@ export const makeStorage = options => Effect.gen(function* () {
243
1105
  }
244
1106
  let inserted = 0;
245
1107
  for (const chunk of Arr.chunksOf(ids.values(), 200)) {
246
- inserted += yield* insertNewIds(idsTableSql, chunk);
1108
+ // Stored ids keep their first sequence.
1109
+ inserted += yield* insertNew(idsTableSql, chunk, ["entry_id"]);
247
1110
  }
248
1111
  if (inserted === 0) continue;
249
1112
  const row = {
@@ -260,13 +1123,15 @@ export const makeStorage = options => Effect.gen(function* () {
260
1123
  });
261
1124
  nextSequence += batch.entryIds.length;
262
1125
  }
1126
+ if (stored.length > 0) {
1127
+ yield* sql`UPDATE ${storesSql} SET next_sequence = ${nextSequence} WHERE scope = ${scope}`;
1128
+ yield* upsertBucket(scope, Math.floor(now / hourMillis), nextSequence - 1);
1129
+ // Published after the outermost commit, as `rollback` publishes.
1130
+ yield* sql.afterCommit(Effect.asVoid(PubSub.publishAll(pubsub, stored)));
1131
+ }
263
1132
  return stored;
264
1133
  });
265
- return yield* semaphore.withPermits(1)(Effect.gen(function* () {
266
- const stored = yield* sql.withTransaction(writeBatches);
267
- yield* PubSub.publishAll(pubsub, stored);
268
- return stored;
269
- }));
1134
+ return yield* semaphore.withPermits(1)(sql.withTransaction(writeBatches));
270
1135
  }, Effect.orDie, Effect.scoped, withTracerDisabled),
271
1136
  entrySequences: Effect.fnUntraced(function* (publicKey, storeId, entryIds) {
272
1137
  const {
@@ -284,22 +1149,91 @@ export const makeStorage = options => Effect.gen(function* () {
284
1149
  }
285
1150
  return acks;
286
1151
  }, Effect.orDie, Effect.scoped, withTracerDisabled),
1152
+ // Reads the store row, the active set and the tail as separate
1153
+ // statements. A transaction would take the SQLite write lock on every
1154
+ // stream open, and set rows never change before they are pruned.
287
1155
  changes: Effect.fnUntraced(function* (publicKey, storeId, startSequence) {
288
1156
  const scopeKey = makeEncryptedScopeKey(publicKey, storeId);
289
1157
  const {
290
1158
  pubsub,
1159
+ scope,
1160
+ snapshotTableSql,
291
1161
  tableSql
292
1162
  } = yield* RcMap.get(resources, scopeKey);
293
1163
  const subscription = yield* PubSub.subscribe(pubsub);
1164
+ const live = Stream.fromSubscription(subscription);
1165
+ const store = yield* selectStore(scope);
1166
+ // Below the base an active snapshot replaces the originals.
1167
+ if (store.activeBatchSet !== undefined && startSequence <= store.base) {
1168
+ const set = yield* sql`
1169
+ SELECT sequence, iv, entry_ids, encrypted_batch FROM ${snapshotTableSql}
1170
+ WHERE batch_set = ${store.activeBatchSet} AND last_sequence >= ${startSequence}
1171
+ ORDER BY batch_position ASC
1172
+ `.pipe(Effect.flatMap(decodeBatches));
1173
+ // The final batch ends at the base, so only an empty set reads nothing.
1174
+ if (set.length === 0) {
1175
+ return Stream.die(new Error(`The active compaction snapshot of store ${storeId} has no batches`));
1176
+ }
1177
+ const tail = yield* sql`
1178
+ SELECT sequence, iv, entry_ids, encrypted_batch FROM ${tableSql}
1179
+ WHERE sequence > ${store.base}
1180
+ ORDER BY sequence ASC
1181
+ `.pipe(Effect.flatMap(decodeBatches));
1182
+ return Stream.fromArray([...set, ...tail]).pipe(Stream.concat(live));
1183
+ }
294
1184
  const initial = yield* sql`
295
1185
  SELECT sequence, iv, entry_ids, encrypted_batch FROM ${tableSql}
296
1186
  WHERE last_sequence >= ${startSequence}
297
1187
  ORDER BY sequence ASC
298
1188
  `.pipe(Effect.flatMap(decodeBatches));
299
- return Stream.fromArray(initial).pipe(Stream.concat(Stream.fromSubscription(subscription)));
300
- }, Effect.orDie, withTracerDisabled, Stream.unwrap)
1189
+ return Stream.fromArray(initial).pipe(Stream.concat(live));
1190
+ }, Effect.orDie, withTracerDisabled, Stream.unwrap),
1191
+ compaction
301
1192
  });
302
1193
  }).pipe(withTracerDisabled);
1194
+ // Rows per statement when streaming compaction input or staged batches.
1195
+ const pageSize = 500;
1196
+ // Sequences of originals deleted per prune chunk.
1197
+ const pruneChunkSequences = 10_000;
1198
+ // Snapshot or staged rows deleted per prune chunk.
1199
+ const pruneChunkRows = 200;
1200
+ // SQL failures are defects for the compaction primitives.
1201
+ const dieOnSqlError = effect => Effect.catchIf(effect, error => SqlError.isSqlError(error), error => Effect.die(error));
1202
+ const optionalNumber = value => value === null || value === undefined ? undefined : Number(value);
1203
+ const optionalString = value => value === null || value === undefined ? undefined : String(value);
1204
+ const toStoreRow = row => ({
1205
+ nextSequence: Number(row.next_sequence),
1206
+ activeActivation: String(row.active_activation),
1207
+ activeBatchSet: optionalString(row.active_batch_set),
1208
+ base: Number(row.base_sequence),
1209
+ activeKeptCount: Number(row.active_kept_count),
1210
+ noOpAt: optionalNumber(row.no_op_at),
1211
+ noOpCount: Number(row.no_op_count),
1212
+ openStage: optionalString(row.open_stage),
1213
+ nextSweepAt: optionalNumber(row.next_sweep_at),
1214
+ sweepLeaseUntil: optionalNumber(row.sweep_lease_until),
1215
+ sweepLeaseOwner: optionalString(row.sweep_lease_owner),
1216
+ prunedThrough: Number(row.pruned_through)
1217
+ });
1218
+ const toStageRow = row => ({
1219
+ id: String(row.id),
1220
+ scope: String(row.scope),
1221
+ state: String(row.state),
1222
+ pinned: String(row.pinned),
1223
+ through: Number(row.through_sequence),
1224
+ expiresAt: Number(row.expires_at)
1225
+ });
1226
+ const toActivationRow = row => ({
1227
+ id: String(row.id),
1228
+ ordinal: Number(row.ordinal),
1229
+ batchSet: optionalString(row.batch_set),
1230
+ base: Number(row.base_sequence),
1231
+ keptCount: optionalNumber(row.kept_count),
1232
+ previous: optionalString(row.previous),
1233
+ supersededAt: optionalNumber(row.superseded_at),
1234
+ prunableAt: optionalNumber(row.prunable_at),
1235
+ pruneStartedAt: optionalNumber(row.prune_started_at)
1236
+ });
303
1237
  const decodeIdRows = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Array(/*#__PURE__*/Schema.Struct({
304
1238
  entry_id: Schema.Uint8Array,
305
1239
  sequence: Schema.Number
@@ -309,6 +1243,13 @@ const concatEntryIds = entryIds => {
309
1243
  for (let i = 0; i < entryIds.length; i++) data.set(entryIds[i], i * 16);
310
1244
  return data;
311
1245
  };
1246
+ const toEntryIds = data => {
1247
+ const entryIds = [];
1248
+ for (let offset = 0; offset < data.byteLength; offset += 16) {
1249
+ entryIds.push(data.subarray(offset, offset + 16));
1250
+ }
1251
+ return entryIds;
1252
+ };
312
1253
  const EncryptedRemoteBatchSql = /*#__PURE__*/Schema.Struct({
313
1254
  sequence: Schema.Natural,
314
1255
  iv: Schema.Uint8Array,
@@ -321,18 +1262,12 @@ const SessionAuthBindingSql = /*#__PURE__*/Schema.Struct({
321
1262
  });
322
1263
  const decodeBatchRows = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Array(EncryptedRemoteBatchSql));
323
1264
  const decodeSessionAuthBindingRows = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Array(SessionAuthBindingSql));
324
- const toEncryptedRemoteBatch = row => {
325
- const entryIds = [];
326
- for (let offset = 0; offset < row.entry_ids.byteLength; offset += 16) {
327
- entryIds.push(row.entry_ids.subarray(offset, offset + 16));
328
- }
329
- return {
330
- sequence: row.sequence,
331
- entryIds: entryIds,
332
- iv: row.iv,
333
- encryptedBatch: row.encrypted_batch
334
- };
335
- };
1265
+ const toEncryptedRemoteBatch = row => ({
1266
+ sequence: row.sequence,
1267
+ entryIds: toEntryIds(row.entry_ids),
1268
+ iv: row.iv,
1269
+ encryptedBatch: row.encrypted_batch
1270
+ });
336
1271
  const decodeBatches = rows => decodeBatchRows(rows).pipe(Effect.map(batches => batches.map(toEncryptedRemoteBatch)));
337
1272
  const decodeSessionAuthBindings = rows => decodeSessionAuthBindingRows(rows);
338
1273
  /**
@@ -345,13 +1280,13 @@ const decodeSessionAuthBindings = rows => decodeSessionAuthBindingRows(rows);
345
1280
  export const layerStorage = options => Layer.effect(EventLogServerEncrypted.Storage)(makeStorage(options));
346
1281
  /**
347
1282
  * Provides SQL-backed encrypted server `Storage` and supplies the default Web
348
- * Crypto `EventLogEncryption` layer.
1283
+ * Crypto `Digest` layer.
349
1284
  *
350
1285
  * @stability unstable
351
1286
  * @category layers
352
1287
  * @since 4.0.0
353
1288
  */
354
- export const layerStorageSubtle = options => layerStorage(options).pipe(Layer.provide(EventLogEncryption.layerSubtle));
1289
+ export const layerStorageSubtle = options => layerStorage(options).pipe(Layer.provide(Digest.layerWebCrypto));
355
1290
  const makeEncryptedScopeKey = (publicKey, storeId) => `${publicKey}/${storeId}`;
356
1291
  const withTracerDisabled = /*#__PURE__*/Effect.withTracerEnabled(false);
357
1292
  //# sourceMappingURL=SqlEventLogServerEncrypted.js.map