effect 4.1.0 → 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 (354) hide show
  1. package/README.md +30 -0
  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 +83 -14
  6. package/dist/BigDecimal.js.map +1 -1
  7. package/dist/Equal.d.ts +2 -2
  8. package/dist/Equal.d.ts.map +1 -1
  9. package/dist/Equal.js +3 -3
  10. package/dist/Equal.js.map +1 -1
  11. package/dist/Hash.d.ts +3 -2
  12. package/dist/Hash.d.ts.map +1 -1
  13. package/dist/Hash.js +5 -4
  14. package/dist/Hash.js.map +1 -1
  15. package/dist/Iterable.d.ts.map +1 -1
  16. package/dist/Iterable.js +11 -8
  17. package/dist/Iterable.js.map +1 -1
  18. package/dist/Path.d.ts.map +1 -1
  19. package/dist/Path.js +5 -1
  20. package/dist/Path.js.map +1 -1
  21. package/dist/Pool.d.ts.map +1 -1
  22. package/dist/Pool.js +2 -1
  23. package/dist/Pool.js.map +1 -1
  24. package/dist/Schema.d.ts +2 -1
  25. package/dist/Schema.d.ts.map +1 -1
  26. package/dist/Schema.js +2 -1
  27. package/dist/Schema.js.map +1 -1
  28. package/dist/SchemaRepresentation.d.ts +4 -2
  29. package/dist/SchemaRepresentation.d.ts.map +1 -1
  30. package/dist/SchemaRepresentation.js +4 -2
  31. package/dist/SchemaRepresentation.js.map +1 -1
  32. package/dist/SchemaTransformation.d.ts +2 -2
  33. package/dist/SchemaTransformation.js +2 -2
  34. package/dist/ai/McpClient.d.ts +171 -19
  35. package/dist/ai/McpClient.d.ts.map +1 -1
  36. package/dist/ai/McpClient.js +277 -66
  37. package/dist/ai/McpClient.js.map +1 -1
  38. package/dist/ai/McpSchema.d.ts +108 -0
  39. package/dist/ai/McpSchema.d.ts.map +1 -1
  40. package/dist/ai/McpSchema.js +99 -0
  41. package/dist/ai/McpSchema.js.map +1 -1
  42. package/dist/ai/McpServer.d.ts +120 -6
  43. package/dist/ai/McpServer.d.ts.map +1 -1
  44. package/dist/ai/McpServer.js +215 -34
  45. package/dist/ai/McpServer.js.map +1 -1
  46. package/dist/ai/Prompt.d.ts.map +1 -1
  47. package/dist/ai/Prompt.js +20 -11
  48. package/dist/ai/Prompt.js.map +1 -1
  49. package/dist/ai/Tool.d.ts.map +1 -1
  50. package/dist/ai/Tool.js +5 -7
  51. package/dist/ai/Tool.js.map +1 -1
  52. package/dist/ai/internal/mcpCore.js +72 -3
  53. package/dist/ai/internal/mcpCore.js.map +1 -1
  54. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +3 -1
  55. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  56. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +3 -1
  57. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  58. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +3 -1
  59. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  60. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +3 -1
  61. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  62. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +42 -7
  63. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  64. package/dist/ai/internal/mcpProtocol.js +12 -0
  65. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  66. package/dist/ai/internal/mcpRuntime.js +3 -3
  67. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  68. package/dist/ai/internal/mcpSkills.d.ts +2 -0
  69. package/dist/ai/internal/mcpSkills.d.ts.map +1 -0
  70. package/dist/ai/internal/mcpSkills.js +428 -0
  71. package/dist/ai/internal/mcpSkills.js.map +1 -0
  72. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  73. package/dist/cluster/ClusterWorkflowEngine.js +15 -5
  74. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  75. package/dist/cluster/Reply.d.ts +1 -1
  76. package/dist/cluster/Reply.d.ts.map +1 -1
  77. package/dist/cluster/Reply.js.map +1 -1
  78. package/dist/cluster/Sharding.d.ts.map +1 -1
  79. package/dist/cluster/Sharding.js +4 -1
  80. package/dist/cluster/Sharding.js.map +1 -1
  81. package/dist/cluster/internal/entityManager.js +15 -7
  82. package/dist/cluster/internal/entityManager.js.map +1 -1
  83. package/dist/crypto/Aead.d.ts +2 -1
  84. package/dist/crypto/Aead.d.ts.map +1 -1
  85. package/dist/crypto/Aead.js +2 -2
  86. package/dist/crypto/Aead.js.map +1 -1
  87. package/dist/crypto/Cipher.d.ts +2 -1
  88. package/dist/crypto/Cipher.d.ts.map +1 -1
  89. package/dist/crypto/Cipher.js +2 -2
  90. package/dist/crypto/Cipher.js.map +1 -1
  91. package/dist/crypto/KeyAgreement.d.ts +3 -2
  92. package/dist/crypto/KeyAgreement.d.ts.map +1 -1
  93. package/dist/crypto/KeyAgreement.js +2 -2
  94. package/dist/crypto/KeyAgreement.js.map +1 -1
  95. package/dist/crypto/KeyFormat.d.ts +33 -1
  96. package/dist/crypto/KeyFormat.d.ts.map +1 -1
  97. package/dist/crypto/KeyFormat.js.map +1 -1
  98. package/dist/crypto/KeyWrap.d.ts +4 -3
  99. package/dist/crypto/KeyWrap.d.ts.map +1 -1
  100. package/dist/crypto/KeyWrap.js +2 -2
  101. package/dist/crypto/KeyWrap.js.map +1 -1
  102. package/dist/crypto/Mac.d.ts +2 -1
  103. package/dist/crypto/Mac.d.ts.map +1 -1
  104. package/dist/crypto/Mac.js +2 -2
  105. package/dist/crypto/Mac.js.map +1 -1
  106. package/dist/crypto/Signature.d.ts +3 -2
  107. package/dist/crypto/Signature.d.ts.map +1 -1
  108. package/dist/crypto/Signature.js +2 -2
  109. package/dist/crypto/Signature.js.map +1 -1
  110. package/dist/crypto/WebCrypto.d.ts +19 -0
  111. package/dist/crypto/WebCrypto.d.ts.map +1 -1
  112. package/dist/crypto/WebCrypto.js.map +1 -1
  113. package/dist/crypto/internal/asymmetric.js.map +1 -1
  114. package/dist/crypto/internal/key.js +3 -2
  115. package/dist/crypto/internal/key.js.map +1 -1
  116. package/dist/encoding/Base64Url.d.ts.map +1 -1
  117. package/dist/encoding/Base64Url.js +26 -3
  118. package/dist/encoding/Base64Url.js.map +1 -1
  119. package/dist/encoding/Ndjson.d.ts +5 -4
  120. package/dist/encoding/Ndjson.d.ts.map +1 -1
  121. package/dist/encoding/Ndjson.js +6 -5
  122. package/dist/encoding/Ndjson.js.map +1 -1
  123. package/dist/encoding/Sse.d.ts +3 -1
  124. package/dist/encoding/Sse.d.ts.map +1 -1
  125. package/dist/encoding/Sse.js +5 -3
  126. package/dist/encoding/Sse.js.map +1 -1
  127. package/dist/eventlog/EventLog.d.ts +7 -0
  128. package/dist/eventlog/EventLog.d.ts.map +1 -1
  129. package/dist/eventlog/EventLog.js +7 -0
  130. package/dist/eventlog/EventLog.js.map +1 -1
  131. package/dist/eventlog/EventLogServer.d.ts +7 -1
  132. package/dist/eventlog/EventLogServer.d.ts.map +1 -1
  133. package/dist/eventlog/EventLogServer.js +27 -12
  134. package/dist/eventlog/EventLogServer.js.map +1 -1
  135. package/dist/eventlog/EventLogServerEncrypted.d.ts +10 -0
  136. package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
  137. package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
  138. package/dist/eventlog/EventLogServerUnencrypted.d.ts +17 -0
  139. package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
  140. package/dist/eventlog/EventLogServerUnencrypted.js +14 -2
  141. package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
  142. package/dist/eventlog/EventLogSessionAuth.d.ts +21 -0
  143. package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
  144. package/dist/eventlog/EventLogSessionAuth.js +22 -0
  145. package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
  146. package/dist/graphql/GraphQLClient.d.ts +3 -1
  147. package/dist/graphql/GraphQLClient.d.ts.map +1 -1
  148. package/dist/graphql/GraphQLClient.js.map +1 -1
  149. package/dist/graphql/GraphQLClientError.d.ts +14 -9
  150. package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
  151. package/dist/graphql/GraphQLClientError.js +14 -21
  152. package/dist/graphql/GraphQLClientError.js.map +1 -1
  153. package/dist/graphql/GraphQLProtocol.d.ts +12 -3
  154. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
  155. package/dist/graphql/GraphQLProtocol.js +4 -1
  156. package/dist/graphql/GraphQLProtocol.js.map +1 -1
  157. package/dist/http/HttpClient.d.ts +189 -7
  158. package/dist/http/HttpClient.d.ts.map +1 -1
  159. package/dist/http/HttpClient.js +75 -2
  160. package/dist/http/HttpClient.js.map +1 -1
  161. package/dist/http/HttpServerRequest.d.ts.map +1 -1
  162. package/dist/http/HttpServerRequest.js +38 -22
  163. package/dist/http/HttpServerRequest.js.map +1 -1
  164. package/dist/http/internal/mimeTypes.d.ts +4 -1
  165. package/dist/http/internal/mimeTypes.d.ts.map +1 -1
  166. package/dist/http/internal/mimeTypes.js +4 -1
  167. package/dist/http/internal/mimeTypes.js.map +1 -1
  168. package/dist/http-api/HttpApiSchema.js +1 -1
  169. package/dist/http-api/HttpApiSchema.js.map +1 -1
  170. package/dist/internal/dateTime.js +49 -24
  171. package/dist/internal/dateTime.js.map +1 -1
  172. package/dist/internal/effect.js +10 -0
  173. package/dist/internal/effect.js.map +1 -1
  174. package/dist/internal/glob.js +234 -56
  175. package/dist/internal/glob.js.map +1 -1
  176. package/dist/internal/graphql.js +21 -7
  177. package/dist/internal/graphql.js.map +1 -1
  178. package/dist/internal/graphqlHeaders.d.ts +7 -0
  179. package/dist/internal/graphqlHeaders.d.ts.map +1 -1
  180. package/dist/internal/graphqlHeaders.js +11 -0
  181. package/dist/internal/graphqlHeaders.js.map +1 -1
  182. package/dist/internal/request.js +29 -42
  183. package/dist/internal/request.js.map +1 -1
  184. package/dist/internal/schema/fromJsonSchemaDocument.js +7 -8
  185. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  186. package/dist/internal/version.js +1 -1
  187. package/dist/jose/JoseError.d.ts +285 -0
  188. package/dist/jose/JoseError.d.ts.map +1 -0
  189. package/dist/jose/JoseError.js +269 -0
  190. package/dist/jose/JoseError.js.map +1 -0
  191. package/dist/jose/Jwe.d.ts +128 -0
  192. package/dist/jose/Jwe.d.ts.map +1 -0
  193. package/dist/jose/Jwe.js +134 -0
  194. package/dist/jose/Jwe.js.map +1 -0
  195. package/dist/jose/Jwk.d.ts +597 -0
  196. package/dist/jose/Jwk.d.ts.map +1 -0
  197. package/dist/jose/Jwk.js +503 -0
  198. package/dist/jose/Jwk.js.map +1 -0
  199. package/dist/jose/Jwks.d.ts +236 -0
  200. package/dist/jose/Jwks.d.ts.map +1 -0
  201. package/dist/jose/Jwks.js +306 -0
  202. package/dist/jose/Jwks.js.map +1 -0
  203. package/dist/jose/Jws.d.ts +177 -0
  204. package/dist/jose/Jws.d.ts.map +1 -0
  205. package/dist/jose/Jws.js +183 -0
  206. package/dist/jose/Jws.js.map +1 -0
  207. package/dist/jose/Jwt.d.ts +270 -0
  208. package/dist/jose/Jwt.d.ts.map +1 -0
  209. package/dist/jose/Jwt.js +293 -0
  210. package/dist/jose/Jwt.js.map +1 -0
  211. package/dist/jose/index.d.ts +51 -0
  212. package/dist/jose/index.d.ts.map +1 -0
  213. package/dist/jose/index.js +52 -0
  214. package/dist/jose/index.js.map +1 -0
  215. package/dist/jose/internal/compact.d.ts +2 -0
  216. package/dist/jose/internal/compact.d.ts.map +1 -0
  217. package/dist/jose/internal/compact.js +179 -0
  218. package/dist/jose/internal/compact.js.map +1 -0
  219. package/dist/net/Host.d.ts.map +1 -1
  220. package/dist/net/Host.js +5 -10
  221. package/dist/net/Host.js.map +1 -1
  222. package/dist/observability/OtlpSerialization.d.ts +1 -1
  223. package/dist/observability/OtlpSerialization.d.ts.map +1 -1
  224. package/dist/observability/OtlpSerialization.js +10 -4
  225. package/dist/observability/OtlpSerialization.js.map +1 -1
  226. package/dist/observability/internal/otlpProtobuf.js +3 -3
  227. package/dist/observability/internal/otlpProtobuf.js.map +1 -1
  228. package/dist/observability/internal/protobuf.js +6 -0
  229. package/dist/observability/internal/protobuf.js.map +1 -1
  230. package/dist/persistence/CircuitBreaker.d.ts +98 -38
  231. package/dist/persistence/CircuitBreaker.d.ts.map +1 -1
  232. package/dist/persistence/CircuitBreaker.js +67 -29
  233. package/dist/persistence/CircuitBreaker.js.map +1 -1
  234. package/dist/persistence/RateLimiter.d.ts +8 -0
  235. package/dist/persistence/RateLimiter.d.ts.map +1 -1
  236. package/dist/persistence/RateLimiter.js +63 -12
  237. package/dist/persistence/RateLimiter.js.map +1 -1
  238. package/dist/persistence/index.d.ts +1 -1
  239. package/dist/persistence/index.js +1 -1
  240. package/dist/reactivity/AtomRegistry.js +17 -7
  241. package/dist/reactivity/AtomRegistry.js.map +1 -1
  242. package/dist/rpc/Rpc.d.ts +1 -1
  243. package/dist/rpc/Rpc.d.ts.map +1 -1
  244. package/dist/rpc/Rpc.js.map +1 -1
  245. package/dist/rpc/RpcClient.d.ts +14 -2
  246. package/dist/rpc/RpcClient.d.ts.map +1 -1
  247. package/dist/rpc/RpcClient.js +38 -21
  248. package/dist/rpc/RpcClient.js.map +1 -1
  249. package/dist/rpc/RpcMessage.d.ts +3 -2
  250. package/dist/rpc/RpcMessage.d.ts.map +1 -1
  251. package/dist/rpc/RpcMessage.js.map +1 -1
  252. package/dist/sql/Migrator.d.ts +9 -1
  253. package/dist/sql/Migrator.d.ts.map +1 -1
  254. package/dist/sql/Migrator.js +18 -6
  255. package/dist/sql/Migrator.js.map +1 -1
  256. package/dist/sql/SqlClient.d.ts +38 -3
  257. package/dist/sql/SqlClient.d.ts.map +1 -1
  258. package/dist/sql/SqlClient.js +4 -3
  259. package/dist/sql/SqlClient.js.map +1 -1
  260. package/dist/sql/Statement.d.ts +41 -3
  261. package/dist/sql/Statement.d.ts.map +1 -1
  262. package/dist/sql/Statement.js +24 -20
  263. package/dist/sql/Statement.js.map +1 -1
  264. package/dist/workflow/Workflow.d.ts +4 -0
  265. package/dist/workflow/Workflow.d.ts.map +1 -1
  266. package/dist/workflow/Workflow.js.map +1 -1
  267. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  268. package/dist/workflow/WorkflowEngine.js +5 -0
  269. package/dist/workflow/WorkflowEngine.js.map +1 -1
  270. package/package.json +5 -1
  271. package/src/BigDecimal.ts +170 -16
  272. package/src/Equal.ts +3 -3
  273. package/src/Hash.ts +5 -4
  274. package/src/Iterable.ts +8 -5
  275. package/src/Path.ts +6 -1
  276. package/src/Pool.ts +2 -1
  277. package/src/Schema.ts +3 -2
  278. package/src/SchemaRepresentation.ts +4 -2
  279. package/src/SchemaTransformation.ts +2 -2
  280. package/src/ai/McpClient.ts +488 -91
  281. package/src/ai/McpSchema.ts +119 -0
  282. package/src/ai/McpServer.ts +261 -38
  283. package/src/ai/Prompt.ts +16 -11
  284. package/src/ai/Tool.ts +5 -10
  285. package/src/ai/internal/mcpCore.ts +165 -12
  286. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +3 -1
  287. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +3 -1
  288. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -1
  289. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -1
  290. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +41 -7
  291. package/src/ai/internal/mcpProtocol.ts +15 -0
  292. package/src/ai/internal/mcpRuntime.ts +6 -3
  293. package/src/ai/internal/mcpSkills.ts +480 -0
  294. package/src/cluster/ClusterWorkflowEngine.ts +22 -8
  295. package/src/cluster/Reply.ts +1 -1
  296. package/src/cluster/Sharding.ts +4 -1
  297. package/src/cluster/internal/entityManager.ts +24 -10
  298. package/src/crypto/Aead.ts +3 -3
  299. package/src/crypto/Cipher.ts +3 -3
  300. package/src/crypto/KeyAgreement.ts +4 -4
  301. package/src/crypto/KeyFormat.ts +30 -1
  302. package/src/crypto/KeyWrap.ts +6 -6
  303. package/src/crypto/Mac.ts +3 -3
  304. package/src/crypto/Signature.ts +4 -4
  305. package/src/crypto/WebCrypto.ts +20 -0
  306. package/src/crypto/internal/asymmetric.ts +2 -1
  307. package/src/crypto/internal/key.ts +12 -5
  308. package/src/encoding/Base64Url.ts +27 -3
  309. package/src/encoding/Ndjson.ts +6 -5
  310. package/src/encoding/Sse.ts +5 -3
  311. package/src/eventlog/EventLog.ts +7 -0
  312. package/src/eventlog/EventLogServer.ts +31 -12
  313. package/src/eventlog/EventLogServerEncrypted.ts +5 -0
  314. package/src/eventlog/EventLogServerUnencrypted.ts +19 -2
  315. package/src/eventlog/EventLogSessionAuth.ts +26 -0
  316. package/src/graphql/GraphQLClient.ts +3 -1
  317. package/src/graphql/GraphQLClientError.ts +15 -22
  318. package/src/graphql/GraphQLProtocol.ts +12 -3
  319. package/src/http/HttpClient.ts +210 -11
  320. package/src/http/HttpServerRequest.ts +61 -23
  321. package/src/http/internal/mimeTypes.ts +4 -1
  322. package/src/http-api/HttpApiSchema.ts +1 -1
  323. package/src/internal/dateTime.ts +54 -24
  324. package/src/internal/effect.ts +6 -0
  325. package/src/internal/glob.ts +279 -63
  326. package/src/internal/graphql.ts +27 -9
  327. package/src/internal/graphqlHeaders.ts +13 -0
  328. package/src/internal/request.ts +51 -58
  329. package/src/internal/schema/fromJsonSchemaDocument.ts +11 -10
  330. package/src/internal/version.ts +1 -1
  331. package/src/jose/JoseError.ts +329 -0
  332. package/src/jose/Jwe.ts +196 -0
  333. package/src/jose/Jwk.ts +777 -0
  334. package/src/jose/Jwks.ts +418 -0
  335. package/src/jose/Jws.ts +312 -0
  336. package/src/jose/Jwt.ts +493 -0
  337. package/src/jose/index.ts +58 -0
  338. package/src/jose/internal/compact.ts +195 -0
  339. package/src/net/Host.ts +5 -10
  340. package/src/observability/OtlpSerialization.ts +13 -5
  341. package/src/observability/internal/otlpProtobuf.ts +6 -6
  342. package/src/observability/internal/protobuf.ts +10 -0
  343. package/src/persistence/CircuitBreaker.ts +113 -48
  344. package/src/persistence/RateLimiter.ts +84 -15
  345. package/src/persistence/index.ts +1 -1
  346. package/src/reactivity/AtomRegistry.ts +19 -8
  347. package/src/rpc/Rpc.ts +3 -1
  348. package/src/rpc/RpcClient.ts +44 -24
  349. package/src/rpc/RpcMessage.ts +3 -2
  350. package/src/sql/Migrator.ts +35 -18
  351. package/src/sql/SqlClient.ts +43 -8
  352. package/src/sql/Statement.ts +52 -22
  353. package/src/workflow/Workflow.ts +4 -0
  354. package/src/workflow/WorkflowEngine.ts +5 -0
@@ -553,9 +553,15 @@ class RegistryImpl implements AtomRegistry {
553
553
  const node = this.ensureNode(atom)
554
554
  if (options?.immediate) {
555
555
  f(node.value())
556
- } else if (node.state === NodeState.stale) {
557
- // a listener hears the next change, which only reaches an up-to-date node
558
- node.value()
556
+ } else {
557
+ // build the node so the listener hears its next change. A failed build
558
+ // is left for the next read to report; an error raised after the value
559
+ // is committed has no later read to report it, so it is rethrown
560
+ try {
561
+ node.value()
562
+ } catch (error) {
563
+ if ((node.state & NodeFlags.waitingForValue) === 0) throw error
564
+ }
559
565
  }
560
566
  const remove = node.subscribe(function() {
561
567
  f(node._value)
@@ -842,11 +848,15 @@ class NodeImpl<A> {
842
848
  }
843
849
 
844
850
  build(): void {
845
- // a lifetime still attached to a node that needs a value belongs to a
846
- // build that failed, or to one that is running and is now superseded
847
- this.disposeLifetime()
848
- this.invalidatedDuringBuild = false
849
851
  const lifetime = makeLifetime(this)
852
+ // release a failed or superseded build; its finalizers run only once, so
853
+ // their failure is rethrown once this build commits
854
+ try {
855
+ this.disposeLifetime()
856
+ } catch (error) {
857
+ lifetime.failure = error
858
+ }
859
+ this.invalidatedDuringBuild = false
850
860
  this.lifetime = lifetime
851
861
  let value: A
852
862
  let read = false
@@ -1126,7 +1136,8 @@ interface Lifetime<A> extends Atom.AtomContext {
1126
1136
  readonly node: NodeImpl<A>
1127
1137
  // the parents read so far by the build; `undefined` once the build has returned
1128
1138
  reads: Set<NodeImpl<any>> | undefined
1129
- // a failure met while reading a parent that still produced its value
1139
+ // rethrown once the build commits: a failure from releasing the previous
1140
+ // lifetime, or from reading a parent that still produced its value
1130
1141
  failure: unknown
1131
1142
  finalizers: Array<() => void> | undefined
1132
1143
  disposed: boolean
package/src/rpc/Rpc.ts CHANGED
@@ -363,7 +363,9 @@ export type SuccessExit<R> = Success<R> extends infer T ? T extends Stream<infer
363
363
  * @category utility types
364
364
  * @since 4.0.0
365
365
  */
366
- export type SuccessChunk<R> = Success<R> extends Stream<infer _A, infer _E, infer _Env> ? _A : never
366
+ export type SuccessChunk<R> = R extends Any ?
367
+ SuccessSchema<R> extends RpcSchema.Stream<infer _A, infer _E> ? _A["Type"] : never
368
+ : never
367
369
 
368
370
  /**
369
371
  * Extracts the RPC error schema, including error schemas contributed by
@@ -113,7 +113,8 @@ export declare namespace RpcClient {
113
113
  * handler's result and error are not returned, but transport errors,
114
114
  * middleware errors and failures before the handler starts still fail the
115
115
  * call. Over HTTP and workers the call completes after the handler has
116
- * finished.
116
+ * finished. A protocol that only delivers requests acknowledges the call
117
+ * itself once it has delivered the request.
117
118
  */
118
119
  readonly discard?: Discard | undefined
119
120
  }
@@ -188,7 +189,8 @@ export declare namespace RpcClient {
188
189
  * handler's result and error are not returned, but transport errors,
189
190
  * middleware errors and failures before the handler starts still fail the
190
191
  * call. Over HTTP and workers the call completes after the handler has
191
- * finished.
192
+ * finished. A protocol that only delivers requests acknowledges the call
193
+ * itself once it has delivered the request.
192
194
  */
193
195
  readonly discard?: Discard | undefined
194
196
  }
@@ -418,19 +420,18 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
418
420
  let fiber: Fiber.Fiber<any, any>
419
421
  return Effect.onInterrupt(
420
422
  Effect.callback<any, any>((resume) => {
421
- // the acknowledgement of a discarded call, held until its send has finished
422
- let ack: Exit.Exit<any, any> | undefined
423
+ let acked = false
423
424
  const entry: ClientEntry = {
424
425
  _tag: "Effect",
425
426
  rpc,
426
427
  context,
427
428
  resume(exit) {
428
- if (discard) {
429
- // Interrupting the send could cancel the call, as an HTTP
430
- // handler is scoped to its request, so the call completes
431
- // once the send has finished too.
432
- ack = exit._tag === "Success" ? Exit.void : exit
433
- if (fiber?.pollUnsafe()) resume(ack)
429
+ if (discard && exit._tag === "Success") {
430
+ // A successful acknowledgement waits for the send to finish, as
431
+ // interrupting the send could cancel the call: an HTTP handler
432
+ // is scoped to its request. Failures complete the call at once.
433
+ acked = true
434
+ if (fiber?.pollUnsafe()) resume(Exit.void)
434
435
  return
435
436
  }
436
437
  resume(exit)
@@ -450,8 +451,8 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
450
451
  if (exit._tag === "Failure") {
451
452
  entries.delete(id)
452
453
  return resume(exit)
453
- } else if (ack) {
454
- return resume(ack)
454
+ } else if (acked) {
455
+ return resume(Exit.void)
455
456
  }
456
457
  })
457
458
  }),
@@ -604,8 +605,10 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
604
605
  if (!entry || entry._tag !== "Queue") return Effect.void
605
606
  return Queue.offerAll(entry.queue, message.values).pipe(
606
607
  supportsAck
607
- ? Effect.flatMap(() =>
608
- options.onFromClient({
608
+ ? Effect.flatMap((dropped) =>
609
+ // values are dropped when the queue was shutdown by an interrupted
610
+ // consumer, so the server should not be asked for more
611
+ dropped.length > 0 ? Effect.void : options.onFromClient({
609
612
  message: { _tag: "Ack", requestId: message.requestId },
610
613
  context: entry.context
611
614
  })
@@ -831,8 +834,10 @@ export const make: <Rpcs extends Rpc.Any, const Flatten extends boolean = false>
831
834
  }
832
835
  case "ClientProtocolError": {
833
836
  const exit = Exit.fail(message.error)
834
- const requestIds = Array.from(entries.keys())
835
- entries.clear()
837
+ const requestIds = message.requestIds === undefined
838
+ ? Array.from(entries.keys())
839
+ : message.requestIds.map(RequestId).filter((requestId) => entries.has(requestId))
840
+ for (const requestId of requestIds) entries.delete(requestId)
836
841
  return Effect.forEach(
837
842
  requestIds,
838
843
  (requestId) => write({ _tag: "Exit", clientId: 0, requestId, exit: exit as any })
@@ -953,6 +958,16 @@ export const withHeaders: {
953
958
  * Use to provide the transport boundary for RPC clients over HTTP, WebSocket,
954
959
  * workers, sockets, or custom protocols.
955
960
  *
961
+ * **Details**
962
+ *
963
+ * A `Request` that `send` accepts stays pending until a reply names its id: an
964
+ * `Exit`, or a `ClientProtocolError` that lists it in `requestIds`. Until then
965
+ * the client keeps the request and the caller's context, unless the caller is
966
+ * interrupted, the client closes or the connection fails. A transport that
967
+ * only delivers requests, such as a message queue, writes a void `Exit` for a
968
+ * `discard` request once the request is delivered, and fails `send` with an
969
+ * `RpcClientError` for any other request.
970
+ *
956
971
  * @stability unstable
957
972
  * @category services
958
973
  * @since 4.0.0
@@ -1599,21 +1614,26 @@ export const makeProtocolWorker = (
1599
1614
  undefined
1600
1615
  }).pipe(
1601
1616
  Effect.tapCause((cause) => {
1617
+ const requestIdsByClient = new Map<number, Array<string | number>>()
1602
1618
  for (const [requestId, entry] of entries) {
1603
1619
  if (entry.worker !== backing) continue
1604
1620
  entries.delete(requestId)
1605
1621
  entry.latch.openUnsafe()
1622
+ const requestIds = requestIdsByClient.get(entry.clientId)
1623
+ if (requestIds) requestIds.push(requestId)
1624
+ else requestIdsByClient.set(entry.clientId, [requestId])
1606
1625
  }
1607
- const error = Cause.findError(cause)
1608
- return broadcast({
1609
- _tag: "ClientProtocolError",
1610
- error: new RpcClientError({
1611
- reason: Result.isSuccess(error) ? error.success.reason : new RpcClientDefect({
1612
- message: "Error in worker",
1613
- cause: Cause.squash(cause)
1614
- })
1626
+ const found = Cause.findError(cause)
1627
+ const error = new RpcClientError({
1628
+ reason: Result.isSuccess(found) ? found.success.reason : new RpcClientDefect({
1629
+ message: "Error in worker",
1630
+ cause: Cause.squash(cause)
1615
1631
  })
1616
1632
  })
1633
+ return Effect.forEach(
1634
+ requestIdsByClient,
1635
+ ([clientId, requestIds]) => writeResponse(clientId, { _tag: "ClientProtocolError", error, requestIds })
1636
+ )
1617
1637
  }),
1618
1638
  Effect.retry(Schedule.spaced(1000)),
1619
1639
  Effect.annotateLogs({
@@ -325,8 +325,8 @@ export interface ResponseExitEncoded {
325
325
  }
326
326
 
327
327
  /**
328
- * A server-to-client protocol message reporting a client protocol error to all
329
- * affected in-flight requests.
328
+ * A server-to-client protocol message failing in-flight requests with a client
329
+ * protocol error: those in `requestIds`, or all of them when it is omitted.
330
330
  *
331
331
  * @stability unstable
332
332
  * @category models
@@ -335,6 +335,7 @@ export interface ResponseExitEncoded {
335
335
  export interface ClientProtocolError {
336
336
  readonly _tag: "ClientProtocolError"
337
337
  readonly error: RpcClientError
338
+ readonly requestIds?: ReadonlyArray<string | number>
338
339
  }
339
340
 
340
341
  /**
@@ -12,7 +12,7 @@
12
12
  import * as Arr from "../Array.ts"
13
13
  import * as Data from "../Data.ts"
14
14
  import * as Effect from "../Effect.ts"
15
- import { pipe } from "../Function.ts"
15
+ import { identity, pipe } from "../Function.ts"
16
16
  import * as Option from "../Option.ts"
17
17
  import * as Order from "../Order.ts"
18
18
  import * as Client from "./SqlClient.ts"
@@ -99,13 +99,22 @@ export class MigrationError extends Data.TaggedError("MigrationError")<{
99
99
  * migrations. ClickHouse runs without a transaction and records each migration
100
100
  * only after it succeeds.
101
101
  *
102
+ * **Details**
103
+ *
104
+ * `transformWrites` transforms only the effects that create the migrations
105
+ * table and insert into it, for clients such as ClickHouse that run row-less
106
+ * statements differently from queries. Reads of the migrations table and the
107
+ * migrations themselves are not transformed. It defaults to `identity`.
108
+ *
102
109
  * @stability unstable
103
110
  * @category constructors
104
111
  * @since 4.0.0
105
112
  */
106
113
  export const make = <RD = never>({
114
+ transformWrites = identity,
107
115
  dumpSchema = () => Effect.void
108
116
  }: {
117
+ transformWrites?: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
109
118
  dumpSchema?: (
110
119
  path: string,
111
120
  migrationsTable: string
@@ -123,7 +132,7 @@ export const make = <RD = never>({
123
132
  Effect.gen(function*() {
124
133
  const sql = yield* Client.SqlClient
125
134
 
126
- const ensureMigrationsTable = sql.onDialectOrElse({
135
+ const createMigrationsTable = transformWrites(sql.onDialectOrElse({
127
136
  mssql: () =>
128
137
  sql`IF OBJECT_ID(N'${sql.literal(table)}', N'U') IS NULL
129
138
  CREATE TABLE ${sql(table)} (
@@ -147,14 +156,25 @@ export const make = <RD = never>({
147
156
  ) ENGINE = MergeTree
148
157
  ORDER BY migration_id`,
149
158
  pg: () =>
150
- Effect.flatMap(
151
- migrationsTableExists(sql, table),
152
- (exists) =>
153
- exists ? Effect.void : Effect.asVoid(sql`CREATE TABLE ${sql(table)} (
159
+ sql`CREATE TABLE ${sql(table)} (
154
160
  migration_id integer primary key,
155
161
  created_at timestamp with time zone not null default now(),
156
162
  name text not null
157
- )`).pipe(
163
+ )`,
164
+ orElse: () =>
165
+ sql`CREATE TABLE IF NOT EXISTS ${sql(table)} (
166
+ migration_id integer PRIMARY KEY NOT NULL,
167
+ created_at datetime NOT NULL DEFAULT current_timestamp,
168
+ name VARCHAR(255) NOT NULL
169
+ )`
170
+ }))
171
+
172
+ const ensureMigrationsTable = sql.onDialectOrElse({
173
+ pg: () =>
174
+ Effect.flatMap(
175
+ migrationsTableExists(sql, table),
176
+ (exists) =>
177
+ exists ? Effect.void : Effect.asVoid(createMigrationsTable).pipe(
158
178
  Effect.catch((error) =>
159
179
  Effect.flatMap(
160
180
  migrationsTableExists(sql, table),
@@ -163,22 +183,19 @@ ORDER BY migration_id`,
163
183
  )
164
184
  )
165
185
  ),
166
- orElse: () =>
167
- sql`CREATE TABLE IF NOT EXISTS ${sql(table)} (
168
- migration_id integer PRIMARY KEY NOT NULL,
169
- created_at datetime NOT NULL DEFAULT current_timestamp,
170
- name VARCHAR(255) NOT NULL
171
- )`
186
+ orElse: () => createMigrationsTable
172
187
  })
173
188
 
174
189
  const insertMigrations = (
175
190
  rows: Arr.NonEmptyReadonlyArray<[id: number, name: string]>
176
191
  ) =>
177
- sql`INSERT INTO ${sql(table)} ${
178
- sql.insert(
179
- Arr.map(rows, ([migration_id, name]) => ({ migration_id, name }))
180
- )
181
- }`.withoutTransform
192
+ transformWrites(
193
+ sql`INSERT INTO ${sql(table)} ${
194
+ sql.insert(
195
+ Arr.map(rows, ([migration_id, name]) => ({ migration_id, name }))
196
+ )
197
+ }`.withoutTransform
198
+ )
182
199
 
183
200
  const latestMigration = Effect.map(
184
201
  sql<{ migration_id: number; name: string; created_at: Date }>`SELECT migration_id, name, created_at FROM ${
@@ -81,6 +81,10 @@ export interface SqlClient extends Constructor {
81
81
  * client's, such as one that sets its isolation level, provide
82
82
  * `BeginTransaction`.
83
83
  *
84
+ * To retry serialization failures or deadlocks, wrap the outermost
85
+ * `withTransaction` in `Effect.retry`. Each attempt starts a fresh
86
+ * transaction and re-runs the whole effect, including `COMMIT`.
87
+ *
84
88
  * **Gotchas**
85
89
  *
86
90
  * Deferred invalidations run individually after the commit and don't join an
@@ -88,6 +92,35 @@ export interface SqlClient extends Constructor {
88
92
  * the transaction's context defers, and a fiber that outlives the
89
93
  * transaction can't defer through it, as with `afterCommit`. Transactions
90
94
  * that don't go through `makeWithTransaction` invalidate immediately.
95
+ *
96
+ * Retrying a nested transaction only retries its savepoint, which can't
97
+ * recover an outer transaction the database has aborted. Since a retried
98
+ * effect runs more than once, keep non-transactional side effects in
99
+ * `afterCommit`, which discards the callbacks of a failed attempt but
100
+ * doesn't guarantee they run exactly once. Don't retry `ConnectionError`: a
101
+ * `COMMIT` that lost its connection may have committed, and retrying would
102
+ * apply its writes twice.
103
+ *
104
+ * **Example** (Retrying serialization failures and deadlocks)
105
+ *
106
+ * ```ts
107
+ * import { Effect, Schedule } from "effect"
108
+ * import { SqlClient } from "effect/sql"
109
+ *
110
+ * const program = Effect.gen(function*() {
111
+ * const sql = yield* SqlClient.SqlClient
112
+ * return yield* sql.withTransaction(
113
+ * sql`UPDATE accounts SET balance = balance - 10 WHERE id = 1`
114
+ * ).pipe(
115
+ * Effect.provideService(SqlClient.BeginTransaction, "BEGIN ISOLATION LEVEL SERIALIZABLE"),
116
+ * Effect.retry({
117
+ * while: (error) => error.reason._tag === "SerializationError" || error.reason._tag === "DeadlockError",
118
+ * times: 5,
119
+ * schedule: Schedule.exponential("10 millis").pipe(Schedule.jittered)
120
+ * })
121
+ * )
122
+ * })
123
+ * ```
91
124
  */
92
125
  readonly withTransaction: <R, E, A>(
93
126
  self: Effect.Effect<A, E, R>
@@ -206,9 +239,8 @@ export declare namespace SqlClient {
206
239
  readonly spanAttributes: ReadonlyArray<readonly [string, unknown]>
207
240
  readonly transactionService?: Context.Service<TransactionConnection, TransactionConnection.Service>
208
241
  /**
209
- * Whether the transaction control statements - `BEGIN`, `COMMIT`,
210
- * `ROLLBACK`, and the savepoint pair - can be prepared like any other
211
- * statement.
242
+ * Whether the transaction control statements - `BEGIN`, `COMMIT` and
243
+ * `SAVEPOINT` - can be prepared like any other statement.
212
244
  *
213
245
  * **Details**
214
246
  *
@@ -216,6 +248,9 @@ export declare namespace SqlClient {
216
248
  * prepare them stops parsing them again each time. Off by default,
217
249
  * because several databases refuse to prepare transaction control at all;
218
250
  * a driver has to say that its own does not.
251
+ *
252
+ * Rollbacks and savepoint releases always run unprepared, so a prepared
253
+ * statement the backend has dropped cannot stop cleanup.
219
254
  */
220
255
  readonly prepareTransactionControls?: boolean | undefined
221
256
  readonly beginTransaction?: string | undefined
@@ -274,20 +309,20 @@ export const make = Effect.fnUntraced(function*(options: SqlClient.MakeOptions)
274
309
  const rollback = options.rollback ?? "ROLLBACK"
275
310
  const rollbackSavepoint = options.rollbackSavepoint ?? ((name: string) => `ROLLBACK TO SAVEPOINT ${name}`)
276
311
  const transactionAcquirer = options.transactionAcquirer ?? options.acquirer
312
+ const unprepared = (conn: Connection.Connection, sql: string) => conn.executeUnprepared(sql, [], undefined)
277
313
  const control = options.prepareTransactionControls === true
278
314
  ? (conn: Connection.Connection, sql: string) => conn.execute(sql, [], undefined)
279
- : (conn: Connection.Connection, sql: string) => conn.executeUnprepared(sql, [], undefined)
315
+ : unprepared
280
316
  const isTransaction = options.isTransaction
281
317
  const cleanup = isTransaction
282
- ? (conn: Connection.Connection, sql: string) => isTransaction(conn) ? control(conn, sql) : Effect.void
283
- : control
318
+ ? (conn: Connection.Connection, sql: string) => isTransaction(conn) ? unprepared(conn, sql) : Effect.void
319
+ : unprepared
284
320
  const withTransaction = makeWithTransaction({
285
321
  transactionService,
286
322
  spanAttributes: options.spanAttributes,
287
323
  acquireConnection: (restore) => restore(transactionAcquirer!),
288
324
  // Unlike the client's own, a provided statement isn't fixed, so it isn't prepared.
289
- begin: (conn, statement) =>
290
- statement === undefined ? control(conn, beginTransaction) : conn.executeUnprepared(statement, [], undefined),
325
+ begin: (conn, statement) => statement === undefined ? control(conn, beginTransaction) : unprepared(conn, statement),
291
326
  savepoint: (conn, id) => control(conn, savepoint(`effect_sql_${id}`)),
292
327
  releaseSavepoint: releaseSavepoint
293
328
  ? (conn, id) => cleanup(conn, releaseSavepoint(`effect_sql_${id}`))
@@ -27,7 +27,41 @@ import * as Tracer from "../Tracer.ts"
27
27
  import type { Acquirer, Borrower, Connection, Row } from "./SqlConnection.ts"
28
28
  import type { SqlError } from "./SqlError.ts"
29
29
 
30
- const FragmentTypeId = "~effect/sql/Statement/Fragment"
30
+ /**
31
+ * Runtime type identifier for SQL `Fragment` values.
32
+ *
33
+ * @stability unstable
34
+ * @category Type ID
35
+ * @since 4.1.1
36
+ */
37
+ export const FragmentTypeId: unique symbol = Symbol.for("~effect/sql/Statement/Fragment")
38
+
39
+ /**
40
+ * Type of the unique symbol used to brand SQL `Fragment` values.
41
+ *
42
+ * @stability unstable
43
+ * @category Type ID
44
+ * @since 4.1.1
45
+ */
46
+ export type FragmentTypeId = typeof FragmentTypeId
47
+
48
+ /**
49
+ * Runtime type identifier for SQL `Segment` values.
50
+ *
51
+ * @stability unstable
52
+ * @category Type ID
53
+ * @since 4.1.1
54
+ */
55
+ export const SegmentTypeId: unique symbol = Symbol.for("~effect/sql/Statement/Segment")
56
+
57
+ /**
58
+ * Type of the unique symbol used to brand SQL `Segment` values.
59
+ *
60
+ * @stability unstable
61
+ * @category Type ID
62
+ * @since 4.1.1
63
+ */
64
+ export type SegmentTypeId = typeof SegmentTypeId
31
65
 
32
66
  /**
33
67
  * Composable SQL fragment represented as low-level segments that can be
@@ -38,7 +72,7 @@ const FragmentTypeId = "~effect/sql/Statement/Fragment"
38
72
  * @since 4.0.0
39
73
  */
40
74
  export interface Fragment {
41
- readonly [FragmentTypeId]: typeof FragmentTypeId
75
+ readonly [FragmentTypeId]: FragmentTypeId
42
76
  readonly segments: ReadonlyArray<Segment>
43
77
  }
44
78
 
@@ -147,7 +181,7 @@ export const isFragment = (u: unknown): u is Fragment => hasProperty(u, Fragment
147
181
  export const isCustom = <A extends Custom<any, any, any, any>>(
148
182
  kind: A["kind"]
149
183
  ) =>
150
- (u: unknown): u is A => hasProperty(u, "_tag") && u._tag === "Custom" && (u as any).kind === kind
184
+ (u: unknown): u is A => isSegment(u) && u._tag === "Custom" && u.kind === kind
151
185
 
152
186
  /**
153
187
  * Union of low-level segment types that make up a SQL `Fragment`.
@@ -175,6 +209,7 @@ export type Segment =
175
209
  * @since 4.0.0
176
210
  */
177
211
  export interface Literal {
212
+ readonly [SegmentTypeId]: SegmentTypeId
178
213
  readonly _tag: "Literal"
179
214
  readonly value: string
180
215
  readonly params?: ReadonlyArray<unknown> | undefined
@@ -189,6 +224,7 @@ export interface Literal {
189
224
  * @since 4.0.0
190
225
  */
191
226
  export const literal = (value: string, params?: ReadonlyArray<unknown> | undefined): Literal => ({
227
+ [SegmentTypeId]: SegmentTypeId,
192
228
  _tag: "Literal",
193
229
  value,
194
230
  params
@@ -202,6 +238,7 @@ export const literal = (value: string, params?: ReadonlyArray<unknown> | undefin
202
238
  * @since 4.0.0
203
239
  */
204
240
  export interface Identifier {
241
+ readonly [SegmentTypeId]: SegmentTypeId
205
242
  readonly _tag: "Identifier"
206
243
  readonly value: string
207
244
  }
@@ -215,6 +252,7 @@ export interface Identifier {
215
252
  * @since 4.0.0
216
253
  */
217
254
  export const identifier = (value: string): Identifier => ({
255
+ [SegmentTypeId]: SegmentTypeId,
218
256
  _tag: "Identifier",
219
257
  value
220
258
  })
@@ -228,6 +266,7 @@ export const identifier = (value: string): Identifier => ({
228
266
  * @since 4.0.0
229
267
  */
230
268
  export interface Parameter {
269
+ readonly [SegmentTypeId]: SegmentTypeId
231
270
  readonly _tag: "Parameter"
232
271
  readonly value: unknown
233
272
  }
@@ -240,6 +279,7 @@ export interface Parameter {
240
279
  * @since 4.0.0
241
280
  */
242
281
  export const parameter = (value: unknown): Parameter => ({
282
+ [SegmentTypeId]: SegmentTypeId,
243
283
  _tag: "Parameter",
244
284
  value
245
285
  })
@@ -253,6 +293,7 @@ export const parameter = (value: unknown): Parameter => ({
253
293
  * @since 4.0.0
254
294
  */
255
295
  export interface ArrayHelper {
296
+ readonly [SegmentTypeId]: SegmentTypeId
256
297
  readonly _tag: "ArrayHelper"
257
298
  readonly value: ReadonlyArray<unknown | Fragment>
258
299
  }
@@ -265,6 +306,7 @@ export interface ArrayHelper {
265
306
  * @since 4.0.0
266
307
  */
267
308
  export const arrayHelper = (value: ReadonlyArray<unknown | Fragment>): ArrayHelper => ({
309
+ [SegmentTypeId]: SegmentTypeId,
268
310
  _tag: "ArrayHelper",
269
311
  value
270
312
  })
@@ -278,6 +320,7 @@ export const arrayHelper = (value: ReadonlyArray<unknown | Fragment>): ArrayHelp
278
320
  * @since 4.0.0
279
321
  */
280
322
  export interface RecordInsertHelper {
323
+ readonly [SegmentTypeId]: SegmentTypeId
281
324
  readonly _tag: "RecordInsertHelper"
282
325
  readonly value: Arr.NonEmptyReadonlyArray<Record<string, unknown>>
283
326
  /**
@@ -288,6 +331,7 @@ export interface RecordInsertHelper {
288
331
  }
289
332
 
290
333
  const RecordInsertHelperProto = {
334
+ [SegmentTypeId]: SegmentTypeId,
291
335
  _tag: "RecordInsertHelper" as const,
292
336
  returning(this: RecordInsertHelper, sql: string | Identifier | Fragment) {
293
337
  const self = Object.create(Object.getPrototypeOf(this))
@@ -322,6 +366,7 @@ export const recordInsertHelper = (
322
366
  * @since 4.0.0
323
367
  */
324
368
  export interface RecordUpdateHelper {
369
+ readonly [SegmentTypeId]: SegmentTypeId
325
370
  readonly _tag: "RecordUpdateHelper"
326
371
  readonly value: Arr.NonEmptyReadonlyArray<Record<string, unknown>>
327
372
  readonly alias: string
@@ -364,6 +409,7 @@ export const recordUpdateHelper = (
364
409
  * @since 4.0.0
365
410
  */
366
411
  export interface RecordUpdateHelperSingle {
412
+ readonly [SegmentTypeId]: SegmentTypeId
367
413
  readonly _tag: "RecordUpdateHelperSingle"
368
414
  readonly value: Record<string, unknown>
369
415
  readonly omit: ReadonlyArray<string>
@@ -411,6 +457,7 @@ export interface Custom<
411
457
  B = void,
412
458
  C = void
413
459
  > {
460
+ readonly [SegmentTypeId]: SegmentTypeId
414
461
  readonly _tag: "Custom"
415
462
  readonly kind: T
416
463
  readonly paramA: A
@@ -433,7 +480,7 @@ export const custom = <C extends Custom<any, any, any, any>>(
433
480
  paramA: C["paramA"],
434
481
  paramB: C["paramB"],
435
482
  paramC: C["paramC"]
436
- ): C => ({ _tag: "Custom", kind, paramA, paramB, paramC } as C)
483
+ ): C => ({ [SegmentTypeId]: SegmentTypeId, _tag: "Custom", kind, paramA, paramB, paramC } as C)
437
484
 
438
485
  /**
439
486
  * Names the primitive value categories recognized by SQL statement helpers and
@@ -1613,24 +1660,7 @@ const withStatement = <A, X, E, R>(
1613
1660
  )
1614
1661
  })
1615
1662
 
1616
- const isSegment = (u: unknown): u is Segment => {
1617
- if (!hasProperty(u, "_tag")) {
1618
- return false
1619
- }
1620
- switch (u._tag) {
1621
- case "Literal":
1622
- case "Parameter":
1623
- case "ArrayHelper":
1624
- case "RecordInsertHelper":
1625
- case "RecordUpdateHelper":
1626
- case "RecordUpdateHelperSingle":
1627
- case "Identifier":
1628
- case "Custom":
1629
- return true
1630
- default:
1631
- return false
1632
- }
1633
- }
1663
+ const isSegment = (u: unknown): u is Segment => hasProperty(u, SegmentTypeId)
1634
1664
 
1635
1665
  function convertLiteralOrFragment(clause: string | Fragment): Array<Segment> {
1636
1666
  if (typeof clause === "string") {
@@ -88,6 +88,10 @@ export interface Workflow<
88
88
  * nor gets interrupted with it. The cluster engine retains the parent for
89
89
  * observability.
90
90
  *
91
+ * Inside a workflow, an awaited child that is interrupted fails the parent
92
+ * with a defect rather than an interrupt, so the parent can recover with
93
+ * `Effect.catchDefect` or `Effect.exit`.
94
+ *
91
95
  * **Gotchas**
92
96
  *
93
97
  * Do not await a detached execution later. Executing the same payload joins
@@ -583,6 +583,11 @@ export const makeUnsafe = (options: Encoded): WorkflowEngine["Service"] =>
583
583
  if (wrapped._tag === "Suspended") {
584
584
  return yield* Workflow.suspend(parentInstance.value)
585
585
  }
586
+ // A child's interrupt is an outcome for the parent, not an interrupt of
587
+ // the parent's run, so surface it as a defect the parent can observe.
588
+ if (Exit.isFailure(wrapped.exit) && Cause.hasInterruptsOnly(wrapped.exit.cause)) {
589
+ return yield* Effect.die(`${self._tag}.execute: execution "${executionId}" was interrupted`)
590
+ }
586
591
  return yield* wrapped.exit
587
592
  }
588
593