Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'

This commit is contained in:
-Puter
2026-07-19 03:25:10 +05:30
parent dd1071cc10
commit 2daf979036
2214 changed files with 673090 additions and 0 deletions

View File

@@ -0,0 +1,823 @@
# @effect/openapi-generator
## 4.0.0-beta.99
### Patch Changes
- Updated dependencies [[`8ce4795`](https://github.com/Effect-TS/effect/commit/8ce4795ccbaebca4292757db568c005a992546a4), [`80b539f`](https://github.com/Effect-TS/effect/commit/80b539f8aba68f478c75c35c2b4140c4ffc4fada), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`e6e6dba`](https://github.com/Effect-TS/effect/commit/e6e6dba6e9d86e7c2ad27dcedf289db76a19697f), [`bfb203e`](https://github.com/Effect-TS/effect/commit/bfb203e95aa439f731acad37fc3a9a831a190f1c), [`2e9a34a`](https://github.com/Effect-TS/effect/commit/2e9a34ac2bece4f3a206160480c991e3841dc67a), [`55d4eb3`](https://github.com/Effect-TS/effect/commit/55d4eb34f2c64d54f6a25a305b5c5438ebd7934e), [`bddb010`](https://github.com/Effect-TS/effect/commit/bddb010eac3d4436cb094edbbee7460c5440c162), [`a328835`](https://github.com/Effect-TS/effect/commit/a328835e50d76bc96648a1c1550456e8c9f81210), [`5560d05`](https://github.com/Effect-TS/effect/commit/5560d05aa6abdd29466d9c3412cc5e648b0adbde), [`8f6e3ad`](https://github.com/Effect-TS/effect/commit/8f6e3adb185b16e8820b98c509b308086f7ff1af), [`46997fa`](https://github.com/Effect-TS/effect/commit/46997fa60401f5e3c93daa4b61f7df8e31caaab4), [`9e6e12d`](https://github.com/Effect-TS/effect/commit/9e6e12d75c118cd265496f2880490d1f33a5c8bf), [`3394b93`](https://github.com/Effect-TS/effect/commit/3394b93d97d6f24fc38670641d1490289ffca7f1), [`febeabc`](https://github.com/Effect-TS/effect/commit/febeabc3f7c31094da000a23edeaabfe2ab00a38), [`54161c9`](https://github.com/Effect-TS/effect/commit/54161c98f6f3569e0c31842f54e6a257f9421c4c), [`385f7a4`](https://github.com/Effect-TS/effect/commit/385f7a4ee4a7359928597ea56d151dbaf5eb5802), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`7543afe`](https://github.com/Effect-TS/effect/commit/7543afea6f4d97d1f1ad876224323838a48daadd), [`44b9cf3`](https://github.com/Effect-TS/effect/commit/44b9cf3d240d726997b4bbcd0ede48e074d3c456), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`0a8aa6a`](https://github.com/Effect-TS/effect/commit/0a8aa6acb90a72b91c24d17133c950e4cacd8abd), [`c8d9fcf`](https://github.com/Effect-TS/effect/commit/c8d9fcf7b030f7c474effbab2764ce7aee1c7209), [`9ca7f9a`](https://github.com/Effect-TS/effect/commit/9ca7f9a69363e4485645966d5a93b8f9597c5206), [`e7aca89`](https://github.com/Effect-TS/effect/commit/e7aca894bb32fbb785b5830837e6061c415a6015), [`55d7560`](https://github.com/Effect-TS/effect/commit/55d75609b8acf8a1b54c1b1c7fbbb65ec741aa3e), [`f809189`](https://github.com/Effect-TS/effect/commit/f809189ddf6b6011ba43a9901baaa734e315da2a), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`0ebdbe7`](https://github.com/Effect-TS/effect/commit/0ebdbe74463dc84385956d0b1e8c2b79ebab5400), [`7517d09`](https://github.com/Effect-TS/effect/commit/7517d09f12a0b183a81bd425962c4e280a68b05d), [`212493b`](https://github.com/Effect-TS/effect/commit/212493b9a1eb98cd1ef6959c707a2e5784a5ae91), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`80ea8cb`](https://github.com/Effect-TS/effect/commit/80ea8cb9222ca73f564c8267ab2f82966fea027a), [`8df19f4`](https://github.com/Effect-TS/effect/commit/8df19f4fe81d90cc33ace88b9a77e5534f82d604)]:
- effect@4.0.0-beta.99
- @effect/platform-node@4.0.0-beta.99
## 4.0.0-beta.98
### Patch Changes
- Updated dependencies [[`989603b`](https://github.com/Effect-TS/effect-smol/commit/989603b60ab1197b64acf214208e0d370cd1f842), [`214c458`](https://github.com/Effect-TS/effect-smol/commit/214c458084bb6995d543cd37d1055f24be3d454e), [`a037273`](https://github.com/Effect-TS/effect-smol/commit/a0372736ac34796969b051bbba4717d7983f1ebe), [`97fdaa9`](https://github.com/Effect-TS/effect-smol/commit/97fdaa9c1f522c65e579365d314a07878e2b904f), [`b24d248`](https://github.com/Effect-TS/effect-smol/commit/b24d248c8df44222ce642087cde2bd859a2dc709), [`19c222c`](https://github.com/Effect-TS/effect-smol/commit/19c222cac2353a3d7b7733caecb00556fffe9a5c), [`eec85dd`](https://github.com/Effect-TS/effect-smol/commit/eec85ddba09ea326fd268ee33eeffd47e50d4671), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`8849052`](https://github.com/Effect-TS/effect-smol/commit/884905232d1e9a365e046d8dde27bf9c5707f57f), [`c15e16a`](https://github.com/Effect-TS/effect-smol/commit/c15e16ad130d1fbde25d912b7ac55995066cb35b), [`01d00a3`](https://github.com/Effect-TS/effect-smol/commit/01d00a3abfbf1f37996cdbe738ea5137c646cdd7), [`8bd4589`](https://github.com/Effect-TS/effect-smol/commit/8bd458975a1b3a8ed042eccf317b93d28ded91e7), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`6e08428`](https://github.com/Effect-TS/effect-smol/commit/6e08428d980501b856f846ad3f3f0e4ea46e7786), [`388dcf9`](https://github.com/Effect-TS/effect-smol/commit/388dcf953f65d317547f34d40e6443c5f264205f), [`2b7ce2b`](https://github.com/Effect-TS/effect-smol/commit/2b7ce2b513e7ec2a77822f1116dc6ffb6ba93f4e), [`87bea7e`](https://github.com/Effect-TS/effect-smol/commit/87bea7e16259246f3bcdf565446394751abca953), [`ce38dc3`](https://github.com/Effect-TS/effect-smol/commit/ce38dc33bda805a684432cca071f4dc3c6b9a1ba), [`a807cd1`](https://github.com/Effect-TS/effect-smol/commit/a807cd170341deca8a1cfb52c4222585f2431bb9), [`fd8a356`](https://github.com/Effect-TS/effect-smol/commit/fd8a356f06a8c9ce4e7e0a13fc4021c178ed31de), [`c2a5edc`](https://github.com/Effect-TS/effect-smol/commit/c2a5edc3abd31ad5bc123362bc1213e03e4095c3), [`5946da3`](https://github.com/Effect-TS/effect-smol/commit/5946da3804a1be5e752b05b96bd058cdba50a1bf), [`4ae0c5f`](https://github.com/Effect-TS/effect-smol/commit/4ae0c5ffcbe6c56ddfcb05c639112a079483539e), [`5b2a0bc`](https://github.com/Effect-TS/effect-smol/commit/5b2a0bceea3a28a33a58555210c90a415dc74a76), [`72ac585`](https://github.com/Effect-TS/effect-smol/commit/72ac585884befde6af9208da738699a93f1bae79), [`5e8c1b8`](https://github.com/Effect-TS/effect-smol/commit/5e8c1b82bfafa121311f987a49ab75395e3647a7), [`0f9c078`](https://github.com/Effect-TS/effect-smol/commit/0f9c07841b04183f485ee6e6458de73b290b09f5)]:
- effect@4.0.0-beta.98
- @effect/platform-node@4.0.0-beta.98
## 4.0.0-beta.97
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.97
- @effect/platform-node@4.0.0-beta.97
## 4.0.0-beta.96
### Patch Changes
- Updated dependencies [[`1503f45`](https://github.com/Effect-TS/effect-smol/commit/1503f45cb5bb2a74f4705252ec505a1f0ade7e62), [`57fe793`](https://github.com/Effect-TS/effect-smol/commit/57fe79316ffbc380b30626a168981fb26ae97459), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`97f29df`](https://github.com/Effect-TS/effect-smol/commit/97f29df457f7ffd07cfb4b379315c12c086af805)]:
- effect@4.0.0-beta.96
- @effect/platform-node@4.0.0-beta.96
## 4.0.0-beta.95
### Patch Changes
- Updated dependencies [[`a482442`](https://github.com/Effect-TS/effect-smol/commit/a482442abdeb490e9652b854ec3495e4aa7273e7), [`fbefa85`](https://github.com/Effect-TS/effect-smol/commit/fbefa850fab2f0a302c20614496aeaaa2a8b5590), [`0b4a32f`](https://github.com/Effect-TS/effect-smol/commit/0b4a32f4260f0d8500942a133001b0d349328102), [`18a49e1`](https://github.com/Effect-TS/effect-smol/commit/18a49e1786679456258002ff9397faf02f678c2d), [`266cb90`](https://github.com/Effect-TS/effect-smol/commit/266cb90bb2c17aabc40563c32db334f09ba3d74b), [`912f095`](https://github.com/Effect-TS/effect-smol/commit/912f095a34572bbd3cedf6edb27878443e3e4a95), [`a6718f9`](https://github.com/Effect-TS/effect-smol/commit/a6718f9e00a15ca903b0732da46116cbf3d6aca7), [`bef5154`](https://github.com/Effect-TS/effect-smol/commit/bef51540a243aa2f872a00c01d0cd58b7a769baa), [`18e0564`](https://github.com/Effect-TS/effect-smol/commit/18e0564bd0f8ebbdfcaf1e2c21529948e9e4a81d), [`fb50f14`](https://github.com/Effect-TS/effect-smol/commit/fb50f14fc3657c1973785aa5b72ecf0b0d28e0b2)]:
- effect@4.0.0-beta.95
- @effect/platform-node@4.0.0-beta.95
## 4.0.0-beta.94
### Patch Changes
- Updated dependencies [[`95a0e9b`](https://github.com/Effect-TS/effect-smol/commit/95a0e9bb62797af0e81c9998773405f248f218c5), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63), [`f11ce73`](https://github.com/Effect-TS/effect-smol/commit/f11ce73af60823754dc24194f4ffc561b9ea1c2d), [`ff30b6e`](https://github.com/Effect-TS/effect-smol/commit/ff30b6e7c2c63ffc56a4c5818d6d86b01b5ad528), [`1caab3c`](https://github.com/Effect-TS/effect-smol/commit/1caab3cc30f626efbf15e59d74f539a487e5c85c), [`aa80c47`](https://github.com/Effect-TS/effect-smol/commit/aa80c4775a04db87553e5568764cab7e32a72814), [`c2ae4fc`](https://github.com/Effect-TS/effect-smol/commit/c2ae4fce2f03a4cd1861c2b1179da7df656e662d), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63)]:
- effect@4.0.0-beta.94
- @effect/platform-node@4.0.0-beta.94
## 4.0.0-beta.93
### Patch Changes
- Updated dependencies [[`00652fe`](https://github.com/Effect-TS/effect-smol/commit/00652fe95c18f87208e91343eb8bf218faa2f677), [`6c58167`](https://github.com/Effect-TS/effect-smol/commit/6c5816746eaf91d2a3c7c899c5720809fa230ae3), [`2bc5415`](https://github.com/Effect-TS/effect-smol/commit/2bc541501a7ef89e542d7cb98e96beb53cd205cc), [`e11cccc`](https://github.com/Effect-TS/effect-smol/commit/e11cccc7d5fe631abccc7d6e3bd296938de0fa2e), [`ba7e77e`](https://github.com/Effect-TS/effect-smol/commit/ba7e77e046b8641a3a4e9750bb88ca4a1d063d3f), [`5713ee7`](https://github.com/Effect-TS/effect-smol/commit/5713ee7edbc3054efde407b2286bbfd45bbc6e1c)]:
- effect@4.0.0-beta.93
- @effect/platform-node@4.0.0-beta.93
## 4.0.0-beta.92
### Patch Changes
- Updated dependencies [[`affdc13`](https://github.com/Effect-TS/effect-smol/commit/affdc139045cc325dce321a84a580fdc1b2da7b9)]:
- effect@4.0.0-beta.92
- @effect/platform-node@4.0.0-beta.92
## 4.0.0-beta.91
### Patch Changes
- Updated dependencies [[`b135b25`](https://github.com/Effect-TS/effect-smol/commit/b135b2517fca9e7839734ace3699a7dfa75b9075), [`aaa21a3`](https://github.com/Effect-TS/effect-smol/commit/aaa21a369a171c600db294f2a4f640583043e150), [`3475ee6`](https://github.com/Effect-TS/effect-smol/commit/3475ee6c2bda6b05c6d7a12ce30c8bb840b5b1a6)]:
- effect@4.0.0-beta.91
- @effect/platform-node@4.0.0-beta.91
## 4.0.0-beta.90
### Patch Changes
- Updated dependencies [[`d237fdf`](https://github.com/Effect-TS/effect-smol/commit/d237fdf726481f76eb52a6196e111b24122bc3d5)]:
- effect@4.0.0-beta.90
- @effect/platform-node@4.0.0-beta.90
## 4.0.0-beta.89
### Patch Changes
- Updated dependencies [[`b7d46ab`](https://github.com/Effect-TS/effect-smol/commit/b7d46ab7e1a29d8711817bab583c9febf48a0dad), [`7777e15`](https://github.com/Effect-TS/effect-smol/commit/7777e1540fd3680dd8346723cffec812b9384669), [`5376197`](https://github.com/Effect-TS/effect-smol/commit/5376197ca8e50358a41b1fd3cec27bd1ec680ec6)]:
- effect@4.0.0-beta.89
- @effect/platform-node@4.0.0-beta.89
## 4.0.0-beta.88
### Patch Changes
- Updated dependencies [[`911f1b8`](https://github.com/Effect-TS/effect-smol/commit/911f1b84790ce42b3a70c95b33e6f6fd9e74de8b), [`8beeeea`](https://github.com/Effect-TS/effect-smol/commit/8beeeea52879d8613a39468848f01c3092bd54d4), [`c306fcf`](https://github.com/Effect-TS/effect-smol/commit/c306fcfeb1ef38455156932a1faf49292b1318da)]:
- effect@4.0.0-beta.88
- @effect/platform-node@4.0.0-beta.88
## 4.0.0-beta.87
### Patch Changes
- [#2469](https://github.com/Effect-TS/effect-smol/pull/2469) [`12fcf35`](https://github.com/Effect-TS/effect-smol/commit/12fcf35b92879f70a0e3b32b18359986439b9283) Thanks @jbmusso! - Fix the generated SSE `sseRequest` helper to reference `Schema.ConstraintDecoder` instead of the no-longer-exported `Schema.Decoder`.
For specs with `text/event-stream` responses the generator emitted a helper typed as `Schema.Decoder<Type, DecodingServices>`, but `Schema` exports that decode-only interface as `ConstraintDecoder`, so generated clients failed to compile (`'"effect/Schema"' has no exported member named 'Decoder'`). The emitted helper now uses `Schema.ConstraintDecoder`.
- Updated dependencies [[`5a0c1a4`](https://github.com/Effect-TS/effect-smol/commit/5a0c1a4faee5707b5cc35e646ff1ffdad70f1956), [`1eea2ea`](https://github.com/Effect-TS/effect-smol/commit/1eea2ea3795ba47316b82b1ac8d4612c0ba389ed)]:
- effect@4.0.0-beta.87
- @effect/platform-node@4.0.0-beta.87
## 4.0.0-beta.86
### Patch Changes
- Updated dependencies [[`0b5795a`](https://github.com/Effect-TS/effect-smol/commit/0b5795a0ab4395e8f15955d8d96f2303084bfc64), [`3e3a859`](https://github.com/Effect-TS/effect-smol/commit/3e3a859ec6351a9e0d31674aabbd48fcefabb12e), [`7dbec24`](https://github.com/Effect-TS/effect-smol/commit/7dbec240dbf3bca599a20c486632abce694ef5ab), [`d8c00a1`](https://github.com/Effect-TS/effect-smol/commit/d8c00a171ac7141e8adc08c332d1162d9a9d56fc), [`85b6317`](https://github.com/Effect-TS/effect-smol/commit/85b631701e935866f2762bd595237aa718370cd9), [`28b4196`](https://github.com/Effect-TS/effect-smol/commit/28b4196390d3ab83be1567b65440919a9061fcc3), [`6d0fda0`](https://github.com/Effect-TS/effect-smol/commit/6d0fda0d0cbdfffc523c89c57dfdb1608f84fb12), [`108a933`](https://github.com/Effect-TS/effect-smol/commit/108a9335ff8571928197e5847a09c28ac83d6f46), [`7e1f455`](https://github.com/Effect-TS/effect-smol/commit/7e1f455fab5005d769b939c91e519d450f802cf9), [`46b3e79`](https://github.com/Effect-TS/effect-smol/commit/46b3e79944cfdae7901eb148135c85b7eb39834e)]:
- effect@4.0.0-beta.86
- @effect/platform-node@4.0.0-beta.86
## 4.0.0-beta.85
### Patch Changes
- Updated dependencies [[`328d97c`](https://github.com/Effect-TS/effect-smol/commit/328d97cc53c0dcb89077a5623e35b095eaa59a8c), [`8441836`](https://github.com/Effect-TS/effect-smol/commit/8441836e6dde70e8ae2126be9cefe9b45798b134), [`074e436`](https://github.com/Effect-TS/effect-smol/commit/074e4361091289104cb0ab6959dc3b0ea7794a6a), [`c1dfd60`](https://github.com/Effect-TS/effect-smol/commit/c1dfd60663eb13a58916f3712d877499943b628a), [`2ba316b`](https://github.com/Effect-TS/effect-smol/commit/2ba316bd15fcbf1c50626500d44a2c9b3bec19f5), [`7ce7344`](https://github.com/Effect-TS/effect-smol/commit/7ce7344c41056c79e2ee19ee6a9346c0f1d227c1)]:
- effect@4.0.0-beta.85
- @effect/platform-node@4.0.0-beta.85
## 4.0.0-beta.84
### Patch Changes
- Updated dependencies [[`87f52ba`](https://github.com/Effect-TS/effect-smol/commit/87f52ba16c4370ffa3f84bf8e53038e1419c284e), [`b8ee07f`](https://github.com/Effect-TS/effect-smol/commit/b8ee07ffda8903b5ec2e45a786ddcba59f128fda), [`867c0d7`](https://github.com/Effect-TS/effect-smol/commit/867c0d70a09079b040260d45a1e92ff04dbfbf2f), [`b93bc6c`](https://github.com/Effect-TS/effect-smol/commit/b93bc6c9cb27b909a41d094c97c4f9d25bbc6d6b), [`57d387f`](https://github.com/Effect-TS/effect-smol/commit/57d387f92c30ab63e15e3e641f0a903b65886610), [`bacca41`](https://github.com/Effect-TS/effect-smol/commit/bacca4141c2400effae1eabfdb36c89a459cf246), [`0f8ac79`](https://github.com/Effect-TS/effect-smol/commit/0f8ac7959d29ed68c68ce25aabd6bf0cb7e63ecc), [`25b4482`](https://github.com/Effect-TS/effect-smol/commit/25b448270c01317703f25107e1480d4cd0246d9a), [`9cf3a25`](https://github.com/Effect-TS/effect-smol/commit/9cf3a25c66b0c44a52be9829870c44517ea52db2), [`8def767`](https://github.com/Effect-TS/effect-smol/commit/8def7674b1787f91035298cda4d122937e87ef72)]:
- effect@4.0.0-beta.84
- @effect/platform-node@4.0.0-beta.84
## 4.0.0-beta.83
### Patch Changes
- Updated dependencies [[`1f2e8ce`](https://github.com/Effect-TS/effect-smol/commit/1f2e8ceef09e0a791c850ed2ade01f97089596f9)]:
- effect@4.0.0-beta.83
- @effect/platform-node@4.0.0-beta.83
## 4.0.0-beta.82
### Patch Changes
- Updated dependencies [[`193690b`](https://github.com/Effect-TS/effect-smol/commit/193690b642ea802bbed40d663bd677251bbe9dc3)]:
- effect@4.0.0-beta.82
- @effect/platform-node@4.0.0-beta.82
## 4.0.0-beta.81
### Patch Changes
- [#2270](https://github.com/Effect-TS/effect-smol/pull/2270) [`4500fbf`](https://github.com/Effect-TS/effect-smol/commit/4500fbfe00763d8a72af6e5d6c5988e8bd4ade36) Thanks @IMax153! - Add HTTP API streaming response support
- Updated dependencies [[`93cb4f8`](https://github.com/Effect-TS/effect-smol/commit/93cb4f8fbfb9e07cb9dc86ce6b155fd1f8167914), [`60341d9`](https://github.com/Effect-TS/effect-smol/commit/60341d9ca744d0473ce3fab621ca9bd225af3a39), [`1105ab5`](https://github.com/Effect-TS/effect-smol/commit/1105ab56cb724212f7ea7b431396ce82e8fd0484), [`4500fbf`](https://github.com/Effect-TS/effect-smol/commit/4500fbfe00763d8a72af6e5d6c5988e8bd4ade36)]:
- effect@4.0.0-beta.81
- @effect/platform-node@4.0.0-beta.81
## 4.0.0-beta.80
### Patch Changes
- Updated dependencies [[`d944330`](https://github.com/Effect-TS/effect-smol/commit/d94433090ee03f426d43e13b883abae4494e55e6), [`f48659f`](https://github.com/Effect-TS/effect-smol/commit/f48659fdcc84930ebc1e5b45b540c0f973389182), [`7652aaa`](https://github.com/Effect-TS/effect-smol/commit/7652aaa3bdbc39f241fe58b54b9a43b713e22e12), [`98630b7`](https://github.com/Effect-TS/effect-smol/commit/98630b7c8f679c352ba6796636c85688fa009d8d), [`90ae23c`](https://github.com/Effect-TS/effect-smol/commit/90ae23cf07284da5e1bcd9dffa882e85df7e617b)]:
- effect@4.0.0-beta.80
- @effect/platform-node@4.0.0-beta.80
## 4.0.0-beta.79
### Patch Changes
- [#2350](https://github.com/Effect-TS/effect-smol/pull/2350) [`3bbdf8c`](https://github.com/Effect-TS/effect-smol/commit/3bbdf8cbbdad27c889fa30436fdcfd8eeb606b6c) Thanks @wer416182-afk! - Declare `swagger2openapi` as a runtime dependency so published `openapigen` installs can resolve the converter imported by `OpenApiGenerator`.
- Updated dependencies [[`b9704dc`](https://github.com/Effect-TS/effect-smol/commit/b9704dc9de9f1649ad502371014fe869b69a49a3), [`a207113`](https://github.com/Effect-TS/effect-smol/commit/a207113f66837bb54416926718a9a7d66774d079), [`5e9b9e2`](https://github.com/Effect-TS/effect-smol/commit/5e9b9e217b164ebfd4a002dd4380b3b1563200c3), [`7c128ae`](https://github.com/Effect-TS/effect-smol/commit/7c128aef458a1e2d224712e51c483c9badad1d44), [`0ada457`](https://github.com/Effect-TS/effect-smol/commit/0ada457c0513d8d908254ab77ebb7d29d2b523d6), [`d7cc5a2`](https://github.com/Effect-TS/effect-smol/commit/d7cc5a2bede3de10943aa0c6bdb4f26836a91efd), [`aad63be`](https://github.com/Effect-TS/effect-smol/commit/aad63becf65e0a6b076e94f8973be7bbe7fbd46f), [`09809f6`](https://github.com/Effect-TS/effect-smol/commit/09809f60f19ec98232f98b33e33e02ecb7e4fbd6), [`2fddda5`](https://github.com/Effect-TS/effect-smol/commit/2fddda5311929f46b61e503f0ade4fc749e8c77d), [`5f21768`](https://github.com/Effect-TS/effect-smol/commit/5f2176833399757c4500d8875b7f2fba0393de75), [`f27003e`](https://github.com/Effect-TS/effect-smol/commit/f27003e00524ff83f20dd9909f62b2f8795efe03)]:
- effect@4.0.0-beta.79
- @effect/platform-node@4.0.0-beta.79
## 4.0.0-beta.78
### Patch Changes
- Updated dependencies [[`7836b8e`](https://github.com/Effect-TS/effect-smol/commit/7836b8eb8bb0f3e04cdf554ee070caccf74f00c1), [`35d49a3`](https://github.com/Effect-TS/effect-smol/commit/35d49a3a09bdba6b513de87ddcead9e61a1042ba), [`4093258`](https://github.com/Effect-TS/effect-smol/commit/40932580e65bafab5f23c5f14b520cb411d0b2cd)]:
- effect@4.0.0-beta.78
- @effect/platform-node@4.0.0-beta.78
## 4.0.0-beta.77
### Patch Changes
- Updated dependencies [[`6e9a5ca`](https://github.com/Effect-TS/effect-smol/commit/6e9a5ca62a61156fd67b2518ad3ab14ac0d25f23), [`302f398`](https://github.com/Effect-TS/effect-smol/commit/302f3984ce206e35d86ddd99d3b72be144850a51)]:
- effect@4.0.0-beta.77
- @effect/platform-node@4.0.0-beta.77
## 4.0.0-beta.76
### Patch Changes
- Updated dependencies [[`016108a`](https://github.com/Effect-TS/effect-smol/commit/016108a472af7048ddbbfd05f233e67529fafe12), [`95c03d2`](https://github.com/Effect-TS/effect-smol/commit/95c03d2c55930668c215b5a41c23cf7742fead84), [`07299a3`](https://github.com/Effect-TS/effect-smol/commit/07299a33c09fd52faa9810d30835a2622c752386)]:
- effect@4.0.0-beta.76
- @effect/platform-node@4.0.0-beta.76
## 4.0.0-beta.75
### Patch Changes
- Updated dependencies [[`81b187c`](https://github.com/Effect-TS/effect-smol/commit/81b187c17a0d8817b58232826939154010ae49d7), [`ad4b535`](https://github.com/Effect-TS/effect-smol/commit/ad4b535e17f94ce35261829d5a3675f0a7808b4e), [`a29c2e7`](https://github.com/Effect-TS/effect-smol/commit/a29c2e7e3570920156702671d6f3367cd0195f6c), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`ffea4ec`](https://github.com/Effect-TS/effect-smol/commit/ffea4ecf2925f6a4c9fd13079d47584cbf2bed00), [`4255c9b`](https://github.com/Effect-TS/effect-smol/commit/4255c9ba78bb98c7838fbe9dccdd8465e9da5427)]:
- effect@4.0.0-beta.75
- @effect/platform-node@4.0.0-beta.75
## 4.0.0-beta.74
### Patch Changes
- Updated dependencies [[`b1fc6a4`](https://github.com/Effect-TS/effect-smol/commit/b1fc6a4b4d0ca7fa9fd162799ae17c86f2f7ee8e)]:
- effect@4.0.0-beta.74
- @effect/platform-node@4.0.0-beta.74
## 4.0.0-beta.73
### Patch Changes
- [#2292](https://github.com/Effect-TS/effect-smol/pull/2292) [`ba21055`](https://github.com/Effect-TS/effect-smol/commit/ba2105521173fbef145aa6d986bed1facb3bd94e) Thanks @tim-smart! - Add HttpApi generation support for custom OpenAPI HTTP security schemes.
- Updated dependencies [[`361ca30`](https://github.com/Effect-TS/effect-smol/commit/361ca30eb6e134feece547d6e00f82be4cb23f75), [`b9598c6`](https://github.com/Effect-TS/effect-smol/commit/b9598c6a209e75bfdb87ee3b024ecd1e3923ff6e)]:
- effect@4.0.0-beta.73
- @effect/platform-node@4.0.0-beta.73
## 4.0.0-beta.72
### Patch Changes
- Updated dependencies [[`73e67d1`](https://github.com/Effect-TS/effect-smol/commit/73e67d119a84d697773eaecb4865c6a71eb1a9cb), [`01d71ec`](https://github.com/Effect-TS/effect-smol/commit/01d71ec5a75f3c2747a8d3b1ad9701d1e27b7ce5), [`fcd707e`](https://github.com/Effect-TS/effect-smol/commit/fcd707e091a16e1b35343c901cc4052274e32239)]:
- effect@4.0.0-beta.72
- @effect/platform-node@4.0.0-beta.72
## 4.0.0-beta.71
### Patch Changes
- Updated dependencies [[`d8ac76b`](https://github.com/Effect-TS/effect-smol/commit/d8ac76b5bad458c42cebe8a0c1b3843f955ac293), [`2c3c00a`](https://github.com/Effect-TS/effect-smol/commit/2c3c00af6faba7b7d422af26a7a2bbc35636d230), [`3751e7c`](https://github.com/Effect-TS/effect-smol/commit/3751e7cf353e7a54cd692c37401207d9afba1e63), [`fc5f25b`](https://github.com/Effect-TS/effect-smol/commit/fc5f25b03ada5fc2431987768a74d3d3e75ca485), [`7ccced4`](https://github.com/Effect-TS/effect-smol/commit/7ccced42867c14c013b01160b3d292f14c05bd04), [`a2e1fe5`](https://github.com/Effect-TS/effect-smol/commit/a2e1fe5835c98c8ee4393a091b1d11b75126e349), [`4a4a36b`](https://github.com/Effect-TS/effect-smol/commit/4a4a36b10e6e616cad07584a43908f6a7e07e618), [`d350292`](https://github.com/Effect-TS/effect-smol/commit/d3502922b4740fa9d745797cbc3775cb67839b6d), [`730afb6`](https://github.com/Effect-TS/effect-smol/commit/730afb66696adf9bd5a328cbca29df9c05968771), [`df1b008`](https://github.com/Effect-TS/effect-smol/commit/df1b008f370f414c2a67a7b8139ef747af8e5fba), [`6d469d5`](https://github.com/Effect-TS/effect-smol/commit/6d469d567a7c41d7e5343bdee21d45b07b0e8190)]:
- effect@4.0.0-beta.71
- @effect/platform-node@4.0.0-beta.71
## 4.0.0-beta.70
### Patch Changes
- Updated dependencies [[`af7782d`](https://github.com/Effect-TS/effect-smol/commit/af7782d3008d08b043f3a3f261516001514b2b4e), [`7212d70`](https://github.com/Effect-TS/effect-smol/commit/7212d701a3eee7b3553ff502e2c066126e52e839)]:
- effect@4.0.0-beta.70
- @effect/platform-node@4.0.0-beta.70
## 4.0.0-beta.69
### Patch Changes
- Updated dependencies [[`70ea04a`](https://github.com/Effect-TS/effect-smol/commit/70ea04aa96a2a7859d738d414e1f0e3ed081a27a), [`d0ea8b0`](https://github.com/Effect-TS/effect-smol/commit/d0ea8b03f7d73ae076c1db12666141e480d11178), [`a57674b`](https://github.com/Effect-TS/effect-smol/commit/a57674b64845e9e75a456cf907bfdcb858859118), [`59aa334`](https://github.com/Effect-TS/effect-smol/commit/59aa334fbd0a504dda3c36f6d2ef1be7449b4b8b), [`8f4208e`](https://github.com/Effect-TS/effect-smol/commit/8f4208ee83bc7bdaa6793b5429847b45aab72470)]:
- effect@4.0.0-beta.69
- @effect/platform-node@4.0.0-beta.69
## 4.0.0-beta.68
### Patch Changes
- Updated dependencies [[`af8267f`](https://github.com/Effect-TS/effect-smol/commit/af8267f2f3588c3fb611e9286f6f933f29ce1217), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`f136bb7`](https://github.com/Effect-TS/effect-smol/commit/f136bb763048cbc6b17edd26496dba3e2415b9fa), [`6f38f07`](https://github.com/Effect-TS/effect-smol/commit/6f38f07d5941a211b251383aaab0f4f55e8a6557), [`aec9c40`](https://github.com/Effect-TS/effect-smol/commit/aec9c401a53db227f18bf5e0c84db7130ad862d6)]:
- effect@4.0.0-beta.68
- @effect/platform-node@4.0.0-beta.68
## 4.0.0-beta.67
### Patch Changes
- Updated dependencies [[`a42ef66`](https://github.com/Effect-TS/effect-smol/commit/a42ef6632abbddfa820995ae310ccc84ae8d9b6f), [`35594f8`](https://github.com/Effect-TS/effect-smol/commit/35594f811cafe471acd490114b103a1f8392c8d8), [`8bddd62`](https://github.com/Effect-TS/effect-smol/commit/8bddd628cb623f9533d345082583ff51cead6836), [`4be4c8d`](https://github.com/Effect-TS/effect-smol/commit/4be4c8d60862aa963869ee2ed9ffa048ffac0527), [`0c9d3ab`](https://github.com/Effect-TS/effect-smol/commit/0c9d3ab43eb721a370ed8306260cbac218c27e87), [`b156acc`](https://github.com/Effect-TS/effect-smol/commit/b156accd2691b4a051f823affdece7c39923ce85), [`d16c034`](https://github.com/Effect-TS/effect-smol/commit/d16c03434ee3e6dcd3bfc82b65d99e881d89025b), [`b559d68`](https://github.com/Effect-TS/effect-smol/commit/b559d68845f848a10153395778f035682d399075), [`a3de5d9`](https://github.com/Effect-TS/effect-smol/commit/a3de5d9215e5cc4a62e2666efbd7c1bf595eb84f), [`7e6c12e`](https://github.com/Effect-TS/effect-smol/commit/7e6c12ec9b3a5945f6c26e272cc8f6390541ad3e), [`098167a`](https://github.com/Effect-TS/effect-smol/commit/098167a220fe07da6f14455818733ab1b269c9dd)]:
- effect@4.0.0-beta.67
- @effect/platform-node@4.0.0-beta.67
## 4.0.0-beta.66
### Patch Changes
- Updated dependencies [[`ca2498e`](https://github.com/Effect-TS/effect-smol/commit/ca2498e702ac2d83fb7187707b7eb069bdb261a2), [`cd7d1fb`](https://github.com/Effect-TS/effect-smol/commit/cd7d1fba7e2e2c5ac3ad64e1be433440a5bda436), [`19a7033`](https://github.com/Effect-TS/effect-smol/commit/19a703367ec817cffc41d152da9b594827408e2b), [`33d26b4`](https://github.com/Effect-TS/effect-smol/commit/33d26b4210b2e974f146a71e7eed962f8ce00900), [`856766b`](https://github.com/Effect-TS/effect-smol/commit/856766b2c506aaed6d2df1d63bf3a5b1b062e1d4), [`079c7df`](https://github.com/Effect-TS/effect-smol/commit/079c7df82559bb9ce10a86dffb85d25e6ce07dc3)]:
- effect@4.0.0-beta.66
- @effect/platform-node@4.0.0-beta.66
## 4.0.0-beta.65
### Patch Changes
- Updated dependencies [[`6f11454`](https://github.com/Effect-TS/effect-smol/commit/6f11454a9b6c3bd00f6b35fd7af14a2f2d63a0a2)]:
- effect@4.0.0-beta.65
- @effect/platform-node@4.0.0-beta.65
## 4.0.0-beta.64
### Patch Changes
- Updated dependencies [[`7d4877a`](https://github.com/Effect-TS/effect-smol/commit/7d4877a1929cdb690280ea254326c04f2ec97ea5)]:
- effect@4.0.0-beta.64
- @effect/platform-node@4.0.0-beta.64
## 4.0.0-beta.63
### Patch Changes
- Updated dependencies [[`7f927ff`](https://github.com/Effect-TS/effect-smol/commit/7f927ffb7a9801dcfc4096c29e369d13d65cd0ac), [`a696b3e`](https://github.com/Effect-TS/effect-smol/commit/a696b3e83a8504cdbe261a18c10a1cc0619ae102)]:
- effect@4.0.0-beta.63
- @effect/platform-node@4.0.0-beta.63
## 4.0.0-beta.62
### Patch Changes
- Updated dependencies [[`4ab4b90`](https://github.com/Effect-TS/effect-smol/commit/4ab4b9007dc27a52ffabc6fcb37c96eeec795bf7)]:
- effect@4.0.0-beta.62
- @effect/platform-node@4.0.0-beta.62
## 4.0.0-beta.61
### Patch Changes
- Updated dependencies [[`50790af`](https://github.com/Effect-TS/effect-smol/commit/50790af9b190c38d10fb0723837d49b66432638f), [`71f7c3d`](https://github.com/Effect-TS/effect-smol/commit/71f7c3df997deda92c84146d569696dab3bd645c), [`aae8797`](https://github.com/Effect-TS/effect-smol/commit/aae8797b9cb383be0c182dd58d03d787c354238b)]:
- effect@4.0.0-beta.61
- @effect/platform-node@4.0.0-beta.61
## 4.0.0-beta.60
### Patch Changes
- Updated dependencies [[`f69d567`](https://github.com/Effect-TS/effect-smol/commit/f69d5675dcff9f4137295752baf066b7153fdc09), [`7909c95`](https://github.com/Effect-TS/effect-smol/commit/7909c954b8f6244a35a4b429f8dd0dff45dad620), [`bbb4dcc`](https://github.com/Effect-TS/effect-smol/commit/bbb4dcc6c406b83a416b4ad3541cc02037c420e4), [`7af2207`](https://github.com/Effect-TS/effect-smol/commit/7af2207901eabf3132c1b7010a69b3899c06fbbe), [`848b40a`](https://github.com/Effect-TS/effect-smol/commit/848b40a4bd4bf54a5098617d50c33c88eee8270a)]:
- effect@4.0.0-beta.60
- @effect/platform-node@4.0.0-beta.60
## 4.0.0-beta.59
### Patch Changes
- Updated dependencies [[`56837ea`](https://github.com/Effect-TS/effect-smol/commit/56837ea2a338395b35550641374e9e589bd8b71d)]:
- effect@4.0.0-beta.59
- @effect/platform-node@4.0.0-beta.59
## 4.0.0-beta.58
### Patch Changes
- Updated dependencies [[`11993d4`](https://github.com/Effect-TS/effect-smol/commit/11993d4934c66f5dc611b8bbf553f01d501ef8f7), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec)]:
- effect@4.0.0-beta.58
- @effect/platform-node@4.0.0-beta.58
## 4.0.0-beta.57
### Patch Changes
- Updated dependencies [[`a971f5c`](https://github.com/Effect-TS/effect-smol/commit/a971f5cbd92dfe4274420bf0966595eb35531060), [`8e110c5`](https://github.com/Effect-TS/effect-smol/commit/8e110c5f02a429ccc43a91df8678e402138c0851)]:
- effect@4.0.0-beta.57
- @effect/platform-node@4.0.0-beta.57
## 4.0.0-beta.56
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.56
- @effect/platform-node@4.0.0-beta.56
## 4.0.0-beta.55
### Patch Changes
- Updated dependencies [[`42cc744`](https://github.com/Effect-TS/effect-smol/commit/42cc744570968deb365fb46d47b53d3277050c93), [`04855ce`](https://github.com/Effect-TS/effect-smol/commit/04855ceeca4d40c55a5750dd9893b691f8ea741a)]:
- effect@4.0.0-beta.55
- @effect/platform-node@4.0.0-beta.55
## 4.0.0-beta.54
### Patch Changes
- Updated dependencies [[`e4b74f9`](https://github.com/Effect-TS/effect-smol/commit/e4b74f9c01a0e9b6cd58416de4af3a26d51da7c8), [`4c72808`](https://github.com/Effect-TS/effect-smol/commit/4c728081851c66dacf889a816535671bc841ae96)]:
- effect@4.0.0-beta.54
- @effect/platform-node@4.0.0-beta.54
## 4.0.0-beta.53
### Patch Changes
- Updated dependencies [[`0768509`](https://github.com/Effect-TS/effect-smol/commit/07685094e931af07d104165195826a535b55fa7e), [`476aede`](https://github.com/Effect-TS/effect-smol/commit/476aede69c6efa06b5781ca5eb3e3b128ca29141), [`4f79c54`](https://github.com/Effect-TS/effect-smol/commit/4f79c542e7b508c235ff485d862cc8b29a8260c5), [`4be6a7c`](https://github.com/Effect-TS/effect-smol/commit/4be6a7cf35dab2a01d652f56dd35f0358c5a7e88), [`88927eb`](https://github.com/Effect-TS/effect-smol/commit/88927ebb896162cdba103b36553280b58e0facac)]:
- effect@4.0.0-beta.53
- @effect/platform-node@4.0.0-beta.53
## 4.0.0-beta.52
### Patch Changes
- Updated dependencies [[`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`cf3a311`](https://github.com/Effect-TS/effect-smol/commit/cf3a311d863a8abb818840c3b80f847e621c43c1), [`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`131fdd5`](https://github.com/Effect-TS/effect-smol/commit/131fdd5b1f26531e265fe1a08f002002f47c276e)]:
- effect@4.0.0-beta.52
- @effect/platform-node@4.0.0-beta.52
## 4.0.0-beta.51
### Patch Changes
- Updated dependencies [[`778d2af`](https://github.com/Effect-TS/effect-smol/commit/778d2afe9b5154bc1f9abae46d93ea7e54c87344), [`4e24dcf`](https://github.com/Effect-TS/effect-smol/commit/4e24dcf75037f65eebc1eb68623bc7cbf9d5512a), [`4b1c015`](https://github.com/Effect-TS/effect-smol/commit/4b1c0150e9bdb5559ed32d250deb66e17b4240c7), [`454f8ad`](https://github.com/Effect-TS/effect-smol/commit/454f8adad822929c3ef60f8280d0987226b049fd), [`6754a0c`](https://github.com/Effect-TS/effect-smol/commit/6754a0cd18626b06805a079cc5265525a5eb7d27), [`90f7fd5`](https://github.com/Effect-TS/effect-smol/commit/90f7fd5243871b30980964135db4512b8119fa82), [`d7e1519`](https://github.com/Effect-TS/effect-smol/commit/d7e151974934201fd93fa4c8a1192ee9a5d965a0), [`72a8122`](https://github.com/Effect-TS/effect-smol/commit/72a81228e09782bae512f7d041bbfbc78bc668d0)]:
- effect@4.0.0-beta.51
- @effect/platform-node@4.0.0-beta.51
## 4.0.0-beta.50
### Patch Changes
- Updated dependencies [[`07be594`](https://github.com/Effect-TS/effect-smol/commit/07be594825de60f8e1b2102d21dbb9b8fc63b414), [`ae02433`](https://github.com/Effect-TS/effect-smol/commit/ae02433103ce28f53a0c9bfb4a44e75773289b7b)]:
- effect@4.0.0-beta.50
- @effect/platform-node@4.0.0-beta.50
## 4.0.0-beta.49
### Patch Changes
- Updated dependencies [[`7d87873`](https://github.com/Effect-TS/effect-smol/commit/7d8787340ff549370f6f2a88b612e9ebbfd6ba45), [`c2f6f90`](https://github.com/Effect-TS/effect-smol/commit/c2f6f901b200a6e515b4f02c93ce8005b7bbf1c5), [`216f13c`](https://github.com/Effect-TS/effect-smol/commit/216f13c1fce454a21b489bb915714a17e791a1ac)]:
- effect@4.0.0-beta.49
- @effect/platform-node@4.0.0-beta.49
## 4.0.0-beta.48
### Patch Changes
- Updated dependencies [[`4da56ec`](https://github.com/Effect-TS/effect-smol/commit/4da56ecff129b2da40137ffede23a73cc4e532d8), [`a5e6f77`](https://github.com/Effect-TS/effect-smol/commit/a5e6f774bab195cf50ecdc818240765f69a3bf4a), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070)]:
- effect@4.0.0-beta.48
- @effect/platform-node@4.0.0-beta.48
## 4.0.0-beta.47
### Patch Changes
- Updated dependencies [[`c584726`](https://github.com/Effect-TS/effect-smol/commit/c58472674e750e6938df955044eab88feda95e45), [`86a91a4`](https://github.com/Effect-TS/effect-smol/commit/86a91a4f0c59286dfa9393232d8020dea70ed4db), [`131caf9`](https://github.com/Effect-TS/effect-smol/commit/131caf9525151a0cb29803a8f1dffa0f4f479d12), [`c3615c8`](https://github.com/Effect-TS/effect-smol/commit/c3615c88379b9daf252df0db72c6ac5a20326406)]:
- effect@4.0.0-beta.47
- @effect/platform-node@4.0.0-beta.47
## 4.0.0-beta.46
### Patch Changes
- Updated dependencies [[`3a30b9e`](https://github.com/Effect-TS/effect-smol/commit/3a30b9e2ec2bd8b8193e1aa139f6878a07e3f5ee)]:
- effect@4.0.0-beta.46
- @effect/platform-node@4.0.0-beta.46
## 4.0.0-beta.45
### Patch Changes
- Updated dependencies [[`5c3af6d`](https://github.com/Effect-TS/effect-smol/commit/5c3af6d554f60be34f8fc21d598d9a298ae11beb)]:
- effect@4.0.0-beta.45
- @effect/platform-node@4.0.0-beta.45
## 4.0.0-beta.44
### Patch Changes
- [#1910](https://github.com/Effect-TS/effect-smol/pull/1910) [`698296f`](https://github.com/Effect-TS/effect-smol/commit/698296f919859010dfcc713ad29927157f8aec00) Thanks @craigsmitham! - Support `application/x-www-form-urlencoded` request bodies in `httpclient` output format. Previously, form-urlencoded request bodies were silently dropped, producing operations with no payload parameter. The generator now emits `HttpClientRequest.bodyUrlParams` for these endpoints, matching the existing pattern for `multipart/form-data` (`bodyFormData`) and `application/json` (`bodyJsonUnsafe`). The `httpapi` format was already handling this content type correctly.
- [#1961](https://github.com/Effect-TS/effect-smol/pull/1961) [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970) Thanks @IMax153! - Rename the `ServiceMap` module to `Context` across exports, docs, and tests.
- Updated dependencies [[`e3f0621`](https://github.com/Effect-TS/effect-smol/commit/e3f0621454c3f5d11070d30619da27c9232cadc1), [`5b476ab`](https://github.com/Effect-TS/effect-smol/commit/5b476abc0bd7e9bb59135ea1bcad2e4936227ced), [`6b40e5a`](https://github.com/Effect-TS/effect-smol/commit/6b40e5a4a6bd2087c15a3d7374d25057fdedfa16), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`3b09fb3`](https://github.com/Effect-TS/effect-smol/commit/3b09fb31c40c2802b01f21c23bcdd1fe7fb0aa82), [`2370410`](https://github.com/Effect-TS/effect-smol/commit/237041062e5af4594d32db91597e34e70a632877), [`dabc272`](https://github.com/Effect-TS/effect-smol/commit/dabc272444a700eb629c07ba3e77671a841ca86e), [`08b63c3`](https://github.com/Effect-TS/effect-smol/commit/08b63c3df11bd35c9fd6090dbd166287fdc40664), [`dfff04c`](https://github.com/Effect-TS/effect-smol/commit/dfff04c4c2b1d352dfad83992a6dce1280c85cf9), [`9baed9e`](https://github.com/Effect-TS/effect-smol/commit/9baed9e17e84702e6e480fcef6f86404f9e24be9), [`7846792`](https://github.com/Effect-TS/effect-smol/commit/7846792adc7e1631d62d26d657bd7ba6139f369b), [`1556a24`](https://github.com/Effect-TS/effect-smol/commit/1556a247623636b7ebe438fb56d77f1a7bf957bb), [`7c11bc2`](https://github.com/Effect-TS/effect-smol/commit/7c11bc292ab8e46252fe8f7576fb685917bfb8b5), [`b5ea591`](https://github.com/Effect-TS/effect-smol/commit/b5ea5913ec1d45d0dd12a327b9dd966bda2f6d02), [`0853afa`](https://github.com/Effect-TS/effect-smol/commit/0853afaeb1633b2d7f8b66893bd01c3aa1ef2c22), [`ac845f3`](https://github.com/Effect-TS/effect-smol/commit/ac845f3ab40e0b8719576e7f9bc16ea2e0e02cd4), [`b80c462`](https://github.com/Effect-TS/effect-smol/commit/b80c46247480f47bb64fc480fab48a3f37bc8888), [`b3f535d`](https://github.com/Effect-TS/effect-smol/commit/b3f535d9a7ac13b5fb984c29f93561c57a081ff0), [`6fe2e93`](https://github.com/Effect-TS/effect-smol/commit/6fe2e93cc2f1b173ef89651d74b6a5d2626b3226), [`cda8004`](https://github.com/Effect-TS/effect-smol/commit/cda800451c1ffbdddfc08415aed7b2d91e0412ee), [`8335477`](https://github.com/Effect-TS/effect-smol/commit/8335477a8a936a24b5f3ee6203c1b268bd1bfc3c), [`8c836f9`](https://github.com/Effect-TS/effect-smol/commit/8c836f99ab1e896b9580a71d67773625baff2eaf), [`718ff6f`](https://github.com/Effect-TS/effect-smol/commit/718ff6fe3e3d3820cefd67d2bff1b2224fe08060), [`7eed84f`](https://github.com/Effect-TS/effect-smol/commit/7eed84fc33c5781a6fb11bf4fd189d424902ebd4), [`5df46fe`](https://github.com/Effect-TS/effect-smol/commit/5df46fe2f654d59ab5fc1578f4fc27fa40368ef9), [`82dd0f2`](https://github.com/Effect-TS/effect-smol/commit/82dd0f26c6442b07143762ef7bc33742d3978dd6), [`03ae41e`](https://github.com/Effect-TS/effect-smol/commit/03ae41e7304cffac9f18feea22b73468feafc43a), [`4677a0a`](https://github.com/Effect-TS/effect-smol/commit/4677a0a58f95eea38a211efcd3f345f237a9e44a), [`87e1fc8`](https://github.com/Effect-TS/effect-smol/commit/87e1fc8b67e4901d75f567b2fecc3841ab762cc4), [`c1af1b7`](https://github.com/Effect-TS/effect-smol/commit/c1af1b756f63291e9c0298cf95c98a6920a0c2a0), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`c8a877b`](https://github.com/Effect-TS/effect-smol/commit/c8a877b53e8f29616335719e5dd1c3992dddf780), [`7da961a`](https://github.com/Effect-TS/effect-smol/commit/7da961ae4916229d2246699a5d3b20e5b2dd2020)]:
- effect@4.0.0-beta.44
- @effect/platform-node@4.0.0-beta.44
## 4.0.0-beta.43
### Patch Changes
- Updated dependencies [[`2ae33d0`](https://github.com/Effect-TS/effect-smol/commit/2ae33d050914915f7cb9c25ab0a020901e08d596), [`979811a`](https://github.com/Effect-TS/effect-smol/commit/979811a4c3f7ed21ed18ef560c49fb7f5569e80e), [`eb7dbef`](https://github.com/Effect-TS/effect-smol/commit/eb7dbeffa883386ad912815e62c0820cac1fdf8e), [`cf50eb4`](https://github.com/Effect-TS/effect-smol/commit/cf50eb49cb04706dae5185f624708117c413dee8), [`1d046fe`](https://github.com/Effect-TS/effect-smol/commit/1d046fe484560e23f3e22cb23eec6433f8f1fa02)]:
- effect@4.0.0-beta.43
- @effect/platform-node@4.0.0-beta.43
## 4.0.0-beta.42
### Patch Changes
- Updated dependencies [[`924e216`](https://github.com/Effect-TS/effect-smol/commit/924e216caa7e0bbf22e994a0cd2ce8b1f0f0b3ee), [`80e7f0c`](https://github.com/Effect-TS/effect-smol/commit/80e7f0cd9116e811e97b0ce30a77a8d1ecd072aa), [`f8328bf`](https://github.com/Effect-TS/effect-smol/commit/f8328bf0314da3dc7f31d314f94a5840e8d5217f), [`66d1c06`](https://github.com/Effect-TS/effect-smol/commit/66d1c06039079129707a230f7ad8c676439d7133), [`bee800b`](https://github.com/Effect-TS/effect-smol/commit/bee800bf285192a01bec72a7b7b51bc1159434e6), [`8930441`](https://github.com/Effect-TS/effect-smol/commit/8930441dee6f94c59c583d18d3ebd677cf1f2623)]:
- effect@4.0.0-beta.42
- @effect/platform-node@4.0.0-beta.42
## 4.0.0-beta.41
### Patch Changes
- [#1852](https://github.com/Effect-TS/effect-smol/pull/1852) [`54769ed`](https://github.com/Effect-TS/effect-smol/commit/54769ed9aa8ee513bbdc4a15d51e2e4042e67394) Thanks @tim-smart! - Finalize the OpenAPI generator public migration by replacing the `typeOnly` option and `--type-only` CLI flag with the `format` option and `--format` flag, and by adding `httpapi` as a supported output alongside `httpclient` and `httpclient-type-only`.
- [#1852](https://github.com/Effect-TS/effect-smol/pull/1852) [`54769ed`](https://github.com/Effect-TS/effect-smol/commit/54769ed9aa8ee513bbdc4a15d51e2e4042e67394) Thanks @tim-smart! - Fix generated schema declaration ordering when non-recursive schemas reference recursive schemas, preventing TypeScript use-before-declaration errors in generated clients and HttpApi modules.
- Updated dependencies [[`36f5c21`](https://github.com/Effect-TS/effect-smol/commit/36f5c2174d31ab42c4598bf81f178f40d0802283), [`d8ce758`](https://github.com/Effect-TS/effect-smol/commit/d8ce758669d6297ae932ac3251d83e7b49b22f30), [`11aab4c`](https://github.com/Effect-TS/effect-smol/commit/11aab4c6d37d5691adafc2d33da1a631b28ce814), [`3bc1efb`](https://github.com/Effect-TS/effect-smol/commit/3bc1efb53dd75b4a40de46f1f80c7f8a7d50af86), [`70e724e`](https://github.com/Effect-TS/effect-smol/commit/70e724e604604d4be1061cd8da0d360494998c84), [`738dee7`](https://github.com/Effect-TS/effect-smol/commit/738dee7edfd70af82dc4d2376db3a8ebe603eb48), [`2111963`](https://github.com/Effect-TS/effect-smol/commit/2111963f19b4c28c800664a8fac9590c1321885f), [`198a553`](https://github.com/Effect-TS/effect-smol/commit/198a553d9ce45f6a00bfc4d65ed0640669602d95)]:
- effect@4.0.0-beta.41
- @effect/platform-node@4.0.0-beta.41
## 4.0.0-beta.40
### Patch Changes
- Updated dependencies [[`f62860f`](https://github.com/Effect-TS/effect-smol/commit/f62860f0e5e45978fabf7256ae620a13152a772a), [`973f281`](https://github.com/Effect-TS/effect-smol/commit/973f2812529aadc1cc54598b2039799fa72b80f8)]:
- effect@4.0.0-beta.40
- @effect/platform-node@4.0.0-beta.40
## 4.0.0-beta.39
### Patch Changes
- Updated dependencies [[`f91fd3d`](https://github.com/Effect-TS/effect-smol/commit/f91fd3db39fe5628439fd175fba201a65a1aa9d0), [`edaae9d`](https://github.com/Effect-TS/effect-smol/commit/edaae9d65f464f941d7eddd723cd33d324f4b071), [`b47db0b`](https://github.com/Effect-TS/effect-smol/commit/b47db0bd5802064b6a24b3ea27c6ff2e0520d513), [`82d3c8e`](https://github.com/Effect-TS/effect-smol/commit/82d3c8e4f3f49b00df611b25aa6f8f74ec21b59b), [`7c22b31`](https://github.com/Effect-TS/effect-smol/commit/7c22b315d198dcbf44ae8cdb8b37879e1c9e3996)]:
- effect@4.0.0-beta.39
- @effect/platform-node@4.0.0-beta.39
## 4.0.0-beta.38
### Patch Changes
- Updated dependencies [[`f4dbe5b`](https://github.com/Effect-TS/effect-smol/commit/f4dbe5b26b9c2d33fae024bf44afbdf8541792cd), [`a71a607`](https://github.com/Effect-TS/effect-smol/commit/a71a607c89fb6669a12a562c2c23be81dfbe1adb), [`66a0494`](https://github.com/Effect-TS/effect-smol/commit/66a0494ed75cd12f2721dcbb1d8a072e3d9e14b6), [`5ef7218`](https://github.com/Effect-TS/effect-smol/commit/5ef7218fc559d57301fe929b8a0cab4033f4f1fd), [`472d260`](https://github.com/Effect-TS/effect-smol/commit/472d260655bc311fba5c2c6e23bb77d8f7e36ba0)]:
- effect@4.0.0-beta.38
- @effect/platform-node@4.0.0-beta.38
## 4.0.0-beta.37
### Patch Changes
- Updated dependencies [[`f7a0b71`](https://github.com/Effect-TS/effect-smol/commit/f7a0b711da8fdd645597dee29cacc5619c6afcf2), [`1e223c3`](https://github.com/Effect-TS/effect-smol/commit/1e223c30ccf835dfbb21284535d78549efaeca80), [`53740f4`](https://github.com/Effect-TS/effect-smol/commit/53740f47aa76d114b7d535649fb50efc54a09608), [`8c7cf89`](https://github.com/Effect-TS/effect-smol/commit/8c7cf89f719e580cbce1bf6c24e6996f1992a0a6), [`b6b81a9`](https://github.com/Effect-TS/effect-smol/commit/b6b81a940eaafcbc792d25413d6c02c707de31b2), [`8f4c1f9`](https://github.com/Effect-TS/effect-smol/commit/8f4c1f97ed60f8810b0b327b50117ffb2d8260d4), [`f2479f9`](https://github.com/Effect-TS/effect-smol/commit/f2479f9d3113b1f012db17a3852b4e28f478cf9c), [`c919921`](https://github.com/Effect-TS/effect-smol/commit/c9199217fad65529421d2cf95ecfff41257090fd), [`7af90c2`](https://github.com/Effect-TS/effect-smol/commit/7af90c2e3c99038eafa39650433839523790e2fe), [`f3be185`](https://github.com/Effect-TS/effect-smol/commit/f3be18569e5ca57c25eabf00df3ca601ebab43c7)]:
- effect@4.0.0-beta.37
- @effect/platform-node@4.0.0-beta.37
## 4.0.0-beta.36
### Patch Changes
- Updated dependencies [[`60fcbcc`](https://github.com/Effect-TS/effect-smol/commit/60fcbcc43d09471e8f7e0969955d99dcefc5be81), [`0a60837`](https://github.com/Effect-TS/effect-smol/commit/0a6083713124440e630030375bab367e8d7df24e), [`49164d2`](https://github.com/Effect-TS/effect-smol/commit/49164d2c20a8d21b66514992c4a15d8521f6b36e), [`334b6e4`](https://github.com/Effect-TS/effect-smol/commit/334b6e4f76fe11941b516d61f57e268bc31f0ca6), [`5700695`](https://github.com/Effect-TS/effect-smol/commit/5700695f76ae6da6b94c9c87d4dd2b8054fb829b), [`f8f4456`](https://github.com/Effect-TS/effect-smol/commit/f8f445644f3aa7ec093cab7445198a62ba18a480), [`969d24f`](https://github.com/Effect-TS/effect-smol/commit/969d24fdfa48c4838e811983848d9cb4e9b3b12c), [`851eda0`](https://github.com/Effect-TS/effect-smol/commit/851eda0533946e39bacaaf581896320d7a4f3e8c), [`8059c1c`](https://github.com/Effect-TS/effect-smol/commit/8059c1c3eba9a90af7cd889ea261bcb8fff0c185), [`6f83295`](https://github.com/Effect-TS/effect-smol/commit/6f8329546a73eaddc7cb5e85ea8e37e73fbfb611), [`65f7f57`](https://github.com/Effect-TS/effect-smol/commit/65f7f5737575fed668987462c96d29a446707c32), [`e7fabd2`](https://github.com/Effect-TS/effect-smol/commit/e7fabd2265db690eae5cfc9b83730c84699aef61), [`89c3e98`](https://github.com/Effect-TS/effect-smol/commit/89c3e985401eb38f33a3ae21a94ad27de3c1d28b), [`53794ab`](https://github.com/Effect-TS/effect-smol/commit/53794ab7af30aa5c5004ecf53659fafbe4b10542)]:
- effect@4.0.0-beta.36
- @effect/platform-node@4.0.0-beta.36
## 4.0.0-beta.35
### Patch Changes
- Updated dependencies [[`9252b43`](https://github.com/Effect-TS/effect-smol/commit/9252b43560f507709c2985abcf52a7837b23ddf8), [`7daf387`](https://github.com/Effect-TS/effect-smol/commit/7daf3870a656882a488a60f67881e6808c8f4d04), [`e1664a3`](https://github.com/Effect-TS/effect-smol/commit/e1664a38bc31ef4ceb4e9324c7226e1e99bf9c07), [`fdaa6e0`](https://github.com/Effect-TS/effect-smol/commit/fdaa6e0a41b6b6605438fa8557441792135380a2), [`19aa47e`](https://github.com/Effect-TS/effect-smol/commit/19aa47ef7b470e427620edca8970dd9cdd551216), [`c667dad`](https://github.com/Effect-TS/effect-smol/commit/c667dad07777b860e4764a3ba9a6cc41c236cd98), [`764d150`](https://github.com/Effect-TS/effect-smol/commit/764d1501bc5026b60fc8aef6cb02a5a87c762801), [`3c27098`](https://github.com/Effect-TS/effect-smol/commit/3c27098b5685a63db2c2eff654a250c94d3fcfa7), [`3015c2d`](https://github.com/Effect-TS/effect-smol/commit/3015c2dc25fb44694978b4ff921af9b24178fcc0)]:
- effect@4.0.0-beta.35
- @effect/platform-node@4.0.0-beta.35
## 4.0.0-beta.34
### Patch Changes
- Updated dependencies [[`f2f75ee`](https://github.com/Effect-TS/effect-smol/commit/f2f75ee564bce1cd95f5189c7bdeeed4f92dacb1), [`342fc4b`](https://github.com/Effect-TS/effect-smol/commit/342fc4b051739e32e7977159f26ff9541eda664f), [`5d704ee`](https://github.com/Effect-TS/effect-smol/commit/5d704ee10d20e8eb107e34bb8a21feb5aa4a7685), [`00add69`](https://github.com/Effect-TS/effect-smol/commit/00add69b59551e9df34772eb927638b093f6d71e), [`58217d3`](https://github.com/Effect-TS/effect-smol/commit/58217d318a7d716ccd707cce0f41573946939c28), [`f4e2aba`](https://github.com/Effect-TS/effect-smol/commit/f4e2aba01b76d1e3059b297e3cc942284dfeafb2), [`e3b44b6`](https://github.com/Effect-TS/effect-smol/commit/e3b44b6a2af9ee21dc5c1e928f0c20af857fa7a9), [`e1472b7`](https://github.com/Effect-TS/effect-smol/commit/e1472b7525c5d57a48bdec2353c3b742f7f916c0), [`7686320`](https://github.com/Effect-TS/effect-smol/commit/7686320cd123fa352b5c3d076fb18a3cac0a9bba)]:
- effect@4.0.0-beta.34
- @effect/platform-node@4.0.0-beta.34
## 4.0.0-beta.33
### Patch Changes
- Updated dependencies [[`571447d`](https://github.com/Effect-TS/effect-smol/commit/571447da67334449f8ae3d6ecb3d77ea4e0c4295)]:
- effect@4.0.0-beta.33
- @effect/platform-node@4.0.0-beta.33
## 4.0.0-beta.32
### Patch Changes
- Updated dependencies [[`bf8fff8`](https://github.com/Effect-TS/effect-smol/commit/bf8fff8a5f54b6df74cb7bbb42346fe9ba52435a), [`1af3ef3`](https://github.com/Effect-TS/effect-smol/commit/1af3ef3e3ca7fd417d0fc15f8ca8fe207eba4f74), [`27fea0f`](https://github.com/Effect-TS/effect-smol/commit/27fea0f66910de5905f40fd63f8ddbb6f7ac5aba), [`2ad6c1b`](https://github.com/Effect-TS/effect-smol/commit/2ad6c1b2c85a3a0fe351e3d56636a75eb76b4b4e), [`398ac3e`](https://github.com/Effect-TS/effect-smol/commit/398ac3e01cb75efce0e4e2913d1450cf65866732), [`51fe22f`](https://github.com/Effect-TS/effect-smol/commit/51fe22f3266e417b6c541aaed4b75d246fac91e7), [`4605db6`](https://github.com/Effect-TS/effect-smol/commit/4605db69cfacddbdbf1525865ddfde135158090c), [`f4de1b0`](https://github.com/Effect-TS/effect-smol/commit/f4de1b087c998d0bad1d9468f70b7d16c13b9f6f), [`60214f2`](https://github.com/Effect-TS/effect-smol/commit/60214f2080b2aeb091f691140eb20acb741691c3), [`c4b8b0f`](https://github.com/Effect-TS/effect-smol/commit/c4b8b0ffa8efb47c4cd7578a8943d6868509373f), [`6d9393a`](https://github.com/Effect-TS/effect-smol/commit/6d9393a0770a18722d23340e77f15455de341245), [`6de4efe`](https://github.com/Effect-TS/effect-smol/commit/6de4efe463c783614ceb0c094d77a336a899cbe0), [`4f969d1`](https://github.com/Effect-TS/effect-smol/commit/4f969d1563ba755ffa116c8ae409bb3436bd881d), [`6cc67c8`](https://github.com/Effect-TS/effect-smol/commit/6cc67c855e054ee3f3ac3485dca5f7805e79e8fb), [`8531a22`](https://github.com/Effect-TS/effect-smol/commit/8531a22ffbb52e11a030b09f358cafbfdf5edff7), [`b226760`](https://github.com/Effect-TS/effect-smol/commit/b22676067617f15c00722a3a63fd7c2c172c3d45), [`47a51ab`](https://github.com/Effect-TS/effect-smol/commit/47a51aba0ecdf3ef478bfa28a498bca188399bd4), [`1521d02`](https://github.com/Effect-TS/effect-smol/commit/1521d02e1f19f1d795edaaf862c1a1031d9c755e)]:
- effect@4.0.0-beta.32
- @effect/platform-node@4.0.0-beta.32
## 4.0.0-beta.31
### Patch Changes
- Updated dependencies [[`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6f23f0e`](https://github.com/Effect-TS/effect-smol/commit/6f23f0ed4cba573cd9395c2e582f582fe7271544), [`654aaec`](https://github.com/Effect-TS/effect-smol/commit/654aaec593305521b65dd042c204d761cc6e8c28), [`2958a42`](https://github.com/Effect-TS/effect-smol/commit/2958a42078966a8713a98f00485ab36484d5eccf), [`95d27a2`](https://github.com/Effect-TS/effect-smol/commit/95d27a239ed5147302605ab0b3147a056541b0c7), [`0fbaea8`](https://github.com/Effect-TS/effect-smol/commit/0fbaea8f9555a8044cec31a770394db613fc78e2), [`21d5d5e`](https://github.com/Effect-TS/effect-smol/commit/21d5d5e0439fd4d9bb6e508377215b1087555d45), [`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6e49959`](https://github.com/Effect-TS/effect-smol/commit/6e499590357a104c81779b3176cd3f84e4f91064), [`8f5805d`](https://github.com/Effect-TS/effect-smol/commit/8f5805dbdd0d1bc0ff0727cc398c8d80e544edee), [`990df2c`](https://github.com/Effect-TS/effect-smol/commit/990df2c3ceeb32e659acc10cc9485617f7b3c423)]:
- effect@4.0.0-beta.31
- @effect/platform-node@4.0.0-beta.31
## 4.0.0-beta.30
### Patch Changes
- Updated dependencies [[`c88e5b7`](https://github.com/Effect-TS/effect-smol/commit/c88e5b723ff09da4edaef6ce14d927ca01104a32), [`947d0e4`](https://github.com/Effect-TS/effect-smol/commit/947d0e4268ba5c4020ead380aa80812c7342408f), [`7517908`](https://github.com/Effect-TS/effect-smol/commit/75179085d159b88a1ab0bce70669d76dcf0d79a4), [`a49ecd5`](https://github.com/Effect-TS/effect-smol/commit/a49ecd5a183d7e7d33f47ff95e9d2dea5a12ead5), [`6993e33`](https://github.com/Effect-TS/effect-smol/commit/6993e3329122c834c20bacea72d8678232f4f103), [`514f2a2`](https://github.com/Effect-TS/effect-smol/commit/514f2a2ae54580fcacdbe2ea2196a83a852d0748), [`3214b47`](https://github.com/Effect-TS/effect-smol/commit/3214b47676de2d33fddc5fecfc2d226e6e83cc7b), [`95ec5ed`](https://github.com/Effect-TS/effect-smol/commit/95ec5ed345de77c893049e182d37a37cf164a268)]:
- effect@4.0.0-beta.30
- @effect/platform-node@4.0.0-beta.30
## 4.0.0-beta.29
### Patch Changes
- Updated dependencies [[`9d93adb`](https://github.com/Effect-TS/effect-smol/commit/9d93adb1c1795d1978391b30d7d2972c88052662), [`b52721c`](https://github.com/Effect-TS/effect-smol/commit/b52721cf0d11a567722b060c8536e3bdd4161f07), [`a891c7b`](https://github.com/Effect-TS/effect-smol/commit/a891c7b12f415b2287613dd4b91a09dfd38ef30d), [`ef26cdf`](https://github.com/Effect-TS/effect-smol/commit/ef26cdfb65d9955fc7e161629191930c2cc2c63f), [`82fd3ed`](https://github.com/Effect-TS/effect-smol/commit/82fd3ed922063ee5a34f96f3993c15c7515e4f67)]:
- effect@4.0.0-beta.29
- @effect/platform-node@4.0.0-beta.29
## 4.0.0-beta.28
### Patch Changes
- Updated dependencies [[`ff533f2`](https://github.com/Effect-TS/effect-smol/commit/ff533f203cd06302ad08032a27e01269b4a2d4c6), [`dc803ee`](https://github.com/Effect-TS/effect-smol/commit/dc803ee52ebd3e9f931118f0dfcb804542847556), [`d660b1c`](https://github.com/Effect-TS/effect-smol/commit/d660b1c99cb93d4f79715e91c7a4486801c0eefa), [`93a05e3`](https://github.com/Effect-TS/effect-smol/commit/93a05e3eaa624058b162aedd66aad70102837270), [`2a65cf6`](https://github.com/Effect-TS/effect-smol/commit/2a65cf6fd81ef63d944e6fb51f058d439bf4a834), [`a561a40`](https://github.com/Effect-TS/effect-smol/commit/a561a40cc41c548c2cf3153aca065ee92ee8aa57), [`29cd24d`](https://github.com/Effect-TS/effect-smol/commit/29cd24d1fe78480a72eeb38a90281ffddc0530bc), [`662a8e6`](https://github.com/Effect-TS/effect-smol/commit/662a8e6857dac64a7cd13bd8df4b0674654622f8), [`d2b52ba`](https://github.com/Effect-TS/effect-smol/commit/d2b52bae5b9336cf59729fbdcc4d7f09512b0cbf), [`407c3b4`](https://github.com/Effect-TS/effect-smol/commit/407c3b43a5d1414558e0e33b6f1fc0e6a6d489cc), [`42bc7ce`](https://github.com/Effect-TS/effect-smol/commit/42bc7ce5480f6f2953c39f8cb5c850d61df6f5a2), [`e741322`](https://github.com/Effect-TS/effect-smol/commit/e74132226cbfee24234311c7c1c13e6b7391384e), [`5c75fa8`](https://github.com/Effect-TS/effect-smol/commit/5c75fa8fb71163bc4c035ba1a215574dfd4badfc), [`747177b`](https://github.com/Effect-TS/effect-smol/commit/747177b0602f12d4461a843e953dfdffbeb0a429), [`326cd48`](https://github.com/Effect-TS/effect-smol/commit/326cd4828bce573fe985f35152155464bf4c5a70), [`627e922`](https://github.com/Effect-TS/effect-smol/commit/627e922b8d1e9521eae5e1caa5d667ad00b1619a), [`662287e`](https://github.com/Effect-TS/effect-smol/commit/662287e9abc76c941ccc2ee330aa07904d571341)]:
- effect@4.0.0-beta.28
- @effect/platform-node@4.0.0-beta.28
## 4.0.0-beta.27
### Patch Changes
- Updated dependencies [[`903a839`](https://github.com/Effect-TS/effect-smol/commit/903a839e94239e6ec4568315af28e405bcad95f4), [`91a0168`](https://github.com/Effect-TS/effect-smol/commit/91a016836680a6669308ecf464d3584bcc4ae1b7), [`c890f9a`](https://github.com/Effect-TS/effect-smol/commit/c890f9a1b3a989ed22528bd5a43326342e05b142), [`1e985f2`](https://github.com/Effect-TS/effect-smol/commit/1e985f237d250b51b91de22dde77160c1e778ce7)]:
- effect@4.0.0-beta.27
- @effect/platform-node@4.0.0-beta.27
## 4.0.0-beta.26
### Patch Changes
- Updated dependencies [[`fb21462`](https://github.com/Effect-TS/effect-smol/commit/fb21462642cdd5b1bada92f3eba18ae20445be42), [`2ed26b1`](https://github.com/Effect-TS/effect-smol/commit/2ed26b139805700e3df39efaa768ff01565e5c86), [`e832a57`](https://github.com/Effect-TS/effect-smol/commit/e832a57b570fe38f010c1fd99bceac5a325a9e07), [`7f01be7`](https://github.com/Effect-TS/effect-smol/commit/7f01be7f8db363d4b2e88e6b5571e96bb815786f), [`e965143`](https://github.com/Effect-TS/effect-smol/commit/e9651431e114479e6becf8ca7b1ed99ac7e91ccc), [`b9b80f1`](https://github.com/Effect-TS/effect-smol/commit/b9b80f1f15e152ceef0a727d150b7dc230abae99), [`98252aa`](https://github.com/Effect-TS/effect-smol/commit/98252aa0c0b17fc73fbdad65d0a1104965f9fc0f), [`56fbd94`](https://github.com/Effect-TS/effect-smol/commit/56fbd94311ad19a05001ad649d9e34ab00c74541), [`3faa109`](https://github.com/Effect-TS/effect-smol/commit/3faa109b7d093fbf14ad410d3e11d663f16e28f1), [`692ecfe`](https://github.com/Effect-TS/effect-smol/commit/692ecfed99fe58056b7a5afe001f4fcd1a61c446), [`1e70b72`](https://github.com/Effect-TS/effect-smol/commit/1e70b72d0b210474d0e96a15a5cfc279eae37e0c), [`ecf0782`](https://github.com/Effect-TS/effect-smol/commit/ecf07829ef2dfc01d8943c96c4fe9c1b44b97926)]:
- effect@4.0.0-beta.26
- @effect/platform-node@4.0.0-beta.26
## 4.0.0-beta.25
### Patch Changes
- Updated dependencies [[`fa17bb5`](https://github.com/Effect-TS/effect-smol/commit/fa17bb5be9f2533d01e11322b14804c7dec43714), [`f46e5b5`](https://github.com/Effect-TS/effect-smol/commit/f46e5b5ca2a918ee4d9270167e79db223077c96f), [`ce4767c`](https://github.com/Effect-TS/effect-smol/commit/ce4767cadcacc6ce8ff4c3a0d0fbc82ede655f63), [`c830a8b`](https://github.com/Effect-TS/effect-smol/commit/c830a8b6c292a6528d7f9318759d34800b00372d)]:
- effect@4.0.0-beta.25
- @effect/platform-node@4.0.0-beta.25
## 4.0.0-beta.24
### Patch Changes
- Updated dependencies [[`a909e1c`](https://github.com/Effect-TS/effect-smol/commit/a909e1c1ac2bc707527f5073776e3e7d239688d9), [`8814a4e`](https://github.com/Effect-TS/effect-smol/commit/8814a4ef78d67144d27689370af10099ea210399), [`3f942c5`](https://github.com/Effect-TS/effect-smol/commit/3f942c51cefa7b2ffa7c49e8c8a2c887570ba4c0), [`774ed59`](https://github.com/Effect-TS/effect-smol/commit/774ed59c52b2ab578bbb897c4f551f812231e1d2), [`f54b8d3`](https://github.com/Effect-TS/effect-smol/commit/f54b8d398fedad1815fd1f4c49814ab938cfc385)]:
- effect@4.0.0-beta.24
- @effect/platform-node@4.0.0-beta.24
## 4.0.0-beta.23
### Patch Changes
- Updated dependencies [[`5c73c41`](https://github.com/Effect-TS/effect-smol/commit/5c73c41b69eaeab80fcd62c9bfda490b446d1966)]:
- effect@4.0.0-beta.23
- @effect/platform-node@4.0.0-beta.23
## 4.0.0-beta.22
### Patch Changes
- Updated dependencies [[`0874332`](https://github.com/Effect-TS/effect-smol/commit/0874332f7c81118b06ac2eb105e0710211631479), [`c592dcd`](https://github.com/Effect-TS/effect-smol/commit/c592dcde0697e322065c8f418c0480ef910cb183), [`1dbe28d`](https://github.com/Effect-TS/effect-smol/commit/1dbe28dac8299cd3e218c9768450cfd173b5e294), [`564d730`](https://github.com/Effect-TS/effect-smol/commit/564d730b6bbf38dd8548a3b046e7a693b28699a4), [`3cfadc4`](https://github.com/Effect-TS/effect-smol/commit/3cfadc458b070c6cba6c5674b72a059f1e49118b), [`6634fd0`](https://github.com/Effect-TS/effect-smol/commit/6634fd07da067d80b8261fb2959d1a952b9e412e), [`d10dabe`](https://github.com/Effect-TS/effect-smol/commit/d10dabeb7af9a368f995829cd36ad08167cd8f95), [`f82f549`](https://github.com/Effect-TS/effect-smol/commit/f82f549a09e950e9d4987f279a800f4d953f0939), [`78a3382`](https://github.com/Effect-TS/effect-smol/commit/78a3382ddfbe034408f7480fa794733d9e82147b)]:
- effect@4.0.0-beta.22
- @effect/platform-node@4.0.0-beta.22
## 4.0.0-beta.21
### Patch Changes
- Updated dependencies [[`e691909`](https://github.com/Effect-TS/effect-smol/commit/e691909495ccb162ea7bfa351dd74632b99997cb), [`d5f413f`](https://github.com/Effect-TS/effect-smol/commit/d5f413f3c8fc57f2413cc5649c2003d6d4e5a6d7), [`139d152`](https://github.com/Effect-TS/effect-smol/commit/139d152941e562a073b5be12e8d66c8a4d4a8a57), [`947e3d4`](https://github.com/Effect-TS/effect-smol/commit/947e3d436ab8a017efda9b29be523efd1ca8df28), [`84b2cce`](https://github.com/Effect-TS/effect-smol/commit/84b2ccefe2aa3a7413b86738a4dc33cdb311ca55), [`7f5305e`](https://github.com/Effect-TS/effect-smol/commit/7f5305e69f5a33309e77b08a576edb25d7daaee2), [`9e6fd84`](https://github.com/Effect-TS/effect-smol/commit/9e6fd8471c93a3c643929151a3bdb62cb9c0ca0e), [`fdb8a4b`](https://github.com/Effect-TS/effect-smol/commit/fdb8a4b172721fbefe98bd5aa6fe4f0efd1da3eb), [`0f986ef`](https://github.com/Effect-TS/effect-smol/commit/0f986ef22f196fe091a7afdbd179485a7d888882), [`9355fc0`](https://github.com/Effect-TS/effect-smol/commit/9355fc0ffb5b7382146a5aed9eea83974b10d007)]:
- effect@4.0.0-beta.21
- @effect/platform-node@4.0.0-beta.21
## 4.0.0-beta.20
### Patch Changes
- Updated dependencies [[`842a624`](https://github.com/Effect-TS/effect-smol/commit/842a624f79d5e1407460b0ef3ab27d14d48ccf74), [`4785eef`](https://github.com/Effect-TS/effect-smol/commit/4785eef5d7cf1edb96ef2509aed2ba4d1edf3862), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`12ee8e2`](https://github.com/Effect-TS/effect-smol/commit/12ee8e27df7eb393d83a5e403390d0cfc82ca732), [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34), [`989d1cc`](https://github.com/Effect-TS/effect-smol/commit/989d1cca936fce0cc459057825ba40e3f5ef3827)]:
- effect@4.0.0-beta.20
- @effect/platform-node@4.0.0-beta.20
## 4.0.0-beta.19
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.19
- @effect/platform-node@4.0.0-beta.19
## 4.0.0-beta.18
### Patch Changes
- Updated dependencies [[`01e31fd`](https://github.com/Effect-TS/effect-smol/commit/01e31fdf8e5206849d23cbafd23a346f2f177ab8), [`0890aab`](https://github.com/Effect-TS/effect-smol/commit/0890aab15ed9c5ba52c383a72fdc6a444d7504d5), [`725260b`](https://github.com/Effect-TS/effect-smol/commit/725260b53f5142d6af7a93a2f9f464f974eda92d)]:
- effect@4.0.0-beta.18
- @effect/platform-node@4.0.0-beta.18
## 4.0.0-beta.17
### Patch Changes
- Updated dependencies [[`8f59c32`](https://github.com/Effect-TS/effect-smol/commit/8f59c32922597a48392744f7203e284866747781)]:
- effect@4.0.0-beta.17
- @effect/platform-node@4.0.0-beta.17
## 4.0.0-beta.16
### Patch Changes
- Updated dependencies [[`bf9096c`](https://github.com/Effect-TS/effect-smol/commit/bf9096c52a7d8791d93d232739e523eb84f6625a), [`29f81ca`](https://github.com/Effect-TS/effect-smol/commit/29f81ca07c67dba265804b140a7487fb15a5fc6b), [`68eb28c`](https://github.com/Effect-TS/effect-smol/commit/68eb28c2b0fc67a9f6204ade9bd16c5b37803bfb)]:
- effect@4.0.0-beta.16
- @effect/platform-node@4.0.0-beta.16
## 4.0.0-beta.15
### Patch Changes
- Updated dependencies [[`24ae609`](https://github.com/Effect-TS/effect-smol/commit/24ae60995d2fd7d621be356cdfdfd328c79639ba), [`0e3c059`](https://github.com/Effect-TS/effect-smol/commit/0e3c059987caa55ebd0c134f7c7b147c639c328e), [`e843b0a`](https://github.com/Effect-TS/effect-smol/commit/e843b0a7d7e7b600a0b3bd477f24e2e4cd26bc8b), [`f4389a2`](https://github.com/Effect-TS/effect-smol/commit/f4389a2cca3c5bbf00d69779f52ce41255f15a28), [`5b73de0`](https://github.com/Effect-TS/effect-smol/commit/5b73de095b3402d0c5c74092ace6ce18ebfad566), [`595d2d6`](https://github.com/Effect-TS/effect-smol/commit/595d2d6e7d50419f3532bd39266191532ace38f2)]:
- effect@4.0.0-beta.15
- @effect/platform-node@4.0.0-beta.15
## 4.0.0-beta.14
### Patch Changes
- Updated dependencies [[`c414700`](https://github.com/Effect-TS/effect-smol/commit/c414700ef1932e4b67d0102856de417336912350), [`a30c969`](https://github.com/Effect-TS/effect-smol/commit/a30c9699c0d736cf3952041e45d508b7d58907a9)]:
- effect@4.0.0-beta.14
- @effect/platform-node@4.0.0-beta.14
## 4.0.0-beta.13
### Patch Changes
- Updated dependencies [[`368f4c3`](https://github.com/Effect-TS/effect-smol/commit/368f4c363dd117e6f5a19ad77b161176cfd29fdd), [`db8a579`](https://github.com/Effect-TS/effect-smol/commit/db8a579e93e93ff73b1e60712732e03b597b916b), [`668b703`](https://github.com/Effect-TS/effect-smol/commit/668b70337e9ddbb0d1ae2282a95c282ce404e562), [`d40e76b`](https://github.com/Effect-TS/effect-smol/commit/d40e76b973543979e60e04a6baca04a8c65bdfc2), [`6e18cf8`](https://github.com/Effect-TS/effect-smol/commit/6e18cf883e9905ca718a6697b6a2a4bbd42739aa), [`86062e8`](https://github.com/Effect-TS/effect-smol/commit/86062e8a0c61bca5412fc40d2cf151d676901f08), [`c27ce75`](https://github.com/Effect-TS/effect-smol/commit/c27ce75d34c74dcfc6dba1bf77f1ce88f410a0de), [`e2d4fbf`](https://github.com/Effect-TS/effect-smol/commit/e2d4fbfeeda6a5d2a4c5aeb0501d8240c248b9eb), [`114ab42`](https://github.com/Effect-TS/effect-smol/commit/114ab42ad0edc590d29169675a493e0e915aa58f), [`484caec`](https://github.com/Effect-TS/effect-smol/commit/484caec47cccac8b86db2910742e406dfc7173ab)]:
- effect@4.0.0-beta.13
- @effect/platform-node@4.0.0-beta.13
## 4.0.0-beta.12
### Patch Changes
- Updated dependencies [[`70a74e8`](https://github.com/Effect-TS/effect-smol/commit/70a74e88a8767c9d4acdb9e5f25aec9a33588d07), [`b5b6e10`](https://github.com/Effect-TS/effect-smol/commit/b5b6e10621d54bf8c9857fec0d647ced78ecd857), [`f5ce5a9`](https://github.com/Effect-TS/effect-smol/commit/f5ce5a915359c6ebf254079e1da23cab6cde34fb), [`a29eb70`](https://github.com/Effect-TS/effect-smol/commit/a29eb702ffe3fc58bd28c4d7857298cd65d73668), [`c7b36e5`](https://github.com/Effect-TS/effect-smol/commit/c7b36e541a23e9a00f64e25b23851e51a37dfce5), [`9381d6d`](https://github.com/Effect-TS/effect-smol/commit/9381d6d4d9d819a81a46e56d0364c76e92a4fbca), [`88439f1`](https://github.com/Effect-TS/effect-smol/commit/88439f13ca13549f3e4822c48c4f019c14fc2bcc), [`e35307d`](https://github.com/Effect-TS/effect-smol/commit/e35307dbeb8eb26a9923f958b894a8eaaf259bf2), [`c7df4bc`](https://github.com/Effect-TS/effect-smol/commit/c7df4bce34009474c63d62a807abfdafb76971eb), [`accaf3b`](https://github.com/Effect-TS/effect-smol/commit/accaf3be7ac8da36e2334c509c23b8c9e88ea160), [`3e1c270`](https://github.com/Effect-TS/effect-smol/commit/3e1c2707bbdf67720af1509642b8ced195790882), [`6cd81f7`](https://github.com/Effect-TS/effect-smol/commit/6cd81f73baad86f5bbfa455a55d75cde71e9611a), [`f222da3`](https://github.com/Effect-TS/effect-smol/commit/f222da3cdb44554f3324c2c52d0d005ee575053e), [`61f901d`](https://github.com/Effect-TS/effect-smol/commit/61f901d830005b66e22d1de889fda132aeea97cd)]:
- effect@4.0.0-beta.12
- @effect/platform-node@4.0.0-beta.12
## 4.0.0-beta.11
### Patch Changes
- Updated dependencies [[`88659ed`](https://github.com/Effect-TS/effect-smol/commit/88659edb26e3623d557dccfe914c2c949672da16), [`f2915e8`](https://github.com/Effect-TS/effect-smol/commit/f2915e8e2efe80d50c281e53f297b9701d6dc199), [`eb71ace`](https://github.com/Effect-TS/effect-smol/commit/eb71acebbe0f228e4920278013beee3b67d62310), [`2a16999`](https://github.com/Effect-TS/effect-smol/commit/2a169996c7513d377ac47adbfd68e1490457135c), [`d42dd52`](https://github.com/Effect-TS/effect-smol/commit/d42dd52f11203f8e749fb5d3ecf7153e4a5a6814), [`339adaf`](https://github.com/Effect-TS/effect-smol/commit/339adaf850a62a892adebcb208c2d9dddf3b97b3), [`de19645`](https://github.com/Effect-TS/effect-smol/commit/de1964526d01102dd1cb99c8cfdd3e8df1f49ef1), [`9b1dc3b`](https://github.com/Effect-TS/effect-smol/commit/9b1dc3bcf2a1b68d0a67e3465db5ad01a1a56997), [`e4cb2f5`](https://github.com/Effect-TS/effect-smol/commit/e4cb2f55b30f4771ec1bf613ced36d6d96464dd5), [`8bced95`](https://github.com/Effect-TS/effect-smol/commit/8bced954ecb35d4489197a57b0efe927e7d75f49), [`9431420`](https://github.com/Effect-TS/effect-smol/commit/94314207c8019918200fbcb97aec992219f801f0), [`948dca2`](https://github.com/Effect-TS/effect-smol/commit/948dca22e4f672ba7a6db57f9899272bec7c08b8), [`d18e327`](https://github.com/Effect-TS/effect-smol/commit/d18e32765a2665e31ffb31e746bf983fcfac34c5), [`ab512f7`](https://github.com/Effect-TS/effect-smol/commit/ab512f7be1c0e6b359da921e22cd4944e4c57d3e)]:
- effect@4.0.0-beta.11
- @effect/platform-node@4.0.0-beta.11
## 4.0.0-beta.10
### Patch Changes
- Updated dependencies [[`371acab`](https://github.com/Effect-TS/effect-smol/commit/371acabb58d56f3a7a5e3e33d3d5fdc9f5573c74), [`856d774`](https://github.com/Effect-TS/effect-smol/commit/856d7741f1e296dd5048c6ff2b44b95d023e6ae4), [`b9e9202`](https://github.com/Effect-TS/effect-smol/commit/b9e92023c38caa322975d77cfe83e2d34ac9305a), [`1d1a974`](https://github.com/Effect-TS/effect-smol/commit/1d1a974bd280c81bff5d4505491cda03ba7a3f36), [`6bfe2a6`](https://github.com/Effect-TS/effect-smol/commit/6bfe2a659bc6335db75709931f405da45301cba2), [`b12c811`](https://github.com/Effect-TS/effect-smol/commit/b12c81157be287b1649c210616a244b50ec094d2), [`d17d98a`](https://github.com/Effect-TS/effect-smol/commit/d17d98ad78e2b44d95ef434adab79ac3c35e75ab), [`68c3c7c`](https://github.com/Effect-TS/effect-smol/commit/68c3c7cb1e06ed94fa5c4c123a234b4ccbfdecd8)]:
- effect@4.0.0-beta.10
- @effect/platform-node@4.0.0-beta.10
## 4.0.0-beta.9
### Patch Changes
- Updated dependencies [[`3386557`](https://github.com/Effect-TS/effect-smol/commit/338655731564a7be9f8859dedbf4d5bcac6eb350), [`b6666e3`](https://github.com/Effect-TS/effect-smol/commit/b6666e3cf6bd44ba1a8704e65c256c30359cb422)]:
- effect@4.0.0-beta.9
- @effect/platform-node@4.0.0-beta.9
## 4.0.0-beta.8
### Patch Changes
- Updated dependencies [[`246e672`](https://github.com/Effect-TS/effect-smol/commit/246e672dbbd7848d60e0c78fd66671b2f10b3752), [`807dec0`](https://github.com/Effect-TS/effect-smol/commit/807dec03801b4c58a6d00c237b6d98d6386911df)]:
- effect@4.0.0-beta.8
- @effect/platform-node@4.0.0-beta.8
## 4.0.0-beta.7
### Patch Changes
- Updated dependencies [[`a2bda6d`](https://github.com/Effect-TS/effect-smol/commit/a2bda6d4ef6de9d9b0c53ae2df5434f778d6161a), [`1f95a2b`](https://github.com/Effect-TS/effect-smol/commit/1f95a2b5aa9524bb38f4437f4691a664bf463ca1), [`a8d5e79`](https://github.com/Effect-TS/effect-smol/commit/a8d5e792fec201a83af0eb92fc79928d055125fd), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`06d8a03`](https://github.com/Effect-TS/effect-smol/commit/06d8a0391631e6130e3ab25227e59817852e227f), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`f9e883e`](https://github.com/Effect-TS/effect-smol/commit/f9e883e266fbda870336ee62f46b7ac85ba3de6e), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430)]:
- effect@4.0.0-beta.7
- @effect/platform-node@4.0.0-beta.7
## 4.0.0-beta.6
### Patch Changes
- Updated dependencies [[`3247da2`](https://github.com/Effect-TS/effect-smol/commit/3247da28331f345f68be5dbd2974a7e03d300fe1), [`f205705`](https://github.com/Effect-TS/effect-smol/commit/f2057050dbd034b8c186be2d40c3d03ee63a5a3b), [`f35022c`](https://github.com/Effect-TS/effect-smol/commit/f35022c212e4111527e1bb43f360a67b2b49fa85), [`8622721`](https://github.com/Effect-TS/effect-smol/commit/86227217b02d43680a3c6f3c21731b1d852c91f5), [`fc660ab`](https://github.com/Effect-TS/effect-smol/commit/fc660ab8b5ebae38b8d6b96cbf2f9b880cc09253), [`f37dc33`](https://github.com/Effect-TS/effect-smol/commit/f37dc335f64622fa9ce8d6d1d5dd8fc3f260257b), [`3662f32`](https://github.com/Effect-TS/effect-smol/commit/3662f328fcfa3b2fa01ffa79da40e12e93fcede8), [`a7d436f`](https://github.com/Effect-TS/effect-smol/commit/a7d436f438dcd7f49b9485e4e95a4511f31fad7d), [`6856a41`](https://github.com/Effect-TS/effect-smol/commit/6856a415d7eddd9d73d60919e976f1d071421be4), [`8c417d0`](https://github.com/Effect-TS/effect-smol/commit/8c417d03475e5e12d00dca0c4781d0af7e66b86c), [`5419570`](https://github.com/Effect-TS/effect-smol/commit/5419570ba47ce882a3a10882707b46f66e464906), [`449c5ed`](https://github.com/Effect-TS/effect-smol/commit/449c5ed5318e8a874e730420bcf52918fa2ec80f), [`4b5ec12`](https://github.com/Effect-TS/effect-smol/commit/4b5ec12f87f95f2a3cd8fe4d5b26c6eb0529381a), [`df87937`](https://github.com/Effect-TS/effect-smol/commit/df879375fc3b169c43f9c434b3775e12b80dffe4), [`5dbfca8`](https://github.com/Effect-TS/effect-smol/commit/5dbfca8d1dbb6d18d1605d4f8562e99c86e2ff11), [`e629497`](https://github.com/Effect-TS/effect-smol/commit/e6294973d55597ab6b6deca6babbe1e946b2c91d), [`981c991`](https://github.com/Effect-TS/effect-smol/commit/981c991cd78db34def815d5754379d737157f005), [`1ca2ed6`](https://github.com/Effect-TS/effect-smol/commit/1ca2ed67301a5dc40ae0ed94346b99f26fd22bbe), [`45722bd`](https://github.com/Effect-TS/effect-smol/commit/45722bde974458311f11ad237711363a10ec6894), [`eb2a85e`](https://github.com/Effect-TS/effect-smol/commit/eb2a85ed4dc162b2535d304799333a5a20477fd0)]:
- effect@4.0.0-beta.6
- @effect/platform-node@4.0.0-beta.6
## 4.0.0-beta.5
### Patch Changes
- Updated dependencies [[`f6e133e`](https://github.com/Effect-TS/effect-smol/commit/f6e133e9a16b32317bd09ff08c12b97a0ae44600), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8), [`a88e206`](https://github.com/Effect-TS/effect-smol/commit/a88e206e44dc66ca5a2b45bedc797877c5dbb083), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8)]:
- effect@4.0.0-beta.5
- @effect/platform-node@4.0.0-beta.5
## 4.0.0-beta.4
### Patch Changes
- Updated dependencies [[`c5a18ef`](https://github.com/Effect-TS/effect-smol/commit/c5a18ef44171e3880bf983faee74529908974b32), [`bc6b885`](https://github.com/Effect-TS/effect-smol/commit/bc6b885b94d887a200657c0775dfa874dc15bc0c)]:
- effect@4.0.0-beta.4
- @effect/platform-node@4.0.0-beta.4
## 4.0.0-beta.3
### Patch Changes
- Updated dependencies [[`3a0cf36`](https://github.com/Effect-TS/effect-smol/commit/3a0cf36eff106ba48d74e133c1598cd40613e530), [`c4da328`](https://github.com/Effect-TS/effect-smol/commit/c4da328d32fad1d61e0e538f5d371edf61521d7e)]:
- effect@4.0.0-beta.3
- @effect/platform-node@4.0.0-beta.3
## 4.0.0-beta.2
### Patch Changes
- Updated dependencies [[`a22ce73`](https://github.com/Effect-TS/effect-smol/commit/a22ce73b2bd9305b7ba665694d2255c0e6d5a8d0), [`ebdabf7`](https://github.com/Effect-TS/effect-smol/commit/ebdabf79ff4e62c8384aa8cf9a8d2787d536ee78), [`8f663bb`](https://github.com/Effect-TS/effect-smol/commit/8f663bb121021bf12bd264e8ae385187cb7a5dae)]:
- effect@4.0.0-beta.2
- @effect/platform-node@4.0.0-beta.2
## 4.0.0-beta.1
### Patch Changes
- Updated dependencies [[`0fecf70`](https://github.com/Effect-TS/effect-smol/commit/0fecf70048057623eed7c584a06671773a2b1743), [`709569e`](https://github.com/Effect-TS/effect-smol/commit/709569ed76bead9ebb0670599e4d890a07ca5a43)]:
- effect@4.0.0-beta.1
- @effect/platform-node@4.0.0-beta.1
## 4.0.0-beta.0
### Major Changes
- [#1183](https://github.com/Effect-TS/effect-smol/pull/1183) [`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66) Thanks @tim-smart! - v4 beta
### Patch Changes
- Updated dependencies [[`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66)]:
- @effect/platform-node@4.0.0-beta.0
- effect@4.0.0-beta.0

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Effectful Technologies Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,4 @@
{
"$schema": "../../../node_modules/@effect/docgen/schema.json",
"exclude": ["**/*.ts"]
}

View File

@@ -0,0 +1,77 @@
{
"name": "@effect/openapi-generator",
"type": "module",
"version": "4.0.0-beta.99",
"license": "MIT",
"description": "Generate Effect Schema types, HTTP clients, and HttpApi modules from OpenAPI specifications",
"homepage": "https://effect.website",
"repository": {
"type": "git",
"url": "https://github.com/Effect-TS/effect.git",
"directory": "packages/tools/openapi-generator"
},
"bugs": {
"url": "https://github.com/Effect-TS/effect/issues"
},
"tags": [
"typescript",
"openapi",
"codegen"
],
"keywords": [
"typescript",
"openapi",
"codegen"
],
"bin": {
"openapigen": "./src/bin.ts"
},
"sideEffects": [],
"exports": {
"./package.json": "./package.json",
"./*": "./src/*.ts",
"./bin": null,
"./main": null
},
"files": [
"src/**/*.ts",
"dist/**/*.js",
"dist/**/*.js.map",
"dist/**/*.d.ts",
"dist/**/*.d.ts.map"
],
"publishConfig": {
"access": "public",
"provenance": true,
"bin": {
"openapigen": "./dist/bin.js"
},
"exports": {
"./package.json": "./package.json",
"./*": "./dist/*.js",
"./bin": null,
"./main": null
}
},
"scripts": {
"build": "tsc -b tsconfig.json && pnpm babel",
"babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
"check": "tsc -b tsconfig.json",
"test": "vitest",
"coverage": "vitest --coverage"
},
"peerDependencies": {
"@effect/platform-node": "workspace:^",
"effect": "workspace:^"
},
"dependencies": {
"swagger2openapi": "^7.0.8"
},
"devDependencies": {
"@types/swagger2openapi": "^7.0.4",
"effect": "workspace:^",
"json-schema-typed": "^8.0.2",
"openapi-typescript": "^7.13.0",
"yaml": "^2.9.0"
}
}

View File

@@ -0,0 +1,552 @@
/**
* Renders parsed OpenAPI operations into generated Effect HttpApi source code.
*
* This module maps normalized OpenAPI metadata, tags, request bodies,
* responses, and security requirements into import declarations, HttpApi
* groups, endpoint definitions, schema annotations, and middleware classes.
* The rendered TypeScript can be emitted by the OpenAPI generator as an
* executable Effect `HttpApi` module.
*
* @since 4.0.0
*/
import type {
ParsedOpenApi,
ParsedOpenApiSecurityScheme,
ParsedOpenApiTag,
ParsedOperation,
ParsedOperationMediaTypeSchema,
ParsedOperationResponse
} from "./ParsedOperation.ts"
import * as Utils from "./Utils.ts"
interface GroupRenderModel {
readonly identifier: string
readonly topLevel: boolean
readonly metadata: ParsedOpenApiTag | undefined
readonly operations: ReadonlyArray<ParsedOperation>
readonly constName: string
}
interface SecurityRenderModel {
readonly securityDeclarations: ReadonlyArray<string>
readonly middlewareDeclarations: ReadonlyArray<string>
readonly endpointMiddlewares: ReadonlyMap<string, ReadonlyArray<string>>
}
const fallbackGroupIdentifier = "default"
/**
* Render the import declarations required by generated HttpApi source.
*
* **Details**
*
* The schema namespace import is named by the caller so generated code can
* avoid collisions with symbols already present in the output module. Multipart
* support is included only when the parsed OpenAPI document needs it.
*
* @category code generation
* @since 4.0.0
*/
export const imports = (
importName: string,
options?: {
readonly multipart?: boolean | undefined
}
): string =>
[
`import * as ${importName} from "effect/Schema"`,
...(options?.multipart === true ? [`import { Multipart } from "effect/unstable/http"`] : []),
`import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, HttpApiSecurity, OpenApi } from "effect/unstable/httpapi"`
].join("\n")
/**
* Convert a parsed OpenAPI document into Effect HttpApi source code.
*
* **Details**
*
* The generated implementation contains security declarations, reusable
* middleware classes, HttpApi groups, endpoint definitions, and OpenAPI
* annotations derived from the parsed operation metadata.
*
* @category code generation
* @since 4.0.0
*/
export const toImplementation = (
_importName: string,
name: string,
parsed: ParsedOpenApi
): string => {
const security = buildSecurityRenderModel(parsed)
const groups = groupOperations(parsed)
const groupSources = groups.map((group) => renderGroup(group, security.endpointMiddlewares))
const metadataAnnotations = renderApiAnnotations(parsed)
let apiValue = `export class ${name} extends HttpApi.make(${JSON.stringify(name)})`
for (const annotation of metadataAnnotations) {
apiValue += `\n .${annotation}`
}
if (groups.length > 0) {
apiValue += `\n .add(${groups.map((group) => group.constName).join(", ")})`
}
apiValue += ` {}`
return [
...security.securityDeclarations,
...security.middlewareDeclarations,
...groupSources,
apiValue
].join("\n\n")
}
const groupOperations = (parsed: ParsedOpenApi): ReadonlyArray<GroupRenderModel> => {
const tagMetadata = new Map(parsed.tags.map((tag) => [tag.name, tag]))
const byIdentifier = new Map<string, {
readonly identifier: string
topLevel: boolean
readonly metadata: ParsedOpenApiTag | undefined
readonly operations: Array<ParsedOperation>
}>()
for (const operation of parsed.operations) {
const identifier = operation.tags[0] ?? fallbackGroupIdentifier
const topLevel = operation.tags.length === 0
const existing = byIdentifier.get(identifier)
if (existing) {
if (topLevel) {
existing.topLevel = true
}
existing.operations.push(operation)
continue
}
byIdentifier.set(identifier, {
identifier,
topLevel,
metadata: tagMetadata.get(identifier),
operations: [operation]
})
}
const allocateName = makeNameAllocator()
const groups: Array<GroupRenderModel> = []
for (const group of byIdentifier.values()) {
const baseName = ensureIdentifier(group.identifier, "Group")
groups.push({
...group,
constName: allocateName(`${baseName}Group`)
})
}
return groups
}
const renderGroup = (
group: GroupRenderModel,
endpointMiddlewares: ReadonlyMap<string, ReadonlyArray<string>>
): string => {
let source = `class ${group.constName} extends HttpApiGroup.make(${JSON.stringify(group.identifier)}${
group.topLevel ? ", { topLevel: true }" : ""
})`
const allocateEndpointName = makeNameAllocator()
const endpointSources = group.operations.map((operation) =>
renderEndpoint(
operation,
allocateEndpointName(operation.id),
endpointMiddlewares.get(toOperationKey(operation)) ?? []
)
)
if (endpointSources.length > 0) {
source += `\n .add(${endpointSources.join(", \n ")})`
}
if (group.metadata?.description !== undefined) {
source += `\n .annotate(OpenApi.Description, ${JSON.stringify(group.metadata.description)})`
}
if (group.metadata?.externalDocs !== undefined) {
source += `\n .annotate(OpenApi.ExternalDocs, ${JSON.stringify(group.metadata.externalDocs)})`
}
source += ` {}`
return source
}
const renderEndpoint = (
operation: ParsedOperation,
endpointName: string,
endpointMiddlewares: ReadonlyArray<string>
): string => {
const options: Array<string> = []
if (operation.pathSchema !== undefined) {
options.push(`params: ${operation.pathSchema}`)
}
if (operation.querySchema !== undefined) {
options.push(`query: ${operation.querySchema}`)
}
if (operation.headersSchema !== undefined) {
options.push(`headers: ${operation.headersSchema}`)
}
const payload = renderPayload(operation)
if (payload !== undefined) {
options.push(`payload: ${payload}`)
}
const success = renderResponseSet(operation.responses, "success")
if (success !== undefined) {
options.push(`success: ${success}`)
}
const error = renderResponseSet(operation.responses, "error")
if (error !== undefined) {
options.push(`error: ${error}`)
}
const endpoint = options.length === 0
? `HttpApiEndpoint.${operation.method}(${JSON.stringify(endpointName)}, ${
JSON.stringify(toHttpApiPath(operation.path))
})`
: `HttpApiEndpoint.${operation.method}(${JSON.stringify(endpointName)}, ${
JSON.stringify(toHttpApiPath(operation.path))
}, { ${options.join(", ")} })`
const annotations: Array<string> = []
if (operation.operationId !== undefined) {
annotations.push(`annotate(OpenApi.Identifier, ${JSON.stringify(operation.operationId)})`)
}
if (operation.metadata.summary !== undefined) {
annotations.push(`annotate(OpenApi.Summary, ${JSON.stringify(operation.metadata.summary)})`)
}
if (operation.metadata.description !== undefined) {
annotations.push(`annotate(OpenApi.Description, ${JSON.stringify(operation.metadata.description)})`)
}
if (operation.metadata.deprecated) {
annotations.push(`annotate(OpenApi.Deprecated, true)`)
}
if (operation.metadata.externalDocs !== undefined) {
annotations.push(`annotate(OpenApi.ExternalDocs, ${JSON.stringify(operation.metadata.externalDocs)})`)
}
if (annotations.length === 0 && endpointMiddlewares.length === 0) {
return endpoint
}
let out = endpoint
for (const middleware of endpointMiddlewares) {
out += `\n .middleware(${middleware})`
}
for (const annotation of annotations) {
out += `\n .${annotation}`
}
return out
}
const renderPayload = (operation: ParsedOperation): string | undefined => {
if (!methodSupportsBody(operation.method)) {
return
}
const payloads = operation.requestBodyRepresentable.map((schema) => renderMediaSchema(schema))
if (payloads.length === 0) {
return
}
if (operation.requestBody?.required === false) {
payloads.unshift("HttpApiSchema.NoContent")
}
return joinSchemas(payloads)
}
const renderResponseSet = (
responses: ReadonlyArray<ParsedOperationResponse>,
target: "success" | "error"
): string | undefined => {
const rendered: Array<string> = []
for (const response of responses) {
const status = toStatus(response.status)
if (status === undefined) {
continue
}
const isSuccess = status < 400
if ((target === "success") !== isSuccess) {
continue
}
if (response.isEmpty) {
rendered.push(`HttpApiSchema.Empty(${status})`)
continue
}
for (const media of response.representable) {
rendered.push(applyStatus(renderMediaSchema(media), status, target))
}
}
if (rendered.length === 0) {
return
}
return joinSchemas(rendered)
}
const joinSchemas = (schemas: ReadonlyArray<string>): string =>
schemas.length === 1 ? schemas[0] : `[${schemas.join(", ")}]`
const renderMediaSchema = (media: ParsedOperationMediaTypeSchema): string => {
if (media.effectStream === "sse") {
const options = media.contentType === "text/event-stream"
? `{ events: ${media.schema}, error: ${media.errorSchema} }`
: `{ contentType: ${JSON.stringify(media.contentType)}, events: ${media.schema}, error: ${media.errorSchema} }`
return `HttpApiSchema.StreamSse(${options})`
}
if (media.effectStream === "uint8array") {
if (media.contentType === "application/octet-stream") {
return "HttpApiSchema.StreamUint8Array()"
}
return `HttpApiSchema.StreamUint8Array({ contentType: ${JSON.stringify(media.contentType)} })`
}
switch (media.encoding) {
case "json": {
if (media.contentType === "application/json") {
return media.schema
}
return `${media.schema}.pipe(HttpApiSchema.asJson({ contentType: ${JSON.stringify(media.contentType)} }))`
}
case "multipart": {
return `${media.schema}.pipe(HttpApiSchema.asMultipart())`
}
case "form-url-encoded": {
if (media.contentType === "application/x-www-form-urlencoded") {
return `${media.schema}.pipe(HttpApiSchema.asFormUrlEncoded())`
}
return `${media.schema}.pipe(HttpApiSchema.asFormUrlEncoded({ contentType: ${
JSON.stringify(media.contentType)
} }))`
}
case "text": {
if (media.contentType === "text/plain") {
return `${media.schema}.pipe(HttpApiSchema.asText())`
}
return `${media.schema}.pipe(HttpApiSchema.asText({ contentType: ${JSON.stringify(media.contentType)} }))`
}
case "binary": {
if (media.contentType === "application/octet-stream") {
return `${media.schema}.pipe(HttpApiSchema.asUint8Array())`
}
return `${media.schema}.pipe(HttpApiSchema.asUint8Array({ contentType: ${JSON.stringify(media.contentType)} }))`
}
}
}
const renderApiAnnotations = (parsed: ParsedOpenApi): ReadonlyArray<string> => {
const annotations: Array<string> = [
`annotate(OpenApi.Title, ${JSON.stringify(parsed.metadata.title)})`,
`annotate(OpenApi.Version, ${JSON.stringify(parsed.metadata.version)})`
]
if (parsed.metadata.summary !== undefined) {
annotations.push(`annotate(OpenApi.Summary, ${JSON.stringify(parsed.metadata.summary)})`)
}
if (parsed.metadata.description !== undefined) {
annotations.push(`annotate(OpenApi.Description, ${JSON.stringify(parsed.metadata.description)})`)
}
if (parsed.metadata.license !== undefined) {
annotations.push(`annotate(OpenApi.License, ${JSON.stringify(parsed.metadata.license)})`)
}
if (parsed.metadata.servers !== undefined) {
annotations.push(`annotate(OpenApi.Servers, ${JSON.stringify(parsed.metadata.servers)})`)
}
return annotations
}
const buildSecurityRenderModel = (parsed: ParsedOpenApi): SecurityRenderModel => {
const allocateName = makeNameAllocator()
const securityDeclarations: Array<string> = []
const middlewareDeclarations: Array<string> = []
const endpointMiddlewares = new Map<string, ReadonlyArray<string>>()
const schemeDeclarations = new Map<string, string>()
const middlewareNames = new Map<string, string>()
for (const securityScheme of parsed.securitySchemes) {
const baseName = ensureIdentifier(securityScheme.name, "Security")
const declarationName = allocateName(`${baseName}Security`)
schemeDeclarations.set(securityScheme.name, declarationName)
securityDeclarations.push(`export const ${declarationName} = ${renderSecurityScheme(securityScheme)}`)
}
for (const operation of parsed.operations) {
if (operation.effectiveSecurity.length === 0) {
continue
}
if (operation.effectiveSecurity.some((requirement) => Object.keys(requirement).length === 0)) {
continue
}
const operationMiddlewareNames: Array<string> = []
const seenOrSchemes = new Set<string>()
const andRequirements: Array<ReadonlyArray<string>> = []
for (const requirement of operation.effectiveSecurity) {
const schemes = Object.keys(requirement)
if (schemes.length === 1) {
const schemeName = schemes[0]
if (schemeDeclarations.has(schemeName) && !seenOrSchemes.has(schemeName)) {
seenOrSchemes.add(schemeName)
}
} else if (schemes.length > 1) {
andRequirements.push([...schemes].sort())
}
}
const orSchemeNames = Array.from(seenOrSchemes).sort()
if (orSchemeNames.length > 0) {
const className = getOrSecurityMiddlewareName(orSchemeNames)
operationMiddlewareNames.push(className)
}
const seenAndRequirements = new Set<string>()
for (const requirement of andRequirements) {
const key = requirement.join("\u0000")
if (seenAndRequirements.has(key)) {
continue
}
seenAndRequirements.add(key)
const className = getAndSecurityMiddlewareName(requirement)
operationMiddlewareNames.push(className)
}
if (operationMiddlewareNames.length > 0) {
endpointMiddlewares.set(toOperationKey(operation), operationMiddlewareNames)
}
}
return {
securityDeclarations,
middlewareDeclarations,
endpointMiddlewares
}
function getOrSecurityMiddlewareName(schemes: ReadonlyArray<string>): string {
const key = `or:${schemes.join("\u0000")}`
const existing = middlewareNames.get(key)
if (existing !== undefined) {
return existing
}
const className = allocateName(`${getSecurityMiddlewareBaseName(schemes, "Or")}SecurityMiddleware`)
const securityEntries = schemes.map((name) => `${JSON.stringify(name)}: ${schemeDeclarations.get(name)!}`).join(
", "
)
middlewareDeclarations.push(
`export class ${className} extends HttpApiMiddleware.Service<${className}>()(${
JSON.stringify(`${schemes.join(" | ")} security`)
}, { security: { ${securityEntries} } }) {}`
)
middlewareNames.set(key, className)
return className
}
function getAndSecurityMiddlewareName(schemes: ReadonlyArray<string>): string {
const key = `and:${schemes.join("\u0000")}`
const existing = middlewareNames.get(key)
if (existing !== undefined) {
return existing
}
const className = allocateName(`${getSecurityMiddlewareBaseName(schemes, "And")}SecurityMiddleware`)
middlewareDeclarations.push(
`class ${className} extends HttpApiMiddleware.Service<${className}>()(${
JSON.stringify(`${schemes.join(" & ")} security`)
}) {}`
)
middlewareNames.set(key, className)
return className
}
}
const getSecurityMiddlewareBaseName = (
schemes: ReadonlyArray<string>,
joiner: "And" | "Or"
): string => {
const [head, ...tail] = schemes.map((scheme) => ensureIdentifier(scheme, "Security"))
return tail.length === 0 ? head : [head, ...tail.map((scheme) => `${joiner}${scheme}`)].join("")
}
const renderSecurityScheme = (securityScheme: ParsedOpenApiSecurityScheme): string => {
let source: string
switch (securityScheme.type) {
case "basic": {
source = "HttpApiSecurity.basic"
break
}
case "bearer": {
source = "HttpApiSecurity.bearer"
break
}
case "http": {
source = `HttpApiSecurity.http({ scheme: ${JSON.stringify(securityScheme.scheme!)} })`
break
}
case "apiKey": {
source = `HttpApiSecurity.apiKey({ key: ${JSON.stringify(securityScheme.key!)}, in: ${
JSON.stringify(securityScheme.in!)
} })`
break
}
}
if (securityScheme.description !== undefined) {
source += `.pipe(HttpApiSecurity.annotate(OpenApi.Description, ${JSON.stringify(securityScheme.description)}))`
}
if (
(securityScheme.type === "bearer" || securityScheme.type === "http") && securityScheme.bearerFormat !== undefined
) {
source += `.pipe(HttpApiSecurity.annotate(OpenApi.Format, ${JSON.stringify(securityScheme.bearerFormat)}))`
}
return source
}
const toOperationKey = (operation: ParsedOperation): string => `${operation.method}:${operation.path}`
const toHttpApiPath = (path: string): string => path.replace(/{([^}]+)}/g, ":$1")
const toStatus = (status: string): number | undefined => {
if (!/^\d{3}$/.test(status)) {
return
}
return Number(status)
}
const applyStatus = (schema: string, status: number, target: "success" | "error"): string => {
if ((target === "success" && status === 200) || (target === "error" && status === 500)) {
return schema
}
return `${schema}.pipe(HttpApiSchema.status(${status}))`
}
const methodSupportsBody = (method: ParsedOperation["method"]): boolean =>
method !== "get" && method !== "head" && method !== "options" && method !== "trace"
const ensureIdentifier = (value: string, fallback: string): string => {
const sanitized = Utils.identifier(value)
return sanitized.length > 0 ? sanitized : fallback
}
const makeNameAllocator = () => {
const used = new Set<string>()
return (base: string) => {
let candidate = base
let index = 2
while (used.has(candidate)) {
candidate = `${base}${index}`
index += 1
}
used.add(candidate)
return candidate
}
}

View File

@@ -0,0 +1,331 @@
/**
* Generate TypeScript source for JSON Schema declarations extracted from
* OpenAPI documents.
*
* This module is the schema-rendering stage of the OpenAPI generator. Callers
* register named OpenAPI 3.0 or 3.1 schemas, provide the reusable component
* definitions for the document, and receive source text containing exported
* TypeScript aliases plus Effect Schema runtime values. The generator first
* normalizes OpenAPI-specific schema shapes into Effect's JSON Schema model,
* then delegates recursive analysis and runtime expression construction to
* `SchemaRepresentation`.
*
* The renderer keeps the emitted module usable for both human maintainers and
* automated code-generation consumers by grouping recursive declarations,
* reusable references, and locally registered schemas. It also contains the
* HttpApi-specific multipart file substitutions that map generated schemas to
* Effect's multipart runtime types.
*
* @since 4.0.0
*/
import * as Arr from "effect/Array"
import * as JsonSchema from "effect/JsonSchema"
import * as Rec from "effect/Record"
import * as SchemaRepresentation from "effect/SchemaRepresentation"
type Source = "openapi-3.0" | "openapi-3.1"
interface GenerateOptions {
readonly onEnter?: ((js: JsonSchema.JsonSchema) => JsonSchema.JsonSchema) | undefined
}
interface GenerateHttpApiOptions extends GenerateOptions {
readonly multipartSchemaRefs?: {
readonly singleFile: string
readonly files: string
} | undefined
}
/**
* Create a stateful JSON Schema code generator for OpenAPI-derived schemas.
*
* **Details**
*
* Schemas registered with the returned generator are converted into TypeScript
* type aliases and Effect Schema runtime declarations, with reusable OpenAPI
* component definitions supplied at generation time.
*
* @category code generation
* @since 4.0.0
*/
export function make() {
const store: Record<string, JsonSchema.JsonSchema> = {}
function addSchema(name: string, schema: JsonSchema.JsonSchema): string {
if (name in store) {
throw new Error(`Schema ${name} already exists`)
}
store[name] = schema
return name
}
function generate(
source: Source,
components: JsonSchema.Definitions,
typeOnly: boolean,
options?: GenerateOptions
) {
const generated = makeCodeDocument(source, components, options)
if (generated === undefined) {
return ""
}
const nonRecursiveReferences = generated.codeDocument.references.nonRecursives
const recursiveReferences = Object.entries(generated.codeDocument.references.recursives)
const nonRecursives = nonRecursiveReferences.map(({ $ref, code }) =>
renderSchemaTypeAndRuntime($ref, code, typeOnly)
)
const recursiveDeclarations: Array<string> = []
const recursives: Array<string> = []
if (typeOnly) {
for (const [$ref, code] of recursiveReferences) {
recursives.push(renderSchemaTypeAndRuntime($ref, code, true))
}
} else {
const recursivelyForwardReferenced = collectForwardReferencedRecursives(
nonRecursiveReferences,
recursiveReferences
)
const recursiveInternalNames = makeRecursiveInternalNameMap(
recursivelyForwardReferenced,
[
...nonRecursiveReferences.map(({ $ref }) => $ref),
...recursiveReferences.map(([$ref]) => $ref),
...generated.nameMap
]
)
for (const [$ref, code] of recursiveReferences) {
if (recursivelyForwardReferenced.has($ref)) {
const internalName = recursiveInternalNames.get($ref)!
recursiveDeclarations.push(renderRecursiveReferenceDeclaration($ref, code, internalName))
recursives.push(`const ${internalName} = ${code.runtime}`)
continue
}
recursives.push(renderSchemaTypeAndRuntime($ref, code, false))
}
}
const codes = generated.codeDocument.codes.map((code, i) =>
renderSchemaTypeAndRuntime(generated.nameMap[i], code, typeOnly)
)
return render("recursive declarations", recursiveDeclarations) +
render("non-recursive definitions", nonRecursives) +
render("recursive definitions", recursives) +
render("schemas", codes)
}
function generateHttpApi(
source: Source,
components: JsonSchema.Definitions,
options?: GenerateHttpApiOptions
) {
const generated = makeCodeDocument(source, components, options)
if (generated === undefined) {
return ""
}
const nonRecursiveReferences = generated.codeDocument.references.nonRecursives
const recursiveReferences = Object.entries(generated.codeDocument.references.recursives)
const nonRecursives = nonRecursiveReferences.map(({ $ref, code }) =>
renderSchemaTypeAndRuntime($ref, code, false, options?.multipartSchemaRefs)
)
const recursivelyForwardReferenced = collectForwardReferencedRecursives(nonRecursiveReferences, recursiveReferences)
const recursiveInternalNames = makeRecursiveInternalNameMap(
recursivelyForwardReferenced,
[
...nonRecursiveReferences.map(({ $ref }) => $ref),
...recursiveReferences.map(([$ref]) => $ref),
...generated.nameMap
]
)
const recursiveDeclarations: Array<string> = []
const recursives: Array<string> = []
for (const [$ref, code] of recursiveReferences) {
if (recursivelyForwardReferenced.has($ref)) {
const internalName = recursiveInternalNames.get($ref)!
recursiveDeclarations.push(renderRecursiveReferenceDeclaration($ref, code, internalName))
recursives.push(`const ${internalName} = ${code.runtime}`)
continue
}
recursives.push(renderSchemaTypeAndRuntime($ref, code, false, options?.multipartSchemaRefs))
}
const codes = generated.codeDocument.codes.map((code, i) =>
renderSchemaTypeAndRuntime(generated.nameMap[i], code, false, options?.multipartSchemaRefs)
)
return render("recursive declarations", recursiveDeclarations) +
render("non-recursive definitions", nonRecursives) +
render("recursive definitions", recursives) +
render("schemas", codes)
}
function makeCodeDocument(
source: Source,
components: JsonSchema.Definitions,
options?: GenerateOptions
): {
readonly nameMap: Array<string>
readonly codeDocument: SchemaRepresentation.CodeDocument
} | undefined {
const nameMap: Array<string> = []
const schemas: Array<JsonSchema.JsonSchema> = []
const definitions: JsonSchema.Definitions = Rec.map(
components,
(js) => fromSchemaOpenApi(source, js).schema
)
for (const [name, js] of Object.entries(store)) {
nameMap.push(name)
schemas.push(fromSchemaOpenApi(source, js).schema)
}
if (!Arr.isArrayNonEmpty(schemas)) {
return
}
const multiDocument: SchemaRepresentation.MultiDocument = SchemaRepresentation.fromJsonSchemaMultiDocument({
dialect: "draft-2020-12",
schemas,
definitions
}, {
onEnter(js) {
const out = { ...js }
if (out.type === "object" && out.additionalProperties === undefined) {
out.additionalProperties = false
}
return options?.onEnter?.(out) ?? out
}
})
return {
nameMap,
codeDocument: SchemaRepresentation.toCodeDocument(multiDocument)
}
}
return { addSchema, generate, generateHttpApi } as const
}
function fromSchemaOpenApi(source: Source, jsonSchema: JsonSchema.JsonSchema) {
switch (source) {
case "openapi-3.1":
return JsonSchema.fromSchemaOpenApi3_1(jsonSchema)
case "openapi-3.0":
return JsonSchema.fromSchemaOpenApi3_0(jsonSchema)
}
}
function renderSchemaTypeAndRuntime(
$ref: string,
code: SchemaRepresentation.Code,
typeOnly: boolean,
multipartSchemaRefs?: {
readonly singleFile: string
readonly files: string
}
) {
if (!typeOnly && multipartSchemaRefs !== undefined) {
if ($ref === multipartSchemaRefs.singleFile) {
return [
`export type ${$ref} = Multipart.PersistedFile`,
`export const ${$ref} = Multipart.SingleFileSchema`
].join("\n")
}
if ($ref === multipartSchemaRefs.files) {
return [
`export type ${$ref} = ReadonlyArray<Multipart.PersistedFile>`,
`export const ${$ref} = Multipart.FilesSchema`
].join("\n")
}
}
const strings = [`export type ${$ref} = ${code.Type}`]
if (!typeOnly) {
strings.push(`export const ${$ref} = ${code.runtime}`)
}
return strings.join("\n")
}
function renderRecursiveReferenceDeclaration(
$ref: string,
code: SchemaRepresentation.Code,
internalName: string
): string {
return [
`export type ${$ref} = ${code.Type}`,
`export const ${$ref} = Schema.suspend((): Schema.Codec<${$ref}> => ${internalName})`
].join("\n")
}
function render(title: string, as: ReadonlyArray<string>) {
if (as.length === 0) return ""
return "// " + title + "\n" + as.join("\n") + "\n"
}
const tokenPattern = /[A-Za-z_$][A-Za-z0-9_$]*/g
function collectForwardReferencedRecursives(
nonRecursives: ReadonlyArray<{
readonly $ref: string
readonly code: SchemaRepresentation.Code
}>,
recursives: ReadonlyArray<readonly [string, SchemaRepresentation.Code]>
): Set<string> {
const recursiveNames = new Set(recursives.map(([name]) => name))
const recursiveIndexes = new Map(recursives.map(([name], index) => [name, index]))
const referenced = new Set<string>()
for (const { code } of nonRecursives) {
for (const token of code.runtime.matchAll(tokenPattern)) {
const identifier = token[0]
if (recursiveNames.has(identifier)) {
referenced.add(identifier)
}
}
}
for (let index = 0; index < recursives.length; index++) {
const [, code] = recursives[index]
for (const token of code.runtime.matchAll(tokenPattern)) {
const identifier = token[0]
const referencedIndex = recursiveIndexes.get(identifier)
if (referencedIndex !== undefined && referencedIndex > index) {
referenced.add(identifier)
}
}
}
return referenced
}
function makeRecursiveInternalNameMap(
recursiveNames: ReadonlySet<string>,
existingNames: ReadonlyArray<string>
): Map<string, string> {
const usedNames = new Set(existingNames)
const internalNames = new Map<string, string>()
for (const name of recursiveNames) {
let candidate = `__recursive_${name}`
while (usedNames.has(candidate)) {
candidate = `_${candidate}`
}
usedNames.add(candidate)
internalNames.set(name, candidate)
}
return internalNames
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,539 @@
/**
* OpenAPI spec patching utilities.
*
* Handles parsing and applying JSON Patch documents (RFC 6902) to OpenAPI
* specs. Supports patches from:
* - JSON files (.json)
* - YAML files (.yaml, .yml)
* - Inline JSON strings
*
* @since 4.0.0
*/
import * as Effect from "effect/Effect"
import * as FileSystem from "effect/FileSystem"
import { constFalse, constUndefined } from "effect/Function"
import * as JsonPatch from "effect/JsonPatch"
import * as Path from "effect/Path"
import * as Predicate from "effect/Predicate"
import * as Schema from "effect/Schema"
import * as Yaml from "yaml"
// =============================================================================
// Error Types
// =============================================================================
/**
* Error thrown when parsing a JSON Patch input fails.
*
* **Details**
*
* This error occurs when:
* - A patch file cannot be read
* - JSON or YAML syntax is invalid
* - The file format is unsupported
*
* **Example** (Creating a parse error)
*
* ```ts
* import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
*
* const error = new OpenApiPatch.JsonPatchParseError({
* source: "./patches/fix.json",
* reason: "Unexpected token at position 42"
* })
*
* console.log(error.message)
* // "Failed to parse patch from ./patches/fix.json: Unexpected token at position 42"
* ```
*
* @category errors
* @since 4.0.0
*/
export class JsonPatchParseError extends Schema.ErrorClass<JsonPatchParseError>("JsonPatchParseError")({
_tag: Schema.tag("JsonPatchParseError"),
source: Schema.String,
reason: Schema.String
}) {
override get message() {
return `Failed to parse patch from ${this.source}: ${this.reason}`
}
}
/**
* Error thrown when a parsed value does not conform to the JSON Patch schema.
*
* **Details**
*
* This error occurs when:
* - The patch is not an array
* - An operation is missing required fields (op, path)
* - An operation has an unsupported op value
* - An add/replace operation is missing the value field
*
* **Example** (Creating a validation error)
*
* ```ts
* import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
*
* const error = new OpenApiPatch.JsonPatchValidationError({
* source: "inline",
* reason: "Expected 'add' | 'remove' | 'replace' at [0].op, got 'copy'"
* })
*
* console.log(error.message)
* // "Invalid JSON Patch from inline: Expected 'add' | 'remove' | 'replace' at [0].op, got 'copy'"
* ```
*
* @category errors
* @since 4.0.0
*/
export class JsonPatchValidationError extends Schema.ErrorClass<JsonPatchValidationError>("JsonPatchValidationError")({
_tag: Schema.tag("JsonPatchValidationError"),
source: Schema.String,
reason: Schema.String
}) {
override get message() {
return `Invalid JSON Patch from ${this.source}: ${this.reason}`
}
}
/**
* Error thrown when applying a JSON Patch operation fails.
*
* **Details**
*
* This error occurs when:
* - A path does not exist for remove/replace operations
* - An array index is out of bounds
* - The target location is not a valid container
*
* **Example** (Creating an application error)
*
* ```ts
* import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
*
* const error = new OpenApiPatch.JsonPatchApplicationError({
* source: "./patches/fix.json",
* operationIndex: 2,
* operation: "remove",
* path: "/paths/~1users",
* reason: "Property \"users\" does not exist"
* })
*
* console.log(error.message)
* // "Failed to apply patch from ./patches/fix.json: operation 2 (remove at /paths/~1users): Property \"users\" does not exist"
* ```
*
* @category errors
* @since 4.0.0
*/
export class JsonPatchApplicationError
extends Schema.ErrorClass<JsonPatchApplicationError>("JsonPatchApplicationError")({
_tag: Schema.tag("JsonPatchApplicationError"),
source: Schema.String,
operationIndex: Schema.Number,
operation: Schema.String,
path: Schema.String,
reason: Schema.String
})
{
override get message() {
return `Failed to apply patch from ${this.source}: operation ${this.operationIndex} ` +
`(${this.operation} at ${this.path}): ${this.reason}`
}
}
/**
* Error thrown when multiple JSON Patch operations fail.
*
* **Details**
*
* This error aggregates all application errors so users can see every
* failing operation at once instead of fixing them one at a time.
*
* **Example** (Creating an aggregate error)
*
* ```ts
* import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
*
* const error = new OpenApiPatch.JsonPatchAggregateError({
* errors: [
* new OpenApiPatch.JsonPatchApplicationError({
* source: "./fix.json",
* operationIndex: 0,
* operation: "replace",
* path: "/info/x",
* reason: "Property does not exist"
* }),
* new OpenApiPatch.JsonPatchApplicationError({
* source: "./fix.json",
* operationIndex: 2,
* operation: "remove",
* path: "/paths/~1users",
* reason: "Property does not exist"
* })
* ]
* })
*
* console.log(error.message)
* // "2 patch operations failed:\n 1. ..."
* ```
*
* @category errors
* @since 4.0.0
*/
export class JsonPatchAggregateError extends Schema.ErrorClass<JsonPatchAggregateError>("JsonPatchAggregateError")({
_tag: Schema.tag("JsonPatchAggregateError"),
errors: Schema.Array(Schema.Unknown)
}) {
override get message() {
const errors = this.errors as ReadonlyArray<JsonPatchApplicationError>
const count = errors.length
const plural = count === 1 ? "operation" : "operations"
const details = errors
.map((e, i) => ` ${i + 1}. [${e.source}] op ${e.operationIndex} (${e.operation} at ${e.path}): ${e.reason}`)
.join("\n")
return `${count} patch ${plural} failed:\n${details}`
}
}
// =============================================================================
// Schema
// =============================================================================
/**
* Schema for a JSON Patch "add" operation.
*
* @category schemas
* @since 4.0.0
*/
export const JsonPatchAdd: Schema.Codec<
Extract<
JsonPatch.JsonPatchOperation,
{ op: "add" }
>
> = Schema.Struct({
op: Schema.Literal("add"),
path: Schema.String,
value: Schema.Json,
description: Schema.optionalKey(Schema.String)
})
/**
* Schema for a JSON Patch "remove" operation.
*
* @category schemas
* @since 4.0.0
*/
export const JsonPatchRemove: Schema.Codec<
Extract<
JsonPatch.JsonPatchOperation,
{ op: "remove" }
>
> = Schema.Struct({
op: Schema.Literal("remove"),
path: Schema.String,
description: Schema.optionalKey(Schema.String)
})
/**
* Schema for a JSON Patch "replace" operation.
*
* @category schemas
* @since 4.0.0
*/
export const JsonPatchReplace: Schema.Codec<
Extract<
JsonPatch.JsonPatchOperation,
{ op: "replace" }
>
> = Schema.Struct({
op: Schema.Literal("replace"),
path: Schema.String,
value: Schema.Json,
description: Schema.optionalKey(Schema.String)
})
/**
* Schema for a single JSON Patch operation.
*
* **Details**
*
* Supports the subset of RFC 6902 operations that Effect's JsonPatch module
* implements: `add`, `remove`, and `replace`.
*
* @category schemas
* @since 4.0.0
*/
export const JsonPatchOperation: Schema.Codec<JsonPatch.JsonPatchOperation> = Schema.Union([
JsonPatchAdd,
JsonPatchRemove,
JsonPatchReplace
])
/**
* Schema for a JSON Patch document (array of operations).
*
* **Details**
*
* A JSON Patch document is an ordered list of operations to apply to a JSON
* document. Operations are applied in sequence, with each operation seeing
* the result of previous operations.
*
* **Example** (Decoding a patch document)
*
* ```ts
* import { Schema } from "effect"
* import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
*
* const patch = Schema.decodeUnknownSync(OpenApiPatch.JsonPatchDocument)([
* { op: "add", path: "/foo", value: "bar" },
* { op: "remove", path: "/baz" },
* { op: "replace", path: "/qux", value: 42 }
* ])
* ```
*
* @category schemas
* @since 4.0.0
*/
export const JsonPatchDocument = Schema.Array(JsonPatchOperation)
/**
* Type for a JSON Patch document.
*
* @category types
* @since 4.0.0
*/
export type JsonPatchDocument = typeof JsonPatchDocument.Type
// =============================================================================
// Parsing Functions
// =============================================================================
const decodeJsonPatchDocument = Schema.decodeUnknownEffect(JsonPatchDocument)
/**
* Check if a string looks like it could be a file path.
*
* Heuristic: contains path separators or ends with a known extension.
*/
const looksLikeFilePath = (input: string): boolean => {
const trimmed = input.trim()
if (trimmed.startsWith("[")) return false
if (trimmed.includes("/") || trimmed.includes("\\")) return true
if (/\.(json|yaml|yml)$/i.test(trimmed)) return true
return false
}
/**
* Determine file format from extension.
*/
const getFileFormat = Effect.fn(function*(filePath: string) {
const path = yield* Path.Path
const { ext } = path.parse(filePath)
if (ext === ".json") return "json"
if (ext === ".yaml" || ext === ".yml") return "yaml"
return undefined
})
/**
* Check if a file path exists and is a file.
*/
const checkFileExists = Effect.fn("checkFileExists")(function*(filePath: string) {
const fs = yield* FileSystem.FileSystem
const path = yield* Path.Path
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath)
const exists = yield* Effect.orElseSucceed(fs.exists(absolutePath), constFalse)
if (!exists) return false
const stat = yield* Effect.orElseSucceed(fs.stat(absolutePath), constUndefined)
return Predicate.isNotUndefined(stat) && stat.type === "File"
})
/**
* Parse content as JSON.
*/
const parseJsonContent = Effect.fnUntraced(function*(content: string, source: string) {
return yield* Effect.try({
try: () => JSON.parse(content) as unknown,
catch: (error) =>
new JsonPatchParseError({
source,
reason: error instanceof Error ? error.message : String(error)
})
})
})
/**
* Parse content as YAML.
*/
const parseYamlContent = Effect.fnUntraced(function*(content: string, source: string) {
return yield* Effect.try({
try: () => Yaml.parse(content) as unknown,
catch: (error) =>
new JsonPatchParseError({
source,
reason: error instanceof Error ? error.message : String(error)
})
})
})
/**
* Read and parse a patch file.
*/
const parsePatchFile = Effect.fn("parsePatchFile")(function*(filePath: string) {
const fs = yield* FileSystem.FileSystem
const path = yield* Path.Path
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath)
const fileFormat = yield* getFileFormat(filePath)
if (Predicate.isUndefined(fileFormat)) {
return yield* new JsonPatchParseError({
source: filePath,
reason: `Unsupported file format. Expected .json, .yaml, or .yml`
})
}
const content = yield* Effect.mapError(fs.readFileString(absolutePath), (error) =>
new JsonPatchParseError({
source: filePath,
reason: `Failed to read file: ${error.message}`
}))
const parsed = fileFormat === "json"
? yield* parseJsonContent(content, filePath)
: yield* parseYamlContent(content, filePath)
return yield* Effect.mapError(decodeJsonPatchDocument(parsed), (error) =>
new JsonPatchValidationError({
source: filePath,
reason: error.message
}))
})
/**
* Parse inline JSON string as a patch document.
*/
const parseInlinePatch = Effect.fn("parseInlinePatch")(function*(input: string) {
const parsed = yield* parseJsonContent(input, "inline")
return yield* Effect.mapError(decodeJsonPatchDocument(parsed), (error) =>
new JsonPatchValidationError({
source: "inline",
reason: error.message
}))
})
/**
* Parse a JSON Patch from either a file path or inline JSON string.
*
* **Details**
*
* The input is first checked as a file path. If the file exists, it is read
* and parsed based on its extension (.json, .yaml, .yml). Otherwise, the
* input is parsed as inline JSON.
*
* **Example** (Parsing patch input)
*
* ```ts
* import { Effect } from "effect"
* import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
*
* // From inline JSON
* const fromInline = OpenApiPatch.parsePatchInput(
* '[{"op":"replace","path":"/info/title","value":"My API"}]'
* )
*
* // From file path
* const fromFile = OpenApiPatch.parsePatchInput("./patches/fix-api.json")
*
* const program = Effect.gen(function*() {
* const patch = yield* fromInline
* console.log(patch)
* // [{ op: "replace", path: "/info/title", value: "My API" }]
* })
* ```
*
* @category parsing
* @since 4.0.0
*/
export const parsePatchInput = Effect.fn("parsePatchInput")(function*(input: string) {
if (looksLikeFilePath(input)) {
const exists = yield* checkFileExists(input)
if (exists) {
return yield* parsePatchFile(input)
}
}
return yield* parseInlinePatch(input)
})
// =============================================================================
// Application Functions
// =============================================================================
/**
* Apply a sequence of JSON patches to a document.
*
* **Details**
*
* Patches are applied in order, with each patch operating on the result of
* the previous one. All operations are attempted, and if any fail, the errors
* are accumulated and reported together so users can fix all issues at once.
*
* **Example** (Applying patches)
*
* ```ts
* import { Effect } from "effect"
* import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
*
* const document = { info: { title: "Old Title" }, paths: {} }
* const patches = [
* {
* source: "inline",
* patch: [{ op: "replace" as const, path: "/info/title", value: "New Title" }]
* }
* ]
*
* const program = Effect.gen(function*() {
* const result = yield* OpenApiPatch.applyPatches(patches, document)
* console.log(result)
* // { info: { title: "New Title" }, paths: {} }
* })
* ```
*
* @category application
* @since 4.0.0
*/
export const applyPatches = Effect.fn("applyPatches")(function*(
patches: ReadonlyArray<{ readonly source: string; readonly patch: JsonPatchDocument }>,
document: Schema.Json
) {
let result: Schema.Json = document
const errors: Array<JsonPatchApplicationError> = []
for (const { source, patch } of patches) {
for (let i = 0; i < patch.length; i++) {
const op = patch[i]
yield* Effect.ignore(Effect.try({
try: () => {
result = JsonPatch.apply([op], result)
},
catch: (error) =>
errors.push(
new JsonPatchApplicationError({
source,
operationIndex: i,
operation: op.op,
path: op.path,
reason: error instanceof Error ? error.message : String(error)
})
)
}))
}
}
if (errors.length > 0) {
return yield* new JsonPatchAggregateError({ errors })
}
return result
})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,277 @@
/**
* Normalized OpenAPI operation model shared by the generator pipeline.
*
* This module records the shape produced after an OpenAPI document is resolved
* into stable generator inputs: document metadata, tags, security schemes,
* per-operation parameters, request bodies, response media types, derived
* schema references, path templates, and streaming capabilities. Renderers
* consume this representation to emit HttpClient or HttpApi modules without
* reinterpreting raw OpenAPI path-item structures.
*
* @since 4.0.0
*/
import type * as Types from "effect/Types"
import type {
OpenAPISecurityRequirement,
OpenAPISpecExternalDocs,
OpenAPISpecLicense,
OpenAPISpecMethodName,
OpenAPISpecServer
} from "effect/unstable/httpapi/OpenApi"
/**
* Root OpenAPI metadata preserved for generated client and HttpApi output.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOpenApiMetadata {
readonly title: string
readonly version: string
readonly summary: string | undefined
readonly description: string | undefined
readonly license: OpenAPISpecLicense | undefined
readonly servers: ReadonlyArray<OpenAPISpecServer> | undefined
}
/**
* Tag metadata used to group and annotate generated operations.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOpenApiTag {
readonly name: string
readonly description: string | undefined
readonly externalDocs: OpenAPISpecExternalDocs | undefined
}
/**
* Supported security scheme extracted from an OpenAPI components section.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOpenApiSecurityScheme {
readonly name: string
readonly type: "basic" | "bearer" | "apiKey" | "http"
readonly description: string | undefined
readonly bearerFormat: string | undefined
readonly scheme: string | undefined
readonly key: string | undefined
readonly in: "header" | "query" | "cookie" | undefined
}
/**
* Normalized OpenAPI document consumed by the generator renderers.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOpenApi {
readonly metadata: ParsedOpenApiMetadata
readonly tags: ReadonlyArray<ParsedOpenApiTag>
readonly securitySchemes: ReadonlyArray<ParsedOpenApiSecurityScheme>
readonly operations: ReadonlyArray<ParsedOperation>
}
/**
* Documentation and lifecycle metadata associated with an operation.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOperationMetadata {
readonly summary: string | undefined
readonly description: string | undefined
readonly deprecated: boolean
readonly externalDocs: OpenAPISpecExternalDocs | undefined
}
/**
* Resolved OpenAPI parameter grouped by where it appears in the request.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOperationParameter {
readonly name: string
readonly in: "path" | "query" | "header" | "cookie"
readonly required: boolean
readonly description: string | undefined
readonly schema: {}
}
/**
* Summary of the request body declaration before per-media schemas are rendered.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOperationRequestBody {
readonly required: boolean
readonly contentTypes: Array<string>
}
/**
* Encoding strategy the generator can use for a request or response media type.
*
* @category models
* @since 4.0.0
*/
export type ParsedOperationMediaTypeEncoding =
| "json"
| "multipart"
| "form-url-encoded"
| "text"
| "binary"
/**
* Media type whose schema can be represented in generated Effect code.
*
* @category models
* @since 4.0.0
*/
export type ParsedOperationMediaTypeSchema =
| {
readonly contentType: string
readonly encoding: ParsedOperationMediaTypeEncoding
readonly schema: string
readonly effectStream?: undefined
}
| {
readonly contentType: string
readonly encoding: "text"
readonly schema: string
readonly effectStream: "sse"
readonly errorSchema: string
}
| {
readonly contentType: string
readonly encoding: "binary"
readonly schema?: undefined
readonly effectStream: "uint8array"
}
/**
* Parsed response metadata together with generated schema references.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOperationResponse {
readonly status: string
readonly description: string | undefined
readonly contentTypes: Array<string>
readonly hasHeaders: boolean
readonly isEmpty: boolean
readonly representable: ReadonlyArray<ParsedOperationMediaTypeSchema>
}
/**
* Resolved security requirement applied to a parsed operation.
*
* @category models
* @since 4.0.0
*/
export type ParsedOperationSecurityRequirement = Readonly<OpenAPISecurityRequirement>
/**
* Normalized operation model shared by all OpenAPI generator backends.
*
* @category models
* @since 4.0.0
*/
export interface ParsedOperation {
readonly id: string
readonly operationId: string | undefined
readonly path: string
readonly method: OpenAPISpecMethodName
readonly tags: ReadonlyArray<string>
readonly metadata: ParsedOperationMetadata
readonly parameters: {
readonly path: ReadonlyArray<ParsedOperationParameter>
readonly query: ReadonlyArray<ParsedOperationParameter>
readonly header: ReadonlyArray<ParsedOperationParameter>
readonly cookie: ReadonlyArray<ParsedOperationParameter>
}
readonly requestBody: ParsedOperationRequestBody | undefined
readonly responses: ReadonlyArray<ParsedOperationResponse>
readonly defaultResponse: ParsedOperationResponse | undefined
readonly effectiveSecurity: ReadonlyArray<ParsedOperationSecurityRequirement>
readonly description: string | undefined
readonly params?: string
readonly paramsOptional: boolean
readonly urlParams: ReadonlyArray<string>
readonly headers: ReadonlyArray<string>
readonly cookies: ReadonlyArray<string>
readonly payload?: string
readonly payloadFormData: boolean
readonly payloadFormUrlEncoded: boolean
readonly pathSchema: string | undefined
readonly querySchema: string | undefined
readonly querySchemaOptional: boolean
readonly headersSchema: string | undefined
readonly headersSchemaOptional: boolean
readonly requestBodyRepresentable: ReadonlyArray<ParsedOperationMediaTypeSchema>
readonly pathIds: ReadonlyArray<string>
readonly pathTemplate: string
readonly successSchemas: ReadonlyMap<string, string>
readonly errorSchemas: ReadonlyMap<string, string>
readonly voidSchemas: ReadonlySet<string>
// SSE streaming response schema (text/event-stream)
readonly sseSchema?: string
// Binary stream response (application/octet-stream)
readonly binaryResponse: boolean
}
/**
* Creates a mutable operation accumulator populated with parser defaults.
*
* @category constructors
* @since 4.0.0
*/
export const makeDeepMutable = (options: {
readonly id: string
readonly method: OpenAPISpecMethodName
readonly pathIds: Array<string>
readonly pathTemplate: string
readonly description: string | undefined
}): Types.DeepMutable<ParsedOperation> => ({
...options,
operationId: undefined,
path: "",
tags: [],
metadata: {
summary: undefined,
description: options.description,
deprecated: false,
externalDocs: undefined
},
parameters: {
path: [],
query: [],
header: [],
cookie: []
},
requestBody: undefined,
responses: [],
defaultResponse: undefined,
effectiveSecurity: [],
urlParams: [],
headers: [],
cookies: [],
payloadFormData: false,
payloadFormUrlEncoded: false,
pathSchema: undefined,
querySchema: undefined,
querySchemaOptional: true,
headersSchema: undefined,
headersSchemaOptional: true,
requestBodyRepresentable: [],
successSchemas: new Map(),
errorSchemas: new Map(),
voidSchemas: new Set(),
paramsOptional: true,
binaryResponse: false
})

View File

@@ -0,0 +1,111 @@
/**
* Shared utility helpers for the OpenAPI generator.
*
* This module centralizes the small transformations used while rendering
* generated TypeScript, including operation-name normalization, optional
* description handling, safe JSDoc comment emission, and direct array merging
* for code-generation accumulators.
*
* @since 4.0.0
*/
import * as String from "effect/String"
import * as UndefinedOr from "effect/UndefinedOr"
/**
* Converts an OpenAPI name into the generator's camel-case form.
*
* **Details**
*
* Separators are removed, leading digits are ignored, and letters following a
* separator or digit are upper-cased without otherwise changing letter casing.
*
* @category converting
* @since 4.0.0
*/
export const camelize = (self: string): string => {
let str = ""
let hadSymbol = false
for (let i = 0; i < self.length; i++) {
const charCode = self.charCodeAt(i)
if (
(charCode >= 65 && charCode <= 90) ||
(charCode >= 97 && charCode <= 122)
) {
str += hadSymbol ? self[i].toUpperCase() : self[i]
hadSymbol = false
} else if (charCode >= 48 && charCode <= 57) {
if (str.length > 0) {
str += self[i]
hadSymbol = true
}
} else if (str.length > 0) {
hadSymbol = true
}
}
return str
}
/**
* Converts an OpenAPI operation id into the exported operation identifier used
* by generated TypeScript modules.
*
* @category converting
* @since 4.0.0
*/
export const identifier = (operationId: string) => String.capitalize(camelize(operationId))
/**
* Extracts a trimmed, non-empty string from an unknown value.
*
* **Details**
*
* Returns `undefined` for non-string values and for strings containing only
* whitespace.
*
* @category filtering
* @since 4.0.0
*/
export const nonEmptyString = (a: unknown): string | undefined => {
if (typeof a === "string") {
const trimmed = String.trim(a)
if (String.isNonEmpty(trimmed)) {
return trimmed
}
}
}
/**
* Renders an optional description as a JSDoc block for generated TypeScript.
*
* **Details**
*
* Returns an empty string when the description is absent and escapes any
* closing comment marker so generated source remains syntactically valid.
*
* @category converting
* @since 4.0.0
*/
export const toComment = UndefinedOr.match({
onUndefined: () => "",
onDefined: (description: string) =>
`/**
* ${description.replace(/\*\//g, " * /").split("\n").join("\n* ")}
*/\n`
})
/**
* Appends every element from `source` into `destination` in order.
*
* **Details**
*
* This mutates `destination` directly, which avoids allocating an intermediate
* array when generator code needs to merge collections.
*
* @category concatenating
* @since 4.0.0
*/
export const spreadElementsInto = <A>(source: Array<A>, destination: Array<A>): void => {
for (let i = 0; i < source.length; i++) {
destination.push(source[i])
}
}

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env node
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
import * as NodeServices from "@effect/platform-node/NodeServices"
import * as Effect from "effect/Effect"
import { run } from "./main.ts"
run.pipe(
Effect.provide(NodeServices.layer),
NodeRuntime.runMain
)

View File

@@ -0,0 +1,116 @@
/**
* Command-line entry point for generating Effect HTTP clients or HttpApi
* definitions from an OpenAPI specification.
*
* The CLI reads a spec file, optionally applies JSON patches in order, selects
* the generator layer for the requested output format, reports generation
* warnings to stderr, and writes the generated source to stdout.
*
* @since 4.0.0
*/
import * as Console from "effect/Console"
import * as Effect from "effect/Effect"
import type * as Schema from "effect/Schema"
import * as CliError from "effect/unstable/cli/CliError"
import * as Command from "effect/unstable/cli/Command"
import * as Flag from "effect/unstable/cli/Flag"
import type { OpenAPISpec } from "effect/unstable/httpapi/OpenApi"
import * as OpenApiGenerator from "./OpenApiGenerator.ts"
import * as OpenApiPatch from "./OpenApiPatch.ts"
const spec = Flag.fileParse("spec").pipe(
Flag.withAlias("s"),
Flag.withDescription("The OpenAPI spec file to generate output from")
)
const name = Flag.string("name").pipe(
Flag.withAlias("n"),
Flag.withDescription("The name of the generated output"),
Flag.withDefault("Client")
)
const format = Flag.choice("format", ["httpclient", "httpclient-type-only", "httpapi"] as const).pipe(
Flag.withAlias("f"),
Flag.withDescription(
"Output format to generate: httpclient | httpclient-type-only | httpapi (default: httpclient)"
),
Flag.withDefault("httpclient")
)
const patch = Flag.string("patch").pipe(
Flag.withAlias("p"),
Flag.withDescription(
"JSON patch to apply to OpenAPI spec before generation. " +
"Can be a file path (.json, .yaml, .yml) or inline JSON array. " +
"Multiple patches are applied in order."
),
Flag.between(0, Infinity)
)
const root = Command.make("openapigen", { spec, format, name, patch }).pipe(
Command.withHandler(Effect.fnUntraced(function*({ name, spec, format, patch }) {
let patchedSpec: Schema.Json = spec as Schema.Json
if (patch.length > 0) {
const parsedPatches = yield* Effect.forEach(
patch,
(input) =>
OpenApiPatch.parsePatchInput(input).pipe(
Effect.map((p) => ({ source: input, patch: p })),
Effect.mapError((error) => new CliError.UserError({ cause: error }))
)
)
patchedSpec = yield* OpenApiPatch.applyPatches(parsedPatches, patchedSpec).pipe(
Effect.mapError((error) => new CliError.UserError({ cause: error }))
)
}
const generator = yield* OpenApiGenerator.OpenApiGenerator
const warnings: Array<OpenApiGenerator.OpenApiGeneratorWarning> = []
const source = yield* generator.generate(patchedSpec as unknown as OpenAPISpec, {
name,
format,
onWarning: (warning) => {
warnings.push(warning)
}
})
yield* Effect.forEach(
warnings,
(warning) => Console.error(formatWarning(warning)),
{ discard: true }
)
return yield* Console.log(source)
})),
Command.provide(({ format }) =>
format === "httpclient-type-only"
? OpenApiGenerator.layerTransformerTs
: OpenApiGenerator.layerTransformerSchema
)
)
/**
* Runs the OpenAPI generator command-line program.
*
* **Details**
*
* The command reads an OpenAPI specification, optionally applies JSON patches,
* generates source code in the selected format, writes any generation warnings
* to stderr, and prints the generated source to stdout.
*
* @category running
* @since 4.0.0
*/
export const run: Effect.Effect<void, CliError.CliError, Command.Environment> = Command.run(root, {
version: "0.0.0"
})
const formatWarning = (warning: OpenApiGenerator.OpenApiGeneratorWarning): string => {
const context = [
warning.method?.toUpperCase(),
warning.path,
warning.operationId ? `(${warning.operationId})` : undefined
].filter((value): value is string => value !== undefined)
return context.length > 0
? `WARNING [${warning.code}] ${context.join(" ")}: ${warning.message}`
: `WARNING [${warning.code}] ${warning.message}`
}

View File

@@ -0,0 +1,214 @@
import * as JsonSchemaGenerator from "@effect/openapi-generator/JsonSchemaGenerator"
import { describe, expect, it } from "@effect/vitest"
describe("JsonSchemaGenerator", () => {
it("schema & no definitions", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { type: "string" })
const definitions = {}
const result = generator.generate("openapi-3.1", definitions, false)
expect(result).toBe(`// schemas
export type A = string
export const A = Schema.String
`)
})
it("schema & definitions", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { $ref: "#/components/schemas/B" })
const definitions = {
B: { type: "string" }
}
const result = generator.generate("openapi-3.1", definitions, false)
expect(result).toBe(`// non-recursive definitions
export type B = string
export const B = Schema.String
// schemas
export type A = B
export const A = B
`)
})
it("onEnter strips specified keys", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { type: "string", description: "desc", examples: ["ex"] })
const definitions = {}
const result = generator.generate("openapi-3.1", definitions, false, {
onEnter: (js) => {
const out = { ...js }
delete out.examples
return out
}
})
expect(result).toBe(`// schemas
export type A = string
export const A = Schema.String.annotate({ "description": "desc" })
`)
})
it("default preserves all annotations", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { type: "string", description: "desc", examples: ["ex"] })
const definitions = {}
const result = generator.generate("openapi-3.1", definitions, false)
expect(result).toBe(`// schemas
export type A = string
export const A = Schema.String.annotate({ "description": "desc", "examples": ["ex"] })
`)
})
it("generateHttpApi emits explicit type and const declarations", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { type: "string" })
generator.addSchema("B", {
type: "object",
properties: {
id: {
type: "string"
}
},
required: ["id"],
additionalProperties: false
})
const result = generator.generateHttpApi("openapi-3.1", {})
expect(result).toContain(`export type A = string
export const A = Schema.String`)
expect(result).toContain(`export type B = { readonly "id": string }
export const B = Schema.Struct({ "id": Schema.String })`)
expect(result).not.toContain("Schema.Class<")
expect(result).not.toContain("Schema.Opaque<")
})
it("recursive schema", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { $ref: "#/components/schemas/B" })
const definitions = {
B: {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/B"
}
}
},
"required": [
"name",
"children"
],
"additionalProperties": false
}
}
const result = generator.generate("openapi-3.1", definitions, false)
expect(result).toBe(`// recursive definitions
export type B = { readonly "name": string, readonly "children": ReadonlyArray<B> }
export const B = Schema.Struct({ "name": Schema.String, "children": Schema.Array(Schema.suspend((): Schema.Codec<B> => B)) })
// schemas
export type A = B
export const A = B
`)
})
it("hoists recursive definitions referenced by earlier recursive definitions", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { $ref: "#/components/schemas/ResourcesNetworkCard" })
const definitions = {
ResourcesNetworkCard: {
type: "object",
properties: {
sriov: {
$ref: "#/components/schemas/ResourcesNetworkCardSRIOV"
}
},
additionalProperties: false
},
ResourcesNetworkCardSRIOV: {
type: "object",
properties: {
vfs: {
type: "array",
items: {
$ref: "#/components/schemas/ResourcesNetworkCard"
}
}
},
additionalProperties: false
}
}
const result = generator.generate("openapi-3.1", definitions, false)
const recursiveDeclaration =
"export const ResourcesNetworkCardSRIOV = Schema.suspend((): Schema.Codec<ResourcesNetworkCardSRIOV> => __recursive_ResourcesNetworkCardSRIOV)"
expect(result).toContain(recursiveDeclaration)
expect(result).toContain("const __recursive_ResourcesNetworkCardSRIOV =")
expect(result.indexOf(recursiveDeclaration)).toBeLessThan(
result.indexOf("export const ResourcesNetworkCard =")
)
expect(result.indexOf("export const ResourcesNetworkCard =")).toBeLessThan(
result.indexOf("const __recursive_ResourcesNetworkCardSRIOV =")
)
})
it("renders recursive definitions before non-recursive references for runtime generation", () => {
const generator = JsonSchemaGenerator.make()
generator.addSchema("A", { $ref: "#/components/schemas/ErrorResponse" })
const definitions = {
InnerErrors: {
type: "object",
properties: {
field: {
type: "string"
}
},
required: ["field"],
additionalProperties: false
},
ErrorDetails: {
oneOf: [
{
type: "object",
additionalProperties: {
$ref: "#/components/schemas/ErrorDetails"
}
},
{
$ref: "#/components/schemas/InnerErrors"
}
]
},
ErrorResponse: {
type: "object",
properties: {
errors: {
$ref: "#/components/schemas/ErrorDetails"
}
},
additionalProperties: false
}
}
const runtimeResult = generator.generate("openapi-3.1", definitions, false)
const recursiveDeclaration =
"export const ErrorDetails = Schema.suspend((): Schema.Codec<ErrorDetails> => __recursive_ErrorDetails)"
expect(runtimeResult).toContain(recursiveDeclaration)
expect(runtimeResult).toContain("const __recursive_ErrorDetails =")
expect(runtimeResult.indexOf(recursiveDeclaration)).toBeLessThan(
runtimeResult.indexOf("export const ErrorResponse =")
)
const httpApiResult = generator.generateHttpApi("openapi-3.1", definitions)
expect(httpApiResult).toContain(recursiveDeclaration)
expect(httpApiResult).toContain("const __recursive_ErrorDetails =")
expect(httpApiResult.indexOf(recursiveDeclaration)).toBeLessThan(
httpApiResult.indexOf("export const ErrorResponse =")
)
})
})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
import * as NodeServices from "@effect/platform-node/NodeServices"
import { assert, describe, it } from "@effect/vitest"
import { Effect, Layer, Stdio, Stream } from "effect"
import * as Exit from "effect/Exit"
import { TestConsole } from "effect/testing"
import { CliOutput } from "effect/unstable/cli"
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
const makeLayer = (args: ReadonlyArray<string>) =>
Layer.mergeAll(
TestConsole.layer,
CliOutput.layer(CliOutput.defaultFormatter({ colors: false })),
NodeServices.layer,
Stdio.layerTest({ args: Effect.succeed(args) })
)
const fixturePath = (fileName: string) => `${import.meta.dirname}/fixtures/${fileName}`
const cliProcessPath = `${import.meta.dirname}/../src/bin.ts`
type CliMainModule = {
readonly run: Effect.Effect<void>
}
const runCli = Effect.fnUntraced(function*(args: ReadonlyArray<string>) {
const module = (yield* Effect.promise(
() => import(new URL("../src/main.ts", import.meta.url).href)
)) as CliMainModule
return yield* Effect.gen(function*() {
const exit = yield* Effect.exit(module.run)
const stdoutLines = yield* TestConsole.logLines
const stderrLines = yield* TestConsole.errorLines
const stdout = stdoutLines.length > 0 ? String(stdoutLines[stdoutLines.length - 1]) : ""
const stderr = stderrLines.map(String).join("\n")
return { exit, stdout, stderr } as const
}).pipe(Effect.provide(makeLayer(args)))
})
const runCliProcess = Effect.fnUntraced(function*(args: ReadonlyArray<string>) {
const handle = yield* ChildProcess.make("node", [cliProcessPath, ...args])
return yield* Effect.all({
exitCode: handle.exitCode,
stdout: Stream.mkString(Stream.decodeText(handle.stdout)),
stderr: Stream.mkString(Stream.decodeText(handle.stderr))
}, { concurrency: "unbounded" })
})
describe("openapigen CLI", () => {
it.effect("documents --format values and default in --help", () =>
Effect.gen(function*() {
const result = yield* runCli(["--help"])
assert.isTrue(Exit.isSuccess(result.exit))
assert.include(result.stdout, "--format")
assert.include(result.stdout, "httpclient")
assert.include(result.stdout, "httpclient-type-only")
assert.include(result.stdout, "httpapi")
assert.include(result.stdout, "default: httpclient")
assert.strictEqual(result.stderr, "")
}))
it.effect("routes --format values and defaults to httpclient", () =>
Effect.gen(function*() {
const spec = fixturePath("cli-basic-spec.json")
const defaultResult = yield* runCli(["--spec", spec, "--name", "CliClient"])
const httpclientResult = yield* runCli([
"--spec",
spec,
"--name",
"CliClient",
"--format",
"httpclient"
])
const typeOnlyResult = yield* runCli([
"--spec",
spec,
"--name",
"CliClient",
"--format",
"httpclient-type-only"
])
const httpapiResult = yield* runCli([
"--spec",
spec,
"--name",
"CliClient",
"--format",
"httpapi"
])
assert.isTrue(Exit.isSuccess(defaultResult.exit))
assert.isTrue(Exit.isSuccess(httpclientResult.exit))
assert.isTrue(Exit.isSuccess(typeOnlyResult.exit))
assert.isTrue(Exit.isSuccess(httpapiResult.exit))
assert.strictEqual(defaultResult.stderr, "")
assert.strictEqual(httpclientResult.stderr, "")
assert.strictEqual(typeOnlyResult.stderr, "")
assert.strictEqual(httpapiResult.stderr, "")
assert.strictEqual(defaultResult.stdout, httpclientResult.stdout)
assert.include(httpclientResult.stdout, "import * as Schema from \"effect/Schema\"")
assert.notInclude(typeOnlyResult.stdout, "import * as Schema from \"effect/Schema\"")
assert.include(typeOnlyResult.stdout, "import type * as HttpClient from \"effect/unstable/http/HttpClient\"")
assert.include(httpapiResult.stdout, "export class CliClient extends HttpApi.make(\"CliClient\")")
}))
it.effect("rejects legacy --type-only flag", () =>
Effect.gen(function*() {
const spec = fixturePath("cli-basic-spec.json")
const result = yield* runCli(["--spec", spec, "--name", "CliClient", "--type-only"])
assert.isTrue(Exit.isFailure(result.exit))
assert.include(result.stdout, "USAGE")
assert.include(result.stderr, "Unrecognized flag: --type-only")
}))
it.effect("writes warnings to stderr and keeps stdout as generated source", () =>
Effect.gen(function*() {
const spec = fixturePath("cli-warning-spec.json")
const result = yield* runCli(["--spec", spec, "--name", "CliClient"])
assert.isTrue(Exit.isSuccess(result.exit))
assert.include(result.stdout, "export const make = (")
assert.include(result.stderr, "WARNING [cookie-parameter-dropped]")
assert.include(result.stderr, "cookie-parameter-dropped")
assert.notInclude(result.stdout, "cookie-parameter-dropped")
assert.notInclude(result.stderr, "export const make = (")
}))
it.effect("separates generated source and warnings when spawned as a child process", () =>
Effect.gen(function*() {
const spec = fixturePath("cli-warning-spec.json")
const result = yield* runCliProcess(["--spec", spec, "--name", "CliClient"])
assert.strictEqual(result.exitCode, ChildProcessSpawner.ExitCode(0))
assert.include(result.stdout, "export const make = (")
assert.notInclude(result.stdout, "WARNING [")
assert.notInclude(result.stdout, "cookie-parameter-dropped")
assert.include(
result.stderr,
"WARNING [cookie-parameter-dropped] GET /users/{id} (getUser): Cookie parameter \"session\" was dropped because non-security cookie parameters are not supported."
)
assert.notInclude(result.stderr, "export const make = (")
}).pipe(Effect.scoped, Effect.provide(NodeServices.layer)))
})

View File

@@ -0,0 +1,380 @@
import * as OpenApiPatch from "@effect/openapi-generator/OpenApiPatch"
import * as NodeServices from "@effect/platform-node/NodeServices"
import { assert, describe, it } from "@effect/vitest"
import * as Effect from "effect/Effect"
import * as Exit from "effect/Exit"
import * as Path from "effect/Path"
const testLayer = NodeServices.layer
describe("OpenApiPatch", () => {
describe("parsePatchInput", () => {
describe("inline JSON", () => {
it.effect("parses valid inline JSON patch", () =>
Effect.gen(function*() {
const result = yield* OpenApiPatch.parsePatchInput(
"[{\"op\":\"add\",\"path\":\"/foo\",\"value\":\"bar\"}]"
)
assert.deepStrictEqual(result, [{ op: "add", path: "/foo", value: "bar" }])
}).pipe(Effect.provide(testLayer)))
it.effect("parses inline JSON with multiple operations", () =>
Effect.gen(function*() {
const result = yield* OpenApiPatch.parsePatchInput(
"[{\"op\":\"add\",\"path\":\"/a\",\"value\":1},{\"op\":\"remove\",\"path\":\"/b\"},{\"op\":\"replace\",\"path\":\"/c\",\"value\":true}]"
)
assert.strictEqual(result.length, 3)
assert.strictEqual(result[0].op, "add")
assert.strictEqual(result[1].op, "remove")
assert.strictEqual(result[2].op, "replace")
}).pipe(Effect.provide(testLayer)))
it.effect("fails on invalid JSON syntax", () =>
Effect.gen(function*() {
const exit = yield* Effect.exit(
OpenApiPatch.parsePatchInput("[{\"op\":\"add\" \"path\":\"/foo\"}]")
)
assert.isTrue(Exit.isFailure(exit))
}).pipe(Effect.provide(testLayer)))
it.effect("fails on unsupported operation", () =>
Effect.gen(function*() {
const exit = yield* Effect.exit(
OpenApiPatch.parsePatchInput("[{\"op\":\"copy\",\"from\":\"/a\",\"path\":\"/b\"}]")
)
assert.isTrue(Exit.isFailure(exit))
}).pipe(Effect.provide(testLayer)))
it.effect("fails on missing path field", () =>
Effect.gen(function*() {
const exit = yield* Effect.exit(
OpenApiPatch.parsePatchInput("[{\"op\":\"add\",\"value\":\"test\"}]")
)
assert.isTrue(Exit.isFailure(exit))
}).pipe(Effect.provide(testLayer)))
it.effect("fails on missing value for add operation", () =>
Effect.gen(function*() {
const exit = yield* Effect.exit(
OpenApiPatch.parsePatchInput("[{\"op\":\"add\",\"path\":\"/foo\"}]")
)
assert.isTrue(Exit.isFailure(exit))
}).pipe(Effect.provide(testLayer)))
it.effect("allows missing value for remove operation", () =>
Effect.gen(function*() {
const result = yield* OpenApiPatch.parsePatchInput(
"[{\"op\":\"remove\",\"path\":\"/foo\"}]"
)
assert.deepStrictEqual(result, [{ op: "remove", path: "/foo" }])
}).pipe(Effect.provide(testLayer)))
})
describe("file paths", () => {
it.effect("parses valid JSON file", () =>
Effect.gen(function*() {
const pathService = yield* Path.Path
const filePath = pathService.join(
import.meta.dirname,
"fixtures/patches/valid-add.json"
)
const result = yield* OpenApiPatch.parsePatchInput(filePath)
assert.strictEqual(result.length, 1)
assert.strictEqual(result[0].op, "add")
assert.strictEqual(result[0].path, "/info/x-custom")
}).pipe(Effect.provide(testLayer)))
it.effect("parses valid YAML file", () =>
Effect.gen(function*() {
const pathService = yield* Path.Path
const filePath = pathService.join(
import.meta.dirname,
"fixtures/patches/valid-patch.yaml"
)
const result = yield* OpenApiPatch.parsePatchInput(filePath)
assert.strictEqual(result.length, 2)
assert.strictEqual(result[0].op, "replace")
assert.strictEqual(result[1].op, "add")
}).pipe(Effect.provide(testLayer)))
it.effect("parses multiple operations from JSON file", () =>
Effect.gen(function*() {
const pathService = yield* Path.Path
const filePath = pathService.join(
import.meta.dirname,
"fixtures/patches/valid-multiple.json"
)
const result = yield* OpenApiPatch.parsePatchInput(filePath)
assert.strictEqual(result.length, 3)
}).pipe(Effect.provide(testLayer)))
it.effect("fails on file with unsupported operation", () =>
Effect.gen(function*() {
const pathService = yield* Path.Path
const filePath = pathService.join(
import.meta.dirname,
"fixtures/patches/invalid-op.json"
)
const exit = yield* Effect.exit(OpenApiPatch.parsePatchInput(filePath))
assert.isTrue(Exit.isFailure(exit))
}).pipe(Effect.provide(testLayer)))
it.effect("fails on file with missing path", () =>
Effect.gen(function*() {
const pathService = yield* Path.Path
const filePath = pathService.join(
import.meta.dirname,
"fixtures/patches/missing-path.json"
)
const exit = yield* Effect.exit(OpenApiPatch.parsePatchInput(filePath))
assert.isTrue(Exit.isFailure(exit))
}).pipe(Effect.provide(testLayer)))
it.effect("falls back to inline JSON when file does not exist", () =>
Effect.gen(function*() {
const result = yield* OpenApiPatch.parsePatchInput(
"[{\"op\":\"add\",\"path\":\"/x\",\"value\":1}]"
)
assert.deepStrictEqual(result, [{ op: "add", path: "/x", value: 1 }])
}).pipe(Effect.provide(testLayer)))
})
})
describe("applyPatches", () => {
it.effect("applies single add patch", () =>
Effect.gen(function*() {
const document = { info: { title: "Test" } }
const patches = [{
source: "test",
patch: [{ op: "add" as const, path: "/info/version", value: "1.0.0" }]
}]
const result = yield* OpenApiPatch.applyPatches(patches, document)
assert.deepStrictEqual(result, { info: { title: "Test", version: "1.0.0" } })
}))
it.effect("applies single remove patch", () =>
Effect.gen(function*() {
const document = { info: { title: "Test", deprecated: true } }
const patches = [{
source: "test",
patch: [{ op: "remove" as const, path: "/info/deprecated" }]
}]
const result = yield* OpenApiPatch.applyPatches(patches, document)
assert.deepStrictEqual(result, { info: { title: "Test" } })
}))
it.effect("applies single replace patch", () =>
Effect.gen(function*() {
const document = { info: { title: "Old Title" } }
const patches = [{
source: "test",
patch: [{ op: "replace" as const, path: "/info/title", value: "New Title" }]
}]
const result = yield* OpenApiPatch.applyPatches(patches, document)
assert.deepStrictEqual(result, { info: { title: "New Title" } })
}))
it.effect("applies multiple patches in sequence", () =>
Effect.gen(function*() {
const document = { info: { title: "Original", version: "0.0.1" } }
const patches = [
{
source: "patch1",
patch: [{ op: "replace" as const, path: "/info/title", value: "Step 1" }]
},
{
source: "patch2",
patch: [{ op: "replace" as const, path: "/info/version", value: "1.0.0" }]
},
{
source: "patch3",
patch: [{ op: "add" as const, path: "/info/x-patched", value: true }]
}
]
const result = yield* OpenApiPatch.applyPatches(patches, document)
assert.deepStrictEqual(result, {
info: { title: "Step 1", version: "1.0.0", "x-patched": true }
})
}))
it.effect("applies multiple operations within a single patch", () =>
Effect.gen(function*() {
const document = { info: { title: "Test" }, paths: {} }
const patches = [{
source: "test",
patch: [
{ op: "replace" as const, path: "/info/title", value: "Updated" },
{ op: "add" as const, path: "/info/version", value: "2.0.0" }
]
}]
const result = yield* OpenApiPatch.applyPatches(patches, document)
assert.deepStrictEqual(result, {
info: { title: "Updated", version: "2.0.0" },
paths: {}
})
}))
it.effect("fails when path does not exist for replace", () =>
Effect.gen(function*() {
const document = { info: { title: "Test" } }
const patches = [{
source: "test",
patch: [{ op: "replace" as const, path: "/info/nonexistent", value: "x" }]
}]
const exit = yield* Effect.exit(OpenApiPatch.applyPatches(patches, document))
assert.isTrue(Exit.isFailure(exit))
}))
it.effect("fails when path does not exist for remove", () =>
Effect.gen(function*() {
const document = { info: { title: "Test" } }
const patches = [{
source: "test",
patch: [{ op: "remove" as const, path: "/info/nonexistent" }]
}]
const exit = yield* Effect.exit(OpenApiPatch.applyPatches(patches, document))
assert.isTrue(Exit.isFailure(exit))
}))
it.effect("accumulates multiple errors", () =>
Effect.gen(function*() {
const document = { info: { title: "Test" } }
const patches = [{
source: "test.json",
patch: [
{ op: "replace" as const, path: "/info/nonexistent1", value: "x" },
{ op: "remove" as const, path: "/info/nonexistent2" },
{ op: "replace" as const, path: "/info/nonexistent3", value: "y" }
]
}]
const exit = yield* Effect.exit(OpenApiPatch.applyPatches(patches, document))
assert.isTrue(Exit.isFailure(exit))
if (Exit.isFailure(exit)) {
const failure = exit.cause.reasons[0]
if (failure._tag === "Fail") {
assert.strictEqual(failure.error._tag, "JsonPatchAggregateError")
assert.strictEqual(failure.error.errors.length, 3)
assert.include(failure.error.message, "3 patch operations failed")
assert.include(failure.error.message, "/info/nonexistent1")
assert.include(failure.error.message, "/info/nonexistent2")
assert.include(failure.error.message, "/info/nonexistent3")
}
}
}))
it.effect("accumulates errors across multiple patches", () =>
Effect.gen(function*() {
const document = { info: { title: "Test" } }
const patches = [
{
source: "patch1.json",
patch: [{ op: "remove" as const, path: "/info/missing1" }]
},
{
source: "patch2.json",
patch: [{ op: "remove" as const, path: "/info/missing2" }]
}
]
const exit = yield* Effect.exit(OpenApiPatch.applyPatches(patches, document))
assert.isTrue(Exit.isFailure(exit))
if (Exit.isFailure(exit)) {
const failure = exit.cause.reasons[0]
if (failure._tag === "Fail") {
assert.strictEqual(failure.error.errors.length, 2)
assert.include(failure.error.message, "patch1.json")
assert.include(failure.error.message, "patch2.json")
}
}
}))
it.effect("preserves unmodified parts of document", () =>
Effect.gen(function*() {
const document = {
info: { title: "Test", description: "Unchanged" },
paths: { "/users": { get: {} } },
components: { schemas: {} }
}
const patches = [{
source: "test",
patch: [{ op: "replace" as const, path: "/info/title", value: "Changed" }]
}]
const result = yield* OpenApiPatch.applyPatches(patches, document)
assert.strictEqual((result as { info: { description: string } }).info.description, "Unchanged")
assert.deepStrictEqual((result as { paths: object }).paths, { "/users": { get: {} } })
assert.deepStrictEqual((result as { components: object }).components, { schemas: {} })
}))
it.effect("returns original document when no patches provided", () =>
Effect.gen(function*() {
const document = { info: { title: "Test" } }
const result = yield* OpenApiPatch.applyPatches([], document)
assert.deepStrictEqual(result, document)
}))
})
describe("error messages", () => {
it.effect("JsonPatchParseError has descriptive message", () =>
Effect.gen(function*() {
const error = new OpenApiPatch.JsonPatchParseError({
source: "./fix.json",
reason: "Unexpected token"
})
assert.strictEqual(
error.message,
"Failed to parse patch from ./fix.json: Unexpected token"
)
}))
it.effect("JsonPatchValidationError has descriptive message", () =>
Effect.gen(function*() {
const error = new OpenApiPatch.JsonPatchValidationError({
source: "inline",
reason: "Missing 'path' field"
})
assert.strictEqual(
error.message,
"Invalid JSON Patch from inline: Missing 'path' field"
)
}))
it.effect("JsonPatchApplicationError has descriptive message", () =>
Effect.gen(function*() {
const error = new OpenApiPatch.JsonPatchApplicationError({
source: "./fix.json",
operationIndex: 2,
operation: "remove",
path: "/info/x",
reason: "Property does not exist"
})
assert.strictEqual(
error.message,
"Failed to apply patch from ./fix.json: operation 2 (remove at /info/x): Property does not exist"
)
}))
it.effect("JsonPatchAggregateError has descriptive message", () =>
Effect.gen(function*() {
const error = new OpenApiPatch.JsonPatchAggregateError({
errors: [
new OpenApiPatch.JsonPatchApplicationError({
source: "./fix.json",
operationIndex: 0,
operation: "replace",
path: "/info/x",
reason: "Property does not exist"
}),
new OpenApiPatch.JsonPatchApplicationError({
source: "./other.json",
operationIndex: 1,
operation: "remove",
path: "/paths/~1users",
reason: "Path not found"
})
]
})
assert.include(error.message, "2 patch operations failed")
assert.include(error.message, "1. [./fix.json] op 0 (replace at /info/x)")
assert.include(error.message, "2. [./other.json] op 1 (remove at /paths/~1users)")
}))
})
})

View File

@@ -0,0 +1,33 @@
import * as Utils from "@effect/openapi-generator/Utils"
import { describe, expect, it } from "vitest"
describe("Utils", () => {
describe("camelize", () => {
it("removes hyphens and capitalizes following letters", () => {
expect(Utils.camelize("my-operation-id")).toBe("myOperationId")
})
it("removes slashes and capitalizes following letters", () => {
expect(Utils.camelize("my/operation/id")).toBe("myOperationId")
})
it("handles numbers", () => {
expect(Utils.camelize("operation-2")).toBe("operation2")
})
it("removes leading numbers", () => {
expect(Utils.camelize("2operation")).toBe("operation")
})
it("handles empty string", () => {
expect(Utils.camelize("")).toBe("")
})
})
describe("identifier", () => {
it("capitalizes camelized string", () => {
expect(Utils.identifier("my-operation")).toBe("MyOperation")
expect(Utils.identifier("operation-2")).toBe("Operation2")
})
})
})

View File

@@ -0,0 +1,35 @@
{
"openapi": "3.0.0",
"info": {
"title": "CLI Basic API",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"operationId": "listUsers",
"responses": {
"200": {
"description": "List users",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {},
"securitySchemes": {}
},
"security": [],
"tags": []
}

View File

@@ -0,0 +1,57 @@
{
"openapi": "3.0.0",
"info": {
"title": "CLI Warning API",
"version": "1.0.0"
},
"paths": {
"/users/{id}": {
"get": {
"operationId": "getUser",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "session",
"in": "cookie",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "User response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": ["id"],
"additionalProperties": false
}
}
}
}
}
}
}
},
"components": {
"schemas": {},
"securitySchemes": {}
},
"security": [],
"tags": []
}

View File

@@ -0,0 +1,7 @@
[
{
"op": "copy",
"from": "/info/title",
"path": "/info/x-title"
}
]

View File

@@ -0,0 +1,6 @@
[
{
"op": "add",
"value": "test"
}
]

View File

@@ -0,0 +1,6 @@
[
{
"op": "add",
"path": "/info/x-missing"
}
]

View File

@@ -0,0 +1,8 @@
[
{
"op": "add",
"path": "/info/x-custom",
"value": "custom-value",
"description": "Add custom extension"
}
]

View File

@@ -0,0 +1,17 @@
[
{
"op": "replace",
"path": "/info/title",
"value": "My Custom API"
},
{
"op": "add",
"path": "/info/x-generator",
"value": "effect-openapi"
},
{
"op": "replace",
"path": "/info/version",
"value": "2.0.0"
}
]

View File

@@ -0,0 +1,6 @@
- op: replace
path: /info/title
value: YAML Patched Title
- op: add
path: /info/x-yaml-patch
value: true

View File

@@ -0,0 +1,6 @@
[
{
"op": "remove",
"path": "/paths/~1deprecated"
}
]

View File

@@ -0,0 +1,7 @@
[
{
"op": "replace",
"path": "/info/title",
"value": "Updated API Title"
}
]

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"references": [
{ "path": "../../effect" },
{ "path": "../../platform-node" }
]
}

View File

@@ -0,0 +1,6 @@
import { mergeConfig, type ViteUserConfig } from "vitest/config"
import shared from "../../../vitest.shared.ts"
const config: ViteUserConfig = {}
export default mergeConfig(shared, config)