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
@@ -1,5 +1,7 @@
1
1
  import * as Arr from "../Array.ts"
2
+ import * as Deferred from "../Deferred.ts"
2
3
  import * as Effect from "../Effect.ts"
4
+ import * as Fiber from "../Fiber.ts"
3
5
  import type * as FileSystem from "../FileSystem.ts"
4
6
  import * as Order from "../Order.ts"
5
7
  import type { PlatformError } from "../PlatformError.ts"
@@ -7,7 +9,7 @@ import * as Predicate from "../Predicate.ts"
7
9
 
8
10
  /** @internal */
9
11
  export type Token =
10
- | { readonly _tag: "Char"; readonly value: string }
12
+ | { readonly _tag: "Char"; readonly code: number }
11
13
  | { readonly _tag: "Any" }
12
14
  | { readonly _tag: "Star" }
13
15
  /** `ranges` holds inclusive code point pairs: `[from0, to0, from1, to1, ...]`. */
@@ -17,7 +19,13 @@ export type Token =
17
19
  export type Node =
18
20
  | { readonly _tag: "Globstar" }
19
21
  | { readonly _tag: "Literal"; readonly value: string }
20
- | { readonly _tag: "Wildcard"; readonly tokens: ReadonlyArray<Token>; readonly leadingDot: boolean }
22
+ | {
23
+ readonly _tag: "Wildcard"
24
+ readonly tokens: ReadonlyArray<Token>
25
+ readonly leadingDot: boolean
26
+ /** Set when the segment is `prefix*suffix`, which needs no backtracking. */
27
+ readonly affixes: { readonly prefix: string; readonly suffix: string } | undefined
28
+ }
21
29
  /** Ends one expanded pattern. */
22
30
  | { readonly _tag: "End"; readonly directoryOnly: boolean }
23
31
 
@@ -33,6 +41,21 @@ export interface Compiled {
33
41
  readonly nodes: ReadonlyArray<Node>
34
42
  readonly initial: States
35
43
  readonly dot: boolean
44
+ /** The same automaton as bitmasks for `match`, when `nodes` fits in 32 bits. */
45
+ readonly masks: Masks | undefined
46
+ }
47
+
48
+ /**
49
+ * Node sets where bit `i` stands for `nodes[i]`.
50
+ *
51
+ * @internal
52
+ */
53
+ export interface Masks {
54
+ readonly initial: number
55
+ readonly globstar: number
56
+ readonly end: number
57
+ /** `End` nodes that also accept files. */
58
+ readonly endFile: number
36
59
  }
37
60
 
38
61
  // -----------------------------------------------------------------------------
@@ -56,7 +79,31 @@ export const compile = (patterns: ReadonlyArray<string>, dot: boolean): Compiled
56
79
  }
57
80
  }
58
81
  }
59
- return { nodes, initial: closure(nodes, starts), dot }
82
+ const initial = closure(nodes, starts)
83
+ return { nodes, initial, dot, masks: nodes.length <= 32 ? compileMasks(nodes, initial) : undefined }
84
+ }
85
+
86
+ const compileMasks = (nodes: ReadonlyArray<Node>, initial: States): Masks => {
87
+ let start = 0
88
+ for (const state of initial) {
89
+ start |= 1 << state
90
+ }
91
+ let globstar = 0
92
+ let end = 0
93
+ let endFile = 0
94
+ for (let i = 0; i < nodes.length; i++) {
95
+ const node = nodes[i]
96
+ const bit = 1 << i
97
+ if (node._tag === "Globstar") {
98
+ globstar |= bit
99
+ } else if (node._tag === "End") {
100
+ end |= bit
101
+ if (!node.directoryOnly) {
102
+ endFile |= bit
103
+ }
104
+ }
105
+ }
106
+ return { initial: start, globstar, end, endFile }
60
107
  }
61
108
 
62
109
  // Reject UNC paths rather than collapsing them into local absolute paths.
@@ -150,7 +197,7 @@ const compileSegment = (chars: ReadonlyArray<string>): Node => {
150
197
  const char = chars[i]
151
198
  if (char === "\\" && i + 1 < chars.length) {
152
199
  literal += chars[++i]
153
- tokens.push({ _tag: "Char", value: chars[i] })
200
+ tokens.push({ _tag: "Char", code: chars[i].codePointAt(0)! })
154
201
  } else if (char === "*") {
155
202
  magic = true
156
203
  if (tokens[tokens.length - 1]?._tag !== "Star") {
@@ -164,7 +211,7 @@ const compileSegment = (chars: ReadonlyArray<string>): Node => {
164
211
  if (parsed === undefined) {
165
212
  classes = false
166
213
  literal += char
167
- tokens.push({ _tag: "Char", value: char })
214
+ tokens.push({ _tag: "Char", code: char.codePointAt(0)! })
168
215
  } else {
169
216
  magic = true
170
217
  tokens.push(parsed.token)
@@ -172,14 +219,43 @@ const compileSegment = (chars: ReadonlyArray<string>): Node => {
172
219
  }
173
220
  } else {
174
221
  literal += char
175
- tokens.push({ _tag: "Char", value: char })
222
+ tokens.push({ _tag: "Char", code: char.codePointAt(0)! })
176
223
  }
177
224
  }
178
225
  if (!magic) {
179
226
  return { _tag: "Literal", value: literal }
180
227
  }
181
228
  const first = tokens[0]
182
- return { _tag: "Wildcard", tokens, leadingDot: first._tag === "Char" && first.value === "." }
229
+ return {
230
+ _tag: "Wildcard",
231
+ tokens,
232
+ leadingDot: first._tag === "Char" && first.code === 0x2e,
233
+ affixes: compileAffixes(tokens)
234
+ }
235
+ }
236
+
237
+ // Lone surrogates stay on the token path: `startsWith` would match them
238
+ // against half of a surrogate pair.
239
+ const compileAffixes = (
240
+ tokens: ReadonlyArray<Token>
241
+ ): { readonly prefix: string; readonly suffix: string } | undefined => {
242
+ let prefix = ""
243
+ let suffix: string | undefined
244
+ for (const token of tokens) {
245
+ if (token._tag === "Star" && suffix === undefined) {
246
+ suffix = ""
247
+ } else if (token._tag === "Char" && (token.code < 0xd800 || token.code > 0xdfff)) {
248
+ const char = String.fromCodePoint(token.code)
249
+ if (suffix === undefined) {
250
+ prefix += char
251
+ } else {
252
+ suffix += char
253
+ }
254
+ } else {
255
+ return undefined
256
+ }
257
+ }
258
+ return suffix === undefined ? undefined : { prefix, suffix }
183
259
  }
184
260
 
185
261
  const range = (from: string, to: string): Array<number> => [from.codePointAt(0)!, to.codePointAt(0)!]
@@ -392,16 +468,15 @@ const expandSequence = (sequence: BraceSequence): Array<string> => {
392
468
  // matching
393
469
  // -----------------------------------------------------------------------------
394
470
 
395
- const matchToken = (token: Token, char: string): boolean => {
471
+ const matchToken = (token: Token, code: number): boolean => {
396
472
  switch (token._tag) {
397
473
  case "Char":
398
- return token.value === char
474
+ return token.code === code
399
475
  case "Any":
400
476
  return true
401
477
  case "Star":
402
478
  return false
403
479
  case "Class": {
404
- const code = char.codePointAt(0)!
405
480
  let found = false
406
481
  for (let i = 0; i < token.ranges.length && !found; i += 2) {
407
482
  found = code >= token.ranges[i] && code <= token.ranges[i + 1]
@@ -411,31 +486,41 @@ const matchToken = (token: Token, char: string): boolean => {
411
486
  }
412
487
  }
413
488
 
489
+ // UTF-16 length of the code point
490
+ const width = (code: number): number => code > 0xffff ? 2 : 1
491
+
414
492
  // Backtrack only to the last `*`, bounding retries by tokens × characters.
415
493
  const matchWildcard = (
416
494
  node: Extract<Node, { _tag: "Wildcard" }>,
417
495
  name: string,
418
- chars: ReadonlyArray<string>,
419
496
  dot: boolean
420
497
  ): boolean => {
421
498
  if (name === "." || name === ".." || (name.startsWith(".") && !dot && !node.leadingDot)) {
422
499
  return false
423
500
  }
501
+ if (node.affixes !== undefined) {
502
+ const { prefix, suffix } = node.affixes
503
+ return name.length >= prefix.length + suffix.length && name.startsWith(prefix) && name.endsWith(suffix)
504
+ }
424
505
  const { tokens } = node
425
506
  let t = 0
426
507
  let c = 0
427
508
  let star = -1
428
509
  let resume = 0
429
- while (c < chars.length) {
510
+ while (c < name.length) {
430
511
  if (tokens[t]?._tag === "Star") {
431
512
  star = t++
432
513
  resume = c
433
- } else if (t < tokens.length && matchToken(tokens[t], chars[c])) {
514
+ continue
515
+ }
516
+ const code = name.codePointAt(c)!
517
+ if (t < tokens.length && matchToken(tokens[t], code)) {
434
518
  t++
435
- c++
519
+ c += width(code)
436
520
  } else if (star !== -1) {
437
521
  t = star + 1
438
- c = ++resume
522
+ resume += width(name.codePointAt(resume)!)
523
+ c = resume
439
524
  } else {
440
525
  return false
441
526
  }
@@ -468,7 +553,6 @@ const step = (compiled: Compiled, states: States, name: string): Step => {
468
553
  const viaGlobstar = new Set<number>()
469
554
  // only a literal segment can name the root of an absolute path
470
555
  const root = isRoot(name)
471
- let chars: ReadonlyArray<string> | undefined
472
556
  for (const state of states) {
473
557
  const node = compiled.nodes[state]
474
558
  switch (node._tag) {
@@ -485,7 +569,7 @@ const step = (compiled: Compiled, states: States, name: string): Step => {
485
569
  break
486
570
  }
487
571
  case "Wildcard": {
488
- if (!root && matchWildcard(node, name, chars ??= Array.from(name), compiled.dot)) {
572
+ if (!root && matchWildcard(node, name, compiled.dot)) {
489
573
  explicit.add(state + 1)
490
574
  }
491
575
  break
@@ -535,20 +619,78 @@ const needsDirectory = (compiled: Compiled, states: States): boolean => {
535
619
  return false
536
620
  }
537
621
 
622
+ // `step` and `union` over bitmasks; a `**` node's successor is the next bit
623
+ const stepMasks = (compiled: Compiled, masks: Masks, states: number, name: string, root: boolean): number => {
624
+ let next = !root && name !== "." && name !== ".." && (compiled.dot || !name.startsWith("."))
625
+ ? states & masks.globstar
626
+ : 0
627
+ // the remaining states are `Literal` and `Wildcard` nodes
628
+ let segments = states & ~(masks.globstar | masks.end)
629
+ while (segments !== 0) {
630
+ const bit = segments & -segments
631
+ segments ^= bit
632
+ const node = compiled.nodes[31 - Math.clz32(bit)]
633
+ if (
634
+ node._tag === "Literal"
635
+ ? node.value === name
636
+ : node._tag === "Wildcard" && !root && matchWildcard(node, name, compiled.dot)
637
+ ) {
638
+ next |= bit << 1
639
+ }
640
+ }
641
+ return next | ((next & masks.globstar) << 1)
642
+ }
643
+
538
644
  /** @internal */
539
645
  export const match = (compiled: Compiled, path: string): boolean => {
540
646
  if (path === "" || isUnc(path)) {
541
647
  return false
542
648
  }
543
- const { directory, segments } = normalize(path.split("/"))
544
- let states = compiled.initial
545
- for (const name of segments) {
546
- states = union(step(compiled, states, name))
547
- if (states.size === 0) {
548
- return false
649
+ const { masks } = compiled
650
+ if (masks === undefined) {
651
+ const { directory, segments } = normalize(path.split("/"))
652
+ let states = compiled.initial
653
+ for (const name of segments) {
654
+ states = union(step(compiled, states, name))
655
+ if (states.size === 0) {
656
+ return false
657
+ }
549
658
  }
659
+ return accepts(compiled, states, directory)
660
+ }
661
+ // The same segments as `normalize(path.split("/"))`, without the arrays
662
+ let states = masks.initial
663
+ let stepped = false
664
+ let start = 0
665
+ let end = path.indexOf("/")
666
+ if (end === 0 || (end === 2 && drive.test(path))) {
667
+ states = stepMasks(compiled, masks, states, path.slice(0, end === 0 ? 0 : 3), true)
668
+ stepped = true
669
+ start = end + 1
670
+ end = path.indexOf("/", start)
550
671
  }
551
- return accepts(compiled, states, directory)
672
+ let directory = false
673
+ let dotted = false
674
+ while (states !== 0) {
675
+ const name = end === -1 ? path.slice(start) : path.slice(start, end)
676
+ if (name === "" || name === ".") {
677
+ dotted ||= name === "."
678
+ directory = end === -1 && stepped
679
+ } else {
680
+ states = stepMasks(compiled, masks, states, name, false)
681
+ stepped = true
682
+ directory = false
683
+ }
684
+ if (end === -1) {
685
+ break
686
+ }
687
+ start = end + 1
688
+ end = path.indexOf("/", start)
689
+ }
690
+ if (!stepped && dotted) {
691
+ states = stepMasks(compiled, masks, states, ".", false)
692
+ }
693
+ return (states & (directory ? masks.end : masks.endFile)) !== 0
552
694
  }
553
695
 
554
696
  // -----------------------------------------------------------------------------
@@ -570,6 +712,8 @@ const isIgnorable = (error: PlatformError): boolean => isMissing(error) || error
570
712
  // Node and Deno report `readLink` on a path that is not a link as "Unknown"
571
713
  const isNotLink = (error: PlatformError): boolean => isIgnorable(error) || error.reason._tag === "Unknown"
572
714
 
715
+ const DIRECTORY_CONCURRENCY = 16
716
+
573
717
  const byName = Order.mapInput(Order.String, (entry: FileSystem.DirectoryEntry) => entry.name)
574
718
 
575
719
  /** @internal */
@@ -660,12 +804,13 @@ const walk = Effect.fnUntraced(function*(
660
804
  readonly next: States | undefined
661
805
  }
662
806
 
663
- const visit = Effect.fnUntraced(function*(
807
+ // Synchronous unless the entry type must be resolved through `stat` / `readLink`
808
+ const visit = (
664
809
  { name, type }: FileSystem.DirectoryEntry,
665
810
  parent: string,
666
811
  states: States,
667
812
  excluded: States
668
- ): Effect.fn.Return<Child | undefined, PlatformError> {
813
+ ): Child | undefined | Effect.Effect<Child | undefined, PlatformError> => {
669
814
  const result = step(compiled, states, name)
670
815
  const next = union(result)
671
816
  if (next.size === 0) {
@@ -674,49 +819,110 @@ const walk = Effect.fnUntraced(function*(
674
819
  const relative = join(parent, name)
675
820
  const nextExcluded = union(step(excludes, excluded, name))
676
821
  const descend = canDescend(compiled, next)
677
- const isDirectory = !descend && !needsDirectory(compiled, next) && !excludeNeedsDirectory
678
- ? false
679
- : type !== "Unknown" && type !== "SymbolicLink"
680
- ? type === "Directory"
681
- : yield* fs.stat(toFsPath(relative)).pipe(
682
- Effect.map((info) => info.type === "Directory"),
683
- Effect.catchIf(isIgnorable, () => Effect.succeed(false))
684
- )
685
- const accepted = accepts(compiled, next, isDirectory)
686
- if ((!accepted && (!isDirectory || !descend)) || accepts(excludes, nextExcluded, isDirectory)) {
687
- return undefined
688
- }
689
- const child = (next: States | undefined): Child => ({ relative, accepted, excluded: nextExcluded, next })
690
- if (!isDirectory || !descend) {
691
- return child(undefined)
822
+ const child = (isDirectory: boolean, link: boolean): Child | undefined => {
823
+ const accepted = accepts(compiled, next, isDirectory)
824
+ if ((!accepted && (!isDirectory || !descend)) || accepts(excludes, nextExcluded, isDirectory)) {
825
+ return undefined
826
+ }
827
+ return {
828
+ relative,
829
+ accepted,
830
+ excluded: nextExcluded,
831
+ next: !isDirectory || !descend
832
+ ? undefined
833
+ : !link || result.globstar.size === 0
834
+ ? next
835
+ // Follow symlinks only through explicit segments, not `**`.
836
+ : canDescend(compiled, result.explicit)
837
+ ? result.explicit
838
+ : undefined
839
+ }
692
840
  }
693
- if (result.globstar.size === 0) {
694
- return child(next)
841
+ if (!descend && !needsDirectory(compiled, next) && !excludeNeedsDirectory) {
842
+ return child(false, false)
695
843
  }
696
- // Follow symlinks only through explicit segments, not `**`.
697
- const followed = canDescend(compiled, result.explicit) ? result.explicit : undefined
698
- if (type !== "Unknown") {
699
- return child(type === "SymbolicLink" ? followed : next)
844
+ if (type !== "Unknown" && type !== "SymbolicLink") {
845
+ return child(type === "Directory", false)
700
846
  }
701
- return yield* fs.readLink(toFsPath(relative)).pipe(
702
- Effect.as(child(followed)),
703
- Effect.catchIf(isNotLink, () => Effect.succeed(child(next)))
847
+ const path = toFsPath(relative)
848
+ return fs.stat(path).pipe(
849
+ Effect.map((info) => info.type === "Directory"),
850
+ Effect.catchIf(isIgnorable, () => Effect.succeed(false)),
851
+ Effect.flatMap((isDirectory) => {
852
+ const resolved = child(isDirectory, type === "SymbolicLink")
853
+ if (type === "SymbolicLink" || resolved?.next === undefined || result.globstar.size === 0) {
854
+ return Effect.succeed(resolved)
855
+ }
856
+ return fs.readLink(path).pipe(
857
+ Effect.as(child(isDirectory, true)),
858
+ Effect.catchIf(isNotLink, () => Effect.succeed(resolved))
859
+ )
860
+ })
861
+ )
862
+ }
863
+
864
+ // Walk subdirectories in forked fibers while slots are free and inline
865
+ // otherwise, bounding directory reads in flight across the whole walk. A
866
+ // forked walk reports its failure through `failed` so the whole walk ends
867
+ // promptly even while its parent is walking another subdirectory inline.
868
+ // Once the root walk is finishing, forked walks only exit because it
869
+ // interrupts them.
870
+ let slots = DIRECTORY_CONCURRENCY
871
+ let finishing = false
872
+ const failed = Deferred.makeUnsafe<never, PlatformError>()
873
+ const fork = (walk: Effect.Effect<Array<string>, PlatformError>) =>
874
+ walk.pipe(
875
+ Effect.onError((cause) =>
876
+ Effect.sync(() => {
877
+ if (!finishing) {
878
+ Deferred.doneUnsafe(failed, Effect.failCause(cause))
879
+ }
880
+ })
881
+ ),
882
+ Effect.ensuring(Effect.sync(() => {
883
+ slots++
884
+ })),
885
+ Effect.forkChild
704
886
  )
705
- })
706
887
 
707
888
  const directory = Effect.fnUntraced(function*(
708
889
  relative: string,
709
890
  states: States,
710
891
  excluded: States,
711
- named: boolean,
712
- out: Array<string>
713
- ): Effect.fn.Return<void, PlatformError> {
892
+ named: boolean
893
+ ): Effect.fn.Return<Array<string>, PlatformError> {
714
894
  const listing = yield* entries(relative, states, named)
715
- const children = yield* Effect.forEach(
716
- listing.entries,
717
- (entry) => visit(entry, relative, states, excluded),
718
- { concurrency: 16 }
719
- )
895
+ const children: Array<Child | undefined> = []
896
+ const pending: Array<Effect.Effect<void, PlatformError>> = []
897
+ for (const entry of listing.entries) {
898
+ const child = visit(entry, relative, states, excluded)
899
+ if (Effect.isEffect(child)) {
900
+ const index = children.push(undefined) - 1
901
+ pending.push(Effect.map(child, (child) => {
902
+ children[index] = child
903
+ }))
904
+ } else {
905
+ children.push(child)
906
+ }
907
+ }
908
+ if (pending.length > 0) {
909
+ yield* Effect.all(pending, { concurrency: 16, discard: true })
910
+ }
911
+ const subtrees: Array<Effect.Effect<Array<string>, PlatformError>> = []
912
+ for (const child of children) {
913
+ if (child?.next !== undefined) {
914
+ const walk = directory(child.relative, child.next, child.excluded, listing.named)
915
+ if (slots > 0) {
916
+ slots--
917
+ subtrees.push(Fiber.join(yield* fork(walk)))
918
+ } else {
919
+ subtrees.push(Effect.succeed(yield* walk))
920
+ }
921
+ }
922
+ }
923
+ // Splice subtrees back in listing order
924
+ const out: Array<string> = []
925
+ let index = 0
720
926
  for (const child of children) {
721
927
  if (child === undefined) {
722
928
  continue
@@ -725,12 +931,22 @@ const walk = Effect.fnUntraced(function*(
725
931
  out.push(child.relative)
726
932
  }
727
933
  if (child.next !== undefined) {
728
- yield* directory(child.relative, child.next, child.excluded, listing.named, out)
934
+ for (const path of yield* subtrees[index++]) {
935
+ out.push(path)
936
+ }
729
937
  }
730
938
  }
939
+ return out
731
940
  })
732
941
 
733
- const out: Array<string> = []
734
- yield* directory("", compiled.initial, excludes.initial, true, out)
735
- return out
942
+ // The losing walk is interrupted along with every fiber it forked
943
+ return yield* Effect.raceFirst(
944
+ Effect.ensuring(
945
+ directory("", compiled.initial, excludes.initial, true),
946
+ Effect.sync(() => {
947
+ finishing = true
948
+ })
949
+ ),
950
+ Deferred.await(failed)
951
+ )
736
952
  })
@@ -1,4 +1,5 @@
1
1
  import { isReadonlyArrayNonEmpty, type NonEmptyReadonlyArray } from "../Array.ts"
2
+ import * as Clock from "../Clock.ts"
2
3
  import * as Context from "../Context.ts"
3
4
  import * as Effect from "../Effect.ts"
4
5
  import type * as GraphQL from "../graphql/GraphQL.ts"
@@ -10,10 +11,12 @@ import {
10
11
  GraphQLClientError,
11
12
  type GraphQLError,
12
13
  type Reason,
13
- ResponseError
14
+ ResponseError,
15
+ TransportError
14
16
  } from "../graphql/GraphQLClientError.ts"
15
17
  import type { ExecutionResult, GraphQLProtocol, GraphQLRequest } from "../graphql/GraphQLProtocol.ts"
16
18
  import * as Schema from "../Schema.ts"
19
+ import { parseRetryAfter } from "./graphqlHeaders.ts"
17
20
 
18
21
  /**
19
22
  * Builds a response error with raw data and transport metadata.
@@ -32,6 +35,10 @@ export const responseError = (
32
35
  ...(result?.headers === undefined ? {} : { headers: result.headers })
33
36
  })
34
37
 
38
+ /** A result without an HTTP status, such as a graphql-ws event, counts as 2xx. */
39
+ const isStatusOk = (result: ExecutionResult): boolean =>
40
+ result.status === undefined || (result.status >= 200 && result.status < 300)
41
+
35
42
  /**
36
43
  * Raw responses for partial results, used by resolvers to preserve error
37
44
  * metadata. Mock client results have no entry.
@@ -210,27 +217,38 @@ export const makeCodec = <Op extends GraphQL.Any>(operation: Op) => {
210
217
  Effect.catch(decode(data), schemaFail((description) => new DecodeError({ description })))
211
218
 
212
219
  // GraphQL requires errors when data is null or missing. Check before decoding,
213
- // since schemas such as Schema.Json accept null.
220
+ // since schemas such as Schema.Json accept null. A non-2xx result never
221
+ // succeeds: without errors it is a TransportError carrying the HTTP metadata.
214
222
  const failResponse = (result: ExecutionResult): Effect.Effect<never, GraphQLClientError> => {
215
223
  const errors = result.errors ?? []
216
- return Effect.fail(fail(
217
- isReadonlyArrayNonEmpty(errors)
218
- ? responseError(result, errors)
219
- : new DecodeError({ description: "data is null or missing without errors" })
220
- ))
224
+ if (isReadonlyArrayNonEmpty(errors)) return Effect.fail(fail(responseError(result, errors)))
225
+ if (isStatusOk(result)) {
226
+ return Effect.fail(fail(new DecodeError({ description: "data is null or missing without errors" })))
227
+ }
228
+ return Clock.clockWith((clock) =>
229
+ Effect.fail(fail(
230
+ new TransportError({
231
+ description: `HTTP ${result.status} response without GraphQL errors`,
232
+ status: result.status,
233
+ headers: result.headers,
234
+ retryAfter: parseRetryAfter(result.headers?.["retry-after"], clock.currentTimeMillisUnsafe())
235
+ })
236
+ ))
237
+ )
221
238
  }
222
239
 
223
240
  const decodeStrict = (
224
241
  result: ExecutionResult
225
242
  ): Effect.Effect<GraphQL.Result<Op>, GraphQLClientError, Op["result"]["DecodingServices"]> =>
226
- isReadonlyArrayNonEmpty(result.errors ?? []) || result.data === null || result.data === undefined
243
+ !isStatusOk(result) || isReadonlyArrayNonEmpty(result.errors ?? []) || result.data === null ||
244
+ result.data === undefined
227
245
  ? failResponse(result)
228
246
  : decodeData(result.data)
229
247
 
230
248
  const decodePartial = (
231
249
  result: ExecutionResult
232
250
  ): Effect.Effect<PartialResult<GraphQL.Result<Op>>, GraphQLClientError, Op["result"]["DecodingServices"]> => {
233
- if (result.data === null || result.data === undefined) return failResponse(result)
251
+ if (!isStatusOk(result) || result.data === null || result.data === undefined) return failResponse(result)
234
252
  return Effect.map(decodeData(result.data), (data): PartialResult<GraphQL.Result<Op>> => {
235
253
  const partial = { data, errors: result.errors ?? [] }
236
254
  partialResponses.set(partial, result)
@@ -1,4 +1,6 @@
1
+ import type * as Duration from "../Duration.ts"
1
2
  import * as Headers from "../http/Headers.ts"
3
+ import * as Result from "../Result.ts"
2
4
  import * as Schema from "../Schema.ts"
3
5
  import * as SchemaGetter from "../SchemaGetter.ts"
4
6
 
@@ -9,3 +11,14 @@ export const HeadersSchema = Schema.Record(Schema.String, Schema.String).pipe(
9
11
  encode: SchemaGetter.transform((headers) => ({ ...headers }))
10
12
  })
11
13
  )
14
+
15
+ /**
16
+ * Parses a `Retry-After` header value, either whole seconds or an HTTP date,
17
+ * into a `Duration` measured from `now` (epoch milliseconds). Anything else,
18
+ * such as `1.5` or `-1`, yields `undefined`.
19
+ */
20
+ export const parseRetryAfter = (value: string | undefined, now: number): Duration.Duration | undefined => {
21
+ if (value === undefined) return undefined
22
+ const retryAfter = Headers.parseRetryAfter(value)
23
+ return Result.isSuccess(retryAfter) ? Headers.retryAfterDelay(retryAfter.success, now) : undefined
24
+ }