effect 4.0.2 → 4.0.4

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 (389) hide show
  1. package/AGENTS.md +24 -0
  2. package/CLAUDE.md +24 -0
  3. package/ai-docs/package.json +2 -2
  4. package/ai-docs/src/52_graphql/10_basics.ts +111 -0
  5. package/ai-docs/src/52_graphql/20_testing.ts +45 -0
  6. package/ai-docs/src/52_graphql/30_batching.ts +116 -0
  7. package/ai-docs/src/52_graphql/40_aliased-batching.ts +122 -0
  8. package/ai-docs/src/52_graphql/50_errors-and-retries.ts +74 -0
  9. package/ai-docs/src/52_graphql/index.md +6 -0
  10. package/dist/Context.d.ts.map +1 -1
  11. package/dist/Context.js +12 -3
  12. package/dist/Context.js.map +1 -1
  13. package/dist/Crypto.d.ts +33 -5
  14. package/dist/Crypto.d.ts.map +1 -1
  15. package/dist/Crypto.js +8 -2
  16. package/dist/Crypto.js.map +1 -1
  17. package/dist/DateTime.d.ts +9 -6
  18. package/dist/DateTime.d.ts.map +1 -1
  19. package/dist/DateTime.js +9 -6
  20. package/dist/DateTime.js.map +1 -1
  21. package/dist/Duration.d.ts +6 -6
  22. package/dist/Duration.d.ts.map +1 -1
  23. package/dist/Duration.js +27 -8
  24. package/dist/Duration.js.map +1 -1
  25. package/dist/FileSystem.d.ts +44 -2
  26. package/dist/FileSystem.d.ts.map +1 -1
  27. package/dist/FileSystem.js.map +1 -1
  28. package/dist/RequestResolver.d.ts.map +1 -1
  29. package/dist/RequestResolver.js +4 -10
  30. package/dist/RequestResolver.js.map +1 -1
  31. package/dist/Scheduler.d.ts +9 -0
  32. package/dist/Scheduler.d.ts.map +1 -1
  33. package/dist/Scheduler.js +9 -0
  34. package/dist/Scheduler.js.map +1 -1
  35. package/dist/Schema.d.ts +481 -1
  36. package/dist/Schema.d.ts.map +1 -1
  37. package/dist/Schema.js +362 -4
  38. package/dist/Schema.js.map +1 -1
  39. package/dist/SchemaGetter.d.ts +4 -0
  40. package/dist/SchemaGetter.d.ts.map +1 -1
  41. package/dist/SchemaGetter.js +52 -33
  42. package/dist/SchemaGetter.js.map +1 -1
  43. package/dist/Sink.d.ts.map +1 -1
  44. package/dist/Sink.js +38 -17
  45. package/dist/Sink.js.map +1 -1
  46. package/dist/Stdio.d.ts +7 -1
  47. package/dist/Stdio.d.ts.map +1 -1
  48. package/dist/Stdio.js +1 -0
  49. package/dist/Stdio.js.map +1 -1
  50. package/dist/Stream.d.ts +22 -11
  51. package/dist/Stream.d.ts.map +1 -1
  52. package/dist/Stream.js +21 -6
  53. package/dist/Stream.js.map +1 -1
  54. package/dist/Utils.js +8 -3
  55. package/dist/Utils.js.map +1 -1
  56. package/dist/ai/AnthropicStructuredOutput.js +0 -2
  57. package/dist/ai/AnthropicStructuredOutput.js.map +1 -1
  58. package/dist/ai/Chat.d.ts +2 -2
  59. package/dist/ai/Chat.d.ts.map +1 -1
  60. package/dist/ai/LanguageModel.d.ts +38 -7
  61. package/dist/ai/LanguageModel.d.ts.map +1 -1
  62. package/dist/ai/LanguageModel.js +25 -7
  63. package/dist/ai/LanguageModel.js.map +1 -1
  64. package/dist/ai/McpServer.d.ts +61 -0
  65. package/dist/ai/McpServer.d.ts.map +1 -1
  66. package/dist/ai/McpServer.js +196 -44
  67. package/dist/ai/McpServer.js.map +1 -1
  68. package/dist/ai/Prompt.d.ts.map +1 -1
  69. package/dist/ai/Prompt.js +28 -5
  70. package/dist/ai/Prompt.js.map +1 -1
  71. package/dist/ai/Response.d.ts +4 -3
  72. package/dist/ai/Response.d.ts.map +1 -1
  73. package/dist/ai/Response.js.map +1 -1
  74. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +5 -0
  75. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  76. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  77. package/dist/ai/internal/mcpRuntime.js +1 -0
  78. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  79. package/dist/cli/CliOutput.d.ts +27 -0
  80. package/dist/cli/CliOutput.d.ts.map +1 -1
  81. package/dist/cli/CliOutput.js +33 -10
  82. package/dist/cli/CliOutput.js.map +1 -1
  83. package/dist/cli/Command.d.ts.map +1 -1
  84. package/dist/cli/Command.js +6 -3
  85. package/dist/cli/Command.js.map +1 -1
  86. package/dist/cli/Completions.d.ts +3 -3
  87. package/dist/cli/Completions.d.ts.map +1 -1
  88. package/dist/cli/Completions.js +8 -5
  89. package/dist/cli/Completions.js.map +1 -1
  90. package/dist/cli/GlobalFlag.d.ts +5 -4
  91. package/dist/cli/GlobalFlag.d.ts.map +1 -1
  92. package/dist/cli/GlobalFlag.js +3 -3
  93. package/dist/cli/GlobalFlag.js.map +1 -1
  94. package/dist/cli/Prompt.d.ts +17 -1
  95. package/dist/cli/Prompt.d.ts.map +1 -1
  96. package/dist/cli/Prompt.js +119 -114
  97. package/dist/cli/Prompt.js.map +1 -1
  98. package/dist/cli/internal/ansi.d.ts +0 -1
  99. package/dist/cli/internal/ansi.d.ts.map +1 -1
  100. package/dist/cli/internal/ansi.js +20 -3
  101. package/dist/cli/internal/ansi.js.map +1 -1
  102. package/dist/cli/internal/completions/powershell.d.ts +2 -0
  103. package/dist/cli/internal/completions/powershell.d.ts.map +1 -0
  104. package/dist/cli/internal/completions/powershell.js +273 -0
  105. package/dist/cli/internal/completions/powershell.js.map +1 -0
  106. package/dist/cli/internal/completions/zsh.js +3 -2
  107. package/dist/cli/internal/completions/zsh.js.map +1 -1
  108. package/dist/cli/internal/wizard.d.ts +3 -3
  109. package/dist/cli/internal/wizard.d.ts.map +1 -1
  110. package/dist/cli/internal/wizard.js +21 -18
  111. package/dist/cli/internal/wizard.js.map +1 -1
  112. package/dist/cluster/ClusterSchema.d.ts +28 -0
  113. package/dist/cluster/ClusterSchema.d.ts.map +1 -1
  114. package/dist/cluster/ClusterSchema.js +30 -0
  115. package/dist/cluster/ClusterSchema.js.map +1 -1
  116. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  117. package/dist/cluster/ClusterWorkflowEngine.js +56 -26
  118. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  119. package/dist/cluster/MessageStorage.d.ts +18 -3
  120. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  121. package/dist/cluster/MessageStorage.js +31 -11
  122. package/dist/cluster/MessageStorage.js.map +1 -1
  123. package/dist/cluster/Runners.d.ts.map +1 -1
  124. package/dist/cluster/Runners.js +8 -2
  125. package/dist/cluster/Runners.js.map +1 -1
  126. package/dist/cluster/Sharding.d.ts.map +1 -1
  127. package/dist/cluster/Sharding.js +187 -66
  128. package/dist/cluster/Sharding.js.map +1 -1
  129. package/dist/cluster/SqlMessageStorage.d.ts +59 -19
  130. package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
  131. package/dist/cluster/SqlMessageStorage.js +84 -55
  132. package/dist/cluster/SqlMessageStorage.js.map +1 -1
  133. package/dist/cluster/SqlRunnerStorage.d.ts +68 -19
  134. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  135. package/dist/cluster/SqlRunnerStorage.js +191 -113
  136. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  137. package/dist/cluster/internal/entityManager.js +104 -37
  138. package/dist/cluster/internal/entityManager.js.map +1 -1
  139. package/dist/encoding/Sse.d.ts +6 -0
  140. package/dist/encoding/Sse.d.ts.map +1 -1
  141. package/dist/encoding/Sse.js +62 -70
  142. package/dist/encoding/Sse.js.map +1 -1
  143. package/dist/graphql/GraphQL.d.ts +349 -0
  144. package/dist/graphql/GraphQL.d.ts.map +1 -0
  145. package/dist/graphql/GraphQL.js +207 -0
  146. package/dist/graphql/GraphQL.js.map +1 -0
  147. package/dist/graphql/GraphQLClient.d.ts +562 -0
  148. package/dist/graphql/GraphQLClient.d.ts.map +1 -0
  149. package/dist/graphql/GraphQLClient.js +498 -0
  150. package/dist/graphql/GraphQLClient.js.map +1 -0
  151. package/dist/graphql/GraphQLClientError.d.ts +445 -0
  152. package/dist/graphql/GraphQLClientError.d.ts.map +1 -0
  153. package/dist/graphql/GraphQLClientError.js +479 -0
  154. package/dist/graphql/GraphQLClientError.js.map +1 -0
  155. package/dist/graphql/GraphQLGroup.d.ts +118 -0
  156. package/dist/graphql/GraphQLGroup.d.ts.map +1 -0
  157. package/dist/graphql/GraphQLGroup.js +109 -0
  158. package/dist/graphql/GraphQLGroup.js.map +1 -0
  159. package/dist/graphql/GraphQLMiddleware.d.ts +249 -0
  160. package/dist/graphql/GraphQLMiddleware.d.ts.map +1 -0
  161. package/dist/graphql/GraphQLMiddleware.js +130 -0
  162. package/dist/graphql/GraphQLMiddleware.js.map +1 -0
  163. package/dist/graphql/GraphQLProtocol.d.ts +731 -0
  164. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -0
  165. package/dist/graphql/GraphQLProtocol.js +719 -0
  166. package/dist/graphql/GraphQLProtocol.js.map +1 -0
  167. package/dist/graphql/GraphQLResolver.d.ts +453 -0
  168. package/dist/graphql/GraphQLResolver.d.ts.map +1 -0
  169. package/dist/graphql/GraphQLResolver.js +552 -0
  170. package/dist/graphql/GraphQLResolver.js.map +1 -0
  171. package/dist/graphql/index.d.ts +40 -0
  172. package/dist/graphql/index.d.ts.map +1 -0
  173. package/dist/graphql/index.js +41 -0
  174. package/dist/graphql/index.js.map +1 -0
  175. package/dist/http/FindMyWay/internal/router.js +3 -6
  176. package/dist/http/FindMyWay/internal/router.js.map +1 -1
  177. package/dist/http/HttpClient.d.ts +21 -0
  178. package/dist/http/HttpClient.d.ts.map +1 -1
  179. package/dist/http/HttpClient.js +63 -12
  180. package/dist/http/HttpClient.js.map +1 -1
  181. package/dist/http/HttpEffect.d.ts.map +1 -1
  182. package/dist/http/HttpEffect.js +14 -7
  183. package/dist/http/HttpEffect.js.map +1 -1
  184. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  185. package/dist/http/HttpMiddleware.js +31 -32
  186. package/dist/http/HttpMiddleware.js.map +1 -1
  187. package/dist/http/HttpRouter.d.ts +35 -0
  188. package/dist/http/HttpRouter.d.ts.map +1 -1
  189. package/dist/http/HttpRouter.js +35 -0
  190. package/dist/http/HttpRouter.js.map +1 -1
  191. package/dist/http/HttpServerResponse.d.ts +8 -6
  192. package/dist/http/HttpServerResponse.d.ts.map +1 -1
  193. package/dist/http/HttpServerResponse.js +27 -16
  194. package/dist/http/HttpServerResponse.js.map +1 -1
  195. package/dist/http/HttpTraceContext.d.ts.map +1 -1
  196. package/dist/http/HttpTraceContext.js +4 -2
  197. package/dist/http/HttpTraceContext.js.map +1 -1
  198. package/dist/http/Multipart.d.ts.map +1 -1
  199. package/dist/http/Multipart.js +15 -1
  200. package/dist/http/Multipart.js.map +1 -1
  201. package/dist/http-api/HttpApiScalar.d.ts +5 -0
  202. package/dist/http-api/HttpApiScalar.d.ts.map +1 -1
  203. package/dist/http-api/HttpApiScalar.js.map +1 -1
  204. package/dist/internal/dateTime.js +40 -35
  205. package/dist/internal/dateTime.js.map +1 -1
  206. package/dist/internal/effect.js +19 -9
  207. package/dist/internal/effect.js.map +1 -1
  208. package/dist/internal/graphql.d.ts +2 -0
  209. package/dist/internal/graphql.d.ts.map +1 -0
  210. package/dist/internal/graphql.js +195 -0
  211. package/dist/internal/graphql.js.map +1 -0
  212. package/dist/internal/graphqlHeaders.d.ts +4 -0
  213. package/dist/internal/graphqlHeaders.d.ts.map +1 -0
  214. package/dist/internal/graphqlHeaders.js +11 -0
  215. package/dist/internal/graphqlHeaders.js.map +1 -0
  216. package/dist/internal/request.js +20 -1
  217. package/dist/internal/request.js.map +1 -1
  218. package/dist/internal/schema/toCodeDocument.js +4 -1
  219. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  220. package/dist/internal/schema/toJsonSchemaDocument.js +29 -5
  221. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  222. package/dist/internal/version.js +1 -1
  223. package/dist/net/AddressResolver.d.ts +145 -0
  224. package/dist/net/AddressResolver.d.ts.map +1 -0
  225. package/dist/net/AddressResolver.js +114 -0
  226. package/dist/net/AddressResolver.js.map +1 -0
  227. package/dist/net/Dns.d.ts +596 -0
  228. package/dist/net/Dns.d.ts.map +1 -0
  229. package/dist/net/Dns.js +549 -0
  230. package/dist/net/Dns.js.map +1 -0
  231. package/dist/net/Host.d.ts +369 -0
  232. package/dist/net/Host.d.ts.map +1 -0
  233. package/dist/net/Host.js +397 -0
  234. package/dist/net/Host.js.map +1 -0
  235. package/dist/net/IpInterface.d.ts +76 -3
  236. package/dist/net/IpInterface.d.ts.map +1 -1
  237. package/dist/net/IpInterface.js +53 -4
  238. package/dist/net/IpInterface.js.map +1 -1
  239. package/dist/net/IpNetwork.d.ts +82 -8
  240. package/dist/net/IpNetwork.d.ts.map +1 -1
  241. package/dist/net/IpNetwork.js +60 -4
  242. package/dist/net/IpNetwork.js.map +1 -1
  243. package/dist/net/NetAddress.d.ts +396 -35
  244. package/dist/net/NetAddress.d.ts.map +1 -1
  245. package/dist/net/NetAddress.js +358 -34
  246. package/dist/net/NetAddress.js.map +1 -1
  247. package/dist/net/index.d.ts +15 -0
  248. package/dist/net/index.d.ts.map +1 -1
  249. package/dist/net/index.js +15 -0
  250. package/dist/net/index.js.map +1 -1
  251. package/dist/observability/OtlpMetrics.d.ts.map +1 -1
  252. package/dist/observability/OtlpMetrics.js +15 -21
  253. package/dist/observability/OtlpMetrics.js.map +1 -1
  254. package/dist/observability/PrometheusMetrics.js +1 -0
  255. package/dist/observability/PrometheusMetrics.js.map +1 -1
  256. package/dist/persistence/PersistedQueue.d.ts +32 -3
  257. package/dist/persistence/PersistedQueue.d.ts.map +1 -1
  258. package/dist/persistence/PersistedQueue.js +75 -16
  259. package/dist/persistence/PersistedQueue.js.map +1 -1
  260. package/dist/rpc/RpcClient.d.ts +22 -6
  261. package/dist/rpc/RpcClient.d.ts.map +1 -1
  262. package/dist/rpc/RpcClient.js +12 -5
  263. package/dist/rpc/RpcClient.js.map +1 -1
  264. package/dist/rpc/RpcServer.d.ts +6 -0
  265. package/dist/rpc/RpcServer.d.ts.map +1 -1
  266. package/dist/rpc/RpcServer.js +78 -29
  267. package/dist/rpc/RpcServer.js.map +1 -1
  268. package/dist/socket/DatagramSocket.d.ts +456 -0
  269. package/dist/socket/DatagramSocket.d.ts.map +1 -0
  270. package/dist/socket/DatagramSocket.js +535 -0
  271. package/dist/socket/DatagramSocket.js.map +1 -0
  272. package/dist/socket/Socket.d.ts +20 -1
  273. package/dist/socket/Socket.d.ts.map +1 -1
  274. package/dist/socket/Socket.js +9 -1
  275. package/dist/socket/Socket.js.map +1 -1
  276. package/dist/socket/index.d.ts +5 -0
  277. package/dist/socket/index.d.ts.map +1 -1
  278. package/dist/socket/index.js +5 -0
  279. package/dist/socket/index.js.map +1 -1
  280. package/dist/sql/Migrator.d.ts.map +1 -1
  281. package/dist/sql/Migrator.js +7 -2
  282. package/dist/sql/Migrator.js.map +1 -1
  283. package/dist/sql/SqlClient.d.ts +7 -1
  284. package/dist/sql/SqlClient.d.ts.map +1 -1
  285. package/dist/sql/SqlClient.js +31 -6
  286. package/dist/sql/SqlClient.js.map +1 -1
  287. package/dist/testing/TestCrypto.d.ts +73 -0
  288. package/dist/testing/TestCrypto.d.ts.map +1 -0
  289. package/dist/testing/TestCrypto.js +87 -0
  290. package/dist/testing/TestCrypto.js.map +1 -0
  291. package/dist/testing/index.d.ts +5 -0
  292. package/dist/testing/index.d.ts.map +1 -1
  293. package/dist/testing/index.js +5 -0
  294. package/dist/testing/index.js.map +1 -1
  295. package/dist/workflow/DurableDeferred.d.ts +54 -0
  296. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  297. package/dist/workflow/DurableDeferred.js +108 -50
  298. package/dist/workflow/DurableDeferred.js.map +1 -1
  299. package/dist/workflow/WorkflowEngine.d.ts +24 -2
  300. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  301. package/dist/workflow/WorkflowEngine.js +13 -3
  302. package/dist/workflow/WorkflowEngine.js.map +1 -1
  303. package/package.json +6 -3
  304. package/src/Context.ts +12 -3
  305. package/src/Crypto.ts +43 -9
  306. package/src/DateTime.ts +9 -6
  307. package/src/Duration.ts +36 -16
  308. package/src/FileSystem.ts +48 -2
  309. package/src/RequestResolver.ts +4 -10
  310. package/src/Scheduler.ts +9 -0
  311. package/src/Schema.ts +677 -5
  312. package/src/SchemaGetter.ts +47 -35
  313. package/src/Sink.ts +45 -18
  314. package/src/Stdio.ts +9 -2
  315. package/src/Stream.ts +44 -15
  316. package/src/Utils.ts +8 -5
  317. package/src/ai/AnthropicStructuredOutput.ts +2 -2
  318. package/src/ai/Chat.ts +2 -2
  319. package/src/ai/LanguageModel.ts +96 -24
  320. package/src/ai/McpServer.ts +288 -94
  321. package/src/ai/Prompt.ts +24 -10
  322. package/src/ai/Response.ts +6 -3
  323. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +5 -0
  324. package/src/ai/internal/mcpProtocol.ts +3 -0
  325. package/src/ai/internal/mcpRuntime.ts +2 -0
  326. package/src/cli/CliOutput.ts +33 -16
  327. package/src/cli/Command.ts +4 -3
  328. package/src/cli/Completions.ts +9 -6
  329. package/src/cli/GlobalFlag.ts +19 -11
  330. package/src/cli/Prompt.ts +159 -149
  331. package/src/cli/internal/ansi.ts +26 -3
  332. package/src/cli/internal/completions/powershell.ts +322 -0
  333. package/src/cli/internal/completions/zsh.ts +4 -2
  334. package/src/cli/internal/wizard.ts +34 -26
  335. package/src/cluster/ClusterSchema.ts +32 -0
  336. package/src/cluster/ClusterWorkflowEngine.ts +64 -36
  337. package/src/cluster/MessageStorage.ts +45 -14
  338. package/src/cluster/Runners.ts +8 -2
  339. package/src/cluster/Sharding.ts +219 -58
  340. package/src/cluster/SqlMessageStorage.ts +118 -84
  341. package/src/cluster/SqlRunnerStorage.ts +224 -128
  342. package/src/cluster/internal/entityManager.ts +151 -55
  343. package/src/encoding/Sse.ts +69 -79
  344. package/src/graphql/GraphQL.ts +494 -0
  345. package/src/graphql/GraphQLClient.ts +912 -0
  346. package/src/graphql/GraphQLClientError.ts +542 -0
  347. package/src/graphql/GraphQLGroup.ts +167 -0
  348. package/src/graphql/GraphQLMiddleware.ts +253 -0
  349. package/src/graphql/GraphQLProtocol.ts +1286 -0
  350. package/src/graphql/GraphQLResolver.ts +1010 -0
  351. package/src/graphql/index.ts +48 -0
  352. package/src/http/FindMyWay/internal/router.ts +4 -7
  353. package/src/http/HttpClient.ts +110 -12
  354. package/src/http/HttpEffect.ts +11 -5
  355. package/src/http/HttpMiddleware.ts +33 -34
  356. package/src/http/HttpRouter.ts +35 -0
  357. package/src/http/HttpServerResponse.ts +28 -16
  358. package/src/http/HttpTraceContext.ts +8 -2
  359. package/src/http/Multipart.ts +17 -1
  360. package/src/http-api/HttpApiScalar.ts +5 -0
  361. package/src/internal/dateTime.ts +13 -10
  362. package/src/internal/effect.ts +22 -15
  363. package/src/internal/graphql.ts +263 -0
  364. package/src/internal/graphqlHeaders.ts +11 -0
  365. package/src/internal/request.ts +31 -3
  366. package/src/internal/schema/toCodeDocument.ts +7 -1
  367. package/src/internal/schema/toJsonSchemaDocument.ts +34 -2
  368. package/src/internal/version.ts +1 -1
  369. package/src/net/AddressResolver.ts +222 -0
  370. package/src/net/Dns.ts +1035 -0
  371. package/src/net/Host.ts +544 -0
  372. package/src/net/IpInterface.ts +117 -7
  373. package/src/net/IpNetwork.ts +116 -17
  374. package/src/net/NetAddress.ts +620 -76
  375. package/src/net/index.ts +18 -0
  376. package/src/observability/OtlpMetrics.ts +12 -23
  377. package/src/observability/PrometheusMetrics.ts +1 -0
  378. package/src/persistence/PersistedQueue.ts +146 -53
  379. package/src/rpc/RpcClient.ts +41 -19
  380. package/src/rpc/RpcServer.ts +115 -40
  381. package/src/socket/DatagramSocket.ts +919 -0
  382. package/src/socket/Socket.ts +28 -2
  383. package/src/socket/index.ts +6 -0
  384. package/src/sql/Migrator.ts +21 -5
  385. package/src/sql/SqlClient.ts +39 -15
  386. package/src/testing/TestCrypto.ts +94 -0
  387. package/src/testing/index.ts +6 -0
  388. package/src/workflow/DurableDeferred.ts +160 -47
  389. package/src/workflow/WorkflowEngine.ts +50 -5
@@ -0,0 +1,544 @@
1
+ /**
2
+ * Host names and unresolved host and port endpoints.
3
+ *
4
+ * A `Host` is either a numeric IP address, an IPv6 literal with a zone such as
5
+ * `fe80::1%eth0`, or a DNS domain name. Numeric addresses are parsed directly
6
+ * into `NetAddress` values. The `AddressResolver` service turns a `HostPort`
7
+ * into internet addresses, looking up domain names with the `Dns` service.
8
+ *
9
+ * @stability experimental
10
+ * @since 4.0.0
11
+ */
12
+ import type * as Arr from "../Array.ts"
13
+ import type * as Brand from "../Brand.ts"
14
+ import * as Equal from "../Equal.ts"
15
+ import * as Hash from "../Hash.ts"
16
+ import * as Inspectable from "../Inspectable.ts"
17
+ import { hasProperty } from "../Predicate.ts"
18
+ import * as Result from "../Result.ts"
19
+ import * as NetAddress from "./NetAddress.ts"
20
+
21
+ const TypeId = "~effect/net/Host" as const
22
+
23
+ /**
24
+ * A syntactically valid DNS domain name in lowercase ASCII form.
25
+ *
26
+ * **Details**
27
+ *
28
+ * Names follow the rules used by Go's resolver: at most 253 characters, labels
29
+ * of 1 to 63 letters, digits, hyphens, or underscores, and no label starting or
30
+ * ending with a hyphen. Underscores are allowed so that service names such as
31
+ * `_http._tcp.example.com` are valid. The last label must not be numeric, which
32
+ * keeps IPv4-like strings out. A trailing dot marks a fully qualified name and
33
+ * is preserved. The root name `.` is valid.
34
+ *
35
+ * Internationalized names are stored in their ASCII (`xn--`) form.
36
+ *
37
+ * @see {@link domainNameFromString} for parsing and normalizing domain names
38
+ * @stability experimental
39
+ * @category models
40
+ * @since 4.0.0
41
+ */
42
+ export type DomainName = Brand.Branded<string, "~effect/net/Host/DomainName">
43
+
44
+ /**
45
+ * A domain name or a string to parse and normalize.
46
+ *
47
+ * @stability experimental
48
+ * @category models
49
+ * @since 4.0.0
50
+ */
51
+ export type DomainNameInput = DomainName | string
52
+
53
+ /**
54
+ * A host as written in configuration or a URL authority: a numeric IP address,
55
+ * a scoped IPv6 literal, or a domain name.
56
+ *
57
+ * @see {@link hostFromString} for parsing hosts
58
+ * @stability experimental
59
+ * @category models
60
+ * @since 4.0.0
61
+ */
62
+ export type Host = NetAddress.IpAddress | NetAddress.ScopedIpv6Literal | DomainName
63
+
64
+ /**
65
+ * A host, or a string or any `IpAddressInput` to convert to one.
66
+ *
67
+ * @stability experimental
68
+ * @category models
69
+ * @since 4.0.0
70
+ */
71
+ export type HostInput = Host | NetAddress.IpAddressInput
72
+
73
+ /**
74
+ * An unresolved host and port.
75
+ *
76
+ * @see {@link hostPortFromString} for parsing `host:port` strings
77
+ * @see {@link formatHostPort} for formatting `host:port` strings
78
+ * @stability experimental
79
+ * @category models
80
+ * @since 4.0.0
81
+ */
82
+ export interface HostPort extends Equal.Equal, Hash.Hash, Inspectable.Inspectable {
83
+ readonly _tag: "HostPort"
84
+ readonly host: Host
85
+ readonly port: number
86
+ readonly [TypeId]: typeof TypeId
87
+ }
88
+
89
+ /**
90
+ * The host and port of an endpoint.
91
+ *
92
+ * @see {@link hostPort} for the checked constructor
93
+ * @stability experimental
94
+ * @category models
95
+ * @since 4.0.0
96
+ */
97
+ export interface HostPortParts {
98
+ readonly host: HostInput
99
+ readonly port: number
100
+ }
101
+
102
+ /**
103
+ * An endpoint, or a `host:port` string or parts to convert to one.
104
+ *
105
+ * @stability experimental
106
+ * @category models
107
+ * @since 4.0.0
108
+ */
109
+ export type HostPortInput = HostPort | string | HostPortParts
110
+
111
+ const hostError = (input: unknown, message: string): Result.Result<never, NetAddress.NetAddressError> =>
112
+ Result.fail(new NetAddress.NetAddressError({ input, message }))
113
+
114
+ const asciiNameCharacter = /^[a-z0-9_.-]$/i
115
+ const labelPattern = /^[a-z0-9_](?:[a-z0-9_-]{0,61}[a-z0-9_])?$/
116
+ const numericLabel = /^(?:\d+|0x[0-9a-f]*)$/
117
+
118
+ const validateDomainName = (name: string): string | undefined => {
119
+ if (name === ".") return undefined
120
+ const relative = name.endsWith(".") ? name.slice(0, -1) : name
121
+ if (relative.length === 0) return "domain name must not be empty"
122
+ if (relative.length > 253) return "domain name must be at most 253 characters"
123
+ const labels = relative.split(".")
124
+ for (const label of labels) {
125
+ if (label.length === 0) return "domain name labels must not be empty"
126
+ if (label.length > 63) return "domain name labels must be at most 63 characters"
127
+ if (!labelPattern.test(label)) {
128
+ return "domain name labels must contain only letters, digits, hyphens, or underscores and must not start or end with a hyphen"
129
+ }
130
+ }
131
+ if (numericLabel.test(labels[labels.length - 1])) {
132
+ return "the last domain name label must not be numeric"
133
+ }
134
+ return undefined
135
+ }
136
+
137
+ const toAsciiDomainName = (input: string): string | undefined => {
138
+ let ascii = true
139
+ for (const character of input) {
140
+ if (character.charCodeAt(0) < 0x80) {
141
+ if (!asciiNameCharacter.test(character)) return undefined
142
+ } else {
143
+ ascii = false
144
+ }
145
+ }
146
+ if (ascii) return input.toLowerCase()
147
+ try {
148
+ return new URL(`http://${input}`).hostname
149
+ } catch {
150
+ return undefined
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Returns `true` when a value is a valid, normalized domain name.
156
+ *
157
+ * @stability experimental
158
+ * @category guards
159
+ * @since 4.0.0
160
+ */
161
+ export const isDomainName = (u: unknown): u is DomainName =>
162
+ typeof u === "string" && /^[a-z0-9_.-]+$/.test(u) && validateDomainName(u) === undefined
163
+
164
+ /**
165
+ * Returns `true` when a value is a host.
166
+ *
167
+ * @stability experimental
168
+ * @category guards
169
+ * @since 4.0.0
170
+ */
171
+ export const isHost = (u: unknown): u is Host =>
172
+ NetAddress.isIpAddress(u) || NetAddress.isScopedIpv6Literal(u) || isDomainName(u)
173
+
174
+ /**
175
+ * Returns `true` when a value is an unresolved host and port.
176
+ *
177
+ * @stability experimental
178
+ * @category guards
179
+ * @since 4.0.0
180
+ */
181
+ export const isHostPort = (u: unknown): u is HostPort => hasProperty(u, TypeId)
182
+
183
+ /**
184
+ * Parses and normalizes a domain name.
185
+ *
186
+ * **Details**
187
+ *
188
+ * ASCII names are lowercased. Names containing non-ASCII characters are
189
+ * converted to their ASCII (`xn--`) form using the WHATWG URL host parser, then
190
+ * validated with the same rules as ASCII names. A trailing dot is preserved.
191
+ *
192
+ * **Example** (Normalizing domain names)
193
+ *
194
+ * ```ts import.meta.vitest
195
+ * import { Result } from "effect"
196
+ * import { Host } from "effect/net"
197
+ *
198
+ * Result.getOrThrow(Host.domainNameFromString("Example.COM")) // => "example.com"
199
+ * Result.getOrThrow(Host.domainNameFromString("bücher.example")) // => "xn--bcher-kva.example"
200
+ * Result.isFailure(Host.domainNameFromString("1.2.3.4")) // => true
201
+ * ```
202
+ *
203
+ * @stability experimental
204
+ * @category decoding
205
+ * @since 4.0.0
206
+ */
207
+ export const domainNameFromString = (input: string): Result.Result<DomainName, NetAddress.NetAddressError> => {
208
+ const name = toAsciiDomainName(input)
209
+ if (name === undefined) return hostError(input, "invalid domain name")
210
+ const error = validateDomainName(name)
211
+ return error === undefined ? Result.succeed(name as DomainName) : hostError(input, error)
212
+ }
213
+
214
+ /**
215
+ * Parses a trusted domain name, throwing on failure.
216
+ *
217
+ * @stability experimental
218
+ * @category unsafe
219
+ * @since 4.0.0
220
+ */
221
+ export const domainNameFromStringUnsafe = (input: string): DomainName => Result.getOrThrow(domainNameFromString(input))
222
+
223
+ /**
224
+ * Converts a `DomainNameInput` to a domain name, parsing and normalizing it like
225
+ * `domainNameFromString`.
226
+ *
227
+ * **Details**
228
+ *
229
+ * Domain names are strings, so every input is parsed.
230
+ *
231
+ * @stability experimental
232
+ * @category constructors
233
+ * @since 4.0.0
234
+ */
235
+ export const domainNameFromInput = (input: DomainNameInput): Result.Result<DomainName, NetAddress.NetAddressError> =>
236
+ domainNameFromString(input)
237
+
238
+ /**
239
+ * Converts a trusted `DomainNameInput` to a domain name, throwing on failure.
240
+ *
241
+ * @stability experimental
242
+ * @category unsafe
243
+ * @since 4.0.0
244
+ */
245
+ export const domainNameFromInputUnsafe = (input: DomainNameInput): DomainName =>
246
+ Result.getOrThrow(domainNameFromInput(input))
247
+
248
+ /**
249
+ * Parses a host as a numeric IP address, a scoped IPv6 literal, or a domain
250
+ * name, in that order.
251
+ *
252
+ * **Example** (Parsing hosts)
253
+ *
254
+ * ```ts import.meta.vitest
255
+ * import { Result } from "effect"
256
+ * import { Host, NetAddress } from "effect/net"
257
+ *
258
+ * NetAddress.isIpAddress(Result.getOrThrow(Host.hostFromString("10.0.0.5"))) // => true
259
+ * Result.getOrThrow(Host.hostFromString("fe80::1%eth0")) // => "fe80::1%eth0"
260
+ * Result.getOrThrow(Host.hostFromString("DB.internal")) // => "db.internal"
261
+ * ```
262
+ *
263
+ * @stability experimental
264
+ * @category decoding
265
+ * @since 4.0.0
266
+ */
267
+ export const hostFromString = (input: string): Result.Result<Host, NetAddress.NetAddressError> => {
268
+ if (input.includes("%")) return NetAddress.scopedIpv6LiteralFromString(input)
269
+ if (input.includes(":")) return NetAddress.ipFromString(input)
270
+ if (/^[\d.]+$/.test(input)) {
271
+ const address = NetAddress.ipFromString(input)
272
+ if (Result.isSuccess(address)) return address
273
+ }
274
+ return domainNameFromString(input)
275
+ }
276
+
277
+ /**
278
+ * Parses a trusted host, throwing on failure.
279
+ *
280
+ * @stability experimental
281
+ * @category unsafe
282
+ * @since 4.0.0
283
+ */
284
+ export const hostFromStringUnsafe = (input: string): Host => Result.getOrThrow(hostFromString(input))
285
+
286
+ /**
287
+ * Converts a `HostInput` to a host, parsing strings like `hostFromString` and
288
+ * converting other inputs like `NetAddress.ipFromInput`.
289
+ *
290
+ * @stability experimental
291
+ * @category constructors
292
+ * @since 4.0.0
293
+ */
294
+ export const hostFromInput = (input: HostInput): Result.Result<Host, NetAddress.NetAddressError> =>
295
+ typeof input === "string" ? hostFromString(input) : NetAddress.ipFromInput(input)
296
+
297
+ /**
298
+ * Converts a trusted `HostInput` to a host, throwing on failure.
299
+ *
300
+ * @stability experimental
301
+ * @category unsafe
302
+ * @since 4.0.0
303
+ */
304
+ export const hostFromInputUnsafe = (input: HostInput): Host => Result.getOrThrow(hostFromInput(input))
305
+
306
+ /**
307
+ * Formats a host without brackets.
308
+ *
309
+ * @stability experimental
310
+ * @category encoding
311
+ * @since 4.0.0
312
+ */
313
+ export const formatHost = (self: Host): string => typeof self === "string" ? self : NetAddress.formatIp(self)
314
+
315
+ /**
316
+ * Returns `true` when a domain name ends with a dot, which excludes it from
317
+ * resolver search domains.
318
+ *
319
+ * @stability experimental
320
+ * @category predicates
321
+ * @since 4.0.0
322
+ */
323
+ export const isFullyQualified = (self: DomainName): boolean => self.endsWith(".")
324
+
325
+ /**
326
+ * Appends the trailing dot to a domain name, unless it is already fully
327
+ * qualified.
328
+ *
329
+ * @stability experimental
330
+ * @category converting
331
+ * @since 4.0.0
332
+ */
333
+ export const toFullyQualified = (self: DomainName): DomainName =>
334
+ isFullyQualified(self) ? self : `${self}.` as DomainName
335
+
336
+ /**
337
+ * Removes the trailing dot of a fully qualified domain name, except from the
338
+ * root name `.`.
339
+ *
340
+ * @stability experimental
341
+ * @category converting
342
+ * @since 4.0.0
343
+ */
344
+ export const toRelative = (self: DomainName): DomainName =>
345
+ self.length > 1 && isFullyQualified(self) ? self.slice(0, -1) as DomainName : self
346
+
347
+ const HostPortProto = {
348
+ ...Inspectable.BaseProto,
349
+ _tag: "HostPort",
350
+ [TypeId]: TypeId,
351
+ [Equal.symbol](this: HostPort, that: Equal.Equal): boolean {
352
+ return isHostPort(that) && this.port === that.port && Equal.equals(this.host, that.host)
353
+ },
354
+ [Hash.symbol](this: HostPort): number {
355
+ return Hash.combine(Hash.hash(this.host), Hash.number(this.port))
356
+ },
357
+ toString(this: HostPort): string {
358
+ return formatHostPort(this)
359
+ },
360
+ toJSON(this: HostPort): string {
361
+ return this.toString()
362
+ }
363
+ }
364
+
365
+ const isPort = (port: number): boolean => Number.isInteger(port) && port >= 0 && port <= 0xffff
366
+
367
+ const makeHostPort = (host: Host, port: number): HostPort => {
368
+ const self = Object.create(HostPortProto)
369
+ self.host = host
370
+ self.port = port
371
+ return Object.freeze(self)
372
+ }
373
+
374
+ /**
375
+ * Creates a checked host and port.
376
+ *
377
+ * @stability experimental
378
+ * @category constructors
379
+ * @since 4.0.0
380
+ */
381
+ export const hostPort = (host: Host, port: number): Result.Result<HostPort, NetAddress.NetAddressError> => {
382
+ if (!isHost(host)) return hostError(host, "expected a host")
383
+ if (!isPort(port)) return hostError(port, "port must be an integer from 0 through 65535")
384
+ return Result.succeed(makeHostPort(host, port))
385
+ }
386
+
387
+ /**
388
+ * Creates a trusted host and port, throwing on failure.
389
+ *
390
+ * @stability experimental
391
+ * @category unsafe
392
+ * @since 4.0.0
393
+ */
394
+ export const hostPortUnsafe = (host: Host, port: number): HostPort => Result.getOrThrow(hostPort(host, port))
395
+
396
+ /**
397
+ * Parses `host:port`, `[IPv6]:port`, or `[IPv6%zone]:port`.
398
+ *
399
+ * **Details**
400
+ *
401
+ * IPv6 hosts must be bracketed and other hosts must not be. The port is
402
+ * required and must be an unpadded decimal integer from 0 through 65535. A
403
+ * zone follows the `%` directly; the percent-encoded `%25` separator of RFC
404
+ * 6874 URLs is not decoded, because `[fe80::1%253]:80` would be ambiguous.
405
+ *
406
+ * **Example** (Parsing host and port strings)
407
+ *
408
+ * ```ts import.meta.vitest
409
+ * import { Result } from "effect"
410
+ * import { Host } from "effect/net"
411
+ *
412
+ * const endpoint = Result.getOrThrow(Host.hostPortFromString("DB.internal:5432"))
413
+ * endpoint.host // => "db.internal"
414
+ * endpoint.port // => 5432
415
+ * Host.formatHostPort(Result.getOrThrow(Host.hostPortFromString("[::1]:80"))) // => "[::1]:80"
416
+ * ```
417
+ *
418
+ * @see {@link formatHostPort} for the inverse operation
419
+ * @stability experimental
420
+ * @category decoding
421
+ * @since 4.0.0
422
+ */
423
+ export const hostPortFromString = (input: string): Result.Result<HostPort, NetAddress.NetAddressError> => {
424
+ let hostText: string
425
+ let portText: string
426
+ if (input.startsWith("[")) {
427
+ const end = input.indexOf("]")
428
+ if (end === -1 || input[end + 1] !== ":") return hostError(input, "expected [IPv6]:port")
429
+ hostText = input.slice(1, end)
430
+ portText = input.slice(end + 2)
431
+ if (!hostText.includes(":")) return hostError(input, "only IPv6 addresses use brackets")
432
+ } else {
433
+ const separator = input.lastIndexOf(":")
434
+ if (separator === -1) return hostError(input, "expected host:port")
435
+ if (input.indexOf(":") !== separator) return hostError(input, "IPv6 addresses must be bracketed")
436
+ hostText = input.slice(0, separator)
437
+ portText = input.slice(separator + 1)
438
+ }
439
+ if (!/^(0|[1-9]\d*)$/.test(portText)) {
440
+ return hostError(input, "port must be an unpadded decimal integer")
441
+ }
442
+ const port = Number(portText)
443
+ if (!isPort(port)) return hostError(input, "port must be an integer from 0 through 65535")
444
+ return Result.map(hostFromString(hostText), (host) => makeHostPort(host, port))
445
+ }
446
+
447
+ /**
448
+ * Parses a trusted `host:port` string, throwing on failure.
449
+ *
450
+ * @stability experimental
451
+ * @category unsafe
452
+ * @since 4.0.0
453
+ */
454
+ export const hostPortFromStringUnsafe = (input: string): HostPort => Result.getOrThrow(hostPortFromString(input))
455
+
456
+ /**
457
+ * Converts a `HostPortInput` to an endpoint, parsing strings like
458
+ * `hostPortFromString`, converting parts like `hostPort`, and returning
459
+ * endpoints unchanged.
460
+ *
461
+ * @stability experimental
462
+ * @category constructors
463
+ * @since 4.0.0
464
+ */
465
+ export const hostPortFromInput = (input: HostPortInput): Result.Result<HostPort, NetAddress.NetAddressError> => {
466
+ if (isHostPort(input)) return Result.succeed(input)
467
+ if (typeof input === "string") return hostPortFromString(input)
468
+ if (!hasProperty(input, "host") || !hasProperty(input, "port")) {
469
+ return hostError(input, "expected a host and port")
470
+ }
471
+ const port = input.port
472
+ return Result.flatMap(hostFromInput(input.host), (host) => hostPort(host, port))
473
+ }
474
+
475
+ /**
476
+ * Converts a trusted `HostPortInput` to an endpoint, throwing on failure.
477
+ *
478
+ * @stability experimental
479
+ * @category unsafe
480
+ * @since 4.0.0
481
+ */
482
+ export const hostPortFromInputUnsafe = (input: HostPortInput): HostPort => Result.getOrThrow(hostPortFromInput(input))
483
+
484
+ /**
485
+ * Formats a host and port, bracketing IPv6 hosts.
486
+ *
487
+ * @see {@link hostPortFromString} for the inverse operation
488
+ * @stability experimental
489
+ * @category encoding
490
+ * @since 4.0.0
491
+ */
492
+ export const formatHostPort = (self: HostPort): string => {
493
+ const host = formatHost(self.host)
494
+ return host.includes(":") ? `[${host}]:${self.port}` : `${host}:${self.port}`
495
+ }
496
+
497
+ /**
498
+ * The addresses of host names, as read from a hosts file.
499
+ *
500
+ * @see {@link parseHostsFile} for reading a hosts file
501
+ * @stability experimental
502
+ * @category models
503
+ * @since 4.0.0
504
+ */
505
+ export type HostsTable = ReadonlyMap<DomainName, Arr.NonEmptyReadonlyArray<NetAddress.IpAddress>>
506
+
507
+ /**
508
+ * Parses a hosts file into the addresses of each host name. Invalid entries
509
+ * are skipped.
510
+ *
511
+ * **Example** (Parsing a hosts file)
512
+ *
513
+ * ```ts import.meta.vitest
514
+ * import { Host, NetAddress } from "effect/net"
515
+ *
516
+ * const hosts = Host.parseHostsFile(`
517
+ * 127.0.0.1 localhost
518
+ * ::1 localhost ip6-localhost # loopback
519
+ * `)
520
+ * hosts.get(Host.domainNameFromStringUnsafe("localhost"))?.map(NetAddress.formatIp) // => ["127.0.0.1", "::1"]
521
+ * ```
522
+ *
523
+ * @stability experimental
524
+ * @category decoding
525
+ * @since 4.0.0
526
+ */
527
+ export const parseHostsFile = (text: string): HostsTable => {
528
+ const hosts = new Map<DomainName, Array<NetAddress.IpAddress>>()
529
+ for (const line of text.split(/\r?\n/)) {
530
+ const comment = line.indexOf("#")
531
+ const [first, ...names] = (comment === -1 ? line : line.slice(0, comment)).trim().split(/\s+/)
532
+ const address = NetAddress.ipFromString(first)
533
+ if (Result.isFailure(address)) continue
534
+ for (const name of names) {
535
+ const domain = domainNameFromString(name)
536
+ if (Result.isFailure(domain)) continue
537
+ const key = toRelative(domain.success)
538
+ const addresses = hosts.get(key)
539
+ if (addresses === undefined) hosts.set(key, [address.success])
540
+ else if (!addresses.some((existing) => Equal.equals(existing, address.success))) addresses.push(address.success)
541
+ }
542
+ }
543
+ return hosts as unknown as HostsTable
544
+ }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import * as Equal from "../Equal.ts"
8
8
  import * as Hash from "../Hash.ts"
9
- import { NodeInspectSymbol } from "../Inspectable.ts"
9
+ import * as Inspectable from "../Inspectable.ts"
10
10
  import { hasProperty } from "../Predicate.ts"
11
11
  import * as Result from "../Result.ts"
12
12
  import * as NetAddress from "./NetAddress.ts"
@@ -23,13 +23,13 @@ const TypeId = "~effect/net/IpInterface" as const
23
23
  * @category models
24
24
  * @since 4.0.0
25
25
  */
26
- export interface IpInterface<out A extends NetAddress.IpAddress = NetAddress.IpAddress> extends Equal.Equal, Hash.Hash {
26
+ export interface IpInterface<out A extends NetAddress.IpAddress = NetAddress.IpAddress>
27
+ extends Equal.Equal, Hash.Hash, Inspectable.Inspectable
28
+ {
27
29
  readonly _tag: "IpInterface"
28
30
  readonly address: A
29
31
  readonly prefixLength: number
30
32
  readonly [TypeId]: typeof TypeId
31
- toString(): string
32
- toJSON(): string
33
33
  }
34
34
 
35
35
  /**
@@ -50,6 +50,46 @@ export type Ipv4Interface = IpInterface<NetAddress.Ipv4Address>
50
50
  */
51
51
  export type Ipv6Interface = IpInterface<NetAddress.Ipv6Address>
52
52
 
53
+ /**
54
+ * An address and prefix length, generic over the accepted address input.
55
+ *
56
+ * @see {@link make} for the checked constructor
57
+ * @stability unstable
58
+ * @category models
59
+ * @since 4.0.0
60
+ */
61
+ export interface IpInterfaceParts<A extends NetAddress.IpAddressInput = NetAddress.IpAddressInput> {
62
+ readonly address: A
63
+ readonly prefixLength: number
64
+ }
65
+
66
+ /**
67
+ * An interface address, or a string or parts to convert to one.
68
+ *
69
+ * @stability unstable
70
+ * @category models
71
+ * @since 4.0.0
72
+ */
73
+ export type IpInterfaceInput = IpInterface | string | IpInterfaceParts
74
+
75
+ /**
76
+ * An IPv4 interface address, or a string or parts to convert to one.
77
+ *
78
+ * @stability unstable
79
+ * @category models
80
+ * @since 4.0.0
81
+ */
82
+ export type Ipv4InterfaceInput = Ipv4Interface | string | IpInterfaceParts<NetAddress.Ipv4AddressInput>
83
+
84
+ /**
85
+ * An IPv6 interface address, or a string or parts to convert to one.
86
+ *
87
+ * @stability unstable
88
+ * @category models
89
+ * @since 4.0.0
90
+ */
91
+ export type Ipv6InterfaceInput = Ipv6Interface | string | IpInterfaceParts<NetAddress.Ipv6AddressInput>
92
+
53
93
  /**
54
94
  * Companion types for parsing IP interface addresses.
55
95
  *
@@ -100,6 +140,7 @@ export const isIpv6Interface = (u: unknown): u is Ipv6Interface =>
100
140
  export const isIpInterface = (u: unknown): u is IpInterface => hasProperty(u, TypeId)
101
141
 
102
142
  const IpInterfaceProto = {
143
+ ...Inspectable.BaseProto,
103
144
  _tag: "IpInterface",
104
145
  [TypeId]: TypeId,
105
146
  [Equal.symbol](this: IpInterface, that: Equal.Equal): boolean {
@@ -115,9 +156,6 @@ const IpInterfaceProto = {
115
156
  },
116
157
  toJSON(this: IpInterface): string {
117
158
  return this.toString()
118
- },
119
- [NodeInspectSymbol](this: IpInterface): string {
120
- return this.toJSON()
121
159
  }
122
160
  }
123
161
 
@@ -231,6 +269,78 @@ export const fromString = (
231
269
  make(address, parts.prefixLength ?? NetAddress.width(address)))
232
270
  )
233
271
 
272
+ const fromParts = <I extends NetAddress.IpAddressInput, A extends NetAddress.IpAddress>(
273
+ input: IpInterfaceParts<I>,
274
+ toAddress: (input: I) => Result.Result<A, NetAddress.NetAddressError>
275
+ ): Result.Result<IpInterface<A>, NetAddress.NetAddressError> =>
276
+ hasProperty(input, "address") && hasProperty(input, "prefixLength")
277
+ ? Result.flatMap(toAddress(input.address), (address) => make(address, input.prefixLength))
278
+ : interfaceError(input, "expected an address and prefix length")
279
+
280
+ /**
281
+ * Converts an `Ipv4InterfaceInput` to an IPv4 interface address, parsing
282
+ * strings like `ipv4FromString` with the given options, converting parts like
283
+ * `make`, and returning interface addresses unchanged.
284
+ *
285
+ * @stability unstable
286
+ * @category constructors
287
+ * @since 4.0.0
288
+ */
289
+ export const ipv4FromInput = (
290
+ input: Ipv4InterfaceInput,
291
+ options?: IpInterface.ParseOptions
292
+ ): Result.Result<Ipv4Interface, NetAddress.NetAddressError> => {
293
+ if (isIpv4Interface(input)) return Result.succeed(input)
294
+ if (typeof input === "string") return ipv4FromString(input, options)
295
+ return fromParts(input, NetAddress.ipv4FromInput)
296
+ }
297
+
298
+ /**
299
+ * Converts an `Ipv6InterfaceInput` to an IPv6 interface address, parsing
300
+ * strings like `ipv6FromString` with the given options, converting parts like
301
+ * `make`, and returning interface addresses unchanged.
302
+ *
303
+ * @stability unstable
304
+ * @category constructors
305
+ * @since 4.0.0
306
+ */
307
+ export const ipv6FromInput = (
308
+ input: Ipv6InterfaceInput,
309
+ options?: IpInterface.ParseOptions
310
+ ): Result.Result<Ipv6Interface, NetAddress.NetAddressError> => {
311
+ if (isIpv6Interface(input)) return Result.succeed(input)
312
+ if (typeof input === "string") return ipv6FromString(input, options)
313
+ return fromParts(input, NetAddress.ipv6FromInput)
314
+ }
315
+
316
+ /**
317
+ * Converts an `IpInterfaceInput` to an interface address, parsing strings like
318
+ * `fromString` with the given options, converting parts like `make`, and
319
+ * returning interface addresses unchanged.
320
+ *
321
+ * @stability unstable
322
+ * @category constructors
323
+ * @since 4.0.0
324
+ */
325
+ export const fromInput = (
326
+ input: IpInterfaceInput,
327
+ options?: IpInterface.ParseOptions
328
+ ): Result.Result<IpInterface, NetAddress.NetAddressError> => {
329
+ if (isIpInterface(input)) return Result.succeed(input)
330
+ if (typeof input === "string") return fromString(input, options)
331
+ return fromParts(input, NetAddress.ipFromInput)
332
+ }
333
+
334
+ /**
335
+ * Converts a trusted `IpInterfaceInput` to an interface address, throwing on failure.
336
+ *
337
+ * @stability unstable
338
+ * @category unsafe
339
+ * @since 4.0.0
340
+ */
341
+ export const fromInputUnsafe = (input: IpInterfaceInput, options?: IpInterface.ParseOptions): IpInterface =>
342
+ Result.getOrThrow(fromInput(input, options))
343
+
234
344
  /**
235
345
  * Creates a trusted interface address, throwing when its prefix length is invalid.
236
346
  *