effect 4.0.6 → 4.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/dist/Arbitrary.d.ts +10 -2
  2. package/dist/Arbitrary.d.ts.map +1 -1
  3. package/dist/Arbitrary.js +2 -1
  4. package/dist/Arbitrary.js.map +1 -1
  5. package/dist/Channel.d.ts.map +1 -1
  6. package/dist/Channel.js +21 -6
  7. package/dist/Channel.js.map +1 -1
  8. package/dist/Data.d.ts +54 -235
  9. package/dist/Data.d.ts.map +1 -1
  10. package/dist/Data.js.map +1 -1
  11. package/dist/Effect.d.ts +1 -1
  12. package/dist/Effect.js +1 -1
  13. package/dist/Equal.d.ts.map +1 -1
  14. package/dist/Equal.js +8 -1
  15. package/dist/Equal.js.map +1 -1
  16. package/dist/Exit.d.ts +3 -3
  17. package/dist/Exit.d.ts.map +1 -1
  18. package/dist/Exit.js.map +1 -1
  19. package/dist/Formatter.d.ts.map +1 -1
  20. package/dist/Formatter.js +27 -13
  21. package/dist/Formatter.js.map +1 -1
  22. package/dist/Hash.d.ts.map +1 -1
  23. package/dist/Hash.js +4 -1
  24. package/dist/Hash.js.map +1 -1
  25. package/dist/JsonPatch.js +4 -0
  26. package/dist/JsonPatch.js.map +1 -1
  27. package/dist/JsonSchema.d.ts.map +1 -1
  28. package/dist/JsonSchema.js +13 -40
  29. package/dist/JsonSchema.js.map +1 -1
  30. package/dist/Metric.d.ts +3 -3
  31. package/dist/MutableList.d.ts +5 -2
  32. package/dist/MutableList.d.ts.map +1 -1
  33. package/dist/MutableList.js +7 -5
  34. package/dist/MutableList.js.map +1 -1
  35. package/dist/MutableRef.d.ts.map +1 -1
  36. package/dist/MutableRef.js +7 -0
  37. package/dist/MutableRef.js.map +1 -1
  38. package/dist/PartitionedSemaphore.d.ts +2 -1
  39. package/dist/PartitionedSemaphore.d.ts.map +1 -1
  40. package/dist/PartitionedSemaphore.js +22 -12
  41. package/dist/PartitionedSemaphore.js.map +1 -1
  42. package/dist/Pool.d.ts.map +1 -1
  43. package/dist/Pool.js +77 -27
  44. package/dist/Pool.js.map +1 -1
  45. package/dist/PubSub.d.ts.map +1 -1
  46. package/dist/PubSub.js +5 -6
  47. package/dist/PubSub.js.map +1 -1
  48. package/dist/Ref.d.ts.map +1 -1
  49. package/dist/Ref.js +8 -0
  50. package/dist/Ref.js.map +1 -1
  51. package/dist/Schedule.js +1 -1
  52. package/dist/Schedule.js.map +1 -1
  53. package/dist/Schema.d.ts +24 -8
  54. package/dist/Schema.d.ts.map +1 -1
  55. package/dist/Schema.js +37 -28
  56. package/dist/Schema.js.map +1 -1
  57. package/dist/SchemaAST.d.ts +4 -2
  58. package/dist/SchemaAST.d.ts.map +1 -1
  59. package/dist/SchemaAST.js +14 -17
  60. package/dist/SchemaAST.js.map +1 -1
  61. package/dist/SchemaParser.d.ts.map +1 -1
  62. package/dist/SchemaParser.js +48 -22
  63. package/dist/SchemaParser.js.map +1 -1
  64. package/dist/SchemaRepresentation.d.ts +19 -10
  65. package/dist/SchemaRepresentation.d.ts.map +1 -1
  66. package/dist/SchemaRepresentation.js +23 -13
  67. package/dist/SchemaRepresentation.js.map +1 -1
  68. package/dist/SchemaTransformation.d.ts +4 -1
  69. package/dist/SchemaTransformation.d.ts.map +1 -1
  70. package/dist/SchemaTransformation.js +9 -2
  71. package/dist/SchemaTransformation.js.map +1 -1
  72. package/dist/Semaphore.d.ts.map +1 -1
  73. package/dist/Semaphore.js +54 -35
  74. package/dist/Semaphore.js.map +1 -1
  75. package/dist/Sink.d.ts.map +1 -1
  76. package/dist/Sink.js +7 -2
  77. package/dist/Sink.js.map +1 -1
  78. package/dist/Stream.d.ts.map +1 -1
  79. package/dist/Stream.js +22 -10
  80. package/dist/Stream.js.map +1 -1
  81. package/dist/String.d.ts +3 -0
  82. package/dist/String.d.ts.map +1 -1
  83. package/dist/String.js +3 -0
  84. package/dist/String.js.map +1 -1
  85. package/dist/Tracer.d.ts +11 -1
  86. package/dist/Tracer.d.ts.map +1 -1
  87. package/dist/Tracer.js +6 -1
  88. package/dist/Tracer.js.map +1 -1
  89. package/dist/ai/LanguageModel.d.ts.map +1 -1
  90. package/dist/ai/LanguageModel.js +9 -0
  91. package/dist/ai/LanguageModel.js.map +1 -1
  92. package/dist/ai/McpSchema.d.ts +110 -110
  93. package/dist/ai/McpSchema.d.ts.map +1 -1
  94. package/dist/ai/McpServer.d.ts.map +1 -1
  95. package/dist/ai/McpServer.js +21 -15
  96. package/dist/ai/McpServer.js.map +1 -1
  97. package/dist/ai/Prompt.d.ts +52 -0
  98. package/dist/ai/Prompt.d.ts.map +1 -1
  99. package/dist/ai/Prompt.js +38 -0
  100. package/dist/ai/Prompt.js.map +1 -1
  101. package/dist/ai/Response.d.ts +16 -3
  102. package/dist/ai/Response.d.ts.map +1 -1
  103. package/dist/ai/Response.js +4 -0
  104. package/dist/ai/Response.js.map +1 -1
  105. package/dist/ai/Tool.d.ts +24 -0
  106. package/dist/ai/Tool.d.ts.map +1 -1
  107. package/dist/ai/Tool.js +5 -2
  108. package/dist/ai/Tool.js.map +1 -1
  109. package/dist/ai/Toolkit.d.ts.map +1 -1
  110. package/dist/ai/Toolkit.js +8 -1
  111. package/dist/ai/Toolkit.js.map +1 -1
  112. package/dist/cli/Argument.d.ts +15 -0
  113. package/dist/cli/Argument.d.ts.map +1 -1
  114. package/dist/cli/Argument.js +5 -0
  115. package/dist/cli/Argument.js.map +1 -1
  116. package/dist/cli/CliError.d.ts +78 -0
  117. package/dist/cli/CliError.d.ts.map +1 -1
  118. package/dist/cli/CliError.js +65 -3
  119. package/dist/cli/CliError.js.map +1 -1
  120. package/dist/cli/CliOutput.d.ts +155 -6
  121. package/dist/cli/CliOutput.d.ts.map +1 -1
  122. package/dist/cli/CliOutput.js +62 -6
  123. package/dist/cli/CliOutput.js.map +1 -1
  124. package/dist/cli/Command.d.ts +24 -16
  125. package/dist/cli/Command.d.ts.map +1 -1
  126. package/dist/cli/Command.js +59 -29
  127. package/dist/cli/Command.js.map +1 -1
  128. package/dist/cli/Completions.d.ts +58 -0
  129. package/dist/cli/Completions.d.ts.map +1 -1
  130. package/dist/cli/Completions.js +49 -9
  131. package/dist/cli/Completions.js.map +1 -1
  132. package/dist/cli/Flag.d.ts +15 -0
  133. package/dist/cli/Flag.d.ts.map +1 -1
  134. package/dist/cli/Flag.js +5 -0
  135. package/dist/cli/Flag.js.map +1 -1
  136. package/dist/cli/GlobalFlag.d.ts.map +1 -1
  137. package/dist/cli/GlobalFlag.js +15 -7
  138. package/dist/cli/GlobalFlag.js.map +1 -1
  139. package/dist/cli/Param.d.ts +9 -0
  140. package/dist/cli/Param.d.ts.map +1 -1
  141. package/dist/cli/Param.js +21 -2
  142. package/dist/cli/Param.js.map +1 -1
  143. package/dist/cli/TerminalCapabilities.d.ts +3 -2
  144. package/dist/cli/TerminalCapabilities.d.ts.map +1 -1
  145. package/dist/cli/TerminalCapabilities.js +3 -1
  146. package/dist/cli/TerminalCapabilities.js.map +1 -1
  147. package/dist/cli/internal/completions/bash.js +73 -27
  148. package/dist/cli/internal/completions/bash.js.map +1 -1
  149. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  150. package/dist/cluster/ClusterWorkflowEngine.js +45 -10
  151. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  152. package/dist/cluster/MessageStorage.d.ts +23 -45
  153. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  154. package/dist/cluster/MessageStorage.js +6 -7
  155. package/dist/cluster/MessageStorage.js.map +1 -1
  156. package/dist/cluster/Sharding.d.ts.map +1 -1
  157. package/dist/cluster/Sharding.js +61 -21
  158. package/dist/cluster/Sharding.js.map +1 -1
  159. package/dist/cluster/ShardingConfig.d.ts +10 -0
  160. package/dist/cluster/ShardingConfig.d.ts.map +1 -1
  161. package/dist/cluster/ShardingConfig.js.map +1 -1
  162. package/dist/cluster/Snowflake.d.ts.map +1 -1
  163. package/dist/cluster/Snowflake.js +3 -7
  164. package/dist/cluster/Snowflake.js.map +1 -1
  165. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  166. package/dist/cluster/SqlRunnerStorage.js +87 -39
  167. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  168. package/dist/cluster/internal/entityManager.js +54 -20
  169. package/dist/cluster/internal/entityManager.js.map +1 -1
  170. package/dist/devtools/DevToolsClient.d.ts.map +1 -1
  171. package/dist/devtools/DevToolsClient.js +2 -0
  172. package/dist/devtools/DevToolsClient.js.map +1 -1
  173. package/dist/encoding/Base64.js +36 -0
  174. package/dist/encoding/Base64.js.map +1 -1
  175. package/dist/encoding/Sse.d.ts.map +1 -1
  176. package/dist/encoding/Sse.js +3 -3
  177. package/dist/encoding/Sse.js.map +1 -1
  178. package/dist/encoding/Toml.d.ts.map +1 -1
  179. package/dist/encoding/Toml.js +21 -16
  180. package/dist/encoding/Toml.js.map +1 -1
  181. package/dist/eventlog/SqlEventJournal.d.ts.map +1 -1
  182. package/dist/eventlog/SqlEventJournal.js +47 -27
  183. package/dist/eventlog/SqlEventJournal.js.map +1 -1
  184. package/dist/eventlog/internal/conflicts.d.ts +0 -1
  185. package/dist/eventlog/internal/conflicts.d.ts.map +1 -1
  186. package/dist/eventlog/internal/conflicts.js +0 -3
  187. package/dist/eventlog/internal/conflicts.js.map +1 -1
  188. package/dist/http/Cookies.d.ts.map +1 -1
  189. package/dist/http/Cookies.js +10 -9
  190. package/dist/http/Cookies.js.map +1 -1
  191. package/dist/http/HttpClient.d.ts +423 -0
  192. package/dist/http/HttpClient.d.ts.map +1 -1
  193. package/dist/http/HttpClient.js +231 -7
  194. package/dist/http/HttpClient.js.map +1 -1
  195. package/dist/http/HttpClientError.d.ts +8 -1
  196. package/dist/http/HttpClientError.d.ts.map +1 -1
  197. package/dist/http/HttpClientError.js +37 -1
  198. package/dist/http/HttpClientError.js.map +1 -1
  199. package/dist/http/HttpClientRequest.d.ts.map +1 -1
  200. package/dist/http/HttpClientRequest.js +1 -1
  201. package/dist/http/HttpClientRequest.js.map +1 -1
  202. package/dist/http/HttpMiddleware.d.ts +1 -1
  203. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  204. package/dist/http/HttpMiddleware.js +14 -16
  205. package/dist/http/HttpMiddleware.js.map +1 -1
  206. package/dist/http/HttpRouter.d.ts.map +1 -1
  207. package/dist/http/HttpRouter.js +2 -1
  208. package/dist/http/HttpRouter.js.map +1 -1
  209. package/dist/http/HttpServerError.js +2 -2
  210. package/dist/http/HttpServerError.js.map +1 -1
  211. package/dist/http/HttpServerRequest.d.ts +6 -1
  212. package/dist/http/HttpServerRequest.d.ts.map +1 -1
  213. package/dist/http/HttpServerRequest.js +73 -10
  214. package/dist/http/HttpServerRequest.js.map +1 -1
  215. package/dist/http/HttpStaticServer.d.ts.map +1 -1
  216. package/dist/http/HttpStaticServer.js +48 -21
  217. package/dist/http/HttpStaticServer.js.map +1 -1
  218. package/dist/http/MultipartParser/internal/contentType.js +3 -3
  219. package/dist/http/UrlParams.d.ts +3 -1
  220. package/dist/http/UrlParams.d.ts.map +1 -1
  221. package/dist/http/UrlParams.js +4 -2
  222. package/dist/http/UrlParams.js.map +1 -1
  223. package/dist/http/internal/compression.js +4 -5
  224. package/dist/http/internal/compression.js.map +1 -1
  225. package/dist/http/internal/decompression.js +3 -0
  226. package/dist/http/internal/decompression.js.map +1 -1
  227. package/dist/http/internal/httpBody.js +8 -1
  228. package/dist/http/internal/httpBody.js.map +1 -1
  229. package/dist/http/internal/urlParams.js +25 -0
  230. package/dist/http/internal/urlParams.js.map +1 -1
  231. package/dist/http-api/HttpApi.d.ts +3 -6
  232. package/dist/http-api/HttpApi.d.ts.map +1 -1
  233. package/dist/http-api/HttpApi.js +3 -6
  234. package/dist/http-api/HttpApi.js.map +1 -1
  235. package/dist/http-api/HttpApiBuilder.d.ts.map +1 -1
  236. package/dist/http-api/HttpApiBuilder.js +27 -11
  237. package/dist/http-api/HttpApiBuilder.js.map +1 -1
  238. package/dist/http-api/HttpApiClient.d.ts +77 -7
  239. package/dist/http-api/HttpApiClient.d.ts.map +1 -1
  240. package/dist/http-api/HttpApiClient.js +142 -44
  241. package/dist/http-api/HttpApiClient.js.map +1 -1
  242. package/dist/http-api/HttpApiEndpoint.d.ts +24 -7
  243. package/dist/http-api/HttpApiEndpoint.d.ts.map +1 -1
  244. package/dist/http-api/HttpApiEndpoint.js +109 -27
  245. package/dist/http-api/HttpApiEndpoint.js.map +1 -1
  246. package/dist/http-api/HttpApiSchema.d.ts +76 -29
  247. package/dist/http-api/HttpApiSchema.d.ts.map +1 -1
  248. package/dist/http-api/HttpApiSchema.js +70 -59
  249. package/dist/http-api/HttpApiSchema.js.map +1 -1
  250. package/dist/http-api/HttpApiTest.d.ts.map +1 -1
  251. package/dist/http-api/HttpApiTest.js +3 -2
  252. package/dist/http-api/HttpApiTest.js.map +1 -1
  253. package/dist/http-api/OpenApi.d.ts.map +1 -1
  254. package/dist/http-api/OpenApi.js +25 -16
  255. package/dist/http-api/OpenApi.js.map +1 -1
  256. package/dist/http-api/internal/toCodecHttpProperties.d.ts +2 -0
  257. package/dist/http-api/internal/toCodecHttpProperties.d.ts.map +1 -0
  258. package/dist/http-api/internal/toCodecHttpProperties.js +38 -0
  259. package/dist/http-api/internal/toCodecHttpProperties.js.map +1 -0
  260. package/dist/internal/arbitrary/runner.d.ts.map +1 -1
  261. package/dist/internal/arbitrary/runner.js +120 -44
  262. package/dist/internal/arbitrary/runner.js.map +1 -1
  263. package/dist/internal/core.js +2 -0
  264. package/dist/internal/core.js.map +1 -1
  265. package/dist/internal/effect.js +21 -13
  266. package/dist/internal/effect.js.map +1 -1
  267. package/dist/internal/equal.js +14 -0
  268. package/dist/internal/equal.js.map +1 -1
  269. package/dist/internal/jsonSchema.d.ts +2 -0
  270. package/dist/internal/jsonSchema.d.ts.map +1 -0
  271. package/dist/internal/jsonSchema.js +28 -0
  272. package/dist/internal/jsonSchema.js.map +1 -0
  273. package/dist/internal/matcher.js +1 -1
  274. package/dist/internal/matcher.js.map +1 -1
  275. package/dist/internal/schema/compilerRegistry.js +4 -1
  276. package/dist/internal/schema/compilerRegistry.js.map +1 -1
  277. package/dist/internal/schema/content.d.ts +2 -0
  278. package/dist/internal/schema/content.d.ts.map +1 -0
  279. package/dist/internal/schema/content.js +13 -0
  280. package/dist/internal/schema/content.js.map +1 -0
  281. package/dist/internal/schema/fromJsonSchemaDocument.js +173 -185
  282. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  283. package/dist/internal/schema/fromRepresentation.js +2 -1
  284. package/dist/internal/schema/fromRepresentation.js.map +1 -1
  285. package/dist/internal/schema/standardSchema.js +8 -2
  286. package/dist/internal/schema/standardSchema.js.map +1 -1
  287. package/dist/internal/schema/toCodeDocument.js +3 -3
  288. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  289. package/dist/internal/schema/toCodec.js +21 -24
  290. package/dist/internal/schema/toCodec.js.map +1 -1
  291. package/dist/internal/schema/toJsonSchemaDocument.js +205 -140
  292. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  293. package/dist/internal/schema/toRepresentation.js +107 -129
  294. package/dist/internal/schema/toRepresentation.js.map +1 -1
  295. package/dist/internal/sql.d.ts +2 -0
  296. package/dist/internal/sql.d.ts.map +1 -0
  297. package/dist/internal/sql.js +13 -0
  298. package/dist/internal/sql.js.map +1 -0
  299. package/dist/internal/version.js +1 -1
  300. package/dist/observability/OtlpProxy.d.ts +154 -0
  301. package/dist/observability/OtlpProxy.d.ts.map +1 -0
  302. package/dist/observability/OtlpProxy.js +235 -0
  303. package/dist/observability/OtlpProxy.js.map +1 -0
  304. package/dist/observability/OtlpTracer.d.ts.map +1 -1
  305. package/dist/observability/OtlpTracer.js +4 -0
  306. package/dist/observability/OtlpTracer.js.map +1 -1
  307. package/dist/observability/index.d.ts +5 -0
  308. package/dist/observability/index.d.ts.map +1 -1
  309. package/dist/observability/index.js +5 -0
  310. package/dist/observability/index.js.map +1 -1
  311. package/dist/persistence/CircuitBreaker.d.ts +897 -0
  312. package/dist/persistence/CircuitBreaker.d.ts.map +1 -0
  313. package/dist/persistence/CircuitBreaker.js +1165 -0
  314. package/dist/persistence/CircuitBreaker.js.map +1 -0
  315. package/dist/persistence/Redis.d.ts +78 -2
  316. package/dist/persistence/Redis.d.ts.map +1 -1
  317. package/dist/persistence/Redis.js +62 -7
  318. package/dist/persistence/Redis.js.map +1 -1
  319. package/dist/persistence/index.d.ts +5 -0
  320. package/dist/persistence/index.d.ts.map +1 -1
  321. package/dist/persistence/index.js +5 -0
  322. package/dist/persistence/index.js.map +1 -1
  323. package/dist/process/ChildProcess.d.ts +30 -4
  324. package/dist/process/ChildProcess.d.ts.map +1 -1
  325. package/dist/process/ChildProcess.js.map +1 -1
  326. package/dist/process/ChildProcessSpawner.d.ts +30 -0
  327. package/dist/process/ChildProcessSpawner.d.ts.map +1 -1
  328. package/dist/process/ChildProcessSpawner.js +5 -2
  329. package/dist/process/ChildProcessSpawner.js.map +1 -1
  330. package/dist/reactivity/AsyncResult.d.ts +1 -1
  331. package/dist/reactivity/AsyncResult.d.ts.map +1 -1
  332. package/dist/rpc/RpcClient.d.ts.map +1 -1
  333. package/dist/rpc/RpcClient.js +9 -8
  334. package/dist/rpc/RpcClient.js.map +1 -1
  335. package/dist/rpc/RpcSerialization.d.ts +14 -0
  336. package/dist/rpc/RpcSerialization.d.ts.map +1 -1
  337. package/dist/rpc/RpcSerialization.js +20 -8
  338. package/dist/rpc/RpcSerialization.js.map +1 -1
  339. package/dist/rpc/RpcServer.d.ts.map +1 -1
  340. package/dist/rpc/RpcServer.js +11 -1
  341. package/dist/rpc/RpcServer.js.map +1 -1
  342. package/dist/rpc/RpcTest.d.ts +1 -0
  343. package/dist/rpc/RpcTest.d.ts.map +1 -1
  344. package/dist/rpc/RpcTest.js +1 -0
  345. package/dist/rpc/RpcTest.js.map +1 -1
  346. package/dist/sql/Migrator.d.ts +2 -1
  347. package/dist/sql/Migrator.d.ts.map +1 -1
  348. package/dist/sql/Migrator.js +11 -8
  349. package/dist/sql/Migrator.js.map +1 -1
  350. package/dist/sql/SqlClient.d.ts +9 -4
  351. package/dist/sql/SqlClient.d.ts.map +1 -1
  352. package/dist/sql/SqlClient.js +8 -1
  353. package/dist/sql/SqlClient.js.map +1 -1
  354. package/dist/sql/SqlResolver.d.ts +4 -0
  355. package/dist/sql/SqlResolver.d.ts.map +1 -1
  356. package/dist/sql/SqlResolver.js +18 -4
  357. package/dist/sql/SqlResolver.js.map +1 -1
  358. package/dist/sql/Statement.d.ts +7 -0
  359. package/dist/sql/Statement.d.ts.map +1 -1
  360. package/dist/sql/Statement.js +21 -2
  361. package/dist/sql/Statement.js.map +1 -1
  362. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  363. package/dist/workflow/DurableDeferred.js +21 -2
  364. package/dist/workflow/DurableDeferred.js.map +1 -1
  365. package/package.json +1 -1
  366. package/src/Arbitrary.ts +10 -2
  367. package/src/Channel.ts +47 -18
  368. package/src/Data.ts +60 -265
  369. package/src/Effect.ts +1 -1
  370. package/src/Equal.ts +8 -1
  371. package/src/Exit.ts +3 -3
  372. package/src/Formatter.ts +28 -13
  373. package/src/Hash.ts +4 -1
  374. package/src/JsonPatch.ts +4 -0
  375. package/src/JsonSchema.ts +12 -40
  376. package/src/Metric.ts +3 -3
  377. package/src/MutableList.ts +7 -5
  378. package/src/MutableRef.ts +8 -1
  379. package/src/PartitionedSemaphore.ts +24 -13
  380. package/src/Pool.ts +109 -37
  381. package/src/PubSub.ts +5 -6
  382. package/src/Ref.ts +8 -0
  383. package/src/Schedule.ts +1 -1
  384. package/src/Schema.ts +48 -25
  385. package/src/SchemaAST.ts +20 -20
  386. package/src/SchemaParser.ts +51 -18
  387. package/src/SchemaRepresentation.ts +31 -34
  388. package/src/SchemaTransformation.ts +9 -2
  389. package/src/Semaphore.ts +67 -40
  390. package/src/Sink.ts +15 -2
  391. package/src/Stream.ts +33 -16
  392. package/src/String.ts +3 -0
  393. package/src/Tracer.ts +15 -1
  394. package/src/ai/LanguageModel.ts +9 -0
  395. package/src/ai/McpServer.ts +14 -13
  396. package/src/ai/Prompt.ts +65 -0
  397. package/src/ai/Response.ts +34 -9
  398. package/src/ai/Tool.ts +38 -2
  399. package/src/ai/Toolkit.ts +4 -1
  400. package/src/cli/Argument.ts +15 -0
  401. package/src/cli/CliError.ts +90 -3
  402. package/src/cli/CliOutput.ts +183 -6
  403. package/src/cli/Command.ts +53 -41
  404. package/src/cli/Completions.ts +51 -0
  405. package/src/cli/Flag.ts +15 -0
  406. package/src/cli/GlobalFlag.ts +6 -7
  407. package/src/cli/Param.ts +29 -6
  408. package/src/cli/TerminalCapabilities.ts +6 -3
  409. package/src/cli/internal/completions/bash.ts +79 -29
  410. package/src/cluster/ClusterWorkflowEngine.ts +46 -16
  411. package/src/cluster/MessageStorage.ts +19 -14
  412. package/src/cluster/Sharding.ts +83 -21
  413. package/src/cluster/ShardingConfig.ts +5 -0
  414. package/src/cluster/Snowflake.ts +3 -7
  415. package/src/cluster/SqlRunnerStorage.ts +110 -47
  416. package/src/cluster/internal/entityManager.ts +75 -22
  417. package/src/devtools/DevToolsClient.ts +2 -0
  418. package/src/encoding/Base64.ts +38 -0
  419. package/src/encoding/Sse.ts +4 -3
  420. package/src/encoding/Toml.ts +33 -16
  421. package/src/eventlog/SqlEventJournal.ts +68 -45
  422. package/src/eventlog/internal/conflicts.ts +0 -4
  423. package/src/http/Cookies.ts +10 -11
  424. package/src/http/HttpClient.ts +547 -7
  425. package/src/http/HttpClientError.ts +35 -1
  426. package/src/http/HttpClientRequest.ts +9 -1
  427. package/src/http/HttpMiddleware.ts +15 -18
  428. package/src/http/HttpRouter.ts +2 -1
  429. package/src/http/HttpServerError.ts +2 -2
  430. package/src/http/HttpServerRequest.ts +102 -16
  431. package/src/http/HttpStaticServer.ts +51 -22
  432. package/src/http/MultipartParser/internal/contentType.ts +3 -3
  433. package/src/http/UrlParams.ts +6 -1
  434. package/src/http/internal/compression.ts +4 -5
  435. package/src/http/internal/decompression.ts +6 -0
  436. package/src/http/internal/httpBody.ts +10 -2
  437. package/src/http/internal/urlParams.ts +29 -0
  438. package/src/http-api/HttpApi.ts +3 -6
  439. package/src/http-api/HttpApiBuilder.ts +45 -15
  440. package/src/http-api/HttpApiClient.ts +218 -72
  441. package/src/http-api/HttpApiEndpoint.ts +121 -30
  442. package/src/http-api/HttpApiSchema.ts +141 -48
  443. package/src/http-api/HttpApiTest.ts +3 -2
  444. package/src/http-api/OpenApi.ts +20 -17
  445. package/src/http-api/internal/toCodecHttpProperties.ts +56 -0
  446. package/src/internal/arbitrary/runner.ts +133 -36
  447. package/src/internal/core.ts +2 -0
  448. package/src/internal/effect.ts +24 -16
  449. package/src/internal/equal.ts +17 -0
  450. package/src/internal/jsonSchema.ts +27 -0
  451. package/src/internal/matcher.ts +1 -1
  452. package/src/internal/schema/compilerRegistry.ts +6 -1
  453. package/src/internal/schema/content.ts +18 -0
  454. package/src/internal/schema/fromJsonSchemaDocument.ts +228 -190
  455. package/src/internal/schema/fromRepresentation.ts +2 -1
  456. package/src/internal/schema/standardSchema.ts +2 -2
  457. package/src/internal/schema/toCodeDocument.ts +4 -4
  458. package/src/internal/schema/toCodec.ts +26 -22
  459. package/src/internal/schema/toJsonSchemaDocument.ts +189 -167
  460. package/src/internal/schema/toRepresentation.ts +122 -140
  461. package/src/internal/sql.ts +14 -0
  462. package/src/internal/version.ts +1 -1
  463. package/src/observability/OtlpProxy.ts +314 -0
  464. package/src/observability/OtlpTracer.ts +4 -0
  465. package/src/observability/index.ts +6 -0
  466. package/src/persistence/CircuitBreaker.ts +1678 -0
  467. package/src/persistence/Redis.ts +129 -8
  468. package/src/persistence/index.ts +6 -0
  469. package/src/process/ChildProcess.ts +30 -4
  470. package/src/process/ChildProcessSpawner.ts +41 -2
  471. package/src/reactivity/AsyncResult.ts +1 -1
  472. package/src/rpc/RpcClient.ts +13 -10
  473. package/src/rpc/RpcSerialization.ts +32 -9
  474. package/src/rpc/RpcServer.ts +16 -1
  475. package/src/rpc/RpcTest.ts +3 -0
  476. package/src/sql/Migrator.ts +12 -7
  477. package/src/sql/SqlClient.ts +11 -4
  478. package/src/sql/SqlResolver.ts +23 -9
  479. package/src/sql/Statement.ts +30 -2
  480. package/src/workflow/DurableDeferred.ts +23 -6
@@ -38,6 +38,14 @@ import * as ShardingConfig from "./ShardingConfig.ts"
38
38
 
39
39
  const withTracerDisabled = Effect.withTracerEnabled(false)
40
40
 
41
+ // The session holding a storage's advisory locks, as reported to other
42
+ // sessions.
43
+ interface LockSession {
44
+ pid: number
45
+ // MySQL only: the lock that pins the session's lock connection
46
+ readonly pinLock: string | undefined
47
+ }
48
+
41
49
  // This exact FNV-1a hash, including its tag and UTF-8 encoding, is a persistent
42
50
  // advisory-lock wire format and must never change.
43
51
  const postgresLockNamespace = (prefix: string): number => {
@@ -69,6 +77,28 @@ const makeStorage = Effect.fnUntraced(function*(options: {
69
77
  // PostgreSQL exposes the signed int4 lock key through pg_locks as an unsigned oid.
70
78
  const pgLockNamespaceOid = pgLockNamespace >>> 0
71
79
 
80
+ // MySQL identifies a lock holder by connection id. Behind Vitess that is the
81
+ // id of vtgate's lock connection for the session, which vtgate replaces
82
+ // once the session releases all of its locks. A lock of the session's own
83
+ // pins that connection and reads its id.
84
+ const pinLockSession = Effect.fnUntraced(function*(conn: Connection, pinLock?: string) {
85
+ while (true) {
86
+ const name = pinLock ?? `cluster:pid:${(Math.random() * Number.MAX_SAFE_INTEGER) | 0}`
87
+ const taken = (yield* conn.executeValues(
88
+ `SELECT GET_LOCK('${name}', 10), IS_USED_LOCK('${name}')`,
89
+ []
90
+ ))[0] as [1 | null, number]
91
+ if (taken[0] === null) continue
92
+ return { pid: Number(taken[1]), pinLock: name }
93
+ }
94
+ })
95
+
96
+ // In milliseconds, capped to the largest value PostgreSQL accepts.
97
+ const idleSessionTimeout = Math.min(
98
+ Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(config.shardLockExpiration))),
99
+ 2147483647
100
+ )
101
+
72
102
  // Keep all PostgreSQL and MySQL shard-lock operations on a rebuildable
73
103
  // reserved connection, including when advisory locks are disabled.
74
104
  const acquireLockConn = singleSession ? undefined : sql.onDialectOrElse({
@@ -77,9 +107,18 @@ const makeStorage = Effect.fnUntraced(function*(options: {
77
107
  const conn = yield* Effect.orDie(sql.reserve).pipe(
78
108
  Scope.provide(scope)
79
109
  )
110
+ // Advisory locks belong to the session, so a frozen runner whose
111
+ // connection stays open would hold them indefinitely. End the session
112
+ // once it has been idle for shardLockExpiration, which releases them.
113
+ // Runners holding shards use it at least every shardLockExpiration / 3.
114
+ // Servers without idle_session_timeout (before PostgreSQL 14) keep the
115
+ // session open.
80
116
  const pid = disableAdvisoryLocks
81
117
  ? 0
82
- : (yield* conn.executeValues("SELECT pg_backend_pid()", []))[0][0] as number
118
+ : (yield* conn.executeValues(
119
+ `SELECT pg_backend_pid(), (SELECT set_config(name, '${idleSessionTimeout}', false) FROM pg_settings WHERE name = 'idle_session_timeout')`,
120
+ []
121
+ ))[0][0] as number
83
122
  if (!disableAdvisoryLocks) {
84
123
  yield* Scope.addFinalizerExit(scope, () =>
85
124
  conn.executeRaw("SELECT pg_advisory_unlock_all()", []).pipe(
@@ -88,26 +127,17 @@ const makeStorage = Effect.fnUntraced(function*(options: {
88
127
  Effect.ignoreCause
89
128
  ))
90
129
  }
91
- return [conn, pid] as const
130
+ return [conn, { pid, pinLock: undefined }] as const
92
131
  }, Effect.orDie),
93
132
  mysql: () =>
94
133
  Effect.fnUntraced(function*(scope: Scope.Scope) {
95
134
  const conn = yield* Effect.orDie(sql.reserve).pipe(
96
135
  Scope.provide(scope)
97
136
  )
98
- if (disableAdvisoryLocks) return [conn, 0] as const
137
+ if (disableAdvisoryLocks) return [conn, { pid: 0, pinLock: undefined }] as const
99
138
  // we need to get the connection id using IS_USED_LOCK to properly
100
139
  // support vitess
101
- let pid: number | undefined = undefined
102
- while (pid === undefined) {
103
- const address = `cluster:pid:${(Math.random() * Number.MAX_SAFE_INTEGER) | 0}`
104
- const taken = (yield* conn.executeValues(
105
- `SELECT GET_LOCK('${address}', 10), IS_USED_LOCK('${address}')`,
106
- []
107
- ))[0] as [1 | null, number]
108
- if (taken[0] === null) continue
109
- pid = taken[1]
110
- }
140
+ const session: LockSession = yield* pinLockSession(conn)
111
141
  if (!disableAdvisoryLocks) {
112
142
  yield* Scope.addFinalizerExit(scope, () =>
113
143
  conn.executeRaw("SELECT RELEASE_ALL_LOCKS()", []).pipe(
@@ -116,7 +146,7 @@ const makeStorage = Effect.fnUntraced(function*(options: {
116
146
  Effect.ignoreCause
117
147
  ))
118
148
  }
119
- return [conn, pid] as const
149
+ return [conn, session] as const
120
150
  }, Effect.orDie),
121
151
  orElse: () => undefined
122
152
  })
@@ -140,7 +170,7 @@ const makeStorage = Effect.fnUntraced(function*(options: {
140
170
  )
141
171
  })
142
172
 
143
- const rebuildLockConn = (from?: readonly [Connection, number]) => {
173
+ const rebuildLockConn = (from?: readonly [Connection, LockSession]) => {
144
174
  const rebuild = lockConn?.rebuildUnsafe({ from })
145
175
  if (!rebuild) return Effect.void
146
176
  // Bound release so a stalled connection cannot block subsequent rebuilds.
@@ -152,13 +182,13 @@ const makeStorage = Effect.fnUntraced(function*(options: {
152
182
  }
153
183
  // SQL failures replace the held connection; deadlines are handled by withLockOperationDeadline.
154
184
  const useLockConn = <A, E, R>(
155
- f: (conn: Connection, pid: number) => Effect.Effect<A, E, R>
185
+ f: (conn: Connection, pid: number, session: LockSession) => Effect.Effect<A, E, R>
156
186
  ): Effect.Effect<A, E, R> =>
157
187
  Effect.flatMap(
158
188
  lockConn!.await,
159
189
  (held) =>
160
190
  Effect.onError(
161
- f(held[0], held[1]),
191
+ f(held[0], held[1].pid, held[1]),
162
192
  (cause) => Cause.hasInterruptsOnly(cause) ? Effect.void : rebuildLockConn(held)
163
193
  )
164
194
  )
@@ -270,6 +300,40 @@ const makeStorage = Effect.fnUntraced(function*(options: {
270
300
  return useLockConn((conn) => conn.executeValues(query, params))
271
301
  }
272
302
 
303
+ // Splits shardIds into the advisory locks held by the lock session and the rest.
304
+ const pgHeldLocks = Effect.fnUntraced(function*(conn: Connection, pid: number, shardIds: ReadonlyArray<string>) {
305
+ const held: Array<string> = []
306
+ const notHeld = new Map(shardIds.map((shardId) => [lockNumbers.get(shardId)!, shardId]))
307
+ const takenLocks = yield* conn.executeValues(
308
+ `SELECT objid FROM pg_locks WHERE locktype = 'advisory' AND granted = true AND classid = ${pgLockNamespaceOid} AND objsubid = 2 AND pid = ${pid} ORDER BY objid`,
309
+ []
310
+ )
311
+ for (let i = 0; i < takenLocks.length; i++) {
312
+ const lockNum = takenLocks[i][0] as number
313
+ const shardId = notHeld.get(lockNum)
314
+ if (shardId === undefined) continue
315
+ held.push(shardId)
316
+ notHeld.delete(lockNum)
317
+ }
318
+ return [held, notHeld] as const
319
+ })
320
+
321
+ const mysqlHeldLocks = Effect.fnUntraced(function*(conn: Connection, pid: number, shardIds: ReadonlyArray<string>) {
322
+ const takenLocks = (yield* conn.executeValues(`SELECT ${allMySqlTakenLocks}`, []))[0] as Array<number | null>
323
+ const held: Array<string> = []
324
+ const notHeld: Array<string> = []
325
+ for (let i = 0; i < shardIds.length; i++) {
326
+ const shardId = shardIds[i]
327
+ const lockTakenBy = takenLocks[shardIdsIndex.get(shardId)!]
328
+ if (lockTakenBy !== null && Number(lockTakenBy) === pid) {
329
+ held.push(shardId)
330
+ } else {
331
+ notHeld.push(shardId)
332
+ }
333
+ }
334
+ return [held, notHeld] as const
335
+ })
336
+
273
337
  const acquireLock = sql.onDialectOrElse({
274
338
  pg: () => {
275
339
  if (disableAdvisoryLocks) {
@@ -293,19 +357,7 @@ const makeStorage = Effect.fnUntraced(function*(options: {
293
357
  }
294
358
  return (_address: string, shardIds: ReadonlyArray<string>) =>
295
359
  useLockConn(Effect.fnUntraced(function*(conn, pid) {
296
- const acquiredShardIds: Array<string> = []
297
- const toAcquire = new Map(shardIds.map((shardId) => [lockNumbers.get(shardId)!, shardId]))
298
- const takenLocks = yield* conn.executeValues(
299
- `SELECT objid FROM pg_locks WHERE locktype = 'advisory' AND granted = true AND classid = ${pgLockNamespaceOid} AND objsubid = 2 AND pid = ${pid} ORDER BY objid`,
300
- []
301
- )
302
- for (let i = 0; i < takenLocks.length; i++) {
303
- const lockNum = takenLocks[i][0] as number
304
- const shardId = toAcquire.get(lockNum)
305
- if (shardId === undefined) continue
306
- acquiredShardIds.push(shardId)
307
- toAcquire.delete(lockNum)
308
- }
360
+ const [acquiredShardIds, toAcquire] = yield* pgHeldLocks(conn, pid, shardIds)
309
361
  if (toAcquire.size === 0) {
310
362
  return acquiredShardIds
311
363
  }
@@ -339,18 +391,7 @@ const makeStorage = Effect.fnUntraced(function*(options: {
339
391
  }
340
392
  return (_address: string, shardIds: ReadonlyArray<string>) =>
341
393
  useLockConn(Effect.fnUntraced(function*(conn, pid) {
342
- const takenLocks = (yield* conn.executeValues(`SELECT ${allMySqlTakenLocks}`, []))[0] as Array<number | null>
343
- const acquiredShardIds: Array<string> = []
344
- const toAcquire: Array<string> = []
345
- for (let i = 0; i < shardIds.length; i++) {
346
- const shardId = shardIds[i]
347
- const lockTakenBy = takenLocks[shardIdsIndex.get(shardId)!]
348
- if (lockTakenBy === pid) {
349
- acquiredShardIds.push(shardId)
350
- } else if (shardIds.includes(shardId)) {
351
- toAcquire.push(shardId)
352
- }
353
- }
394
+ const [acquiredShardIds, toAcquire] = yield* mysqlHeldLocks(conn, pid, shardIds)
354
395
  if (toAcquire.length === 0) {
355
396
  return acquiredShardIds
356
397
  }
@@ -463,9 +504,15 @@ const makeStorage = Effect.fnUntraced(function*(options: {
463
504
  const stringLiteral = (s: string) => sql.literal(wrapString(s))
464
505
  const stringLiteralArr = (arr: ReadonlyArray<string>) => sql.literal(`(${arr.map(wrapString).join(",")})`)
465
506
 
507
+ // An advisory lock refresh only confirms the locks the current lock session
508
+ // holds. A replaced session has lost its predecessor's locks, possibly to
509
+ // another runner in between, so reacquiring them here would hide the loss.
466
510
  const refreshShards = sql.onDialectOrElse({
467
511
  pg: () => {
468
- if (!disableAdvisoryLocks) return acquireLock
512
+ if (!disableAdvisoryLocks) {
513
+ return (_address: string, shardIds: ReadonlyArray<string>) =>
514
+ useLockConn((conn, pid) => Effect.map(pgHeldLocks(conn, pid, shardIds), ([held]) => held))
515
+ }
469
516
  return (address: string, shardIds: ReadonlyArray<string>) =>
470
517
  sql`
471
518
  WITH locked AS (
@@ -483,7 +530,10 @@ const makeStorage = Effect.fnUntraced(function*(options: {
483
530
  )
484
531
  },
485
532
  mysql: () => {
486
- if (!disableAdvisoryLocks) return acquireLock
533
+ if (!disableAdvisoryLocks) {
534
+ return (_address: string, shardIds: ReadonlyArray<string>) =>
535
+ useLockConn((conn, pid) => Effect.map(mysqlHeldLocks(conn, pid, shardIds), ([held]) => held))
536
+ }
487
537
  return (address: string, shardIds: ReadonlyArray<string>) => {
488
538
  const shardIdsStr = stringLiteralArr(shardIds)
489
539
  return sql<Array<{ shard_id: string }>>`
@@ -558,7 +608,7 @@ const makeStorage = Effect.fnUntraced(function*(options: {
558
608
  `SELECT IS_USED_LOCK('${lockName}')`,
559
609
  []
560
610
  )
561
- if (takenLocks.length === 0 || takenLocks[0][0] !== pid) return
611
+ if (takenLocks.length === 0 || Number(takenLocks[0][0]) !== pid) return
562
612
  }
563
613
  }))
564
614
  },
@@ -579,7 +629,12 @@ const makeStorage = Effect.fnUntraced(function*(options: {
579
629
  mysql: () => (address: string) =>
580
630
  disableAdvisoryLocks
581
631
  ? sql`DELETE FROM ${locksTableSql} WHERE address = ${address}`.pipe(execWithLockConn)
582
- : sql`SELECT RELEASE_ALL_LOCKS()`.pipe(execWithLockConn, Effect.asVoid),
632
+ : useLockConn(Effect.fnUntraced(function*(conn, _pid, session) {
633
+ yield* conn.executeRaw("SELECT RELEASE_ALL_LOCKS()", [])
634
+ // Releasing every lock also releases the pin, so take it again.
635
+ const pinned = yield* pinLockSession(conn, session.pinLock)
636
+ session.pid = pinned.pid
637
+ })),
583
638
  orElse: () => (address: string) => sql`DELETE FROM ${locksTableSql} WHERE address = ${address}`
584
639
  })
585
640
 
@@ -612,8 +667,16 @@ const makeStorage = Effect.fnUntraced(function*(options: {
612
667
  withTracerDisabled
613
668
  ),
614
669
 
670
+ // A runner without shards leaves its lock connection idle, so the server
671
+ // may have ended the session, and the first operation on it fails and
672
+ // replaces it. Acquiring does not depend on the previous session, so retry
673
+ // once on the replacement.
615
674
  acquire: (address, shardIds) =>
616
- withLockOperationDeadline(acquireLock(address, shardIds)).pipe(
675
+ withLockOperationDeadline(
676
+ lockConn
677
+ ? Effect.catch(acquireLock(address, shardIds), () => acquireLock(address, shardIds))
678
+ : acquireLock(address, shardIds)
679
+ ).pipe(
617
680
  PersistenceError.refail,
618
681
  withTracerDisabled
619
682
  ),
@@ -20,6 +20,7 @@ import * as Schedule from "../../Schedule.ts"
20
20
  import * as Schema from "../../Schema.ts"
21
21
  import * as SchemaIssue from "../../SchemaIssue.ts"
22
22
  import * as Scope from "../../Scope.ts"
23
+ import type { PersistenceError } from "../ClusterError.ts"
23
24
  import { AlreadyProcessingMessage, EntityNotAssignedToRunner, MailboxFull, MalformedMessage } from "../ClusterError.ts"
24
25
  import * as ClusterMetrics from "../ClusterMetrics.ts"
25
26
  import { isUninterruptibleForServer, Persisted, WithTransaction } from "../ClusterSchema.ts"
@@ -37,6 +38,7 @@ import type { ShardId } from "../ShardId.ts"
37
38
  import type { Sharding } from "../Sharding.ts"
38
39
  import { ShardingConfig } from "../ShardingConfig.ts"
39
40
  import * as Snowflake from "../Snowflake.ts"
41
+ import * as ClusterAbandon from "./clusterAbandon.ts"
40
42
  import { CurrentActivationScope } from "./entityActivation.ts"
41
43
  import { EntityReaper } from "./entityReaper.ts"
42
44
  import { acquireEntity, releaseEntity } from "./interruptors.ts"
@@ -144,6 +146,19 @@ export const make = Effect.fnUntraced(function*<
144
146
  readonly disableFatalDefects?: boolean | undefined
145
147
  readonly defectRetryPolicy?: Schedule.Schedule<any, unknown, never, never> | undefined
146
148
  readonly spanAttributes?: Record<string, string> | undefined
149
+ /**
150
+ * Called once a retiring entity stops accepting requests, so callers
151
+ * waiting on requests it has not started can be released.
152
+ */
153
+ readonly abandonUndelivered: (
154
+ address: EntityAddress,
155
+ isDelivered: (requestId: Snowflake.Snowflake) => boolean
156
+ ) => Effect.Effect<void>
157
+ /**
158
+ * Whether this runner still owns the shard, checked before storing the
159
+ * final reply of a persisted request.
160
+ */
161
+ readonly isShardOwned: (shardId: ShardId) => Effect.Effect<boolean>
147
162
  }
148
163
  ) {
149
164
  const config = yield* ShardingConfig
@@ -248,6 +263,14 @@ export const make = Effect.fnUntraced(function*<
248
263
  // Finalizers run in reverse, so the mailbox series and the resident
249
264
  // mailbox are removed only after the drain finalizer below has finished.
250
265
  const activeRequests: EntityState["activeRequests"] = new Map()
266
+ // Drops a request that has left this entity, starting the idle timer when
267
+ // it was the last one.
268
+ const removeActiveRequest = (requestId: Snowflake.Snowflake) => {
269
+ activeRequests.delete(requestId)
270
+ if (activeRequests.size === 0) {
271
+ state.lastActiveCheck = clock.currentTimeMillisUnsafe()
272
+ }
273
+ }
251
274
  let mailboxSeries: Metric.Registration | undefined
252
275
  yield* Scope.addFinalizer(
253
276
  scope,
@@ -326,27 +349,27 @@ export const make = Effect.fnUntraced(function*<
326
349
  return Effect.void
327
350
  }
328
351
 
329
- // For durable messages, ignore interrupts during shutdown.
330
- // They will be retried when the entity is restarted.
331
- // Also, if the request is uninterruptible, we ignore the
332
- // interrupt.
333
352
  const persisted = storageEnabled && Context.get(request.message.annotations, Persisted)
334
- if (
335
- persisted &&
336
- Exit.hasInterrupts(response.exit) &&
337
- (isShuttingDown || request.terminating || isUninterruptibleForServer(request.message.annotations))
338
- ) {
339
- if (!isShuttingDown && !request.terminating) {
353
+ if (persisted && Exit.isFailure(response.exit) && Cause.hasInterrupts(response.exit.cause)) {
354
+ // A durable request interrupted by shutdown, or abandoned
355
+ // because this owner can no longer serve it, is left for
356
+ // replay instead of being answered.
357
+ const abandoned = ClusterAbandon.isCause(response.exit.cause) &&
358
+ (!isActive() || !options.sharding.hasShardId(address.shardId))
359
+ if (isShuttingDown || request.terminating || abandoned) {
360
+ removeActiveRequest(Snowflake.Snowflake(response.requestId))
361
+ return options.storage.unregisterReplyHandler(request.message.envelope.requestId)
362
+ }
363
+ // An uninterruptible request ignores the interrupt and runs again.
364
+ if (isUninterruptibleForServer(request.message.annotations)) {
340
365
  request.sentExit = false
341
366
  return server.write(0, requestEnvelope(request), requestWriteOptions(request)).pipe(
342
367
  Effect.setContext(handlerContext),
343
368
  Effect.forkIn(handlerScope)
344
369
  )
345
370
  }
346
- activeRequests.delete(Snowflake.Snowflake(response.requestId))
347
- return options.storage.unregisterReplyHandler(request.message.envelope.requestId)
348
371
  }
349
- const save = retryRespond(
372
+ const write = retryRespond(
350
373
  4,
351
374
  Effect.suspend(() =>
352
375
  request.message.respond(
@@ -358,18 +381,36 @@ export const make = Effect.fnUntraced(function*<
358
381
  )
359
382
  )
360
383
  )
384
+ // A runner that lost the shard must not decide the stored
385
+ // result. A rejection is not retried.
386
+ let fenced = false
387
+ const save = !persisted ? write : Effect.flatMap(
388
+ options.isShardOwned(address.shardId),
389
+ (owned): Effect.Effect<void, MalformedMessage | PersistenceError | EntityNotAssignedToRunner> => {
390
+ if (owned) return write
391
+ fenced = true
392
+ return Effect.fail(new EntityNotAssignedToRunner({ address }))
393
+ }
394
+ )
395
+ // Leave the request for the shard's owner: release the claim
396
+ // so its next poll sees it, and let local callers re-route.
397
+ const abandonFenced = Effect.suspend(() => {
398
+ removeActiveRequest(Snowflake.Snowflake(response.requestId))
399
+ return Effect.logDebug("Abandoning a request whose shard this runner no longer owns").pipe(
400
+ Effect.andThen(options.storage.unregisterReplyHandler(request.message.envelope.requestId)),
401
+ Effect.andThen(Effect.ignore(options.storage.resetRequests([request.message.envelope.requestId])))
402
+ )
403
+ })
361
404
  const complete = Effect.sync(() => {
362
405
  if (storageEnabled) {
363
406
  processedRequestIds.add(request.message.envelope.requestId)
364
407
  }
365
- activeRequests.delete(Snowflake.Snowflake(response.requestId))
366
-
367
- // Start the idle timer when the last request completes.
368
- if (activeRequests.size === 0) {
369
- state.lastActiveCheck = clock.currentTimeMillisUnsafe()
370
- }
408
+ removeActiveRequest(Snowflake.Snowflake(response.requestId))
371
409
  })
372
- const respond = Effect.orDie(Effect.andThen(save, complete))
410
+ const respond = Effect.andThen(save, complete).pipe(
411
+ Effect.catchTag("EntityNotAssignedToRunner", () => abandonFenced),
412
+ Effect.orDie
413
+ )
373
414
  const transaction = request.transaction
374
415
  if (!transaction) return respond
375
416
  const exit = response.exit
@@ -381,8 +422,12 @@ export const make = Effect.fnUntraced(function*<
381
422
  }
382
423
 
383
424
  if (Exit.isSuccess(exit)) {
384
- transaction.settle = (outcome) =>
385
- Exit.isSuccess(outcome) ? complete : Effect.flatMap(
425
+ transaction.settle = (outcome) => {
426
+ if (Exit.isSuccess(outcome)) return complete
427
+ // A rejected save failed the transaction, rolling back the
428
+ // handler's writes; the request is left for the owner.
429
+ if (fenced) return abandonFenced
430
+ return Effect.flatMap(
386
431
  hasStoredExit(request.message.envelope.requestId),
387
432
  (committed) => {
388
433
  if (!committed) return restartFrom(outcome.cause)
@@ -393,6 +438,7 @@ export const make = Effect.fnUntraced(function*<
393
438
  )
394
439
  }
395
440
  )
441
+ }
396
442
  return Effect.orDie(save)
397
443
  }
398
444
  transaction.settle = (outcome) =>
@@ -539,6 +585,13 @@ export const make = Effect.fnUntraced(function*<
539
585
  request.terminating = true
540
586
  yield* state.write(0, { _tag: "Interrupt", requestId: requestId as any, interruptors: [] })
541
587
  }
588
+ // A handler this drain waits for may be parked on a request the
589
+ // retired entity will never start, such as its own queued request.
590
+ // Release callers before `Eof`, which waits for handler acquisition.
591
+ yield* options.abandonUndelivered(
592
+ address,
593
+ (requestId) => activeRequests.get(requestId)?.delivered === true
594
+ )
542
595
  yield* state.write(0, { _tag: "Eof" })
543
596
  }).pipe(
544
597
  Effect.andThen(endLatch.await),
@@ -219,6 +219,7 @@ const makeTracerEffect = Effect.gen(function*() {
219
219
  client.sendUnsafe(spanSnapshot(span))
220
220
  const oldEvent = span.event
221
221
  span.event = function(this: Tracer.Span, name, startTime, attributes) {
222
+ if (span.status._tag === "Ended") return
222
223
  client.sendUnsafe({
223
224
  _tag: "SpanEvent",
224
225
  traceId: span.traceId,
@@ -232,6 +233,7 @@ const makeTracerEffect = Effect.gen(function*() {
232
233
 
233
234
  const oldEnd = span.end
234
235
  span.end = function(this: Tracer.Span, endTime, exit) {
236
+ if (span.status._tag === "Ended") return
235
237
  oldEnd.call(this, endTime, exit)
236
238
  client.sendUnsafe(spanSnapshot(span))
237
239
  }
@@ -148,6 +148,16 @@ export const decode = (str: string): Result.Result<Uint8Array, EncodingError> =>
148
148
  export const decodeString = (str: string) => Result.map(decode(str), (_) => decoder.decode(_))
149
149
 
150
150
  const encodeBytes = (bytes: Uint8Array): string => {
151
+ const length = bytes.length
152
+ // Looked up on the prototype at call time, so own or subclass overrides are ignored.
153
+ const toBase64 = (Uint8Array.prototype as { readonly toBase64?: (this: Uint8Array) => string }).toBase64
154
+ // The size guard also keeps detached buffers (length 0) away from native `toBase64`, which throws on them.
155
+ if (length >= 32 && typeof toBase64 === "function") return toBase64.call(bytes)
156
+ // Below ~128 bytes, appending to a string beats filling a byte array and decoding it once.
157
+ return length < 128 ? encodeShort(bytes) : encodeLong(bytes)
158
+ }
159
+
160
+ const encodeShort = (bytes: Uint8Array): string => {
151
161
  const length = bytes.length
152
162
  let result = ""
153
163
  let i: number
@@ -171,10 +181,38 @@ const encodeBytes = (bytes: Uint8Array): string => {
171
181
  return result
172
182
  }
173
183
 
184
+ const encodeLong = (bytes: Uint8Array): string => {
185
+ const length = bytes.length
186
+ const result = new Uint8Array(Math.ceil(length / 3) * 4)
187
+ let i: number
188
+ let j = 0
189
+ for (i = 2; i < length; i += 3, j += 4) {
190
+ result[j] = alphabetCodes[bytes[i - 2] >> 2]
191
+ result[j + 1] = alphabetCodes[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)]
192
+ result[j + 2] = alphabetCodes[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)]
193
+ result[j + 3] = alphabetCodes[bytes[i] & 0x3f]
194
+ }
195
+ if (i === length + 1) {
196
+ result[j] = alphabetCodes[bytes[i - 2] >> 2]
197
+ result[j + 1] = alphabetCodes[(bytes[i - 2] & 0x03) << 4]
198
+ result[j + 2] = paddingCode
199
+ result[j + 3] = paddingCode
200
+ }
201
+ if (i === length) {
202
+ result[j] = alphabetCodes[bytes[i - 2] >> 2]
203
+ result[j + 1] = alphabetCodes[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)]
204
+ result[j + 2] = alphabetCodes[(bytes[i - 1] & 0x0f) << 2]
205
+ result[j + 3] = paddingCode
206
+ }
207
+ return decoder.decode(result)
208
+ }
209
+
174
210
  const encoder = new TextEncoder()
175
211
  const decoder = new TextDecoder()
176
212
  const stripCrlf = (str: string) => str.replace(/[\n\r]/g, "")
177
213
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
214
+ const alphabetCodes = encoder.encode(alphabet)
215
+ const paddingCode = "=".charCodeAt(0)
178
216
  const codes = new Uint8Array(123).fill(255)
179
217
  for (let i = 0; i < alphabet.length; i++) codes[alphabet.charCodeAt(i)] = i
180
218
  codes["=".charCodeAt(0)] = 0
@@ -308,9 +308,10 @@ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeO
308
308
  }
309
309
 
310
310
  function feed(chunk: string): SseError | undefined {
311
- // Strip any UTF-8 byte order mark (BOM) at the start of the stream.
312
- // Note that we do not strip any non - UTF8 BOM, as eventsource streams are
313
- // always decoded as UTF8 as per the specification.
311
+ // Empty chunks must not consume the leading BOM check below
312
+ if (chunk.length === 0) return
313
+
314
+ // Strip a UTF-8 byte order mark (BOM) at the start of the stream
314
315
  if (isFirstChunk && chunk.startsWith(BOM)) {
315
316
  chunk = chunk.slice(BOM.length)
316
317
  }
@@ -47,6 +47,8 @@ class TomlParser {
47
47
  private line = 1
48
48
  private column = 1
49
49
  private readonly explicitTables = new Set<Table>()
50
+ // Tables defined by dotted keys; a header cannot define them again.
51
+ private readonly dottedTables = new Set<Table>()
50
52
 
51
53
  constructor(input: string) {
52
54
  this.input = input
@@ -87,7 +89,7 @@ class TomlParser {
87
89
  this.finishStatement()
88
90
 
89
91
  const table = this.resolveTable(path, array)
90
- if (!array && this.explicitTables.has(table)) {
92
+ if (!array && (this.explicitTables.has(table) || this.dottedTables.has(table))) {
91
93
  this.fail(`Cannot redefine table '${path.join(".")}'`)
92
94
  }
93
95
  if (!array) {
@@ -120,7 +122,7 @@ class TomlParser {
120
122
  value = makeTable()
121
123
  table[key] = value
122
124
  }
123
- if (Array.isArray(value)) {
125
+ if (Array.isArray(value) && !last) {
124
126
  value = value[value.length - 1]
125
127
  }
126
128
  if (!isTable(value)) {
@@ -140,11 +142,12 @@ class TomlParser {
140
142
  const child = makeTable()
141
143
  table[key] = child
142
144
  table = child
143
- } else if (isTable(existing)) {
145
+ } else if (isTable(existing) && !this.explicitTables.has(existing)) {
144
146
  table = existing
145
147
  } else {
146
148
  this.fail(`Cannot redefine existing key '${keys.slice(0, index + 1).join(".")}'`)
147
149
  }
150
+ this.dottedTables.add(table)
148
151
  }
149
152
  const key = keys[keys.length - 1]
150
153
  if (hasOwn.call(table, key)) {
@@ -255,17 +258,17 @@ class TomlParser {
255
258
  if (normalized.endsWith("nan")) return Number.NaN
256
259
  return normalized[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY
257
260
  }
258
- if (/^0x[0-9A-Fa-f](?:_?[0-9A-Fa-f])*$/.test(token)) {
259
- return Number.parseInt(normalized.slice(2), 16)
260
- }
261
- if (/^0o[0-7](?:_?[0-7])*$/.test(token)) {
262
- return Number.parseInt(normalized.slice(2), 8)
263
- }
264
- if (/^0b[01](?:_?[01])*$/.test(token)) {
265
- return Number.parseInt(normalized.slice(2), 2)
266
- }
267
- if (/^[+-]?(?:0|[1-9](?:_?\d)*)$/.test(token)) {
268
- return Number(normalized)
261
+ if (
262
+ /^0x[0-9A-Fa-f](?:_?[0-9A-Fa-f])*$/.test(token) ||
263
+ /^0o[0-7](?:_?[0-7])*$/.test(token) ||
264
+ /^0b[01](?:_?[01])*$/.test(token) ||
265
+ /^[+-]?(?:0|[1-9](?:_?\d)*)$/.test(token)
266
+ ) {
267
+ const value = Number(normalized)
268
+ if (!Number.isSafeInteger(value)) {
269
+ this.fail(`Integer out of range '${token}'`)
270
+ }
271
+ return value
269
272
  }
270
273
  if (
271
274
  /^[+-]?(?:(?:0|[1-9](?:_?\d)*)\.\d(?:_?\d)*(?:[eE][+-]?\d(?:_?\d)*)?|(?:0|[1-9](?:_?\d)*)[eE][+-]?\d(?:_?\d)*)$/
@@ -330,10 +333,15 @@ class TomlParser {
330
333
  } else if (escape === "u" || escape === "U") {
331
334
  const length = escape === "u" ? 4 : 8
332
335
  const hex = this.input.slice(this.index, this.index + length)
333
- if (!new RegExp(`^[0-9A-Fa-f]{${length}}$`).test(hex)) {
336
+ const codePoint = Number.parseInt(hex, 16)
337
+ if (
338
+ !new RegExp(`^[0-9A-Fa-f]{${length}}$`).test(hex) ||
339
+ (codePoint >= 0xD800 && codePoint <= 0xDFFF) ||
340
+ codePoint > 0x10FFFF
341
+ ) {
334
342
  this.fail("Invalid unicode escape")
335
343
  }
336
- output += String.fromCodePoint(Number.parseInt(hex, 16))
344
+ output += String.fromCodePoint(codePoint)
337
345
  this.advance(length)
338
346
  } else {
339
347
  this.fail(`Invalid escape '\\${escape}'`)
@@ -470,9 +478,18 @@ class TomlParser {
470
478
 
471
479
  private advance(count = 1): void {
472
480
  for (let offset = 0; offset < count; offset++) {
481
+ const code = this.input.charCodeAt(this.index)
482
+ if (
483
+ (code < 0x20 && code !== 0x09 && code !== 0x0A && !(code === 0x0D && this.input[this.index + 1] === "\n")) ||
484
+ code === 0x7F
485
+ ) {
486
+ this.fail("Invalid control character")
487
+ }
473
488
  if (this.input[this.index] === "\n") {
474
489
  this.line++
475
490
  this.column = 1
491
+ } else if (this.input[this.index] === "\r" && this.input[this.index + 1] !== "\n") {
492
+ this.fail("Bare carriage return")
476
493
  } else {
477
494
  this.column++
478
495
  }