git.iko.soy/rowtype-yoga/purescript-yoga-fetch-om
on 5ad183e help sign in
67 files changed+1297−3155
since splitsnapshots
unifiedsplit
.c8rc.json−22 .github/workflows/ci.yml+2−7 .gitignore−2 .purslint-cache/props.db LICENSE−21 README.md+17−74 bun.lock+113−220 compile-fail-tests/ClientBadBodyType.purs−12 compile-fail-tests/ClientBadCurriedBodyType.purs−12 compile-fail-tests/ClientBadHeaderType.purs−13 compile-fail-tests/ClientBadOptionalQueryType.purs−13 compile-fail-tests/ClientBadPathType.purs−12 compile-fail-tests/ClientExtraBodyField.purs−12 compile-fail-tests/ClientExtraHeaderField.purs−13 compile-fail-tests/ClientExtraOptionalQueryField.purs−13 compile-fail-tests/ClientExtraPathField.purs−12 compile-fail-tests/ClientMissingBodyArgument.purs−12 compile-fail-tests/ClientMissingBodyField.purs−12 compile-fail-tests/ClientMissingHeaderArgument.purs−13 compile-fail-tests/ClientMissingHeaderField.purs−13 compile-fail-tests/ClientMissingPathField.purs−12 compile-fail-tests/ClientMissingRequiredQuery.purs−13 compile-fail-tests/ClientMissingThirdArgument.purs−13 compile-fail-tests/run.js−101 package.json+5−19 spago.lock+700−430 spago.yaml+22−25 src/Yoga/Fetch/Om.js−1 src/Yoga/Fetch/Om.purs+38−186 src/Yoga/Fetch/Om/BuildUrl.js+2−86 src/Yoga/Fetch/Om/BuildUrl.purs+10−43 src/Yoga/Fetch/Om/ClientFunction.purs+20−37 src/Yoga/Fetch/Om/ExtractParams.purs+1−22 src/Yoga/Fetch/Om/MakeRequest.js−1 src/Yoga/Fetch/Om/MakeRequest.purs+11−31 src/Yoga/Fetch/Om/ParseResponse.purs+17−79 src/Yoga/Fetch/Om/ReadOptional.js−18 src/Yoga/Fetch/Om/ReadOptional.purs−56 src/Yoga/Fetch/Om/Simple.purs+23−8 src/Yoga/Fetch/Om/SplitResponses.purs+2−24 src/Yoga/Fetch/Om/StreamDecode.js−12 src/Yoga/Fetch/Om/StreamDecode.purs−61 test-compile-fail/cases/ClientBogusSignature.purs−9 test-compile-fail/cases/ClientExtraField.purs−12 test-compile-fail/cases/ClientMissingField.purs−12 test-compile-fail/cases/ClientNoArgsPassedArg.purs−12 test-compile-fail/cases/ClientWrongFieldName.purs−12 test-compile-fail/cases/ClientWrongFieldType.purs−12 test-compile-fail/run.sh−26 test/BodylessResponse.Example.purs−27 test/BodylessResponse.Spec.purs−125 test/BuildUrl.Spec.purs+105−154 test/BuildUrlProperty.Spec.purs−256 test/Complete.Example.purs+31−17 test/Example.purs+10−15 test/MakeRequest.Spec.purs+29−46 test/PlainTextResponse.Spec.purs−122 test/Polymorphic.Spec.purs+47−57 test/Roundtrip.Spec.purs−380 test/SplitParams.Spec.purs+31−22 test/StreamDecode.Spec.js−1 test/StreamDecode.Spec.purs−32 test/Test.Main.purs+24 test/Test/Main.purs−14 test/Variant.Spec.purs+11−6 test/setup.js+16 vitest.config.js+10
.c8rc.jsondeleted−22
@@ -1,22 +0,0 @@
1−{
2− "all": true,
3− "include": [
4− "output/Yoga.Fetch.Om*/index.js",
5− "output/Yoga.Fetch.Om*/foreign.js"
6− ],
7− "exclude": [
8− "output/Yoga.Fetch.Om.ExtractParams/index.js",
9− "output/Yoga.Fetch.Om.SplitResponses/index.js"
10− ],
11− "reporter": [
12− "text",
13− "html",
14− "lcov"
15− ],
16− "reports-dir": "coverage",
17− "check-coverage": true,
18− "statements": 95,
19− "branches": 90,
20− "functions": 85,
21− "lines": 95
22−}
.github/workflows/ci.yml+2−7
@@ -12,10 +12,5 @@ jobs:
1212 steps:
1313 - uses: actions/checkout@v4
1414 - uses: rowtype-yoga/purescript-yoga-actions/.github/actions/setup@main
15− - run: bun run coverage
16− - uses: actions/upload-artifact@v4
17− if: always()
18− with:
19− name: coverage
20− path: coverage/
21− - run: bun compile-fail-tests/run.js
15+ - run: spago build
16+ - run: spago test
.gitignore−2
@@ -1,7 +1,5 @@
11output/
22output-compile-fail/
3−coverage/
4−generated-docs/
53node_modules/
64.spago/
75.psc-ide-port
.purslint-cache/props.dbdeleted

Binary file not shown.

LICENSEdeleted−21
@@ -1,21 +0,0 @@
1−The MIT License (MIT)
2−
3−Copyright (c) 2026 Mark Eibes
4−
5−Permission is hereby granted, free of charge, to any person obtaining a copy
6−of this software and associated documentation files (the "Software"), to deal
7−in the Software without restriction, including without limitation the rights
8−to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9−copies of the Software, and to permit persons to whom the Software is
10−furnished to do so, subject to the following conditions:
11−
12−The above copyright notice and this permission notice shall be included in all
13−copies or substantial portions of the Software.
14−
15−THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16−IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17−FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18−AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19−LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20−OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21−SOFTWARE.
README.md+17−74
@@ -8,34 +8,6 @@ Derive type-safe fetch clients from `purescript-yoga-http-api` route definitions
88spago install yoga-fetch-om
99```
1010
11−Optional query examples below use [`justifill`](https://pursuit.purescript.org/packages/purescript-justifill):
12−
13−```bash
14−spago install justifill
15−```
16−
17−## Migrating from 0.7
18−
19−Version 0.8 exposes optional query parameters as explicit `Maybe` fields. Calls that passed plain values no longer compile:
20−
21−```purescript
22−-- 0.7: offset omitted
23−users <- api.listUsers { limit: 10 }
24−
25−-- 0.8: every optional field is explicit
26−users <- api.listUsers { limit: Just 10, offset: Nothing }
27−allUsers <- api.listUsers { limit: Nothing, offset: Nothing }
28−```
29−
30−Import `Maybe(..)` from `Data.Maybe` when constructing these records directly. Alternatively, [`justifill`](https://pursuit.purescript.org/packages/purescript-justifill) wraps supplied values in `Just` and fills omitted fields with `Nothing`:
31−
32−```purescript
33−-- offset remains omitted at the call site
34−users <- api.listUsers (justifill { limit: 10 })
35−```
36−
37−Path parameters and request bodies keep their existing argument shapes.
38−
3911## Quick Start
4012
4113### Example 1: Simple GET and POST Requests
@@ -58,13 +30,8 @@ main = do
5830### Example 2: Type-Safe API Client with Full CRUD
5931
6032```purescript
61−import Justifill (justifill)
62−
6333type UserAPI =
64− { health ::
65− Route GET "health" {}
66− ( ok :: { body :: { status :: String } } )
67− , getUser ::
34+ { getUser ::
6835 Route GET ("users" / "id" : Int) {}
6936 ( ok :: { body :: User }
7037 , notFound :: { body :: ErrorMessage }
@@ -92,8 +59,7 @@ type UserAPI =
9259
9360api = client @UserAPI "https://api.example.com"
9461
95−-- Generated methods use ordinary function application.
96−health <- api.health
62+-- Create a user
9763user <- api.createUser { name: "Alice", email: "alice@example.com" }
9864 # handleErrors
9965 { badRequest: \err -> do
@@ -101,21 +67,15 @@ user <- api.createUser { name: "Alice", email: "alice@example.com" }
10167 throw err
10268 }
10369
104−-- Path parameters and request bodies remain separate arguments.
105−updated <- api.updateUser
106− { id: user.id }
107− { name: "Alice Updated", email: user.email }
70+-- Update with path param + body
71+updated <- api.updateUser { id: user.id } { name: "Alice Updated", email: user.email }
10872 # handleErrors
10973 { notFound: \_ -> throw userNotFound
11074 , badRequest: \err -> throw validationError
11175 }
11276
113−-- Optional query parameters are explicit `Maybe` fields. Justifill can wrap
114−-- supplied values in `Just` and fill omitted fields with `Nothing`.
115−users <- api.listUsers (justifill { limit: 10 })
116−
117−-- The equivalent call without Justifill:
118−allUsers <- api.listUsers { limit: Nothing, offset: Nothing }
77+-- Query with pagination
78+users <- api.listUsers { limit: 10, offset: 0 }
11979```
12080
12181## More Examples
@@ -132,10 +92,10 @@ See the test files for complete, runnable examples with all imports:
13292
13393### ✅ Type-Safe Everything
13494
135−- **Path parameters**: `/users/:id` requires `{ id :: Int }`; names and values are percent-encoded
136−- **Query parameters**: Type-safe, percent-encoded query strings with explicit `Maybe` fields for optional values
137−- **Request bodies**: `JSON`, `PlainText`, and URL-encoded `FormData`
138−- **Response bodies**: Parsed JSON, `PlainText` as `String`, and `Streaming a` as `Strom {} () a`
95+- **Path parameters**: `/users/:id` requires `{ id: Int }`
96+- **Query parameters**: Type-safe query strings with `?`
97+- **Request bodies**: Automatic JSON serialization
98+- **Response bodies**: Automatic JSON parsing
13999- **Error handling**: Exhaustive pattern matching on variants
140100
141101### ✅ Single Source of Truth
@@ -155,12 +115,12 @@ Changes to routes automatically update both client and server!
155115### ✅ Automatic Derivation
156116
157117No manual client code:
158−- ✅ URL building with segment-safe path substitution
159−- ✅ Query construction that preserves existing queries and fragments
160−- ✅ JSON, plain-text, and URL-encoded request serialization
161−- ✅ JSON and plain-text response decoding
162−- ✅ Incremental UTF-8 and binary response streams
118+- ✅ URL building with path parameter substitution
119+- ✅ Query string construction
120+- ✅ JSON request serialization
121+- ✅ JSON response parsing
163122- ✅ Status code → variant mapping
123+- ✅ CORS and credentials handling
164124
165125### ✅ Integration with yoga-om
166126
@@ -180,9 +140,8 @@ The library uses PureScript's type system to:
180140
1811411. **Extract parameters** from route definitions at compile time
182142 - Path params: `"users" / "id" : Int` → `{ id :: Int }`
183− - Optional query params: `:? { limit :: Int }` → `{ limit :: Maybe Int }`
184− - Required query params: `:? { limit :: Required Int }` → `{ limit :: Int }`
185− - Body params: `{ body :: JSON User }` → `User`
143+ - Query params: `:? { limit :: Int }` → `{ limit :: Int }`
144+ - Body params: `{ body :: JSON User }` → request body
186145
1871462. **Build URLs** automatically
188147 - Pattern: `/users/:id` + params: `{ id: 42 }` → `/users/42`
@@ -227,21 +186,6 @@ api = client @UserAPI "https://api.example.com"
227186- ✅ Variant response handling
228187- ✅ Om monad integration
229188
230−## Testing
231−
232−```sh
233−bun test
234−bun run test:compile-fail
235−bun run coverage
236−```
237−The suite combines example-based specifications with QuickCheck properties for
238−URL encoding, suffix preservation, repeated parameters, optional fields, and
239−query-separator invariants.
240−
241−The coverage command uses compiler source maps to report PureScript source
242−coverage, includes JavaScript FFI modules, enforces regression thresholds, and
243−writes HTML and LCOV reports to `coverage/`.
244−
245189## License
246190
247191MIT
@@ -252,4 +196,3 @@ MIT
252196- [purescript-yoga-fastify-om](https://github.com/rowtype-yoga/purescript-yoga-fastify-om) - Server-side counterpart
253197- [purescript-yoga-json](https://github.com/rowtype-yoga/purescript-yoga-json) - JSON serialization
254198- [purescript-yoga-om](https://github.com/rowtype-yoga/purescript-yoga-om) - Om monad
255−
bun.lock+113−220
@@ -3,24 +3,11 @@
33 "workspaces": {
44 "": {
55 "name": "purescript-yoga-fetch-om",
6− "dependencies": {
7− "pino": "^10.3.1",
8− },
96 "devDependencies": {
10− "@fastify/cookie": "^11.1.2",
11− "@fastify/cors": "^11.3.0",
12− "@fastify/formbody": "^9.0.0",
13− "@fastify/helmet": "^13.1.0",
14− "@fastify/multipart": "^10.1.0",
15− "@fastify/rate-limit": "^11.2.0",
16− "@fastify/websocket": "^11.3.0",
17− "argon2": "^0.45.1",
18− "c8": "^12.0.0",
197 "fastify": "^5.7.4",
20− "jose": "^6.2.8",
218 "jsdom": "^28.0.0",
229 "purescript": "^0.15.15",
23− "spago": "^1.0.3",
10+ "spago": "0.93.42",
2411 "vitest": "^2.1.8",
2512 },
2613 },
@@ -34,8 +21,6 @@
3421
3522 "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="],
3623
37− "@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
38−
3924 "@csstools/color-helpers": ["@csstools/color-helpers@6.0.1", "", {}, "sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ=="],
4025
4126 "@csstools/css-calc": ["@csstools/css-calc@3.0.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-q4d82GTl8BIlh/dTnVsWmxnbWJeb3kiU8eUH71UxlxnS+WIaALmtzTL8gR15PkYOexMQYVk0CO4qIG93C1IvPA=="],
@@ -48,8 +33,6 @@
4833
4934 "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="],
5035
51− "@epic-web/invariant": ["@epic-web/invariant@1.0.0", "", {}, "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA=="],
52−
5336 "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
5437
5538 "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
@@ -100,64 +83,38 @@
10083
10184 "@fastify/ajv-compiler": ["@fastify/ajv-compiler@4.0.5", "", { "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0" } }, "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A=="],
10285
103− "@fastify/busboy": ["@fastify/busboy@3.2.1", "", {}, "sha512-tgK4O+57iz5ycYNGXE5ZWj1ES03lD2XnnBYWSbU/3wYZRMQzCUq7Ycds/RdyBZQeL5MU4fxBt6lzbIWf/Bickw=="],
104−
105− "@fastify/cookie": ["@fastify/cookie@11.1.2", "", { "dependencies": { "cookie": "^2.0.0", "fastify-plugin": "^6.0.0" } }, "sha512-Dtrpk/YOGUsbRMvP/8ZqPpwnMRv0qSqodFdoQ2B589Obc7jw4s4Qla+cV72Bsm7WsZJnqlYFX/i7uSBq0xzg6g=="],
106−
107− "@fastify/cors": ["@fastify/cors@11.3.0", "", { "dependencies": { "fastify-plugin": "^6.0.0", "toad-cache": "^3.7.0" } }, "sha512-ggQGua+xHv1MvePbPr0v//xLYEsCXbWspquXCJS9Ot5YoRXq8J8ZWzHnxDBVnbtXosvistXo6LtNzOJswf64Fw=="],
108−
109− "@fastify/deepmerge": ["@fastify/deepmerge@3.2.1", "", {}, "sha512-N5Oqvltoa2r9z1tbx4xjky0oRR60v+T47Ic4J1ukoVQcptLOrIdRnCSdTGmOmajZuHVKlTnfcmrjyqsGEW1ztA=="],
110−
11186 "@fastify/error": ["@fastify/error@4.2.0", "", {}, "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ=="],
11287
11388 "@fastify/fast-json-stringify-compiler": ["@fastify/fast-json-stringify-compiler@5.0.3", "", { "dependencies": { "fast-json-stringify": "^6.0.0" } }, "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ=="],
11489
115− "@fastify/formbody": ["@fastify/formbody@9.0.0", "", { "dependencies": { "fast-querystring": "^1.1.2", "fastify-plugin": "^6.0.0" } }, "sha512-T/af26CSrUARBCvsEmv+DJLPfZlrRKESzqironxP1j7qzuLyKcoZtj6MuTGShuKx1THXugoie2oFbUJxXfGFzA=="],
116−
11790 "@fastify/forwarded": ["@fastify/forwarded@3.0.1", "", {}, "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw=="],
11891
119− "@fastify/helmet": ["@fastify/helmet@13.1.0", "", { "dependencies": { "fastify-plugin": "^6.0.0", "helmet": "^8.0.0" } }, "sha512-SvVOU0IrzYJW1BvSkfq9G1WUdW3dnaRUvg6m0BtgGMBmML62No0VmSu087jecH58SFbicbREgZTPJ89mAguupA=="],
120−
12192 "@fastify/merge-json-schemas": ["@fastify/merge-json-schemas@0.2.1", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A=="],
12293
123− "@fastify/multipart": ["@fastify/multipart@10.1.0", "", { "dependencies": { "@fastify/busboy": "^3.0.0", "@fastify/deepmerge": "^3.0.0", "@fastify/error": "^4.0.0", "fastify-plugin": "^6.0.0", "secure-json-parse": "^4.0.0" } }, "sha512-b2r6CovmLQvLFJ5HJDtxVigZjcO9TwkRGslhiNQxsGJwilm5B3eqvXPOVLudC44zXMXJITpQ/iEATIE7zoXqcQ=="],
124−
12594 "@fastify/proxy-addr": ["@fastify/proxy-addr@5.1.0", "", { "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw=="],
12695
127− "@fastify/rate-limit": ["@fastify/rate-limit@11.2.0", "", { "dependencies": { "@lukeed/ms": "^2.0.2", "fastify-plugin": "^6.0.0", "ip-address": "^10.2.0", "toad-cache": "^3.7.0" } }, "sha512-X7osJd4XSvMoejYrnJkSZYYjY1eNYoBqhjlzf1RakC2204qExFqZFTKj5+T7VuzA/iUI9Z3UoSqQRkB2HpG0oQ=="],
128−
129− "@fastify/websocket": ["@fastify/websocket@11.3.0", "", { "dependencies": { "duplexify": "^4.1.3", "fastify-plugin": "^6.0.0", "ws": "^8.16.0" } }, "sha512-g89ag4BCcD9YP5wBZXixzoLnuf5j89p/sXFcfpCiv2pdEkYYukBEoK3heVzqsp0EAtszVDc2BBZG0KZqeAShIA=="],
130−
13196 "@gar/promisify": ["@gar/promisify@1.1.3", "", {}, "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="],
13297
133− "@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="],
98+ "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
13499
135100 "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
136101
137− "@istanbuljs/schema": ["@istanbuljs/schema@0.1.6", "", {}, "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw=="],
138−
139− "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
140−
141102 "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
142103
143− "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
144−
145− "@lukeed/ms": ["@lukeed/ms@2.0.2", "", {}, "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA=="],
104+ "@nodelib/fs.scandir": ["@nodelib/fs.scandir@3.0.0", "", { "dependencies": { "@nodelib/fs.stat": "3.0.0", "run-parallel": "^1.2.0" } }, "sha512-ktI9+PxfHYtKjF3cLTUAh2N+b8MijCRPNwKJNqTVdL0gB0QxLU2rIRaZ1t71oEa3YBDE6bukH1sR0+CDnpp/Mg=="],
146105
147− "@nodelib/fs.scandir": ["@nodelib/fs.scandir@4.0.1", "", { "dependencies": { "@nodelib/fs.stat": "4.0.0", "run-parallel": "^1.2.0" } }, "sha512-vAkI715yhnmiPupY+dq+xenu5Tdf2TBQ66jLvBIcCddtz+5Q8LbMKaf9CIJJreez8fQ8fgaY+RaywQx8RJIWpw=="],
106+ "@nodelib/fs.stat": ["@nodelib/fs.stat@3.0.0", "", {}, "sha512-2tQOI38s19P9i7X/Drt0v8iMA+KMsgdhB/dyPER+e+2Y8L1Z7QvnuRdW/uLuf5YRFUYmnj4bMA6qCuZHFI1GDQ=="],
148107
149− "@nodelib/fs.stat": ["@nodelib/fs.stat@4.0.0", "", {}, "sha512-ctr6bByzksKRCV0bavi8WoQevU6plSp2IkllIsEqaiKe2mwNNnaluhnRhcsgGZHrrHk57B3lf95MkLMO3STYcg=="],
150−
151− "@nodelib/fs.walk": ["@nodelib/fs.walk@3.0.1", "", { "dependencies": { "@nodelib/fs.scandir": "4.0.1", "fastq": "^1.15.0" } }, "sha512-nIh/M6Kh3ZtOmlY00DaUYB4xeeV6F3/ts1l29iwl3/cfyY/OuCfUx+v08zgx8TKPTifXRcjjqVQ4KB2zOYSbyw=="],
108+ "@nodelib/fs.walk": ["@nodelib/fs.walk@2.0.0", "", { "dependencies": { "@nodelib/fs.scandir": "3.0.0", "fastq": "^1.15.0" } }, "sha512-54voNDBobGdMl3BUXSu7UaDh1P85PGHWlJ5e0XhPugo1JulOyCtp2I+5ri4wplGDJ8QGwPEQW7/x3yTLU7yF1A=="],
152109
153110 "@npmcli/fs": ["@npmcli/fs@2.1.2", "", { "dependencies": { "@gar/promisify": "^1.1.3", "semver": "^7.3.5" } }, "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ=="],
154111
155112 "@npmcli/move-file": ["@npmcli/move-file@2.0.1", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ=="],
156113
157− "@phc/format": ["@phc/format@1.0.0", "", {}, "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ=="],
158−
159114 "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
160115
116+ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
117+
161118 "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="],
162119
163120 "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="],
@@ -212,8 +169,6 @@
212169
213170 "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
214171
215− "@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="],
216−
217172 "@vitest/expect": ["@vitest/expect@2.1.9", "", { "dependencies": { "@vitest/spy": "2.1.9", "@vitest/utils": "2.1.9", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw=="],
218173
219174 "@vitest/mocker": ["@vitest/mocker@2.1.9", "", { "dependencies": { "@vitest/spy": "2.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg=="],
@@ -242,14 +197,12 @@
242197
243198 "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
244199
245− "ansi-regex": ["ansi-regex@6.3.0", "", {}, "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ=="],
200+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
246201
247202 "ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
248203
249204 "aproba": ["aproba@1.2.0", "", {}, "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="],
250205
251− "argon2": ["argon2@0.45.1", "", { "dependencies": { "@phc/format": "^1.0.0", "cross-env": "^10.1.0", "node-addon-api": "^8.9.0", "node-gyp-build": "^4.8.4" } }, "sha512-skm+/WCjkGqCQxF7FG1LuZXM5yvbFjgbfiCGsud2oLgaDhh6b6dbH0b1EkghbM+xx4Bj8Ape+KKgixoIlWZicQ=="],
252−
253206 "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
254207
255208 "asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="],
@@ -262,18 +215,28 @@
262215
263216 "avvio": ["avvio@9.1.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw=="],
264217
265− "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
218+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
219+
220+ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
266221
267222 "bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "^0.14.3" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="],
268223
224+ "better-sqlite3": ["better-sqlite3@11.10.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ=="],
225+
269226 "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
270227
228+ "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
229+
230+ "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
231+
271232 "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="],
272233
273− "brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="],
234+ "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
274235
275236 "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
276237
238+ "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
239+
277240 "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
278241
279242 "buildcheck": ["buildcheck@0.0.7", "", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="],
@@ -282,8 +245,6 @@
282245
283246 "byline": ["byline@5.0.0", "", {}, "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q=="],
284247
285− "c8": ["c8@12.0.0", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.1", "@istanbuljs/schema": "^0.1.3", "find-up": "^5.0.0", "foreground-child": "^3.1.1", "istanbul-lib-coverage": "^3.2.0", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.1.6", "test-exclude": "^8.0.0", "v8-to-istanbul": "^9.0.0", "yargs": "^18.0.0", "yargs-parser": "^21.1.1" }, "peerDependencies": { "monocart-coverage-reports": "^2" }, "optionalPeers": ["monocart-coverage-reports"], "bin": { "c8": "bin/c8.js" } }, "sha512-4zpJvrd1nKWutnnKC2pXkFmb6iM1l+ffN//o1CzlTNwW7GSOs9a1xrLqkC48nU8oEkjmPZLPiwMsIaOvoF4Pqg=="],
286−
287248 "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
288249
289250 "cacache": ["cacache@11.3.3", "", { "dependencies": { "bluebird": "^3.5.5", "chownr": "^1.1.1", "figgy-pudding": "^3.5.1", "glob": "^7.1.4", "graceful-fs": "^4.1.15", "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "move-concurrently": "^1.0.1", "promise-inflight": "^1.0.1", "rimraf": "^2.6.3", "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" } }, "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA=="],
@@ -300,8 +261,6 @@
300261
301262 "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
302263
303− "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
304−
305264 "color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
306265
307266 "color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
@@ -310,9 +269,7 @@
310269
311270 "concat-stream": ["concat-stream@1.6.2", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw=="],
312271
313− "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
314−
315− "cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="],
272+ "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
316273
317274 "copy-concurrently": ["copy-concurrently@1.0.5", "", { "dependencies": { "aproba": "^1.1.1", "fs-write-stream-atomic": "^1.0.8", "iferr": "^0.1.5", "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.0" } }, "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A=="],
318275
@@ -320,8 +277,6 @@
320277
321278 "cpu-features": ["cpu-features@0.0.10", "", { "dependencies": { "buildcheck": "~0.0.6", "nan": "^2.19.0" } }, "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA=="],
322279
323− "cross-env": ["cross-env@10.1.0", "", { "dependencies": { "@epic-web/invariant": "^1.0.0", "cross-spawn": "^7.0.6" }, "bin": { "cross-env": "dist/bin/cross-env.js", "cross-env-shell": "dist/bin/cross-env-shell.js" } }, "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw=="],
324−
325280 "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
326281
327282 "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="],
@@ -336,8 +291,12 @@
336291
337292 "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="],
338293
294+ "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
295+
339296 "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
340297
298+ "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
299+
341300 "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
342301
343302 "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
@@ -346,9 +305,13 @@
346305
347306 "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
348307
349− "duplexify": ["duplexify@4.1.3", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="],
308+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
309+
310+ "duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
350311
351− "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
312+ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
313+
314+ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
352315
353316 "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="],
354317
@@ -364,14 +327,14 @@
364327
365328 "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
366329
367− "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
368−
369330 "escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
370331
371332 "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
372333
373334 "execa": ["execa@2.1.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^3.0.0", "onetime": "^5.1.0", "p-finally": "^2.0.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw=="],
374335
336+ "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
337+
375338 "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
376339
377340 "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
@@ -386,26 +349,26 @@
386349
387350 "fastify": ["fastify@5.7.4", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^6.0.0", "find-my-way": "^9.0.0", "light-my-request": "^6.0.0", "pino": "^10.1.0", "process-warning": "^5.0.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA=="],
388351
389− "fastify-plugin": ["fastify-plugin@6.0.0", "", {}, "sha512-fZOty7z3O7vOliF6d8bHE3wiEh1KcNnKEQensSgTk9C1DvN6nRLS++XVd86v33Hw/8u9Un8A1zDrQ8ujcQDHEg=="],
390−
391352 "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
392353
393354 "figgy-pudding": ["figgy-pudding@3.5.2", "", {}, "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw=="],
394355
356+ "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
357+
395358 "filesize": ["filesize@4.2.1", "", {}, "sha512-bP82Hi8VRZX/TUBKfE24iiUGsB/sfm2WUrwTQyAzQrhO3V9IhcBBNBXMyzLY5orACxRyYJ3d2HeRVX+eFv4lmA=="],
396359
397360 "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
398361
399362 "find-my-way": ["find-my-way@9.4.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", "safe-regex2": "^5.0.0" } }, "sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w=="],
400363
401− "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
402−
403364 "flush-write-stream": ["flush-write-stream@1.1.1", "", { "dependencies": { "inherits": "^2.0.3", "readable-stream": "^2.3.6" } }, "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w=="],
404365
405366 "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
406367
407368 "from2": ["from2@2.3.0", "", { "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g=="],
408369
370+ "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
371+
409372 "fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="],
410373
411374 "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
@@ -418,24 +381,18 @@
418381
419382 "fuse.js": ["fuse.js@7.1.0", "", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="],
420383
421− "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
422−
423− "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
424−
425384 "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
426385
427− "glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
386+ "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
428387
429− "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
388+ "glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
430389
431− "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
390+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
432391
433− "helmet": ["helmet@8.3.0", "", {}, "sha512-Qgpiaws3Sm30Av8Eah6sjMCZZwjlBu+E68rhpCWBshY1lb09HtLwj5GviX0OyQIn+ulUS0iX0AxN5n3tLZzz1w=="],
392+ "has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
434393
435394 "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="],
436395
437− "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="],
438−
439396 "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
440397
441398 "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
@@ -446,6 +403,8 @@
446403
447404 "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
448405
406+ "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
407+
449408 "iferr": ["iferr@0.1.5", "", {}, "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA=="],
450409
451410 "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
@@ -458,7 +417,9 @@
458417
459418 "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
460419
461− "ip-address": ["ip-address@10.5.0", "", {}, "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g=="],
420+ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
421+
422+ "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
462423
463424 "ipaddr.js": ["ipaddr.js@2.3.0", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="],
464425
@@ -484,15 +445,7 @@
484445
485446 "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
486447
487− "istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="],
488−
489− "istanbul-lib-report": ["istanbul-lib-report@3.0.1", "", { "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" } }, "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw=="],
490−
491− "istanbul-reports": ["istanbul-reports@3.2.0", "", { "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA=="],
492−
493− "jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="],
494−
495− "jose": ["jose@6.2.8", "", {}, "sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ=="],
448+ "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
496449
497450 "jsdom": ["jsdom@28.0.0", "", { "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.7.6", "@exodus/bytes": "^1.11.0", "cssstyle": "^5.3.7", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "undici": "^7.20.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA=="],
498451
@@ -506,8 +459,6 @@
506459
507460 "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
508461
509− "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
510−
511462 "log-symbols": ["log-symbols@3.0.0", "", { "dependencies": { "chalk": "^2.4.2" } }, "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ=="],
512463
513464 "log-update": ["log-update@4.0.0", "", { "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", "slice-ansi": "^4.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg=="],
@@ -518,8 +469,6 @@
518469
519470 "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
520471
521− "make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="],
522−
523472 "make-fetch-happen": ["make-fetch-happen@10.2.1", "", { "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", "socks-proxy-agent": "^7.0.0", "ssri": "^9.0.0" } }, "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w=="],
524473
525474 "markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="],
@@ -534,7 +483,9 @@
534483
535484 "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
536485
537− "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="],
486+ "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
487+
488+ "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
538489
539490 "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
540491
@@ -556,6 +507,8 @@
556507
557508 "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
558509
510+ "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
511+
559512 "move-concurrently": ["move-concurrently@1.0.1", "", { "dependencies": { "aproba": "^1.1.1", "copy-concurrently": "^1.0.0", "fs-write-stream-atomic": "^1.0.8", "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.3" } }, "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ=="],
560513
561514 "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
@@ -564,11 +517,11 @@
564517
565518 "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
566519
567− "negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
520+ "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
568521
569− "node-addon-api": ["node-addon-api@8.9.2", "", {}, "sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg=="],
522+ "negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
570523
571− "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
524+ "node-abi": ["node-abi@3.87.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ=="],
572525
573526 "npm-run-path": ["npm-run-path@3.1.0", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg=="],
574527
@@ -582,10 +535,6 @@
582535
583536 "p-finally": ["p-finally@2.0.1", "", {}, "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw=="],
584537
585− "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
586−
587− "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
588−
589538 "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="],
590539
591540 "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
@@ -594,13 +543,11 @@
594543
595544 "parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="],
596545
597− "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
598−
599546 "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
600547
601548 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
602549
603− "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
550+ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
604551
605552 "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
606553
@@ -618,6 +565,8 @@
618565
619566 "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
620567
568+ "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
569+
621570 "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
622571
623572 "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
@@ -642,7 +591,9 @@
642591
643592 "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
644593
645− "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
594+ "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
595+
596+ "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
646597
647598 "readline-sync": ["readline-sync@1.4.10", "", {}, "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw=="],
648599
@@ -692,7 +643,11 @@
692643
693644 "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
694645
695− "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
646+ "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
647+
648+ "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
649+
650+ "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
696651
697652 "slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="],
698653
@@ -706,7 +661,7 @@
706661
707662 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
708663
709− "spago": ["spago@1.0.4", "", { "dependencies": { "@nodelib/fs.walk": "^3.0.1", "env-paths": "^3.0.0", "fs-extra": "^11.3.0", "fuse.js": "^7.1.0", "glob": "^11.0.1", "markdown-it": "^14.1.0", "micromatch": "^4.0.8", "open": "^10.1.0", "picomatch": "^4.0.2", "punycode": "^2.3.1", "readline-sync": "^1.4.10", "semver": "^7.7.1", "spdx-expression-parse": "^4.0.0", "ssh2": "^1.16.0", "supports-color": "^10.0.0", "tar": "^7.4.3", "tmp": "^0.2.3", "xhr2": "^0.2.1", "yaml": "^2.7.0" }, "bin": { "spago": "bin/bundle.js" } }, "sha512-iv1HBJppzGJRWWd/DZ32oselcKrhq/PboBdXy5VMX4GiwRqLCUUp3loZ6Yhm8zymNRKHkN/w52cxX6k521nwng=="],
664+ "spago": ["spago@0.93.42", "", { "dependencies": { "@nodelib/fs.walk": "^2.0.0", "better-sqlite3": "^11.0.0", "env-paths": "^3.0.0", "fs-extra": "^11.2.0", "fuse.js": "^7.0.0", "glob": "^10.4.1", "markdown-it": "^14.1.0", "micromatch": "^4.0.7", "open": "^10.1.0", "picomatch": "^4.0.2", "punycode": "^2.3.1", "readline-sync": "^1.4.10", "semver": "^7.6.2", "spdx-expression-parse": "^4.0.0", "ssh2": "^1.15.0", "supports-color": "^9.4.0", "tar": "^7.2.0", "tmp": "^0.2.3", "xhr2": "^0.2.1", "yaml": "^2.4.5" }, "bin": { "spago": "bin/bundle.js" } }, "sha512-R8/CCfR3jtbN3NFZOso+sBTKsaGDu96j8X55JA8CDx12GyB3f64wpwbIXKJ8Y+E4UMjYWhTB2vkP+kqf4R5Y5w=="],
710665
711666 "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
712667
@@ -728,21 +683,29 @@
728683
729684 "stream-shift": ["stream-shift@1.0.3", "", {}, "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="],
730685
731− "string-width": ["string-width@8.2.2", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg=="],
686+ "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
732687
733− "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
688+ "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
734689
735− "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
690+ "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
691+
692+ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
693+
694+ "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
736695
737696 "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
738697
739− "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
698+ "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
699+
700+ "supports-color": ["supports-color@9.4.0", "", {}, "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw=="],
740701
741702 "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
742703
743704 "tar": ["tar@7.5.7", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="],
744705
745− "test-exclude": ["test-exclude@8.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^13.0.6", "minimatch": "^10.2.2" } }, "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ=="],
706+ "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="],
707+
708+ "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
746709
747710 "thread-stream": ["thread-stream@4.0.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA=="],
748711
@@ -772,6 +735,8 @@
772735
773736 "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="],
774737
738+ "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
739+
775740 "tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="],
776741
777742 "type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
@@ -790,8 +755,6 @@
790755
791756 "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
792757
793− "v8-to-istanbul": ["v8-to-istanbul@9.3.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^2.0.0" } }, "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA=="],
794−
795758 "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
796759
797760 "vite-node": ["vite-node@2.1.9", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA=="],
@@ -810,11 +773,11 @@
810773
811774 "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
812775
813− "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
776+ "wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
814777
815− "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
778+ "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
816779
817− "ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="],
780+ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
818781
819782 "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
820783
@@ -826,58 +789,42 @@
826789
827790 "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
828791
829− "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
792+ "y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="],
830793
831794 "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
832795
833796 "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
834797
835− "yargs": ["yargs@18.1.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^8.2.1", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg=="],
798+ "zen-observable": ["zen-observable@0.8.15", "", {}, "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ=="],
836799
837− "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
800+ "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
838801
839− "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
802+ "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
840803
841− "zen-observable": ["zen-observable@0.8.15", "", {}, "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ=="],
804+ "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
842805
843806 "@npmcli/move-file/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
844807
845808 "@npmcli/move-file/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
846809
810+ "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
811+
847812 "cacache/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
848813
849814 "cacache/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
850815
851816 "cacache/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
852817
853− "cacache/y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="],
854−
855818 "chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
856819
857− "cliui/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
858−
859− "concat-stream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
860−
861820 "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
862821
863− "execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
864−
865− "flush-write-stream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
866−
867− "from2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
822+ "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
868823
869824 "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
870825
871− "fs-write-stream-atomic/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
872−
873− "istanbul-lib-report/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
874−
875− "light-my-request/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
876−
877826 "light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
878827
879− "log-update/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
880−
881828 "make-fetch-happen/cacache": ["cacache@16.1.3", "", { "dependencies": { "@npmcli/fs": "^2.1.0", "@npmcli/move-file": "^2.0.0", "chownr": "^2.0.0", "fs-minipass": "^2.1.0", "glob": "^8.0.1", "infer-owner": "^1.0.4", "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^9.0.0", "tar": "^6.1.11", "unique-filename": "^2.0.0" } }, "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ=="],
882829
883830 "make-fetch-happen/http-proxy-agent": ["http-proxy-agent@5.0.0", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="],
@@ -906,11 +853,7 @@
906853
907854 "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
908855
909− "mississippi/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
910−
911− "parallel-transform/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
912−
913− "pumpify/duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
856+ "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
914857
915858 "pumpify/pump": ["pump@2.0.1", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA=="],
916859
@@ -918,58 +861,40 @@
918861
919862 "purescript-installer/tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
920863
921− "restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
864+ "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
922865
923866 "rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
924867
925868 "slice-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
926869
927− "socks/ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
928−
929870 "socks-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
930871
931− "test-exclude/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
872+ "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
932873
933− "through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
874+ "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
934875
935− "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
876+ "tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
936877
937− "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
878+ "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
938879
939− "yargs/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
880+ "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
940881
941− "@npmcli/move-file/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
882+ "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
942883
943− "cacache/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
884+ "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
944885
945− "cacache/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
886+ "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
946887
947− "chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
948−
949− "concat-stream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
950−
951− "concat-stream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
952−
953− "flush-write-stream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
888+ "@npmcli/move-file/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
954889
955− "flush-write-stream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
890+ "bl/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
956891
957− "from2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
892+ "cacache/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
958893
959− "from2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
894+ "cacache/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
960895
961896 "fs-minipass/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
962897
963− "fs-write-stream-atomic/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
964−
965− "fs-write-stream-atomic/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
966−
967− "log-update/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
968−
969− "log-update/wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
970−
971− "log-update/wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
972−
973898 "make-fetch-happen/cacache/chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
974899
975900 "make-fetch-happen/cacache/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="],
@@ -1000,14 +925,6 @@
1000925
1001926 "minipass-sized/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
1002927
1003− "mississippi/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
1004−
1005− "parallel-transform/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
1006−
1007− "parallel-transform/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
1008−
1009− "pumpify/duplexify/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
1010−
1011928 "purescript-installer/tar/chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
1012929
1013930 "purescript-installer/tar/minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
@@ -1022,22 +939,16 @@
1022939
1023940 "slice-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
1024941
1025− "test-exclude/glob/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
942+ "tar-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
1026943
1027− "through2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
944+ "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
1028945
1029− "through2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
946+ "wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
1030947
1031948 "@npmcli/move-file/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
1032949
1033950 "cacache/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
1034951
1035− "log-update/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
1036−
1037− "log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
1038−
1039− "log-update/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
1040−
1041952 "make-fetch-happen/cacache/glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
1042953
1043954 "make-fetch-happen/cacache/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
@@ -1050,40 +961,22 @@
1050961
1051962 "make-fetch-happen/cacache/unique-filename/unique-slug": ["unique-slug@3.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w=="],
1052963
1053− "mississippi/duplexify/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
1054−
1055− "mississippi/duplexify/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
1056−
1057− "pumpify/duplexify/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
1058−
1059− "pumpify/duplexify/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
1060−
1061964 "purescript-installer/tar/minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
1062965
1063966 "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
1064967
1065968 "slice-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
1066969
1067− "@npmcli/move-file/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
1068−
1069− "cacache/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
970+ "wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
1070971
1071− "log-update/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
972+ "wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
1072973
1073− "make-fetch-happen/cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
974+ "@npmcli/move-file/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
1074975
1075976 "make-fetch-happen/cacache/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
1076977
1077978 "make-fetch-happen/cacache/tar/minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
1078979
1079− "rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
1080−
1081− "@npmcli/move-file/rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
1082−
1083− "make-fetch-happen/cacache/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
1084−
1085980 "make-fetch-happen/cacache/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
1086−
1087− "make-fetch-happen/cacache/rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
1088981 }
1089982}
compile-fail-tests/ClientBadBodyType.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: String
2−module CompileFail.ClientBadBodyType where
3−
4−import Yoga.Fetch.Om (JSON, POST, Route, client)
5−
6−type UserApi =
7− { createUser :: Route POST "users" { body :: JSON { name :: String } } (ok :: { body :: {} })
8− }
9−
10−api = client @UserApi ""
11−
12−bad = api.createUser { name: 42 }
compile-fail-tests/ClientBadCurriedBodyType.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: String
2−module CompileFail.ClientBadCurriedBodyType where
3−
4−import Yoga.Fetch.Om (JSON, POST, Route, client, type (/), type (:))
5−
6−type UserApi =
7− { updateUser :: Route POST ("users" / "id" : Int) { body :: JSON { name :: String } } (ok :: { body :: {} })
8− }
9−
10−api = client @UserApi ""
11−
12−bad = api.updateUser { id: 1 } { name: 42 }
compile-fail-tests/ClientBadHeaderType.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: BearerToken
2−module CompileFail.ClientBadHeaderType where
3−
4−import Yoga.Fetch.Om (GET, Route, client)
5−import Yoga.HTTP.API.Route (BearerToken)
6−
7−type UserApi =
8− { me :: Route GET "me" { headers :: { authorization :: BearerToken } } (ok :: { body :: {} })
9− }
10−
11−api = client @UserApi ""
12−
13−bad = api.me { authorization: "secret" }
compile-fail-tests/ClientBadOptionalQueryType.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: limit
2−module CompileFail.ClientBadOptionalQueryType where
3−
4−import Justifill (justifill)
5−import Yoga.Fetch.Om (GET, Route, client, type (:?))
6−
7−type UserApi =
8− { listUsers :: Route GET ("users" :? { limit :: Int, offset :: Int }) {} (ok :: { body :: {} })
9− }
10−
11−api = client @UserApi ""
12−
13−bad = api.listUsers (justifill { limit: "10" })
compile-fail-tests/ClientBadPathType.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: Int
2−module CompileFail.ClientBadPathType where
3−
4−import Yoga.Fetch.Om (GET, Route, client, type (/), type (:))
5−
6−type UserApi =
7− { getUser :: Route GET ("users" / "id" : Int) {} (ok :: { body :: {} })
8− }
9−
10−api = client @UserApi ""
11−
12−bad = api.getUser { id: "1" }
compile-fail-tests/ClientExtraBodyField.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: extra
2−module CompileFail.ClientExtraBodyField where
3−
4−import Yoga.Fetch.Om (JSON, POST, Route, client)
5−
6−type UserApi =
7− { createUser :: Route POST "users" { body :: JSON { name :: String } } (ok :: { body :: {} })
8− }
9−
10−api = client @UserApi ""
11−
12−bad = api.createUser { name: "Alice", extra: true }
compile-fail-tests/ClientExtraHeaderField.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: extra
2−module CompileFail.ClientExtraHeaderField where
3−
4−import Yoga.Fetch.Om (GET, Route, client)
5−import Yoga.HTTP.API.Route (BearerToken(..))
6−
7−type UserApi =
8− { me :: Route GET "me" { headers :: { authorization :: BearerToken } } (ok :: { body :: {} })
9− }
10−
11−api = client @UserApi ""
12−
13−bad = api.me { authorization: BearerToken "secret", extra: true }
compile-fail-tests/ClientExtraOptionalQueryField.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: extra
2−module CompileFail.ClientExtraOptionalQueryField where
3−
4−import Justifill (justifill)
5−import Yoga.Fetch.Om (GET, Route, client, type (:?))
6−
7−type UserApi =
8− { listUsers :: Route GET ("users" :? { limit :: Int, offset :: Int }) {} (ok :: { body :: {} })
9− }
10−
11−api = client @UserApi ""
12−
13−bad = api.listUsers (justifill { limit: 1, extra: "nope" })
compile-fail-tests/ClientExtraPathField.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: extra
2−module CompileFail.ClientExtraPathField where
3−
4−import Yoga.Fetch.Om (GET, Route, client, type (/), type (:))
5−
6−type UserApi =
7− { getUser :: Route GET ("users" / "id" : Int) {} (ok :: { body :: {} })
8− }
9−
10−api = client @UserApi ""
11−
12−bad = api.getUser { id: 1, extra: true }
compile-fail-tests/ClientMissingBodyArgument.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: Function
2−module CompileFail.ClientMissingBodyArgument where
3−
4−import Yoga.Fetch.Om (JSON, POST, Route, client, type (/), type (:))
5−import Yoga.Om (Om)
6−
7−type UserApi =
8− { updateUser :: Route POST ("users" / "id" : Int) { body :: JSON { token :: String } } (ok :: { body :: {} })
9− }
10−
11−api :: forall ctx err. { updateUser :: { id :: Int } -> Om { | ctx } err {} }
12−api = client @UserApi ""
compile-fail-tests/ClientMissingBodyField.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: name :: String
2−module CompileFail.ClientMissingBodyField where
3−
4−import Yoga.Fetch.Om (JSON, POST, Route, client)
5−
6−type UserApi =
7− { createUser :: Route POST "users" { body :: JSON { name :: String, age :: Int } } (ok :: { body :: {} })
8− }
9−
10−api = client @UserApi ""
11−
12−bad = api.createUser { age: 42 }
compile-fail-tests/ClientMissingHeaderArgument.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: Function
2−module CompileFail.ClientMissingHeaderArgument where
3−
4−import Yoga.Fetch.Om (GET, Route, client, type (/), type (:))
5−import Yoga.HTTP.API.Route (BearerToken)
6−import Yoga.Om (Om)
7−
8−type UserApi =
9− { getUser :: Route GET ("users" / "id" : Int) { headers :: { authorization :: BearerToken } } (ok :: { body :: {} })
10− }
11−
12−api :: forall ctx err. { getUser :: { id :: Int } -> Om { | ctx } err {} }
13−api = client @UserApi ""
compile-fail-tests/ClientMissingHeaderField.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: authorization :: BearerToken
2−module CompileFail.ClientMissingHeaderField where
3−
4−import Yoga.Fetch.Om (GET, Route, client)
5−import Yoga.HTTP.API.Route (BearerToken)
6−
7−type UserApi =
8− { me :: Route GET "me" { headers :: { authorization :: BearerToken } } (ok :: { body :: {} })
9− }
10−
11−api = client @UserApi ""
12−
13−bad = api.me {}
compile-fail-tests/ClientMissingPathField.pursdeleted−12
@@ -1,12 +0,0 @@
1−-- EXPECT: id :: Int
2−module CompileFail.ClientMissingPathField where
3−
4−import Yoga.Fetch.Om (GET, Route, client, type (/), type (:))
5−
6−type UserApi =
7− { getUser :: Route GET ("users" / "id" : Int) {} (ok :: { body :: {} })
8− }
9−
10−api = client @UserApi ""
11−
12−bad = api.getUser {}
compile-fail-tests/ClientMissingRequiredQuery.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: id :: Int
2−module CompileFail.ClientMissingRequiredQuery where
3−
4−import Justifill (justifill)
5−import Yoga.Fetch.Om (GET, Required, Route, client, type (:?))
6−
7−type UserApi =
8− { getUser :: Route GET ("users" :? { id :: Required Int, token :: Required String }) {} (ok :: { body :: {} })
9− }
10−
11−api = client @UserApi ""
12−
13−bad = api.getUser (justifill { token: "secret" })
compile-fail-tests/ClientMissingThirdArgument.pursdeleted−13
@@ -1,13 +0,0 @@
1−-- EXPECT: Function
2−module CompileFail.ClientMissingThirdArgument where
3−
4−import Yoga.Fetch.Om (JSON, POST, Route, client, type (/), type (:))
5−import Yoga.HTTP.API.Route (BearerToken)
6−import Yoga.Om (Om)
7−
8−type UserApi =
9− { updateUser :: Route POST ("users" / "id" : Int) { headers :: { authorization :: BearerToken }, body :: JSON { token :: String } } (ok :: { body :: {} })
10− }
11−
12−api :: forall ctx err. { updateUser :: { id :: Int } -> { authorization :: BearerToken } -> Om { | ctx } err {} }
13−api = client @UserApi ""
compile-fail-tests/run.jsdeleted−101
@@ -1,101 +0,0 @@
1−import { mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
2−import { tmpdir } from "node:os";
3−import { basename, join } from "node:path";
4−
5−const cwd = new URL("..", import.meta.url).pathname;
6−const casesDir = join(cwd, "compile-fail-tests");
7−const decoder = new TextDecoder();
8−
9−function run(args, options = {}) {
10− const proc = Bun.spawnSync(args, {
11− cwd,
12− stdout: options.stdout ?? "pipe",
13− stderr: options.stderr ?? "pipe",
14− });
15− return {
16− code: proc.exitCode,
17− stdout: proc.stdout ? decoder.decode(proc.stdout) : "",
18− stderr: proc.stderr ? decoder.decode(proc.stderr) : "",
19− };
20−}
21−
22−function printResult(label, result) {
23− console.error(`\n${label} failed with exit code ${result.code}`);
24− if (result.stdout) console.error(result.stdout);
25− if (result.stderr) console.error(result.stderr);
26−}
27−
28−function expectedSubstring(file) {
29− const firstLine = readFileSync(file, "utf8").split("\n", 1)[0];
30− return firstLine.startsWith("-- EXPECT: ") ? firstLine.slice("-- EXPECT: ".length) : null;
31−}
32−
33−const sourcesResult = run(["bunx", "spago", "sources", "--quiet", "--json"]);
34−if (sourcesResult.code !== 0) {
35− printResult("spago sources", sourcesResult);
36− process.exit(1);
37−}
38−
39−const excludedSourceGlobs = new Set([
40− "test/**/*.purs",
41− "../purescript-yoga-fastify-om/src/**/*.purs",
42− "../purescript-yoga-fastify-om/local-packages/yoga-fastify/src/**/*.purs",
43−]);
44−
45−const librarySources = JSON.parse(sourcesResult.stdout)
46− .filter((source) => !excludedSourceGlobs.has(source));
47−
48−const cases = readdirSync(casesDir)
49− .filter((file) => file.endsWith(".purs"))
50− .sort()
51− .map((file) => join("compile-fail-tests", file));
52−
53−const libOutput = mkdtempSync(join(tmpdir(), "yoga-fetch-om-lib-"));
54−
55−try {
56− const libResult = run(["bunx", "purs", "compile", ...librarySources, "--output", libOutput], { stdout: "ignore" });
57− if (libResult.code !== 0) {
58− printResult("library compile", libResult);
59− process.exit(1);
60− }
61−
62− let failed = 0;
63−
64− for (const testCase of cases) {
65− const failOutput = mkdtempSync(join(tmpdir(), "yoga-fetch-om-compile-fail-"));
66− try {
67− const result = run([
68− "bunx",
69− "purs",
70− "compile",
71− ...librarySources,
72− testCase,
73− "--output",
74− failOutput,
75− ]);
76−
77− const output = `${result.stdout}\n${result.stderr}`;
78− const expected = expectedSubstring(join(cwd, testCase));
79− const name = basename(testCase, ".purs");
80−
81− if (result.code === 0) {
82− console.error(`FAIL ${name} - compiled successfully (should have failed)`);
83− failed++;
84− } else if (expected && !output.includes(expected)) {
85− printResult(`FAIL ${name}`, result);
86− console.error(`Expected failure to contain: ${expected}`);
87− failed++;
88− } else {
89− console.log(`PASS ${name}`);
90− }
91− } finally {
92− rmSync(failOutput, { recursive: true, force: true });
93− }
94− }
95−
96− if (failed > 0) {
97− process.exit(1);
98− }
99−} finally {
100− rmSync(libOutput, { recursive: true, force: true });
101−}
package.json+5−19
@@ -1,14 +1,12 @@
11{
22 "name": "purescript-yoga-fetch-om",
3− "version": "0.8.1",
3+ "version": "0.4.0",
44 "description": "Derive type-safe fetch clients from yoga HTTP API definitions",
55 "type": "module",
66 "scripts": {
77 "build": "spago build",
8− "test": "spago test",
9− "coverage": "spago build && c8 spago test",
10− "test:compile-fail": "bun compile-fail-tests/run.js",
11− "clean": "rm -rf output .spago coverage",
8+ "test": "spago build && bunx vitest run",
9+ "clean": "rm -rf output .spago",
1210 "watch": "spago build --watch",
1311 "repl": "spago repl",
1412 "docs": "spago docs",
@@ -26,23 +24,11 @@
2624 "author": "",
2725 "license": "MIT",
2826 "devDependencies": {
29− "@fastify/cookie": "^11.1.2",
30− "@fastify/cors": "^11.3.0",
31− "@fastify/formbody": "^9.0.0",
32− "@fastify/helmet": "^13.1.0",
33− "@fastify/multipart": "^10.1.0",
34− "@fastify/rate-limit": "^11.2.0",
35− "@fastify/websocket": "^11.3.0",
36− "argon2": "^0.45.1",
37− "c8": "^12.0.0",
3827 "fastify": "^5.7.4",
39− "jose": "^6.2.8",
4028 "jsdom": "^28.0.0",
4129 "purescript": "^0.15.15",
42− "spago": "^1.0.3",
30+ "spago": "0.93.42",
4331 "vitest": "^2.1.8"
4432 },
45− "dependencies": {
46− "pino": "^10.3.1"
47− }
33+ "dependencies": {}
4834}
spago.lock+700−430
@@ -6,10 +6,7 @@
66 "core": {
77 "dependencies": [
88 {
9− "aff": ">=8.0.0 <9.0.0"
10− },
11− {
12− "arraybuffer-types": ">=3.0.2 <4.0.0"
9+ "aff": ">=7.0.0 <9.0.0"
1310 },
1411 {
1512 "arrays": ">=7.3.0 <8.0.0"
@@ -24,7 +21,7 @@
2421 "exceptions": ">=6.1.0 <7.0.0"
2522 },
2623 {
27− "http-methods": ">=6.1.0 <7.0.0"
24+ "http-methods": ">=6.0.0 <7.0.0"
2825 },
2926 {
3027 "js-fetch": ">=0.2.1 <0.3.0"
@@ -44,9 +41,6 @@
4441 {
4542 "strings": ">=6.0.1 <7.0.0"
4643 },
47− {
48− "tailrec": ">=6.1.0 <7.0.0"
49− },
5044 {
5145 "transformers": ">=6.1.0 <7.0.0"
5246 },
@@ -63,16 +57,13 @@
6357 "variant": ">=8.0.0 <9.0.0"
6458 },
6559 {
66− "yoga-http-api": ">=0.3.4 <0.4.0"
67− },
68− {
69− "yoga-json": ">=5.2.1 <6.0.0"
60+ "yoga-http-api": ">=0.1.0 <0.2.0"
7061 },
7162 {
72− "yoga-om": ">=1.5.2 <2.0.0"
63+ "yoga-json": ">=5.2.0 <6.0.0"
7364 },
7465 {
75− "yoga-om-strom": ">=0.4.3 <0.5.0"
66+ "yoga-om": ">=1.2.2 <2.0.0"
7667 }
7768 ]
7869 },
@@ -81,43 +72,620 @@
8172 {
8273 "console": ">=6.0.0 <7.0.0"
8374 },
84− "enums",
85− "foldable-traversable",
86− "foreign",
87− "foreign-object",
88− "integers",
89− "js-promise",
90− {
91− "js-uri": ">=3.1.0 <4.0.0"
92− },
93− {
94− "justifill": ">=0.5.0 <0.6.0"
95− },
96− "partial",
97− {
98− "quickcheck": ">=8.0.1 <9.0.0"
99− },
10075 {
10176 "refs": ">=6.0.0 <7.0.0"
10277 },
10378 {
104− "spec": ">=8.0.0 <9.0.0"
79+ "vitest": ">=1.0.0 <3.0.0"
10580 },
10681 {
107− "spec-discovery": ">=8.0.0 <9.0.0"
108− },
109− {
110− "spec-quickcheck": ">=5.0.2 <6.0.0"
111− },
112− "yoga-fastify",
113− {
114− "yoga-fastify-om": ">=0.4.7 <0.5.0"
82+ "yoga-fastify-om": ">=0.1.0 <0.2.0"
11583 }
11684 ]
11785 }
11886 }
11987 },
120− "extra_packages": {}
88+ "package_set": {
89+ "address": {
90+ "registry": "73.0.0"
91+ },
92+ "compiler": ">=0.15.15 <0.16.0",
93+ "content": {
94+ "abc-parser": "2.1.0",
95+ "ace": "9.1.0",
96+ "address-rfc2821": "0.1.1",
97+ "aff": "8.0.0",
98+ "aff-bus": "6.0.0",
99+ "aff-coroutines": "9.0.0",
100+ "aff-promise": "4.0.0",
101+ "aff-retry": "2.0.0",
102+ "affjax": "13.0.0",
103+ "affjax-node": "1.0.0",
104+ "affjax-web": "1.0.0",
105+ "ansi": "7.0.0",
106+ "apexcharts": "0.5.0",
107+ "applicative-phases": "1.0.0",
108+ "argonaut": "9.0.0",
109+ "argonaut-aeson-generic": "0.4.1",
110+ "argonaut-codecs": "9.1.0",
111+ "argonaut-core": "7.0.0",
112+ "argonaut-generic": "8.0.0",
113+ "argonaut-traversals": "10.0.0",
114+ "argparse-basic": "2.0.0",
115+ "array-builder": "0.1.2",
116+ "array-search": "0.6.0",
117+ "arraybuffer": "13.2.0",
118+ "arraybuffer-builder": "3.1.0",
119+ "arraybuffer-types": "3.0.2",
120+ "arrays": "7.3.0",
121+ "arrays-extra": "0.6.1",
122+ "arrays-zipper": "2.0.1",
123+ "ask": "1.0.0",
124+ "assert": "6.0.0",
125+ "assert-multiple": "0.4.0",
126+ "avar": "5.0.1",
127+ "axon": "0.0.3",
128+ "b64": "0.0.8",
129+ "barbies": "1.0.1",
130+ "barlow-lens": "0.9.0",
131+ "benchlib": "0.0.4",
132+ "bifunctors": "6.1.0",
133+ "bigints": "7.0.1",
134+ "blessed": "1.0.0",
135+ "bolson": "0.3.9",
136+ "bookhound": "0.1.7",
137+ "bower-json": "3.0.0",
138+ "call-by-name": "4.0.1",
139+ "canvas": "6.0.0",
140+ "canvas-action": "9.0.0",
141+ "cartesian": "1.0.6",
142+ "catenable-lists": "7.0.0",
143+ "cbor-stream": "1.3.0",
144+ "chameleon": "1.0.0",
145+ "chameleon-halogen": "1.0.3",
146+ "chameleon-react-basic": "1.1.0",
147+ "chameleon-styled": "2.5.0",
148+ "chameleon-transformers": "1.0.0",
149+ "channel": "1.0.0",
150+ "checked-exceptions": "3.1.1",
151+ "choku": "1.0.2",
152+ "classless": "0.1.1",
153+ "classless-arbitrary": "0.1.1",
154+ "classless-decode-json": "0.1.1",
155+ "classless-encode-json": "0.1.3",
156+ "classnames": "2.0.0",
157+ "codec": "6.1.0",
158+ "codec-argonaut": "10.0.0",
159+ "codec-json": "2.0.0",
160+ "colors": "7.0.1",
161+ "concur-core": "0.5.0",
162+ "concur-react": "0.5.0",
163+ "concurrent-queues": "3.0.0",
164+ "console": "6.1.0",
165+ "const": "6.0.0",
166+ "contravariant": "6.0.0",
167+ "control": "6.0.0",
168+ "convertable-options": "1.0.0",
169+ "coroutines": "7.0.0",
170+ "css": "6.0.0",
171+ "css-class-name-extractor": "0.0.4",
172+ "css-frameworks": "1.1.0",
173+ "csv-stream": "2.3.0",
174+ "data-mvc": "0.0.2",
175+ "datetime": "6.1.0",
176+ "datetime-parsing": "0.2.0",
177+ "debounce": "0.1.0",
178+ "debug": "6.0.2",
179+ "decimals": "7.1.0",
180+ "default-values": "1.0.1",
181+ "deku": "0.9.23",
182+ "deno": "0.0.5",
183+ "dissect": "1.0.0",
184+ "distributive": "6.0.0",
185+ "dodo-printer": "2.2.3",
186+ "dom-filereader": "7.0.0",
187+ "dom-indexed": "13.0.0",
188+ "dom-simple": "0.4.0",
189+ "dotenv": "4.0.3",
190+ "droplet": "0.6.0",
191+ "dts": "1.0.0",
192+ "dual-numbers": "1.0.3",
193+ "dynamic-buffer": "3.0.1",
194+ "echarts-simple": "0.0.1",
195+ "effect": "4.0.0",
196+ "either": "6.1.0",
197+ "elmish": "0.13.0",
198+ "elmish-enzyme": "0.1.1",
199+ "elmish-hooks": "0.11.0",
200+ "elmish-html": "0.10.0",
201+ "elmish-testing-library": "0.3.2",
202+ "elmish-time-machine": "0.4.2",
203+ "email-validate": "7.0.0",
204+ "encoding": "0.0.9",
205+ "enums": "6.0.1",
206+ "env-names": "0.4.0",
207+ "environment": "2.0.0",
208+ "error": "2.0.0",
209+ "eta-conversion": "0.3.2",
210+ "exceptions": "6.1.0",
211+ "exists": "6.0.0",
212+ "exitcodes": "4.0.0",
213+ "expect-inferred": "3.0.0",
214+ "express": "0.9.1",
215+ "ezfetch": "1.1.1",
216+ "fahrtwind": "2.0.0",
217+ "fakerjs": "0.0.1",
218+ "fallback": "0.1.0",
219+ "fast-vect": "1.2.0",
220+ "fetch": "4.1.0",
221+ "fetch-argonaut": "1.0.1",
222+ "fetch-core": "5.1.0",
223+ "fetch-yoga-json": "1.1.0",
224+ "ffi-simple": "0.5.1",
225+ "fft": "0.3.0",
226+ "fft-js": "0.1.0",
227+ "filterable": "5.0.0",
228+ "fix-functor": "0.1.0",
229+ "fixed-points": "7.0.0",
230+ "fixed-precision": "5.0.0",
231+ "flame": "1.6.0",
232+ "float32": "2.0.0",
233+ "fmt": "0.2.1",
234+ "foldable-traversable": "6.0.0",
235+ "foldable-traversable-extra": "0.0.6",
236+ "foreign": "7.0.0",
237+ "foreign-object": "4.1.0",
238+ "foreign-readwrite": "3.4.0",
239+ "forgetmenot": "0.1.0",
240+ "fork": "6.0.0",
241+ "form-urlencoded": "7.0.0",
242+ "formatters": "7.0.0",
243+ "framer-motion": "2.0.1",
244+ "free": "7.1.0",
245+ "freeap": "7.0.0",
246+ "freer-free": "0.0.1",
247+ "freet": "7.0.0",
248+ "functions": "6.0.0",
249+ "functor1": "3.0.0",
250+ "functors": "5.0.0",
251+ "fuzzy": "0.4.0",
252+ "gen": "4.0.0",
253+ "generate-values": "1.0.1",
254+ "generic-router": "0.0.1",
255+ "geojson": "0.0.5",
256+ "geometria": "2.2.0",
257+ "gesso": "1.0.1",
258+ "gojs": "0.1.1",
259+ "golem-fetch": "0.1.0",
260+ "grain": "3.0.0",
261+ "grain-router": "3.0.0",
262+ "grain-virtualized": "3.0.0",
263+ "graphql-client": "10.1.1",
264+ "graphs": "8.1.0",
265+ "group": "4.1.1",
266+ "halogen": "7.0.0",
267+ "halogen-bootstrap5": "5.3.2",
268+ "halogen-canvas": "1.0.0",
269+ "halogen-css": "10.0.0",
270+ "halogen-declarative-canvas": "0.0.8",
271+ "halogen-echarts-simple": "0.0.4",
272+ "halogen-formless": "4.0.3",
273+ "halogen-helix": "1.1.0",
274+ "halogen-hooks": "0.6.3",
275+ "halogen-hooks-extra": "0.9.0",
276+ "halogen-infinite-scroll": "1.1.0",
277+ "halogen-store": "0.5.4",
278+ "halogen-storybook": "2.0.0",
279+ "halogen-subscriptions": "2.0.0",
280+ "halogen-svg-elems": "8.0.0",
281+ "halogen-typewriter": "1.0.4",
282+ "halogen-use-trigger-hooks": "1.0.0",
283+ "halogen-vdom": "8.0.0",
284+ "halogen-vdom-string-renderer": "0.5.0",
285+ "halogen-xterm": "2.0.0",
286+ "heckin": "2.0.1",
287+ "heterogeneous": "0.7.0",
288+ "homogeneous": "0.4.0",
289+ "http-methods": "6.0.0",
290+ "httpurple": "4.0.0",
291+ "huffman": "0.4.0",
292+ "humdrum": "0.0.1",
293+ "hylograph-canvas": "0.1.0",
294+ "hylograph-d3-kernel": "0.1.0",
295+ "hylograph-graph": "0.1.0",
296+ "hylograph-layout": "0.1.0",
297+ "hylograph-music": "0.1.0",
298+ "hylograph-optics": "0.1.0",
299+ "hylograph-selection": "0.1.1",
300+ "hylograph-simulation": "0.1.0",
301+ "hylograph-simulation-core": "0.1.0",
302+ "hylograph-simulation-halogen": "0.1.0",
303+ "hylograph-transitions": "0.1.0",
304+ "hylograph-wasm-kernel": "0.1.0",
305+ "hyrule": "2.3.8",
306+ "identity": "6.0.0",
307+ "identy": "4.0.1",
308+ "indexed-db": "1.0.0",
309+ "indexed-monad": "3.0.0",
310+ "ink": "0.0.1",
311+ "int64": "3.0.0",
312+ "integers": "6.0.0",
313+ "interpolate": "5.0.2",
314+ "intersection-observer": "1.0.1",
315+ "invariant": "6.0.0",
316+ "jarilo": "1.0.1",
317+ "jelly": "0.10.0",
318+ "jelly-router": "0.3.0",
319+ "jelly-signal": "0.4.0",
320+ "jest": "1.0.0",
321+ "js-abort-controller": "1.0.0",
322+ "js-bigints": "2.2.1",
323+ "js-date": "8.0.0",
324+ "js-fetch": "0.2.1",
325+ "js-fileio": "3.0.0",
326+ "js-intl": "1.1.4",
327+ "js-iterators": "0.1.1",
328+ "js-maps": "0.1.2",
329+ "js-promise": "1.0.0",
330+ "js-promise-aff": "1.0.0",
331+ "js-timers": "6.1.0",
332+ "js-uri": "3.1.0",
333+ "jsdom": "1.0.0",
334+ "json": "1.1.0",
335+ "json-codecs": "5.0.0",
336+ "justifill": "0.5.0",
337+ "jwt": "0.0.9",
338+ "labeled-data": "0.2.0",
339+ "language-cst-parser": "0.14.1",
340+ "lazy": "6.0.0",
341+ "lazy-joe": "1.0.0",
342+ "lcg": "4.0.0",
343+ "leibniz": "5.0.0",
344+ "leveldb": "1.0.1",
345+ "liminal": "1.0.1",
346+ "linalg": "6.0.0",
347+ "lists": "7.0.0",
348+ "literals": "1.0.2",
349+ "logging": "3.0.0",
350+ "logging-journald": "0.4.0",
351+ "lumi-components": "18.0.0",
352+ "machines": "7.0.0",
353+ "maps-eager": "0.5.0",
354+ "marionette": "1.0.0",
355+ "marionette-react-basic-hooks": "0.1.1",
356+ "marked": "0.1.0",
357+ "matrices": "5.0.1",
358+ "matryoshka": "1.0.0",
359+ "maybe": "6.0.0",
360+ "media-types": "6.0.0",
361+ "meowclient": "1.0.0",
362+ "midi": "4.0.0",
363+ "milkis": "9.0.0",
364+ "mimetype": "0.0.1",
365+ "minibench": "4.0.1",
366+ "mmorph": "7.0.0",
367+ "monad-control": "5.0.0",
368+ "monad-logger": "1.3.1",
369+ "monad-loops": "0.5.0",
370+ "monad-unlift": "1.0.1",
371+ "monoid-extras": "0.0.1",
372+ "monoidal": "0.16.0",
373+ "morello": "0.4.0",
374+ "mote": "3.0.0",
375+ "motsunabe": "2.0.0",
376+ "mvc": "0.0.1",
377+ "mysql": "6.0.1",
378+ "n3": "0.1.0",
379+ "nano-id": "1.1.0",
380+ "nanoid": "0.1.0",
381+ "naturals": "3.0.0",
382+ "nested-functor": "0.2.1",
383+ "newtype": "5.0.0",
384+ "nextjs": "0.1.1",
385+ "nextui": "0.2.0",
386+ "node-buffer": "9.0.0",
387+ "node-child-process": "11.1.0",
388+ "node-event-emitter": "3.0.0",
389+ "node-execa": "5.0.0",
390+ "node-fs": "9.2.0",
391+ "node-glob-basic": "2.0.0",
392+ "node-http": "9.1.0",
393+ "node-http2": "1.1.1",
394+ "node-human-signals": "1.0.0",
395+ "node-net": "5.1.0",
396+ "node-os": "5.1.0",
397+ "node-path": "5.0.1",
398+ "node-process": "11.2.0",
399+ "node-readline": "8.1.1",
400+ "node-sqlite3": "8.0.0",
401+ "node-stream-pipes": "2.1.6",
402+ "node-streams": "9.0.1",
403+ "node-tls": "0.3.1",
404+ "node-url": "7.0.1",
405+ "node-workerbees": "0.3.1",
406+ "node-zlib": "0.4.0",
407+ "nonempty": "7.0.0",
408+ "now": "6.0.0",
409+ "npm-package-json": "2.0.0",
410+ "nullable": "6.0.0",
411+ "numberfield": "0.2.2",
412+ "numbers": "9.0.1",
413+ "oak": "3.1.1",
414+ "oak-debug": "1.2.2",
415+ "object-maps": "0.3.0",
416+ "ocarina": "1.5.4",
417+ "oooooooooorrrrrrrmm-lib": "0.0.1",
418+ "open-colors-scales-and-schemes": "1.0.0",
419+ "open-drawing": "6.0.4",
420+ "open-folds": "6.4.0",
421+ "open-foreign-generic": "11.0.3",
422+ "open-memoize": "6.2.0",
423+ "open-mkdirp-aff": "1.2.0",
424+ "open-pairing": "6.2.0",
425+ "open-smolder": "12.0.2",
426+ "options": "7.0.0",
427+ "optparse": "5.0.1",
428+ "ordered-collections": "3.2.0",
429+ "ordered-set": "0.4.0",
430+ "orders": "6.0.0",
431+ "org-doc": "0.1.0",
432+ "owoify": "1.2.0",
433+ "pairs": "9.0.1",
434+ "parallel": "7.0.0",
435+ "parsing": "11.0.0",
436+ "parsing-dataview": "3.2.4",
437+ "partial": "4.0.0",
438+ "pathy": "9.0.0",
439+ "pha": "0.13.0",
440+ "phaser": "0.7.0",
441+ "phylio": "1.1.2",
442+ "pipes": "8.0.0",
443+ "pirates-charm": "0.0.1",
444+ "play": "1.0.0",
445+ "pmock": "0.9.0",
446+ "point-free": "1.0.0",
447+ "pointed-list": "0.5.1",
448+ "polymorphic-vectors": "4.0.0",
449+ "posix-types": "6.0.0",
450+ "postgresql": "2.0.21",
451+ "precise": "6.0.0",
452+ "precise-datetime": "7.0.0",
453+ "prelude": "6.0.2",
454+ "prettier-printer": "3.0.0",
455+ "printf": "0.1.0",
456+ "priority-queue": "0.1.2",
457+ "profunctor": "6.0.1",
458+ "profunctor-lenses": "8.0.0",
459+ "protobuf": "4.4.0",
460+ "psa-utils": "8.0.0",
461+ "psci-support": "6.0.0",
462+ "punycode": "1.0.0",
463+ "pursfmt": "0.17.0",
464+ "qualified-do": "2.2.0",
465+ "quantities": "12.2.0",
466+ "quickcheck": "8.0.1",
467+ "quickcheck-combinators": "0.1.3",
468+ "quickcheck-laws": "7.0.0",
469+ "quickcheck-utf8": "0.0.0",
470+ "random": "6.0.0",
471+ "rationals": "6.0.0",
472+ "rdf": "0.1.0",
473+ "react": "11.0.0",
474+ "react-aria": "0.2.0",
475+ "react-basic": "17.0.0",
476+ "react-basic-classic": "3.0.0",
477+ "react-basic-dnd": "10.1.0",
478+ "react-basic-dom": "7.0.0",
479+ "react-basic-dom-beta": "0.1.1",
480+ "react-basic-emotion": "7.1.0",
481+ "react-basic-hooks": "9.0.0",
482+ "react-basic-storybook": "2.0.0",
483+ "react-dom": "8.0.0",
484+ "react-halo": "3.0.0",
485+ "react-icons": "1.1.5",
486+ "react-markdown": "0.1.0",
487+ "react-testing-library": "4.0.1",
488+ "react-virtuoso": "1.0.0",
489+ "reactix": "0.6.1",
490+ "read": "1.0.1",
491+ "recharts": "1.1.0",
492+ "record": "4.0.0",
493+ "record-extra": "5.0.1",
494+ "record-extra-srghma": "0.2.8",
495+ "record-ptional-fields": "0.1.2",
496+ "record-studio": "1.0.4",
497+ "refs": "6.0.0",
498+ "remotedata": "5.0.1",
499+ "repr": "0.5.0",
500+ "resize-arrays": "0.0.1",
501+ "resize-observer": "1.0.0",
502+ "resource": "2.0.1",
503+ "resourcet": "1.0.0",
504+ "result": "1.0.3",
505+ "return": "0.2.0",
506+ "ring-modules": "5.0.1",
507+ "rito": "0.3.4",
508+ "roman": "0.4.0",
509+ "rough-notation": "1.0.2",
510+ "routing": "11.0.0",
511+ "routing-duplex": "0.7.0",
512+ "run": "5.0.0",
513+ "safe-coerce": "2.0.0",
514+ "safely": "4.0.1",
515+ "school-of-music": "1.3.0",
516+ "selection-foldable": "0.2.0",
517+ "selective-functors": "1.0.1",
518+ "semirings": "7.0.0",
519+ "shuffle": "2.0.0",
520+ "signal": "13.0.0",
521+ "simple-emitter": "3.0.1",
522+ "simple-i18n": "2.0.1",
523+ "simple-json": "9.0.0",
524+ "simple-json-generics": "0.2.1",
525+ "simple-ulid": "3.0.0",
526+ "sized-matrices": "1.0.0",
527+ "sized-vectors": "5.0.2",
528+ "slug": "3.2.0",
529+ "small-ffi": "4.0.1",
530+ "soundfonts": "4.1.0",
531+ "sparse-matrices": "2.0.1",
532+ "sparse-polynomials": "3.0.1",
533+ "spec": "8.1.1",
534+ "spec-discovery": "8.4.0",
535+ "spec-mocha": "5.1.1",
536+ "spec-node": "0.0.3",
537+ "spec-quickcheck": "5.0.2",
538+ "spec-reporter-xunit": "0.7.1",
539+ "splitmix": "2.1.0",
540+ "ssrs": "1.0.0",
541+ "st": "6.2.0",
542+ "statistics": "0.3.2",
543+ "strictlypositiveint": "1.0.1",
544+ "string-parsers": "8.0.0",
545+ "strings": "6.0.1",
546+ "strings-extra": "4.0.0",
547+ "stringutils": "0.0.12",
548+ "structured-logging": "1.0.0",
549+ "substitute": "0.2.3",
550+ "supply": "0.2.0",
551+ "svg-parser": "3.0.0",
552+ "systemd-journald": "0.3.0",
553+ "tagged": "4.0.2",
554+ "tailrec": "6.1.0",
555+ "tanstack-query": "2.0.0",
556+ "tecton": "0.2.1",
557+ "tecton-halogen": "0.2.0",
558+ "test-unit": "17.0.0",
559+ "text-formatting": "0.1.0",
560+ "thermite": "6.3.1",
561+ "thermite-dom": "0.3.1",
562+ "these": "6.0.0",
563+ "threading": "0.0.3",
564+ "tidy": "0.11.1",
565+ "tidy-codegen": "4.0.1",
566+ "tldr": "0.0.0",
567+ "toestand": "0.9.0",
568+ "transformation-matrix": "1.0.1",
569+ "transformers": "6.1.0",
570+ "transit": "1.0.0",
571+ "tree-rose": "4.0.2",
572+ "trivial-unfold": "0.5.0",
573+ "ts-bridge": "4.0.0",
574+ "tuples": "7.0.0",
575+ "two-or-more": "1.0.0",
576+ "type-equality": "4.0.1",
577+ "typedenv": "2.0.1",
578+ "typelevel": "6.0.0",
579+ "typelevel-lists": "2.1.0",
580+ "typelevel-peano": "1.0.1",
581+ "typelevel-prelude": "7.0.0",
582+ "typelevel-regex": "0.0.3",
583+ "typelevel-rows": "0.1.0",
584+ "typisch": "0.4.0",
585+ "uint": "7.0.0",
586+ "ulid": "3.0.1",
587+ "uncurried-transformers": "1.1.0",
588+ "undefined": "2.0.0",
589+ "undefined-is-not-a-problem": "1.1.0",
590+ "unfoldable": "6.0.0",
591+ "unicode": "6.0.0",
592+ "unique": "0.6.1",
593+ "unlift": "1.0.1",
594+ "unordered-collections": "3.1.0",
595+ "unsafe-coerce": "6.0.0",
596+ "unsafe-reference": "5.0.0",
597+ "untagged-to-tagged": "0.1.4",
598+ "untagged-union": "1.0.0",
599+ "uri": "9.0.0",
600+ "url-immutable": "1.0.0",
601+ "url-regex-safe": "0.1.1",
602+ "uuid": "9.0.0",
603+ "uuidv4": "1.0.0",
604+ "validation": "6.0.0",
605+ "variant": "8.0.0",
606+ "variant-encodings": "2.0.0",
607+ "variant-gen": "1.0.0",
608+ "vectorfield": "1.0.1",
609+ "vectors": "2.1.0",
610+ "versions": "7.0.0",
611+ "visx": "0.0.2",
612+ "vitest": "2.0.0",
613+ "web-clipboard": "6.0.0",
614+ "web-cssom": "2.0.0",
615+ "web-cssom-view": "0.1.0",
616+ "web-dom": "6.0.0",
617+ "web-dom-parser": "8.0.0",
618+ "web-dom-xpath": "3.0.0",
619+ "web-encoding": "3.0.0",
620+ "web-events": "4.0.0",
621+ "web-fetch": "4.0.1",
622+ "web-file": "4.0.0",
623+ "web-geometry": "0.1.0",
624+ "web-html": "4.1.1",
625+ "web-pointerevents": "2.0.0",
626+ "web-proletarian": "1.0.0",
627+ "web-promise": "3.2.0",
628+ "web-resize-observer": "2.1.0",
629+ "web-router": "1.0.0",
630+ "web-socket": "4.0.0",
631+ "web-storage": "5.0.0",
632+ "web-streams": "4.0.0",
633+ "web-touchevents": "4.0.0",
634+ "web-uievents": "5.0.0",
635+ "web-url": "2.0.0",
636+ "web-workers": "1.1.0",
637+ "web-xhr": "5.0.1",
638+ "webb-aff-list": "0.0.2",
639+ "webb-array": "0.0.2",
640+ "webb-channel": "0.0.3",
641+ "webb-commandline": "0.0.3",
642+ "webb-directory": "0.0.7",
643+ "webb-file": "0.0.1",
644+ "webb-map": "0.0.1",
645+ "webb-monad": "0.0.2",
646+ "webb-mutex": "0.0.7",
647+ "webb-parsing": "0.0.5",
648+ "webb-random": "0.0.1",
649+ "webb-refer": "0.0.3",
650+ "webb-set": "0.0.1",
651+ "webb-slot": "0.0.1",
652+ "webb-stateful": "0.0.3",
653+ "webb-string": "0.0.1",
654+ "webb-test": "0.0.1",
655+ "webb-thread": "0.0.2",
656+ "webb-writer": "0.0.3",
657+ "webextension-polyfill": "0.1.0",
658+ "webgpu": "0.0.1",
659+ "which": "2.0.0",
660+ "whine-core": "0.0.33",
661+ "xterm": "1.0.0",
662+ "yaml-next": "3.1.1",
663+ "yoga-fetch": "1.0.1",
664+ "yoga-json": "5.1.0",
665+ "yoga-om": "0.1.0",
666+ "yoga-postgres": "6.0.0",
667+ "yoga-react-dom": "1.0.1",
668+ "yoga-redis": "0.1.0",
669+ "yoga-sql-types": "0.1.0",
670+ "yoga-subtlecrypto": "0.1.0",
671+ "yoga-test-docker": "0.1.0",
672+ "yoga-tree": "1.0.0",
673+ "yoga-tree-svg": "0.1.0",
674+ "yoga-tree-utils": "1.0.0",
675+ "z3": "0.0.2",
676+ "zipperarray": "2.0.0"
677+ }
678+ },
679+ "extra_packages": {
680+ "yoga-fastify": "0.2.3",
681+ "yoga-fastify-om": "0.1.0",
682+ "yoga-http-api": "0.1.4",
683+ "yoga-json": "5.2.0",
684+ "yoga-om": "1.2.2",
685+ "yoga-om-layer": "1.0.6",
686+ "yoga-om-strom": "0.2.0",
687+ "yoga-options": "0.1.0"
688+ }
121689 },
122690 "packages": {
123691 "aff": {
@@ -141,26 +709,6 @@
141709 "unsafe-coerce"
142710 ]
143711 },
144− "aff-retry": {
145− "type": "registry",
146− "version": "2.0.0",
147− "integrity": "sha256-rKZNcP4IKSyBFQBVzrCvE0ULoJt0N/uDLLJ0dPotr4k=",
148− "dependencies": [
149− "aff",
150− "arrays",
151− "datetime",
152− "effect",
153− "either",
154− "exceptions",
155− "integers",
156− "maybe",
157− "newtype",
158− "numbers",
159− "prelude",
160− "random",
161− "transformers"
162− ]
163− },
164712 "ansi": {
165713 "type": "registry",
166714 "version": "7.0.0",
@@ -171,47 +719,6 @@
171719 "prelude"
172720 ]
173721 },
174− "argonaut-codecs": {
175− "type": "registry",
176− "version": "9.1.0",
177− "integrity": "sha256-K910SBrmYallESsm8pxJYThs7lig6hxX3M4PW33Cgew=",
178− "dependencies": [
179− "argonaut-core",
180− "arrays",
181− "bifunctors",
182− "either",
183− "foldable-traversable",
184− "foreign-object",
185− "identity",
186− "integers",
187− "lists",
188− "maybe",
189− "nonempty",
190− "ordered-collections",
191− "prelude",
192− "record",
193− "strings",
194− "tuples"
195− ]
196− },
197− "argonaut-core": {
198− "type": "registry",
199− "version": "7.0.0",
200− "integrity": "sha256-gYZihtBIKthVuXXoiPphMookSf8M5aHyFYkcIWqlxTM=",
201− "dependencies": [
202− "arrays",
203− "control",
204− "either",
205− "foreign-object",
206− "functions",
207− "gen",
208− "maybe",
209− "nonempty",
210− "prelude",
211− "strings",
212− "tailrec"
213− ]
214− },
215722 "arraybuffer-types": {
216723 "type": "registry",
217724 "version": "3.0.2",
@@ -409,16 +916,6 @@
409916 "unsafe-coerce"
410917 ]
411918 },
412− "exitcodes": {
413− "type": "registry",
414− "version": "4.0.0",
415− "integrity": "sha256-xBl4aFSEQ05Owp1ZvO6OxxvLK3LVzU35RSbj79SsePQ=",
416− "dependencies": [
417− "enums",
418− "maybe",
419− "prelude"
420− ]
421− },
422919 "foldable-traversable": {
423920 "type": "registry",
424921 "version": "6.0.0",
@@ -565,8 +1062,8 @@
5651062 },
5661063 "http-methods": {
5671064 "type": "registry",
568− "version": "6.1.0",
569− "integrity": "sha256-WbjEmCv6rysY/aKwlrcspyzShi5bIQy+AFpDzPMnSP4=",
1065+ "version": "6.0.0",
1066+ "integrity": "sha256-S0UjrJSgwJxEsFHM/pX77dM8V4HfvvDqdHPckQrtxMs=",
5701067 "dependencies": [
5711068 "either",
5721069 "prelude",
@@ -694,17 +1191,6 @@
6941191 "prelude"
6951192 ]
6961193 },
697− "justifill": {
698− "type": "registry",
699− "version": "0.5.0",
700− "integrity": "sha256-IwoDO72+IROcUvFIwnronb/ny8SepssgTUG3Qp2TEKg=",
701− "dependencies": [
702− "maybe",
703− "prelude",
704− "record",
705− "typelevel-prelude"
706− ]
707− },
7081194 "lazy": {
7091195 "type": "registry",
7101196 "version": "6.0.0",
@@ -716,20 +1202,6 @@
7161202 "prelude"
7171203 ]
7181204 },
719− "lcg": {
720− "type": "registry",
721− "version": "4.0.0",
722− "integrity": "sha256-bUYyfXYmjTn7zSpj2hTiePywzsuD80fCjkonsrpdGOw=",
723− "dependencies": [
724− "effect",
725− "integers",
726− "maybe",
727− "numbers",
728− "partial",
729− "prelude",
730− "random"
731− ]
732− },
7331205 "lists": {
7341206 "type": "registry",
7351207 "version": "7.0.0",
@@ -825,91 +1297,6 @@
8251297 "unsafe-coerce"
8261298 ]
8271299 },
828− "node-event-emitter": {
829− "type": "registry",
830− "version": "3.0.0",
831− "integrity": "sha256-wodx71NxJBPXOaawFn01V1ByYB2vT+I3RuV2giOVKMg=",
832− "dependencies": [
833− "effect",
834− "either",
835− "functions",
836− "maybe",
837− "nullable",
838− "prelude",
839− "unsafe-coerce"
840− ]
841− },
842− "node-fs": {
843− "type": "registry",
844− "version": "9.2.0",
845− "integrity": "sha256-fxioTSm9y47WuYs9F/7nxCh/RVDCl3Kr3Hg779J4SJA=",
846− "dependencies": [
847− "aff",
848− "datetime",
849− "effect",
850− "either",
851− "enums",
852− "exceptions",
853− "functions",
854− "integers",
855− "js-date",
856− "maybe",
857− "node-buffer",
858− "node-path",
859− "node-streams",
860− "nullable",
861− "partial",
862− "prelude",
863− "strings"
864− ]
865− },
866− "node-path": {
867− "type": "registry",
868− "version": "5.0.1",
869− "integrity": "sha256-j7n/SmpVz0FOMJl4XZop3ofJ+MeIPkMNqUEUKHEL6Us=",
870− "dependencies": [
871− "effect"
872− ]
873− },
874− "node-process": {
875− "type": "registry",
876− "version": "11.2.0",
877− "integrity": "sha256-onzKeNmaeYfN3HSDPqSFTrrP2Yl9tnVsfKKhnf2plxM=",
878− "dependencies": [
879− "effect",
880− "exceptions",
881− "foreign",
882− "foreign-object",
883− "maybe",
884− "node-event-emitter",
885− "node-streams",
886− "nullable",
887− "posix-types",
888− "prelude",
889− "strings"
890− ]
891− },
892− "node-streams": {
893− "type": "registry",
894− "version": "9.0.1",
895− "integrity": "sha256-yuVGm/R/tBr4Nphi+a1a984Z0fkmN9Q5wWtSmbmJTpA=",
896− "dependencies": [
897− "aff",
898− "arrays",
899− "effect",
900− "either",
901− "exceptions",
902− "maybe",
903− "node-buffer",
904− "node-event-emitter",
905− "nullable",
906− "prelude",
907− "refs",
908− "st",
909− "tailrec",
910− "unsafe-coerce"
911− ]
912− },
9131300 "nonempty": {
9141301 "type": "registry",
9151302 "version": "7.0.0",
@@ -953,56 +1340,6 @@
9531340 "prelude"
9541341 ]
9551342 },
956− "open-memoize": {
957− "type": "registry",
958− "version": "6.2.0",
959− "integrity": "sha256-yJLuVYX8FocHIJ+6q7efH/Pj4S+e1Y57DZ5Dn7qf4ww=",
960− "dependencies": [
961− "either",
962− "integers",
963− "lazy",
964− "lists",
965− "maybe",
966− "partial",
967− "prelude",
968− "strings",
969− "tuples"
970− ]
971− },
972− "optparse": {
973− "type": "registry",
974− "version": "5.0.1",
975− "integrity": "sha256-2btDjHPRR0uujUQJuZAN9AzvVoXe7yG6IwnY5evERhQ=",
976− "dependencies": [
977− "arrays",
978− "bifunctors",
979− "control",
980− "effect",
981− "either",
982− "enums",
983− "exists",
984− "exitcodes",
985− "foldable-traversable",
986− "free",
987− "integers",
988− "lazy",
989− "lists",
990− "maybe",
991− "newtype",
992− "node-buffer",
993− "node-process",
994− "node-streams",
995− "nonempty",
996− "numbers",
997− "open-memoize",
998− "partial",
999− "prelude",
1000− "strings",
1001− "tailrec",
1002− "transformers",
1003− "tuples"
1004− ]
1005− },
10061343 "ordered-collections": {
10071344 "type": "registry",
10081345 "version": "3.2.0",
@@ -1078,16 +1415,6 @@
10781415 "tuples"
10791416 ]
10801417 },
1081− "posix-types": {
1082− "type": "registry",
1083− "version": "6.0.0",
1084− "integrity": "sha256-Je5jG439F/SFO/IvVevZnWVxDIonrehlKuJkefP2lHE=",
1085− "dependencies": [
1086− "maybe",
1087− "newtype",
1088− "prelude"
1089− ]
1090− },
10911418 "prelude": {
10921419 "type": "registry",
10931420 "version": "6.0.2",
@@ -1109,49 +1436,6 @@
11091436 "tuples"
11101437 ]
11111438 },
1112− "quickcheck": {
1113− "type": "registry",
1114− "version": "8.0.1",
1115− "integrity": "sha256-SfW+maesCt0jnnFhfrgJZGPcWLj8oFcGbcDIVklmdzc=",
1116− "dependencies": [
1117− "arrays",
1118− "console",
1119− "control",
1120− "effect",
1121− "either",
1122− "enums",
1123− "exceptions",
1124− "foldable-traversable",
1125− "gen",
1126− "identity",
1127− "integers",
1128− "lazy",
1129− "lcg",
1130− "lists",
1131− "maybe",
1132− "newtype",
1133− "nonempty",
1134− "numbers",
1135− "partial",
1136− "prelude",
1137− "record",
1138− "st",
1139− "strings",
1140− "tailrec",
1141− "transformers",
1142− "tuples"
1143− ]
1144− },
1145− "random": {
1146− "type": "registry",
1147− "version": "6.0.0",
1148− "integrity": "sha256-7sZRo6P9UIiXDZ9s2QL9T8h3wlIwLdQrR4+oZKOijQw=",
1149− "dependencies": [
1150− "effect",
1151− "integers",
1152− "prelude"
1153− ]
1154− },
11551439 "record": {
11561440 "type": "registry",
11571441 "version": "4.0.0",
@@ -1215,8 +1499,8 @@
12151499 },
12161500 "spec": {
12171501 "type": "registry",
1218− "version": "8.1.2",
1219− "integrity": "sha256-klPPJ6lBQI1CGOAdLJ+ZoGRt8XZippkTKjn53BrSkL0=",
1502+ "version": "8.1.1",
1503+ "integrity": "sha256-b6qjDb7lUJGLV+WEoywLu6BCXC4zGpfbN43mTL+vBeI=",
12201504 "dependencies": [
12211505 "aff",
12221506 "ansi",
@@ -1247,67 +1531,6 @@
12471531 "tuples"
12481532 ]
12491533 },
1250− "spec-discovery": {
1251− "type": "registry",
1252− "version": "8.4.1",
1253− "integrity": "sha256-guvY+NtSOBQmM+HqNJ7woX6taN8hS1wEz7JDmdKF11E=",
1254− "dependencies": [
1255− "aff",
1256− "effect",
1257− "foldable-traversable",
1258− "prelude",
1259− "spec",
1260− "spec-node"
1261− ]
1262− },
1263− "spec-node": {
1264− "type": "registry",
1265− "version": "0.0.2",
1266− "integrity": "sha256-HDW02z3Mj+daux0j9487ApDBX32/gX0od6YR4zvDRZI=",
1267− "dependencies": [
1268− "aff",
1269− "argonaut-codecs",
1270− "argonaut-core",
1271− "arrays",
1272− "control",
1273− "datetime",
1274− "effect",
1275− "either",
1276− "foldable-traversable",
1277− "identity",
1278− "integers",
1279− "maybe",
1280− "newtype",
1281− "node-buffer",
1282− "node-fs",
1283− "node-process",
1284− "now",
1285− "numbers",
1286− "optparse",
1287− "ordered-collections",
1288− "partial",
1289− "prelude",
1290− "spec",
1291− "strings",
1292− "tuples"
1293− ]
1294− },
1295− "spec-quickcheck": {
1296− "type": "registry",
1297− "version": "5.0.2",
1298− "integrity": "sha256-pmnLu9e9Yk+Yx4hi71XwbyuPS27ya4hjTOKk2KFK/Ns=",
1299− "dependencies": [
1300− "aff",
1301− "arrays",
1302− "effect",
1303− "foldable-traversable",
1304− "lists",
1305− "maybe",
1306− "prelude",
1307− "quickcheck",
1308− "tuples"
1309− ]
1310− },
13111534 "st": {
13121535 "type": "registry",
13131536 "version": "6.2.0",
@@ -1488,6 +1711,41 @@
14881711 "unsafe-coerce"
14891712 ]
14901713 },
1714+ "vitest": {
1715+ "type": "registry",
1716+ "version": "2.0.0",
1717+ "integrity": "sha256-GkH1eyMaDyDUSC7kRSJrrD6k397m/SKQcKauVRmZzRI=",
1718+ "dependencies": [
1719+ "aff",
1720+ "effect",
1721+ "either",
1722+ "exceptions",
1723+ "js-promise",
1724+ "js-promise-aff",
1725+ "maybe",
1726+ "nullable",
1727+ "prelude",
1728+ "transformers",
1729+ "unsafe-coerce",
1730+ "web-dom",
1731+ "web-html"
1732+ ]
1733+ },
1734+ "web-dom": {
1735+ "type": "registry",
1736+ "version": "6.0.0",
1737+ "integrity": "sha256-rVeqkxChkjqisDvOujy9BMEgGqJkwX3t6g21bXfiG5o=",
1738+ "dependencies": [
1739+ "effect",
1740+ "enums",
1741+ "maybe",
1742+ "newtype",
1743+ "nullable",
1744+ "prelude",
1745+ "unsafe-coerce",
1746+ "web-events"
1747+ ]
1748+ },
14911749 "web-events": {
14921750 "type": "registry",
14931751 "version": "4.0.0",
@@ -1527,6 +1785,41 @@
15271785 "web-events"
15281786 ]
15291787 },
1788+ "web-html": {
1789+ "type": "registry",
1790+ "version": "4.1.1",
1791+ "integrity": "sha256-yOuZJGUxFrivHKpo/jKuKzFZaq6PPPE64ftSysXFssk=",
1792+ "dependencies": [
1793+ "effect",
1794+ "enums",
1795+ "foreign",
1796+ "functions",
1797+ "js-date",
1798+ "maybe",
1799+ "media-types",
1800+ "newtype",
1801+ "nullable",
1802+ "prelude",
1803+ "unsafe-coerce",
1804+ "web-dom",
1805+ "web-events",
1806+ "web-file",
1807+ "web-storage"
1808+ ]
1809+ },
1810+ "web-storage": {
1811+ "type": "registry",
1812+ "version": "5.0.0",
1813+ "integrity": "sha256-YJfos55oeKtBStNfoWfeflSy8j+4IDym5eaZtIY7YM4=",
1814+ "dependencies": [
1815+ "effect",
1816+ "maybe",
1817+ "nullable",
1818+ "prelude",
1819+ "unsafe-coerce",
1820+ "web-events"
1821+ ]
1822+ },
15301823 "web-streams": {
15311824 "type": "registry",
15321825 "version": "4.0.0",
@@ -1544,8 +1837,8 @@
15441837 },
15451838 "yoga-fastify": {
15461839 "type": "registry",
1547− "version": "0.5.4",
1548− "integrity": "sha256-YenjePRdiApMPpcvkXlzre7c4fIuvk5T19Z2uEAWhR4=",
1840+ "version": "0.2.3",
1841+ "integrity": "sha256-150ocqfhYCc38HeChWbJXie0wn2bjKpORkZFQCRxICo=",
15491842 "dependencies": [
15501843 "aff",
15511844 "arrays",
@@ -1571,27 +1864,25 @@
15711864 },
15721865 "yoga-fastify-om": {
15731866 "type": "registry",
1574− "version": "0.4.7",
1575− "integrity": "sha256-KGQ7lHI7pcSiEQoEq6j6K+3XilcPy8wQ/M9C1y+uaEk=",
1867+ "version": "0.1.0",
1868+ "integrity": "sha256-cgkNDv2BP29DRpw/kY2p7vXhVUofDBZ7lkm1LHsE1Kg=",
15761869 "dependencies": [
15771870 "aff",
15781871 "console",
15791872 "effect",
15801873 "either",
15811874 "exceptions",
1875+ "foldable-traversable",
15821876 "foreign",
15831877 "foreign-object",
15841878 "integers",
15851879 "maybe",
15861880 "node-buffer",
1587− "numbers",
15881881 "prelude",
15891882 "record",
15901883 "refs",
15911884 "routing-duplex",
1592− "strings",
15931885 "transformers",
1594− "typelevel-prelude",
15951886 "unsafe-coerce",
15961887 "variant",
15971888 "yoga-fastify",
@@ -1604,25 +1895,19 @@
16041895 },
16051896 "yoga-http-api": {
16061897 "type": "registry",
1607− "version": "0.3.4",
1608− "integrity": "sha256-DSIJ8YWr5vB5IMpyaBALM+fx57lz3AAAR0MGtfHeWxo=",
1898+ "version": "0.1.4",
1899+ "integrity": "sha256-kFnDMt0KGsi+84RKgmLQEr9HB+XHOgCeLMsFatCeCiY=",
16091900 "dependencies": [
16101901 "aff",
1611− "arraybuffer-types",
16121902 "arrays",
1613− "effect",
16141903 "either",
16151904 "foldable-traversable",
16161905 "foreign",
16171906 "foreign-object",
16181907 "identity",
16191908 "integers",
1620− "js-promise",
1621− "js-promise-aff",
16221909 "maybe",
16231910 "newtype",
1624− "nullable",
1625− "numbers",
16261911 "prelude",
16271912 "record",
16281913 "strings",
@@ -1636,8 +1921,8 @@
16361921 },
16371922 "yoga-json": {
16381923 "type": "registry",
1639− "version": "5.2.1",
1640− "integrity": "sha256-12I7qIf27oKV/D/YRcHtGpN8n7GQ0Bm3uzvI4ZjOQrs=",
1924+ "version": "5.2.0",
1925+ "integrity": "sha256-M+kkz6E1Gf+C4bKnhqoBFIoVcc74loi3rLULePbB204=",
16411926 "dependencies": [
16421927 "arrays",
16431928 "bifunctors",
@@ -1675,11 +1960,10 @@
16751960 },
16761961 "yoga-om": {
16771962 "type": "registry",
1678− "version": "1.5.2",
1679− "integrity": "sha256-2ys14BF6NPgzFDFUIFk0EDxmqkA8+yghIA9TXHlOmE0=",
1963+ "version": "1.2.2",
1964+ "integrity": "sha256-uDV3/o8mUOTIgSEQFqIATPyladzL8BcjURvfS901hgI=",
16801965 "dependencies": [
16811966 "aff",
1682− "aff-retry",
16831967 "avar",
16841968 "console",
16851969 "control",
@@ -1688,14 +1972,12 @@
16881972 "either",
16891973 "exceptions",
16901974 "functions",
1691− "heterogeneous",
16921975 "maybe",
16931976 "newtype",
16941977 "parallel",
16951978 "prelude",
16961979 "record",
16971980 "record-studio",
1698− "refs",
16991981 "tailrec",
17001982 "transformers",
17011983 "tuples",
@@ -1707,35 +1989,23 @@
17071989 },
17081990 "yoga-om-layer": {
17091991 "type": "registry",
1710− "version": "1.2.2",
1711− "integrity": "sha256-Usw2ZysteSYNkEG3CDmVVXUk7cgr1mntkmhbR33nF6M=",
1992+ "version": "1.0.6",
1993+ "integrity": "sha256-RrCaTvkiMwCFarWKbu0Dkq+yFz4hDM7x18gZok2OxV4=",
17121994 "dependencies": [
17131995 "aff",
1714− "aff-retry",
1715− "arrays",
1716− "effect",
1717− "exceptions",
1718− "foldable-traversable",
1719− "maybe",
1720− "nullable",
1721− "ordered-collections",
1722− "parallel",
1996+ "newtype",
17231997 "prelude",
17241998 "record",
17251999 "record-studio",
1726− "refs",
1727− "transformers",
17282000 "type-equality",
1729− "typelevel-prelude",
17302001 "unsafe-coerce",
1731− "variant",
17322002 "yoga-om"
17332003 ]
17342004 },
17352005 "yoga-om-strom": {
17362006 "type": "registry",
1737− "version": "0.4.3",
1738− "integrity": "sha256-dxj1zveEES6lF+iiMHx+qOhUG3zmiJJnlNpwiAfUBRE=",
2007+ "version": "0.2.0",
2008+ "integrity": "sha256-WMrhLQ71RFObD4Vv/X5qef+uIkJ4zgCv/K8l986g1wE=",
17392009 "dependencies": [
17402010 "aff",
17412011 "arrays",
@@ -1765,8 +2035,8 @@
17652035 },
17662036 "yoga-options": {
17672037 "type": "registry",
1768− "version": "0.1.1",
1769− "integrity": "sha256-INtZKI2rw7KcDikzmyXlglsEujSMqHT5jz3ho9dVYC8=",
2038+ "version": "0.1.0",
2039+ "integrity": "sha256-8u9uwmxqwNX7bmA7U+hQ60ZnZxLzvlyYTVNXSIWeXv4=",
17702040 "dependencies": [
17712041 "identity",
17722042 "literals",
spago.yaml+22−25
@@ -1,8 +1,20 @@
1−workspace: {}
1+workspace:
2+ packageSet:
3+ registry: 73.0.0
4+ extraPackages:
5+ yoga-fastify-om: "0.1.0"
6+ yoga-fastify: "0.2.3"
7+ yoga-http-api: "0.1.4"
8+ yoga-om: "1.2.2"
9+ yoga-om-layer: "1.0.6"
10+ yoga-om-strom: "0.2.0"
11+ yoga-options: "0.1.0"
12+ yoga-json: "5.2.0"
13+
214package:
315 name: yoga-fetch-om
416 publish:
5− version: 0.8.1
17+ version: 0.3.0
618 license: MIT
719 location:
820 githubOwner: rowtype-yoga
@@ -10,49 +22,34 @@ package:
1022 include:
1123 - LICENSE
1224 - README.md
25+ - CHANGELOG.md
1326 exclude:
1427 - .github
1528 dependencies:
16− - aff: ">=8.0.0 <9.0.0"
17− - arraybuffer-types: ">=3.0.2 <4.0.0"
29+ - aff: ">=7.0.0 <9.0.0"
1830 - arrays: ">=7.3.0 <8.0.0"
1931 - effect: ">=4.0.0 <5.0.0"
2032 - either: ">=6.1.0 <7.0.0"
2133 - exceptions: ">=6.1.0 <7.0.0"
22− - http-methods: ">=6.1.0 <7.0.0"
34+ - http-methods: ">=6.0.0 <7.0.0"
2335 - js-fetch: ">=0.2.1 <0.3.0"
2436 - js-promise-aff: ">=1.0.0 <2.0.0"
2537 - maybe: ">=6.0.0 <7.0.0"
2638 - prelude: ">=6.0.2 <7.0.0"
2739 - record: ">=4.0.0 <5.0.0"
2840 - strings: ">=6.0.1 <7.0.0"
29− - tailrec: ">=6.1.0 <7.0.0"
3041 - transformers: ">=6.1.0 <7.0.0"
3142 - tuples: ">=7.0.0 <8.0.0"
3243 - typelevel-prelude: ">=7.0.0 <8.0.0"
3344 - unsafe-coerce: ">=6.0.0 <7.0.0"
3445 - variant: ">=8.0.0 <9.0.0"
35− - yoga-http-api: ">=0.3.4 <0.4.0"
36− - yoga-json: ">=5.2.1 <6.0.0"
37− - yoga-om: ">=1.5.2 <2.0.0"
38− - yoga-om-strom: ">=0.4.3 <0.5.0"
46+ - yoga-http-api: ">=0.1.0 <0.2.0"
47+ - yoga-json: ">=5.2.0 <6.0.0"
48+ - yoga-om: ">=1.2.2 <2.0.0"
3949 test:
4050 main: Test.Main
4151 dependencies:
42− - enums
43− - foldable-traversable
44− - foreign
45− - foreign-object
46− - integers
47− - js-promise
48− - partial
49− - yoga-fastify
52+ - vitest: ">=1.0.0 <3.0.0"
5053 - console: ">=6.0.0 <7.0.0"
51− - js-uri: ">=3.1.0 <4.0.0"
52− - justifill: ">=0.5.0 <0.6.0"
53− - quickcheck: ">=8.0.1 <9.0.0"
54+ - yoga-fastify-om: ">=0.1.0 <0.2.0"
5455 - refs: ">=6.0.0 <7.0.0"
55− - spec: ">=8.0.0 <9.0.0"
56− - spec-discovery: ">=8.0.0 <9.0.0"
57− - spec-quickcheck: ">=5.0.2 <6.0.0"
58− - yoga-fastify-om: ">=0.4.7 <0.5.0"
src/Yoga/Fetch/Om.jsdeleted−1
@@ -1 +0,0 @@
1−export const mergeRecords = (left) => (right) => ({ ...right, ...left });
src/Yoga/Fetch/Om.purs+38−186
@@ -1,8 +1,5 @@
11module Yoga.Fetch.Om
22 ( client
3− , clientWith
4− , ClientInputs
5− , ClientWithConfig
63 , deriveClient
74 , class DeriveClient
85 , deriveClientImpl
@@ -10,25 +7,17 @@ module Yoga.Fetch.Om
107 , deriveClientRL
118 , class DeriveClientFn
129 , deriveClientFn
13− , class DeriveClientFnWith
14− , deriveClientFnWith
15− , class DeriveClientWith
16− , deriveClientWithImpl
17− , class DeriveClientWithRL
18− , deriveClientWithRL
1910 , class RecordRow
2011 , class ToHeaders
2112 , toHeaders
2213 , module Yoga.HTTP.API.Route
2314 , module Yoga.HTTP.API.Path
24− , module Yoga.Fetch.Om.StreamDecode
2515 , module Yoga.Fetch.Om.Simple
2616 ) where
2717
2818import Prelude
2919
30−import Data.Symbol (class IsSymbol, reflectSymbol)
31−import Data.Tuple.Nested ((/\))
20+import Data.Symbol (class IsSymbol)
3221import Data.Variant (Variant)
3322import Prim.Row as Row
3423import Prim.RowList (class RowToList, RowList)
@@ -38,31 +27,19 @@ import Type.Proxy (Proxy(..))
3827import Unsafe.Coerce (unsafeCoerce)
3928import JS.Fetch.Headers (Headers)
4029import JS.Fetch.Headers as Headers
30+import Type.Row.Homogeneous (class Homogeneous)
4131import Yoga.Fetch.Om.BuildUrl (class BuildUrl, buildUrl)
4232import Yoga.Fetch.Om.Simple (class DecodeResponse, decodeResponse, FetchError, FetchResponse, get, getWithHeaders, delete, deleteWithHeaders, delete_, post, postWithHeaders, post_, put, putWithHeaders, put_, patch, patchWithHeaders, patch_)
43−import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
4433import Yoga.Fetch.Om.ClientFunction (class BuildClientFn, class CheckBodyIsUnit, buildClientFn)
45−import Yoga.Fetch.Om.ExtractParams (class ExtractRequestBody, class ExtractBodyEncoding, class ExtractRequestHeaders)
46−import Yoga.Fetch.Om.MakeRequest (class MakeRequest, class BodyEncoding, makeRequest, encodingContentType, encodeBody)
34+import Yoga.Fetch.Om.ExtractParams (class ExtractRequestBody, class ExtractRequestHeaders)
35+import Yoga.Fetch.Om.MakeRequest (class MakeRequest, class SerializeBody, makeRequest, serializeBody)
4736import Yoga.Fetch.Om.ParseResponse (class ParseResponse, parseResponse)
4837import Yoga.Fetch.Om.SplitResponses (class SplitResponses)
4938import Yoga.Fetch.Om.Variant (class VariantOrValue, variantOrValue)
5039import Yoga.HTTP.API.Path (Path, Root, Lit, Capture, PathCons, Param, QueryParams, Required, type (/), type (:), type (:?), class PathPattern)
51−import Yoga.HTTP.API.Route (Route(..), GET, POST, PUT, DELETE, PATCH, QUERY, Response(..), JSON, FormData, PlainText, Streaming, NoBody, class HeaderValue, printHeader)
40+import Yoga.HTTP.API.Route (Route(..), GET, POST, PUT, DELETE, PATCH, Response(..), JSON, FormData, NoBody)
5241import Yoga.HTTP.API.Route.Handler (class SegmentPathParams, class SegmentQueryParams)
53−import Yoga.Om (Om, toOm)
54−
55−type ClientInputs providedHeaders providedQuery =
56− { headers :: Record providedHeaders
57− , query :: Record providedQuery
58− }
59−
60−type ClientWithConfig ctx err providedHeaders providedQuery =
61− { baseUrl :: Om { | ctx } err String
62− , provide :: Om { | ctx } err (ClientInputs providedHeaders providedQuery)
63− }
64−
65−foreign import mergeRecords :: forall left right out. Record left -> Record right -> Record out
42+import Yoga.Om (Om, fromAff)
6643
6744-- | Extract row type from Record type
6845class RecordRow :: Type -> Row Type -> Constraint
@@ -77,38 +54,24 @@ class ToHeaders headersRL headers where
7754instance ToHeaders RL.Nil headers where
7855 toHeaders _ _ = Headers.empty
7956
80−else instance
81− ( IsSymbol name
82− , HeaderValue ty
83− , Row.Cons name ty tailRow headers
84− , Row.Lacks name tailRow
85− , ToHeaders tail tailRow
86− ) =>
87− ToHeaders (RL.Cons name ty tail) headers where
88− toHeaders _ headers = Headers.fromFoldable ([ headerName /\ headerValue ] <> Headers.toArray rest)
89− where
90− headerName = reflectSymbol (Proxy :: Proxy name)
91− headerValue = printHeader (Record.get (Proxy :: Proxy name) headers)
92− rest = toHeaders (Proxy :: Proxy tail) (unsafeCoerce headers :: Record tailRow)
57+else instance Homogeneous headers String => ToHeaders headersRL headers where
58+ toHeaders _ = Headers.fromRecord
9359
94−class DeriveClientFn
95− :: forall k1. Type -> k1 -> Type -> Row Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
60+class DeriveClientFn :: forall k1. Type -> k1 -> Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
9661class
97− DeriveClientFn method segments request response ctx extraErr routeErrors result fn
98− | method segments request response -> routeErrors result
99− , method segments request response ctx extraErr -> fn where
62+ DeriveClientFn method segments request response routeErrors successRow result fn
63+ | method segments request response -> routeErrors successRow result fn where
10064 deriveClientFn :: String -> Proxy (Route method segments request response) -> fn
10165
10266instance
10367 ( SegmentPathParams segments pathParams
10468 , SegmentQueryParams segments queryParams
10569 , ExtractRequestBody request body
106− , ExtractBodyEncoding request encoding
10770 , ExtractRequestHeaders request headers
10871 , PathPattern segments
10972 , BuildUrl segments pathParams queryParams
11073 , MakeRequest method
111− , BodyEncoding encoding body
74+ , SerializeBody body
11275 , SplitResponses response successRow routeErrors
11376 , ParseResponse routeErrors successRow
11477 , RowToList successRow successRL
@@ -119,182 +82,71 @@ instance
11982 , RowToList headers headersRL
12083 , ToHeaders headersRL headers
12184 , CheckBodyIsUnit body bodyFlag
122− , Row.Union routeErrors extraErr errRow
123− , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers (Record pathQuery) { | ctx } errRow result fn
85+ , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers routeErrors result fn
12486 ) =>
125− DeriveClientFn method segments request response ctx extraErr routeErrors result fn where
87+ DeriveClientFn method segments request response routeErrors successRow result fn where
12688 deriveClientFn baseUrl _ =
127− buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) identity impl
89+ buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) impl
12890 where
129− impl :: Record pathQuery -> Record headers -> body -> Om { | ctx } errRow result
130− impl pathQueryRec headersRec bodyVal = widenOm do
91+ impl :: Record pathQuery -> Record headers -> body -> Om (Record ()) routeErrors result
92+ impl pathQueryRec headersRec bodyVal = do
13193 let url = buildUrl baseUrl (Proxy :: _ segments) pathParamsRec queryParamsRec
13294 let hdrs = toHeaders (Proxy :: _ headersRL) headersRec
133− let ct = encodingContentType (Proxy :: _ encoding)
134− fetchResp <- makeRequest (Proxy :: _ method) url hdrs ct (encodeBody @encoding bodyVal) # toOm
95+ fetchResp <- makeRequest (Proxy :: _ method) url hdrs (serializeBody bodyVal) # fromAff
13596 variant <- parseResponse fetchResp :: Om (Record ()) routeErrors (Variant successRow)
13697 variantOrValue (Proxy :: _ successRL) variant # pure
13798 where
13899 pathParamsRec = unsafeCoerce pathQueryRec :: Record pathParams
139100 queryParamsRec = unsafeCoerce pathQueryRec :: Record queryParams
140101
141− widenOm :: Om (Record ()) routeErrors ~> Om { | ctx } errRow
142− widenOm = unsafeCoerce
143−
144−class DeriveClientFnWith
145− :: forall k1. Type -> k1 -> Type -> Row Type -> Row Type -> Row Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
146−class
147− DeriveClientFnWith method segments request response providedHeaders providedQuery ctx extraErr routeErrors result fn
148− | method segments request response -> routeErrors result
149− , method segments request response providedHeaders providedQuery ctx extraErr -> fn where
150− deriveClientFnWith
151− :: ClientWithConfig ctx extraErr providedHeaders providedQuery
152− -> Proxy (Route method segments request response)
153− -> fn
154−
155−instance
156− ( SegmentPathParams segments pathParams
157− , SegmentQueryParams segments queryParams
158− , ExtractRequestBody request body
159− , ExtractBodyEncoding request encoding
160− , ExtractRequestHeaders request headers
161− , PathPattern segments
162− , BuildUrl segments pathParams queryParams
163− , MakeRequest method
164− , BodyEncoding encoding body
165− , SplitResponses response successRow routeErrors
166− , Row.Union pathParams queryParams pathQuery
167− , Row.Nub pathQuery pathQuery
168− , ParseResponse routeErrors successRow
169− , RowToList successRow successRL
170− , VariantOrValue successRL successRow result
171− , Row.Union routeErrors extraErr errRow
172− , Row.Union providedQuery remainingQuery queryParams
173− , Row.Union pathParams remainingQuery callerPathQuery
174− , Row.Nub callerPathQuery callerPathQuery
175− , RowToList callerPathQuery callerPathQueryRL
176− , RowToList headers headersRL
177− , ToHeaders headersRL headers
178− , Row.Union providedHeaders remainingHeaders headers
179− , Row.Nub headers headers
180− , RowToList remainingHeaders remainingHeadersRL
181− , CheckBodyIsUnit body bodyFlag
182− , BuildClientFn callerPathQueryRL remainingHeadersRL bodyFlag body callerPathQuery remainingHeaders (Record callerPathQuery) { | ctx } errRow result fn
183− ) =>
184− DeriveClientFnWith method segments request response providedHeaders providedQuery ctx extraErr routeErrors result fn where
185− deriveClientFnWith config _ =
186− buildClientFn (Proxy :: _ callerPathQueryRL) (Proxy :: _ remainingHeadersRL) (Proxy :: _ bodyFlag) identity impl
187− where
188− impl :: Record callerPathQuery -> Record remainingHeaders -> body -> Om { | ctx } errRow result
189− impl pathQueryRec headersRec bodyVal = do
190− baseUrl <- widenExtra config.baseUrl
191− provided <- widenExtra config.provide
192− let pathParamsRec = unsafeCoerce pathQueryRec :: Record pathParams
193− let queryParamsRec = mergeRecords provided.query (unsafeCoerce pathQueryRec :: Record remainingQuery) :: Record queryParams
194− let fullHeaders = mergeRecords provided.headers headersRec :: Record headers
195− let url = buildUrl baseUrl (Proxy :: _ segments) pathParamsRec queryParamsRec
196− let hdrs = toHeaders (Proxy :: _ headersRL) fullHeaders
197− let ct = encodingContentType (Proxy :: _ encoding)
198− fetchResp <- makeRequest (Proxy :: _ method) url hdrs ct (encodeBody @encoding bodyVal) # toOm # widenRoute
199− variant <- parseResponse fetchResp # widenRoute :: Om { | ctx } errRow (Variant successRow)
200− variantOrValue (Proxy :: _ successRL) variant # pure
201− where
202− widenExtra :: Om { | ctx } extraErr ~> Om { | ctx } errRow
203− widenExtra = unsafeCoerce
204−
205− widenRoute :: Om (Record ()) routeErrors ~> Om { | ctx } errRow
206− widenRoute = unsafeCoerce
207−
208−class DeriveClient :: Row Type -> Row Type -> Row Type -> Row Type -> Constraint
209−class DeriveClient ctx extraErr routesRow clientsRow | routesRow ctx extraErr -> clientsRow where
102+class DeriveClient :: Row Type -> Row Type -> Constraint
103+class DeriveClient routesRow clientsRow | routesRow -> clientsRow where
210104 deriveClientImpl :: String -> Proxy (Record routesRow) -> Record clientsRow
211105
212−-- | Derive API client functions from route definitions using VTA.
213−-- | Generated fields use ordinary function application. Path parameters are
214−-- | plain records; optional query parameters are explicit `Maybe` fields.
106+-- | Derive API client functions from route definitions using VTA
215107-- |
216108-- | ```purescript
217109-- | type UserAPI = { getUser :: Route ... }
218110-- | api = client @UserAPI "https://api.example.com"
219−-- | user <- api.getUser { id: 42 }
220−-- | users <- api.listUsers { limit: Just 10, offset: Nothing }
221−-- | health <- api.health
222111-- | ```
223−client :: forall @routes routesRow ctx extraErr clientsRow. RecordRow routes routesRow => DeriveClient ctx extraErr routesRow clientsRow => String -> Record clientsRow
224−client baseUrl = deriveClientImpl @ctx @extraErr baseUrl (Proxy :: _ { | routesRow })
112+client :: forall @routes routesRow clientsRow polyClientsRow. RecordRow routes routesRow => DeriveClient routesRow clientsRow => String -> Record polyClientsRow
113+client baseUrl = polymorphic (deriveClientImpl baseUrl (Proxy :: _ { | routesRow }) :: Record clientsRow)
225114
226115-- | Deprecated: Use `client` with VTA instead
227116-- |
228117-- | ```purescript
229118-- | api = deriveClient @UserAPI "https://api.example.com"
230119-- | ```
231−deriveClient :: forall @routesRow ctx extraErr clientsRow. DeriveClient ctx extraErr routesRow clientsRow => String -> Record clientsRow
232−deriveClient baseUrl = deriveClientImpl @ctx @extraErr baseUrl (Proxy :: _ { | routesRow })
233−
234−clientWith
235− :: forall @routes @providedHeaders @providedQuery routesRow ctx extraErr clientsRow
236− . RecordRow routes routesRow
237− => DeriveClientWith providedHeaders providedQuery ctx extraErr routesRow clientsRow
238− => ClientWithConfig ctx extraErr providedHeaders providedQuery
239− -> Record clientsRow
240−clientWith config = deriveClientWithImpl @providedHeaders @providedQuery @ctx @extraErr config (Proxy :: _ { | routesRow })
241−
242−instance
243− ( RowToList routesRow rl
244− , DeriveClientRL ctx extraErr rl () clientsRow
245− ) =>
246− DeriveClient ctx extraErr routesRow clientsRow where
247− deriveClientImpl baseUrl _ = deriveClientRL @ctx @extraErr baseUrl (Proxy :: _ rl) {}
120+deriveClient :: forall @routesRow clientsRow polyClientsRow. DeriveClient routesRow clientsRow => String -> Record polyClientsRow
121+deriveClient baseUrl = polymorphic (deriveClientImpl baseUrl (Proxy :: _ { | routesRow }) :: Record clientsRow)
248122
249−class DeriveClientWith :: Row Type -> Row Type -> Row Type -> Row Type -> Row Type -> Row Type -> Constraint
250−class DeriveClientWith providedHeaders providedQuery ctx extraErr routesRow clientsRow | providedHeaders providedQuery routesRow ctx extraErr -> clientsRow where
251− deriveClientWithImpl :: ClientWithConfig ctx extraErr providedHeaders providedQuery -> Proxy (Record routesRow) -> Record clientsRow
123+polymorphic :: forall a b. a -> b
124+polymorphic = unsafeCoerce
252125
253126instance
254127 ( RowToList routesRow rl
255− , DeriveClientWithRL providedHeaders providedQuery ctx extraErr rl () clientsRow
256− ) =>
257− DeriveClientWith providedHeaders providedQuery ctx extraErr routesRow clientsRow where
258− deriveClientWithImpl config _ = deriveClientWithRL @providedHeaders @providedQuery @ctx @extraErr config (Proxy :: _ rl) {}
259−
260−class DeriveClientWithRL :: Row Type -> Row Type -> Row Type -> Row Type -> RowList Type -> Row Type -> Row Type -> Constraint
261−class DeriveClientWithRL providedHeaders providedQuery ctx extraErr rl acc out | providedHeaders providedQuery ctx extraErr rl acc -> out where
262− deriveClientWithRL :: ClientWithConfig ctx extraErr providedHeaders providedQuery -> Proxy rl -> Record acc -> Record out
263−
264−instance DeriveClientWithRL providedHeaders providedQuery ctx extraErr RL.Nil acc acc where
265− deriveClientWithRL _ _ acc = acc
266−
267−instance
268− ( IsSymbol label
269− , DeriveClientFnWith method segments request response providedHeaders providedQuery ctx extraErr routeErrors result fn
270− , DeriveClientWithRL providedHeaders providedQuery ctx extraErr tail acc1 acc2
271− , Row.Cons label fn acc2 out
272− , Row.Lacks label acc2
273− , Row.Cons label (Route method segments request response) routeTail routeRow
128+ , DeriveClientRL rl () clientsRow
274129 ) =>
275− DeriveClientWithRL providedHeaders providedQuery ctx extraErr (RL.Cons label (Route method segments request response) tail) acc1 out where
276− deriveClientWithRL config _ acc = Record.insert (Proxy :: _ label) clientFn rest
277− where
278− clientFn = deriveClientFnWith @_ @_ @_ @_ @providedHeaders @providedQuery @ctx @extraErr config (Proxy :: _ (Route method segments request response))
279− rest = deriveClientWithRL @providedHeaders @providedQuery @ctx @extraErr config (Proxy :: _ tail) acc
130+ DeriveClient routesRow clientsRow where
131+ deriveClientImpl baseUrl _ = deriveClientRL baseUrl (Proxy :: _ rl) {}
280132
281−class DeriveClientRL :: Row Type -> Row Type -> RowList Type -> Row Type -> Row Type -> Constraint
282−class DeriveClientRL ctx extraErr rl acc out | ctx extraErr rl acc -> out where
133+class DeriveClientRL :: RowList Type -> Row Type -> Row Type -> Constraint
134+class DeriveClientRL rl acc out | rl acc -> out where
283135 deriveClientRL :: String -> Proxy rl -> Record acc -> Record out
284136
285−instance DeriveClientRL ctx extraErr RL.Nil acc acc where
137+instance DeriveClientRL RL.Nil acc acc where
286138 deriveClientRL _ _ acc = acc
287139
288140instance
289141 ( IsSymbol label
290− , DeriveClientFn method segments request response ctx extraErr routeErrors result fn
291− , DeriveClientRL ctx extraErr tail acc1 acc2
142+ , DeriveClientFn method segments request response routeErrors successRow result fn
143+ , DeriveClientRL tail acc1 acc2
292144 , Row.Cons label fn acc2 out
293145 , Row.Lacks label acc2
294146 , Row.Cons label (Route method segments request response) routeTail routeRow
295147 ) =>
296− DeriveClientRL ctx extraErr (RL.Cons label (Route method segments request response) tail) acc1 out where
148+ DeriveClientRL (RL.Cons label (Route method segments request response) tail) acc1 out where
297149 deriveClientRL baseUrl _ acc = Record.insert (Proxy :: _ label) clientFn rest
298150 where
299− clientFn = deriveClientFn @_ @_ @_ @_ @ctx @extraErr baseUrl (Proxy :: _ (Route method segments request response))
300− rest = deriveClientRL @ctx @extraErr baseUrl (Proxy :: _ tail) acc
151+ clientFn = deriveClientFn baseUrl (Proxy :: _ (Route method segments request response))
152+ rest = deriveClientRL baseUrl (Proxy :: _ tail) acc
src/Yoga/Fetch/Om/BuildUrl.js+2−86
@@ -1,87 +1,3 @@
1−export function encodeURIComponent_(value) {
2− try {
3− return encodeURIComponent(value);
4− } catch (error) {
5− if (!(error instanceof URIError)) throw error;
6− return encodeURIComponent(toWellFormed(value));
7− }
8−}
9−
10−function toWellFormed(value) {
11− if (typeof value.toWellFormed === "function") return value.toWellFormed();
12−
13− let result = "";
14− let sliceStart = 0;
15− for (let index = 0; index < value.length; index += 1) {
16− const codeUnit = value.charCodeAt(index);
17− const previous = index === 0 ? 0 : value.charCodeAt(index - 1);
18− const next =
19− index + 1 === value.length ? 0 : value.charCodeAt(index + 1);
20− const loneHigh =
21− codeUnit >= 0xd800 &&
22− codeUnit <= 0xdbff &&
23− (next < 0xdc00 || next > 0xdfff);
24− const loneLow =
25− codeUnit >= 0xdc00 &&
26− codeUnit <= 0xdfff &&
27− (previous < 0xd800 || previous > 0xdbff);
28−
29− if (!loneHigh && !loneLow) continue;
30− result += value.slice(sliceStart, index) + "\ufffd";
31− sliceStart = index + 1;
32− }
33−
34− return sliceStart === 0 ? value : result + value.slice(sliceStart);
35−}
36−
37−function urlSuffixIndex(url) {
38− const queryIndex = url.indexOf("?");
39− const fragmentIndex = url.indexOf("#");
40− return Math.min(
41− queryIndex === -1 ? url.length : queryIndex,
42− fragmentIndex === -1 ? url.length : fragmentIndex,
43− );
44−}
45−
46−export function appendPath_(base) {
47− return function (path) {
48− const suffixIndex = urlSuffixIndex(base);
49− const prefix = base.slice(0, suffixIndex).replace(/\/+$/, "");
50− return prefix + path + base.slice(suffixIndex);
51− };
52−}
53−
54−
55−export function substitutePathParam_(name) {
56− const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
57− const segment = new RegExp(`(^|/):${escapedName}(?=/|[?#]|$)`, "g");
58− return function (value) {
59− const encodedValue = encodeURIComponent_(value);
60− return function (url) {
61− const suffixIndex = urlSuffixIndex(url);
62− const path = url.slice(0, suffixIndex);
63− return (
64− path.replace(segment, (_, prefix) => prefix + encodedValue) +
65− url.slice(suffixIndex)
66− );
67− };
68− };
69−}
70−
71−export function appendQueryString_(url) {
72− return function (queryString) {
73− if (queryString === "") return url;
74−
75− const fragmentIndex = url.indexOf("#");
76− const beforeFragment =
77− fragmentIndex === -1 ? url : url.slice(0, fragmentIndex);
78− const fragment = fragmentIndex === -1 ? "" : url.slice(fragmentIndex);
79− const separator = beforeFragment.includes("?")
80− ? beforeFragment.endsWith("?") || beforeFragment.endsWith("&")
81− ? ""
82− : "&"
83− : "?";
84−
85− return beforeFragment + separator + queryString + fragment;
86− };
1+export function encodeURIComponent_(s) {
2+ return encodeURIComponent(s);
873}
src/Yoga/Fetch/Om/BuildUrl.purs+10−43
@@ -19,6 +19,7 @@ import Prelude
1919
2020import Data.Array as Array
2121import Data.Maybe (Maybe(..))
22+import Data.String (Pattern(..), Replacement(..))
2223import Data.String as String
2324import Data.Symbol (class IsSymbol, reflectSymbol)
2425import Prim.Row as Row
@@ -27,13 +28,8 @@ import Prim.RowList as RL
2728import Record as Record
2829import Type.Proxy (Proxy(..))
2930import Yoga.HTTP.API.Path (class PathPattern, pathPattern)
30−import Yoga.Fetch.Om.ReadOptional (readOptional)
3131
3232foreign import encodeURIComponent_ :: String -> String
33−foreign import appendPath_ :: String -> String -> String
34−foreign import substitutePathParam_ :: String -> String -> String -> String
35−
36−foreign import appendQueryString_ :: String -> String -> String
3733
3834class BuildUrl :: forall k. k -> Row Type -> Row Type -> Constraint
3935class BuildUrl segments pathParams queryParams | segments -> pathParams queryParams where
@@ -50,7 +46,7 @@ instance
5046 pattern = pathPattern segmentsProxy
5147 withBase = case baseUrl of
5248 "" -> pattern
53− base -> appendPath_ base pattern
49+ base -> base <> pattern
5450 withPathParams = substitutePathParamsImpl (Proxy :: _ pathParams) pathParamsRec withBase
5551 withQueryParams = appendQueryParamsImpl (Proxy :: _ queryParams) queryParamsRec withPathParams
5652
@@ -85,8 +81,8 @@ instance
8581 substitutePathParamsRL _ rec url = substitutePathParamsRL (Proxy :: _ tail) rec replaced
8682 where
8783 value = Record.get (Proxy :: _ name) rec
88− name = reflectSymbol (Proxy :: _ name)
89− replaced = substitutePathParam_ name (serializeParam value) url
84+ pattern = Pattern (":" <> reflectSymbol (Proxy :: _ name))
85+ replaced = String.replaceAll pattern (Replacement (serializeParam value)) url
9086
9187class AppendQueryParams :: Row Type -> Constraint
9288class AppendQueryParams params where
@@ -100,11 +96,12 @@ instance
10096 , AppendQueryParamsRL rl params
10197 ) =>
10298 AppendQueryParams params where
103− appendQueryParamsImpl _ rec url = case pairs of
104− [] -> url
105− _ -> appendQueryString_ url (String.joinWith "&" pairs)
99+ appendQueryParamsImpl _ rec url = url <> queryString
106100 where
107101 pairs = appendQueryParamsRL (Proxy :: _ rl) rec []
102+ queryString = case pairs of
103+ [] -> ""
104+ _ -> "?" <> String.joinWith "&" pairs
108105
109106class AppendQueryParamsRL :: RowList Type -> Row Type -> Constraint
110107class AppendQueryParamsRL rl r where
@@ -113,49 +110,19 @@ class AppendQueryParamsRL rl r where
113110instance AppendQueryParamsRL RL.Nil r where
114111 appendQueryParamsRL _ _ acc = acc
115112
116−-- Maybe (Array ty): optional repeated query param — missing/empty field → no pairs
117113instance
118114 ( IsSymbol name
119115 , SerializeParam ty
120116 , AppendQueryParamsRL tail r
121− ) =>
122− AppendQueryParamsRL (RL.Cons name (Maybe (Array ty)) tail) r where
123− appendQueryParamsRL _ rec acc = appendQueryParamsRL (Proxy :: _ tail) rec acc'
124− where
125− encName = encodeURIComponent_ (reflectSymbol (Proxy :: _ name))
126− acc' = case (readOptional @name rec :: Maybe (Array ty)) of
127− Nothing -> acc
128− Just values -> acc <> map (\v -> encName <> "=" <> encodeURIComponent_ (serializeParam v)) values
129−
130−-- Maybe ty: read with undefined-awareness — missing field → skip
131−else instance
132− ( IsSymbol name
133− , SerializeParam ty
134− , AppendQueryParamsRL tail r
117+ , Row.Cons name (Maybe ty) tailRow r
135118 ) =>
136119 AppendQueryParamsRL (RL.Cons name (Maybe ty) tail) r where
137120 appendQueryParamsRL _ rec acc = appendQueryParamsRL (Proxy :: _ tail) rec acc'
138121 where
139− acc' = case (readOptional @name rec :: Maybe ty) of
122+ acc' = case Record.get (Proxy :: _ name) rec of
140123 Nothing -> acc
141124 Just value -> Array.snoc acc (encodeURIComponent_ (reflectSymbol (Proxy :: _ name)) <> "=" <> encodeURIComponent_ (serializeParam value))
142125
143−-- Array ty: emit one `name=value` pair per element (repeated keys)
144−else instance
145− ( IsSymbol name
146− , SerializeParam ty
147− , AppendQueryParamsRL tail r
148− , Row.Cons name (Array ty) tailRow r
149− ) =>
150− AppendQueryParamsRL (RL.Cons name (Array ty) tail) r where
151− appendQueryParamsRL _ rec acc =
152− appendQueryParamsRL (Proxy :: _ tail) rec (acc <> pairs)
153− where
154− encName = encodeURIComponent_ (reflectSymbol (Proxy :: _ name))
155− values = Record.get (Proxy :: _ name) rec
156− pairs = map (\v -> encName <> "=" <> encodeURIComponent_ (serializeParam v)) values
157−
158−-- bare ty (Required query param): always present
159126else instance
160127 ( IsSymbol name
161128 , SerializeParam ty
src/Yoga/Fetch/Om/ClientFunction.purs+20−37
@@ -22,18 +22,17 @@ instance CheckBodyIsUnit Unit IsUnit
2222else instance CheckBodyIsUnit body IsNotUnit
2323
2424class BuildClientFn
25− :: RowList Type -> RowList Type -> Type -> Type -> Row Type -> Row Type -> Type -> Type -> Row Type -> Type -> Type -> Constraint
25+ :: RowList Type -> RowList Type -> Type -> Type -> Row Type -> Row Type -> Row Type -> Type -> Type -> Constraint
2626class
27− BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers arg ctx errorRow result fn
27+ BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers errorRow result fn
2828 | pathQueryRL -> pathQuery
2929 , headersRL -> headers
30− , pathQueryRL headersRL bodyFlag body arg ctx errorRow result -> fn where
30+ , pathQueryRL headersRL bodyFlag body errorRow result -> fn where
3131 buildClientFn
3232 :: Proxy pathQueryRL
3333 -> Proxy headersRL
3434 -> Proxy bodyFlag
35− -> (arg -> Record pathQuery)
36− -> (Record pathQuery -> Record headers -> body -> Om ctx errorRow result)
35+ -> (Record pathQuery -> Record headers -> body -> Om (Record ()) errorRow result)
3736 -> fn
3837
3938-- No path/query, no headers, no body
@@ -44,12 +43,10 @@ instance
4443 Unit
4544 ()
4645 ()
47− arg
48− ctx
4946 errorRow
5047 result
51− (Om ctx errorRow result) where
52− buildClientFn _ _ _ _ f = f {} {} unit
48+ (Om (Record ()) errorRow result) where
49+ buildClientFn _ _ _ f = f {} {} unit
5350
5451-- No path/query, no headers, with body
5552instance
@@ -59,12 +56,10 @@ instance
5956 body
6057 ()
6158 ()
62− arg
63− ctx
6459 errorRow
6560 result
66− (body -> Om ctx errorRow result) where
67− buildClientFn _ _ _ _ f b = f {} {} b
61+ (body -> Om (Record ()) errorRow result) where
62+ buildClientFn _ _ _ f b = f {} {} b
6863
6964-- No path/query, with headers, no body
7065instance
@@ -74,12 +69,10 @@ instance
7469 Unit
7570 ()
7671 h
77− arg
78− ctx
7972 errorRow
8073 result
81− (Record h -> Om ctx errorRow result) where
82− buildClientFn _ _ _ _ f hdrs = f {} hdrs unit
74+ (Record h -> Om (Record ()) errorRow result) where
75+ buildClientFn _ _ _ f hdrs = f {} hdrs unit
8376
8477-- No path/query, with headers, with body
8578instance
@@ -89,12 +82,10 @@ instance
8982 body
9083 ()
9184 h
92− arg
93− ctx
9485 errorRow
9586 result
96− (Record h -> body -> Om ctx errorRow result) where
97− buildClientFn _ _ _ _ f hdrs b = f {} hdrs b
87+ (Record h -> body -> Om (Record ()) errorRow result) where
88+ buildClientFn _ _ _ f hdrs b = f {} hdrs b
9889
9990-- With path/query, no headers, no body
10091instance
@@ -104,12 +95,10 @@ instance
10495 Unit
10596 pq
10697 ()
107− arg
108− ctx
10998 errorRow
11099 result
111− (arg -> Om ctx errorRow result) where
112− buildClientFn _ _ _ convert f pqr = f (convert pqr) {} unit
100+ (Record pq -> Om (Record ()) errorRow result) where
101+ buildClientFn _ _ _ f pqr = f pqr {} unit
113102
114103-- With path/query, no headers, with body
115104instance
@@ -119,12 +108,10 @@ instance
119108 body
120109 pq
121110 ()
122− arg
123− ctx
124111 errorRow
125112 result
126− (arg -> body -> Om ctx errorRow result) where
127− buildClientFn _ _ _ convert f pqr b = f (convert pqr) {} b
113+ (Record pq -> body -> Om (Record ()) errorRow result) where
114+ buildClientFn _ _ _ f pqr b = f pqr {} b
128115
129116-- With path/query, with headers, no body
130117instance
@@ -134,12 +121,10 @@ instance
134121 Unit
135122 pq
136123 h
137− arg
138− ctx
139124 errorRow
140125 result
141− (arg -> Record h -> Om ctx errorRow result) where
142− buildClientFn _ _ _ convert f pqr hdrs = f (convert pqr) hdrs unit
126+ (Record pq -> Record h -> Om (Record ()) errorRow result) where
127+ buildClientFn _ _ _ f pqr hdrs = f pqr hdrs unit
143128
144129-- With path/query, with headers, with body
145130instance
@@ -149,9 +134,7 @@ instance
149134 body
150135 pq
151136 h
152− arg
153− ctx
154137 errorRow
155138 result
156− (arg -> Record h -> body -> Om ctx errorRow result) where
157− buildClientFn _ _ _ convert f pqr hdrs b = f (convert pqr) hdrs b
139+ (Record pq -> Record h -> body -> Om (Record ()) errorRow result) where
140+ buildClientFn _ _ _ f = f
src/Yoga/Fetch/Om/ExtractParams.purs+1−22
@@ -1,7 +1,6 @@
11module Yoga.Fetch.Om.ExtractParams
22 ( class ExtractRequestHeaders
33 , class ExtractRequestBody
4− , class ExtractBodyEncoding
54 , class FindBodyType
65 , class UnwrapEncoding
76 , class FindHeaders
@@ -10,8 +9,7 @@ module Yoga.Fetch.Om.ExtractParams
109import Data.Unit (Unit)
1110import Prim.RowList (class RowToList, RowList)
1211import Prim.RowList as RL
13−import Yoga.HTTP.API.Route.Encoding (JSON, FormData, NoBody, PlainText)
14−import Yoga.HTTP.API.Route.Handler (Request)
12+import Yoga.HTTP.API.Route.Encoding (JSON, FormData, NoBody)
1513
1614class ExtractRequestBody :: Type -> Type -> Constraint
1715class ExtractRequestBody request body | request -> body
@@ -22,21 +20,6 @@ instance
2220 , UnwrapEncoding encoding body
2321 ) =>
2422 ExtractRequestBody (Record row) body
25−else instance
26− ExtractRequestBody (Record row) body =>
27− ExtractRequestBody (Request (Record row)) body
28−
29−class ExtractBodyEncoding :: Type -> Type -> Constraint
30−class ExtractBodyEncoding request encoding | request -> encoding
31−
32−instance
33− ( RowToList row rl
34− , FindBodyType rl encoding
35− ) =>
36− ExtractBodyEncoding (Record row) encoding
37−else instance
38− ExtractBodyEncoding (Record row) encoding =>
39− ExtractBodyEncoding (Request (Record row)) encoding
4023
4124class ExtractRequestHeaders :: Type -> Row Type -> Constraint
4225class ExtractRequestHeaders request headers | request -> headers
@@ -46,9 +29,6 @@ instance
4629 , FindHeaders rl headers
4730 ) =>
4831 ExtractRequestHeaders (Record row) headers
49−else instance
50− ExtractRequestHeaders (Record row) headers =>
51− ExtractRequestHeaders (Request (Record row)) headers
5232
5333class FindBodyType :: RowList Type -> Type -> Constraint
5434class FindBodyType rl encoding | rl -> encoding
@@ -63,7 +43,6 @@ class UnwrapEncoding encoding body | encoding -> body
6343instance UnwrapEncoding (JSON ty) ty
6444instance UnwrapEncoding (FormData ty) ty
6545instance UnwrapEncoding NoBody Unit
66−instance UnwrapEncoding PlainText String
6746
6847class FindHeaders :: RowList Type -> Row Type -> Constraint
6948class FindHeaders rl headers | rl -> headers
src/Yoga/Fetch/Om/MakeRequest.jsdeleted−1
@@ -1 +0,0 @@
1−export const toUrlEncoded = (obj) => new URLSearchParams(obj).toString();
src/Yoga/Fetch/Om/MakeRequest.purs+11−31
@@ -2,9 +2,8 @@ module Yoga.Fetch.Om.MakeRequest
22 ( class MakeRequest
33 , httpMethod
44 , makeRequest
5− , class BodyEncoding
6− , encodingContentType
7− , encodeBody
5+ , class SerializeBody
6+ , serializeBody
87 ) where
98
109import Prelude
@@ -31,7 +30,6 @@ import JS.Fetch.Duplex as Duplex
3130import JS.Fetch.Response (Response) as FetchResponse
3231import Promise.Aff as Promise
3332import Type.Proxy (Proxy)
34−import Yoga.HTTP.API.Route.Encoding (JSON, FormData, PlainText, NoBody)
3533import Yoga.HTTP.API.Route.Method as Method
3634import Yoga.JSON (class WriteForeign, writeJSON)
3735
@@ -54,19 +52,15 @@ instance MakeRequest Method.DELETE where
5452instance MakeRequest Method.PATCH where
5553 httpMethod _ = PATCH
5654
57−instance MakeRequest Method.QUERY where
58− httpMethod _ = QUERY
59−
6055makeRequest
6156 :: forall method
6257 . MakeRequest method
6358 => Proxy method
6459 -> String
6560 -> Headers
66− -> String
6761 -> Maybe String
6862 -> Aff FetchResponse.Response
69−makeRequest proxy url customHeaders bodyContentType maybeBody = do
63+makeRequest proxy url customHeaders maybeBody = do
7064 request <- Request.new url options # liftEffect
7165 Promise.toAffE $ Fetch.fetch request
7266 where
@@ -76,7 +70,7 @@ makeRequest proxy url customHeaders bodyContentType maybeBody = do
7670 contentTypeArr = case maybeBody of
7771 Nothing -> []
7872 Just _ | hasContentType -> []
79− Just _ -> [ "Content-Type" /\ bodyContentType ]
73+ Just _ -> [ "Content-Type" /\ "application/json" ]
8074 allHeaders = Headers.fromFoldable (contentTypeArr <> customArr)
8175 body = case maybeBody of
8276 Nothing -> Body.empty
@@ -94,25 +88,11 @@ makeRequest proxy url customHeaders bodyContentType maybeBody = do
9488 , cache: Cache.Default
9589 }
9690
97−class BodyEncoding :: Type -> Type -> Constraint
98−class BodyEncoding encoding body | encoding -> body where
99− encodingContentType :: Proxy encoding -> String
100− encodeBody :: body -> Maybe String
101−
102−instance WriteForeign ty => BodyEncoding (JSON ty) ty where
103− encodingContentType _ = "application/json"
104− encodeBody b = Just (writeJSON b)
105−
106−else instance BodyEncoding PlainText String where
107− encodingContentType _ = "text/plain"
108− encodeBody = Just
109−
110−else instance BodyEncoding (FormData ty) ty where
111− encodingContentType _ = "application/x-www-form-urlencoded"
112− encodeBody b = Just (toUrlEncoded b)
113−
114−else instance BodyEncoding NoBody Unit where
115− encodingContentType _ = ""
116− encodeBody _ = Nothing
91+class SerializeBody :: Type -> Constraint
92+class SerializeBody body where
93+ serializeBody :: body -> Maybe String
11794
118−foreign import toUrlEncoded :: forall a. a -> String
95+instance SerializeBody Unit where
96+ serializeBody _ = Nothing
97+else instance WriteForeign body => SerializeBody body where
98+ serializeBody b = Just (writeJSON b)
src/Yoga/Fetch/Om/ParseResponse.purs+17−79
@@ -5,20 +5,16 @@ module Yoga.Fetch.Om.ParseResponse
55 , parseSuccessRL
66 , class ParseErrorRL
77 , parseErrorRL
8− , class ParseResponseBody
9− , parseResponseBody
108 ) where
119
1210import Prelude
1311
1412import Control.Monad.Error.Class (throwError)
15−import Data.Either (Either(..))
1613import Data.Symbol (class IsSymbol)
1714import Data.Variant (Variant)
1815import Data.Variant as Variant
1916import Effect.Aff (Aff)
2017import Effect.Aff as Aff
21−import Effect.Class (liftEffect)
2218import Effect.Exception as Exception
2319import JS.Fetch.Response (Response) as FetchResponse
2420import JS.Fetch.Response as Fetch
@@ -27,24 +23,12 @@ import Prim.RowList (class RowToList, RowList)
2723import Prim.RowList as RL
2824import Promise.Aff as Promise
2925import Type.Proxy (Proxy(..))
30−import Unsafe.Coerce (unsafeCoerce)
31−import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
3226import Yoga.HTTP.API.Route.StatusCode (class StatusCodeMap, statusCodeFor, StatusCode(..))
27+import Data.Either (Either(..))
3328import Yoga.JSON (class ReadForeign, readJSON)
3429import Yoga.JSON.Error (withStringErrors)
35−import Yoga.Om (Om, toOm)
30+import Yoga.Om (Om, fromAff)
3631import Yoga.Om.Error (Exception)
37−import Yoga.Om.Strom (Strom)
38−import Yoga.Om.Strom.WebStream as WebStream
39−
40−class ParseResponseBody (body :: Type) where
41− parseResponseBody :: String -> Either String body
42−
43−instance ParseResponseBody String where
44− parseResponseBody = Right
45−
46−else instance ReadForeign body => ParseResponseBody body where
47− parseResponseBody = withStringErrors <<< readJSON
4832
4933class ParseResponse (errorRow :: Row Type) (successRow :: Row Type) where
5034 parseResponse :: FetchResponse.Response -> Om {} errorRow (Variant successRow)
@@ -58,14 +42,16 @@ instance
5842 ParseResponse errorRow successRow where
5943 parseResponse fetchResp = do
6044 let status = Fetch.status fetchResp
45+ jsonText <- Promise.toAffE (Fetch.text fetchResp) # fromAff
6146 if status >= 200 && status < 300 then
62− parseSuccessRL (Proxy :: _ successRL) status fetchResp # toOm
63− else do
64− errorText <- Promise.toAffE (Fetch.text fetchResp) # toOm
65− parseErrorRL (Proxy :: _ errorRL) (Proxy :: _ errorRow) status errorText
47+ parseSuccessRL (Proxy :: _ successRL) status jsonText # fromAff
48+ else if status >= 400 then
49+ parseErrorRL (Proxy :: _ errorRL) (Proxy :: _ errorRow) status jsonText
50+ else
51+ throwError (Variant.inj (Proxy :: _ "exception") (Exception.error ("Unexpected HTTP status code: " <> show status)))
6652
6753class ParseSuccessRL (rl :: RowList Type) (successRow :: Row Type) | rl -> successRow where
68− parseSuccessRL :: Proxy rl -> Int -> FetchResponse.Response -> Aff (Variant successRow)
54+ parseSuccessRL :: Proxy rl -> Int -> String -> Aff (Variant successRow)
6955
7056instance ParseSuccessRL RL.Nil successRow where
7157 parseSuccessRL _ status _ =
@@ -74,53 +60,19 @@ instance ParseSuccessRL RL.Nil successRow where
7460else instance
7561 ( IsSymbol label
7662 , StatusCodeMap label
77− , StreamDecode a
78− , Row.Cons label (Strom streamContext streamError a) tailRow successRow
79− , Row.Lacks label tailRow
80− , ParseSuccessRL tailRL successRow
81− ) =>
82− ParseSuccessRL (RL.Cons label (Strom streamContext streamError a) tailRL) successRow where
83− parseSuccessRL _ actualStatus fetchResp = do
84− let StatusCode expected = statusCodeFor (Proxy :: _ label)
85− if actualStatus == expected then do
86− webStream <- liftEffect (Fetch.body fetchResp)
87− let strom = decodeStream (WebStream.fromReadableStream (unsafeCoerce webStream)) :: Strom {} () a
88− pure (Variant.inj (Proxy :: _ label) (unsafeCoerce strom))
89− else
90− parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
91−
92−else instance
93− ( IsSymbol label
94− , StatusCodeMap label
95− , Row.Cons label Unit tailRow successRow
96− , Row.Lacks label tailRow
97− , ParseSuccessRL tailRL successRow
98− ) =>
99− ParseSuccessRL (RL.Cons label Unit tailRL) successRow where
100− parseSuccessRL _ actualStatus fetchResp = do
101− let StatusCode expected = statusCodeFor (Proxy :: _ label)
102− if actualStatus == expected then
103− pure (Variant.inj (Proxy :: _ label) unit)
104− else
105− parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
106−else instance
107− ( IsSymbol label
108− , StatusCodeMap label
109− , ParseResponseBody body
63+ , ReadForeign body
11064 , Row.Cons label body tailRow successRow
11165 , Row.Lacks label tailRow
11266 , ParseSuccessRL tailRL successRow
11367 ) =>
11468 ParseSuccessRL (RL.Cons label body tailRL) successRow where
115− parseSuccessRL _ actualStatus fetchResp = do
69+ parseSuccessRL _ actualStatus jsonText = do
11670 let StatusCode expected = statusCodeFor (Proxy :: _ label)
117− if actualStatus == expected then do
118− jsonText <- liftEffect (Fetch.text fetchResp) >>= Promise.toAff
119− case parseResponseBody jsonText of
120− Left errs -> Aff.throwError (Exception.error ("Failed to parse response (status " <> show actualStatus <> "):\n" <> errs))
121− Right parsed -> pure (Variant.inj (Proxy :: _ label) parsed)
71+ if actualStatus == expected then case withStringErrors (readJSON jsonText) of
72+ Left errs -> Aff.throwError (Exception.error ("Failed to parse response (status " <> show actualStatus <> "):\n" <> errs))
73+ Right parsed -> pure (Variant.inj (Proxy :: _ label) parsed)
12274 else
123− parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
75+ parseSuccessRL (Proxy :: _ tailRL) actualStatus jsonText
12476
12577class ParseErrorRL (rl :: RowList Type) (errorRow :: Row Type) | rl -> errorRow where
12678 parseErrorRL
@@ -138,21 +90,7 @@ instance ParseErrorRL RL.Nil errorRow where
13890else instance
13991 ( IsSymbol label
14092 , StatusCodeMap label
141− , Row.Cons label Unit tailRow errorRow
142− , Row.Lacks label tailRow
143− , Row.Cons label Unit tailExc (Exception errorRow)
144− , ParseErrorRL tailRL errorRow
145− ) =>
146− ParseErrorRL (RL.Cons label Unit tailRL) errorRow where
147− parseErrorRL _ errorProxy actualStatus jsonText = do
148− let StatusCode expected = statusCodeFor (Proxy :: _ label)
149− if actualStatus == expected then throwError (Variant.inj (Proxy :: _ label) unit)
150− else parseErrorRL (Proxy :: _ tailRL) errorProxy actualStatus jsonText
151−
152−else instance
153− ( IsSymbol label
154− , StatusCodeMap label
155− , ParseResponseBody body
93+ , ReadForeign body
15694 , Row.Cons label body tailRow errorRow
15795 , Row.Lacks label tailRow
15896 , Row.Cons label body tailExc (Exception errorRow)
@@ -161,7 +99,7 @@ else instance
16199 ParseErrorRL (RL.Cons label body tailRL) errorRow where
162100 parseErrorRL _ errorProxy actualStatus jsonText = do
163101 let StatusCode expected = statusCodeFor (Proxy :: _ label)
164− if actualStatus == expected then case parseResponseBody jsonText of
102+ if actualStatus == expected then case withStringErrors (readJSON jsonText) of
165103 Left errs -> throwError (Variant.inj (Proxy :: _ "exception") (Exception.error ("Failed to parse error response (status " <> show actualStatus <> "):\n" <> errs)))
166104 Right parsed -> throwError (Variant.inj (Proxy :: _ label) parsed)
167105 else
src/Yoga/Fetch/Om/ReadOptional.jsdeleted−18
@@ -1,18 +0,0 @@
1−import * as Maybe from "../Data.Maybe/index.js";
2−
3−export function readOptionalImpl(key) {
4− return function (obj) {
5− const val = obj[key];
6− if (val === undefined || val instanceof Maybe.Nothing) {
7− return Maybe.Nothing.value;
8− }
9− if (val instanceof Maybe.Just) return val;
10− return Maybe.Just.create(val);
11− };
12−}
13−
14−export function readRequiredField(key) {
15− return function (obj) {
16− return obj[key];
17− };
18−}
src/Yoga/Fetch/Om/ReadOptional.pursdeleted−56
@@ -1,56 +0,0 @@
1−module Yoga.Fetch.Om.ReadOptional
2− ( readOptional
3− , class ReadPartialQuery
4− , readPartialQuery
5− ) where
6−
7−import Data.Maybe (Maybe)
8−import Data.Symbol (class IsSymbol, reflectSymbol)
9−import Prim.Row as Row
10−import Prim.RowList (RowList)
11−import Prim.RowList as RL
12−import Record as Record
13−import Type.Proxy (Proxy(..))
14−
15−foreign import readOptionalImpl :: forall a r. String -> Record r -> Maybe a
16−
17−-- | Accept explicit `Maybe` fields as well as undefined-aware partial records.
18−readOptional :: forall @name ty r. IsSymbol name => Record r -> Maybe ty
19−readOptional rec = readOptionalImpl (reflectSymbol (Proxy :: _ name)) rec
20−
21−-- | Read query params from a potentially-partial record.
22−-- | Walks the query params RowList:
23−-- | Maybe ty → read with undefined-awareness (present → Just, absent → Nothing)
24−-- | bare ty → read directly (required, always present)
25−class ReadPartialQuery (rl :: RowList Type) (queryParams :: Row Type) | rl -> queryParams where
26− readPartialQuery :: forall r. Record r -> Record queryParams
27−
28−instance ReadPartialQuery RL.Nil () where
29− readPartialQuery _ = {}
30−
31−-- Maybe ty: optional field, may be undefined at runtime
32−instance
33− ( IsSymbol name
34− , ReadPartialQuery tail tailQuery
35− , Row.Cons name (Maybe ty) tailQuery queryParams
36− , Row.Lacks name tailQuery
37− ) =>
38− ReadPartialQuery (RL.Cons name (Maybe ty) tail) queryParams where
39− readPartialQuery rec = Record.insert (Proxy :: _ name) (readOptional @name rec) rest
40− where
41− rest = readPartialQuery @tail rec
42−
43−-- bare ty: required field (from Required query params), always present
44−else instance
45− ( IsSymbol name
46− , ReadPartialQuery tail tailQuery
47− , Row.Cons name ty tailQuery queryParams
48− , Row.Lacks name tailQuery
49− ) =>
50− ReadPartialQuery (RL.Cons name ty tail) queryParams where
51− readPartialQuery rec = Record.insert (Proxy :: _ name) val rest
52− where
53− val = readRequiredField (reflectSymbol (Proxy :: _ name)) rec
54− rest = readPartialQuery @tail rec
55−
56−foreign import readRequiredField :: forall a r. String -> Record r -> a
src/Yoga/Fetch/Om/Simple.purs+23−8
@@ -32,14 +32,21 @@ import JS.Fetch as Fetch
3232import JS.Fetch.Duplex as Duplex
3333import JS.Fetch.Headers (Headers)
3434import JS.Fetch.Headers as Headers
35+import JS.Fetch.Integrity (Integrity(..))
36+import JS.Fetch.Referrer as Referrer
37+import JS.Fetch.ReferrerPolicy as ReferrerPolicy
3538import JS.Fetch.Request as Request
3639import JS.Fetch.RequestBody as Body
40+import JS.Fetch.RequestCache as Cache
41+import JS.Fetch.RequestCredentials as Credentials
42+import JS.Fetch.RequestMode as Mode
3743import JS.Fetch.Response as Resp
3844import Promise.Aff as Promise
3945import Type.Row.Homogeneous (class Homogeneous)
46+-- PlainText removed, using local phantom type
4047import Yoga.JSON (class ReadForeign, class WriteForeign, readJSON, writeJSON)
4148import Yoga.JSON.Error (withStringErrors)
42−import Yoga.Om (Om, toOm, throw)
49+import Yoga.Om (Om, fromAff, throw)
4350
4451type FetchError = { status :: Int, body :: String }
4552
@@ -49,7 +56,9 @@ class DecodeResponse :: forall k. k -> Type -> Constraint
4956class DecodeResponse a result | a -> result where
5057 decodeResponse :: String -> Either String result
5158
52−instance DecodeResponse String String where
59+data PlainTextResponse
60+
61+instance DecodeResponse PlainTextResponse String where
5362 decodeResponse = Right
5463
5564else instance ReadForeign a => DecodeResponse a a where
@@ -65,8 +74,8 @@ simpleFetch
6574 -> Maybe String
6675 -> Om ctx (fetchError :: FetchError | err) (FetchResponse result)
6776simpleFetch method headers url maybeBody = do
68− resp <- doFetch # toOm
69− respText <- Promise.toAffE (Resp.text resp) # toOm
77+ resp <- doFetch # fromAff
78+ respText <- Promise.toAffE (Resp.text resp) # fromAff
7079 let status = Resp.status resp
7180 let respHeaders = Resp.headers resp
7281 if status >= 200 && status < 300 then
@@ -93,7 +102,13 @@ simpleFetch method headers url maybeBody = do
93102 { method
94103 , headers: allHeaders
95104 , body: reqBody
105+ , credentials: Credentials.SameOrigin
106+ , mode: Mode.Cors
107+ , referrer: Referrer.ReferrerNone
108+ , referrerPolicy: ReferrerPolicy.NoReferrer
109+ , integrity: Integrity ""
96110 , duplex: Duplex.Half
111+ , cache: Cache.Default
97112 }
98113
99114get
@@ -138,7 +153,7 @@ delete_
138153 => String
139154 -> Record headers
140155 -> Om ctx (fetchError :: FetchError | err) Unit
141−delete_ url headers = void $ simpleFetch @String DELETE headers url Nothing
156+delete_ url headers = void $ simpleFetch @PlainTextResponse DELETE headers url Nothing
142157
143158post
144159 :: forall @a result headers body ctx err
@@ -170,7 +185,7 @@ post_
170185 -> Record headers
171186 -> body
172187 -> Om ctx (fetchError :: FetchError | err) Unit
173−post_ url headers body = void $ simpleFetch @String POST headers url (Just (writeJSON body))
188+post_ url headers body = void $ simpleFetch @PlainTextResponse POST headers url (Just (writeJSON body))
174189
175190put
176191 :: forall @a result headers body ctx err
@@ -202,7 +217,7 @@ put_
202217 -> Record headers
203218 -> body
204219 -> Om ctx (fetchError :: FetchError | err) Unit
205−put_ url headers body = void $ simpleFetch @String PUT headers url (Just (writeJSON body))
220+put_ url headers body = void $ simpleFetch @PlainTextResponse PUT headers url (Just (writeJSON body))
206221
207222patch
208223 :: forall @a result headers body ctx err
@@ -234,4 +249,4 @@ patch_
234249 -> Record headers
235250 -> body
236251 -> Om ctx (fetchError :: FetchError | err) Unit
237−patch_ url headers body = void $ simpleFetch @String PATCH headers url (Just (writeJSON body))
252+patch_ url headers body = void $ simpleFetch @PlainTextResponse PATCH headers url (Just (writeJSON body))
src/Yoga/Fetch/Om/SplitResponses.purs+2−24
@@ -1,8 +1,6 @@
11module Yoga.Fetch.Om.SplitResponses
22 ( class IsSuccessStatus
3− , class ExtractBodyRL
43 , class ExtractBody
5− , class ClientResponseBody
64 , class SplitResponses
75 , class SplitResponsesRL
86 , class DispatchResponse
@@ -12,10 +10,7 @@ import Prim.Boolean (True, False)
1210import Prim.Row as Row
1311import Prim.RowList (RowList)
1412import Prim.RowList as RL
15−import Data.Unit (Unit)
1613import Yoga.HTTP.API.Route (Response)
17−import Yoga.HTTP.API.Route.Encoding (PlainText, Streaming)
18−import Yoga.Om.Strom (Strom)
1914
2015class IsSuccessStatus (label :: Symbol) (isSuccess :: Boolean) | label -> isSuccess
2116
@@ -31,29 +26,12 @@ else instance IsSuccessStatus "alreadyReported" True
3126else instance IsSuccessStatus "imUsed" True
3227else instance IsSuccessStatus label False
3328
34−-- Traverse a RowList to find the "body" field; fall back to Unit if absent.
35−class ExtractBodyRL (rl :: RowList Type) (body :: Type) | rl -> body
36−
37−instance ExtractBodyRL RL.Nil Unit
38−
39−instance ExtractBodyRL (RL.Cons "body" body tail) body
40−
41−else instance ExtractBodyRL tail body => ExtractBodyRL (RL.Cons label ty tail) body
42−
43−class ClientResponseBody (encoded :: Type) (decoded :: Type) | encoded -> decoded
44−
45−instance ClientResponseBody PlainText String
46−else instance ClientResponseBody (Streaming a) (Strom {} () a)
47−else instance ClientResponseBody body body
48−
4929class ExtractBody (responseType :: Type) (bodyType :: Type) | responseType -> bodyType
5030
51−instance ClientResponseBody encoded body => ExtractBody (Response headers encoded) body
31+instance ExtractBody (Response headers body) body
5232
5333else instance
54− ( RL.RowToList row rl
55− , ExtractBodyRL rl encoded
56− , ClientResponseBody encoded body
34+ ( Row.Cons "body" body rest row
5735 ) =>
5836 ExtractBody (Record row) body
5937
src/Yoga/Fetch/Om/StreamDecode.jsdeleted−12
@@ -1,12 +0,0 @@
1−export const newUtf8Decoder = () => new TextDecoder();
2−
3−export const decodeUtf8Chunks = (decoder) => (chunks) => () => {
4− const decoded = [];
5− for (const bytes of chunks) {
6− const text = decoder.decode(bytes, { stream: true });
7− if (text !== "") decoded.push(text);
8− }
9− return decoded;
10−};
11−
12−export const flushUtf8Decoder = (decoder) => () => decoder.decode();
src/Yoga/Fetch/Om/StreamDecode.pursdeleted−61
@@ -1,61 +0,0 @@
1−module Yoga.Fetch.Om.StreamDecode
2− ( class StreamDecode
3− , decodeStream
4− ) where
5−
6−import Prelude
7−
8−import Control.Monad.Rec.Class (Step(..))
9−import Data.Array as Array
10−import Data.ArrayBuffer.Types (Uint8Array)
11−import Data.Maybe (Maybe(..))
12−import Data.Tuple.Nested ((/\))
13−import Effect (Effect)
14−import Effect.Class (liftEffect)
15−import Yoga.Om (Om)
16−import Yoga.Om.Strom (Strom)
17−import Yoga.Om.Strom as Strom
18−
19−class StreamDecode a where
20− decodeStream :: Strom {} () Uint8Array -> Strom {} () a
21−
22−instance StreamDecode Uint8Array where
23− decodeStream = identity
24−
25−else instance StreamDecode String where
26− decodeStream stream =
27− Strom.fromOm (liftEffect newUtf8Decoder) >>= \decoder ->
28− decodeUtf8Stream decoder stream
29−
30−data Utf8Decoder
31−
32−foreign import newUtf8Decoder :: Effect Utf8Decoder
33−
34−foreign import decodeUtf8Chunks :: Utf8Decoder -> Array Uint8Array -> Effect (Array String)
35−
36−foreign import flushUtf8Decoder :: Utf8Decoder -> Effect String
37−
38−decodeUtf8Stream :: Utf8Decoder -> Strom {} () Uint8Array -> Strom {} () String
39−decodeUtf8Stream decoder stream = Strom.mkStrom do
40− step <- Strom.runStrom stream
41− case step of
42− Done maybeChunk -> do
43− decoded <- decodeChunk decoder maybeChunk
44− trailing <- liftEffect $ flushUtf8Decoder decoder
45− let
46− finalChunk =
47− if trailing == "" then decoded
48− else Array.snoc decoded trailing
49− pure $ Done (nonEmpty finalChunk)
50− Loop (maybeChunk /\ next) -> do
51− decoded <- decodeChunk decoder maybeChunk
52− pure $ Loop (nonEmpty decoded /\ decodeUtf8Stream decoder next)
53−
54−decodeChunk :: Utf8Decoder -> Maybe (Array Uint8Array) -> Om {} () (Array String)
55−decodeChunk _ Nothing = pure []
56−decodeChunk decoder (Just chunk) = liftEffect $ decodeUtf8Chunks decoder chunk
57−
58−nonEmpty :: forall a. Array a -> Maybe (Array a)
59−nonEmpty values
60− | Array.null values = Nothing
61− | otherwise = Just values
test-compile-fail/cases/ClientBogusSignature.pursdeleted−9
@@ -1,9 +0,0 @@
1−module Test.CompileFail.ClientBogusSignature where
2−
3−import Yoga.Fetch.Om (GET, Route, type (/), type (:), client)
4−
5−type GetUserRoute = Route GET ("users" / "id" : Int) {} (ok :: { body :: { id :: Int, name :: String } })
6−type TestAPI = { getUser :: GetUserRoute }
7−
8−wrong :: { getUser :: String -> Int }
9−wrong = client @TestAPI "http://localhost:3000"
test-compile-fail/cases/ClientExtraField.pursdeleted−12
@@ -1,12 +0,0 @@
1−module Test.CompileFail.ClientExtraField where
2−
3−import Yoga.Fetch.Om (GET, Route, type (/), type (:), client)
4−import Yoga.Om (Om)
5−
6−type GetUserRoute = Route GET ("users" / "id" : Int) {} (ok :: { body :: { id :: Int, name :: String } })
7−type TestAPI = { getUser :: GetUserRoute }
8−
9−api = client @TestAPI "http://localhost:3000"
10−
11−wrong :: forall ctx err. Om ctx err { id :: Int, name :: String }
12−wrong = api.getUser { id: 42, extra: "oops" }
test-compile-fail/cases/ClientMissingField.pursdeleted−12
@@ -1,12 +0,0 @@
1−module Test.CompileFail.ClientMissingField where
2−
3−import Yoga.Fetch.Om (GET, Route, type (/), type (:), client)
4−import Yoga.Om (Om)
5−
6−type GetUserRoute = Route GET ("users" / "id" : Int) {} (ok :: { body :: { id :: Int, name :: String } })
7−type TestAPI = { getUser :: GetUserRoute }
8−
9−api = client @TestAPI "http://localhost:3000"
10−
11−wrong :: forall ctx err. Om ctx err { id :: Int, name :: String }
12−wrong = api.getUser {}
test-compile-fail/cases/ClientNoArgsPassedArg.pursdeleted−12
@@ -1,12 +0,0 @@
1−module Test.CompileFail.ClientNoArgsPassedArg where
2−
3−import Yoga.Fetch.Om (GET, Route, client)
4−import Yoga.Om (Om)
5−
6−type HealthRoute = Route GET "health" {} (ok :: { body :: String })
7−type TestAPI = { health :: HealthRoute }
8−
9−api = client @TestAPI "http://localhost:3000"
10−
11−wrong :: forall ctx err. Om ctx err String
12−wrong = api.health "should not accept this"
test-compile-fail/cases/ClientWrongFieldName.pursdeleted−12
@@ -1,12 +0,0 @@
1−module Test.CompileFail.ClientWrongFieldName where
2−
3−import Yoga.Fetch.Om (GET, Route, type (/), type (:), client)
4−import Yoga.Om (Om)
5−
6−type GetUserRoute = Route GET ("users" / "id" : Int) {} (ok :: { body :: { id :: Int, name :: String } })
7−type TestAPI = { getUser :: GetUserRoute }
8−
9−api = client @TestAPI "http://localhost:3000"
10−
11−wrong :: forall ctx err. Om ctx err { id :: Int, name :: String }
12−wrong = api.getUser { userId: 42 }
test-compile-fail/cases/ClientWrongFieldType.pursdeleted−12
@@ -1,12 +0,0 @@
1−module Test.CompileFail.ClientWrongFieldType where
2−
3−import Yoga.Fetch.Om (GET, Route, type (/), type (:), client)
4−import Yoga.Om (Om)
5−
6−type GetUserRoute = Route GET ("users" / "id" : Int) {} (ok :: { body :: { id :: Int, name :: String } })
7−type TestAPI = { getUser :: GetUserRoute }
8−
9−api = client @TestAPI "http://localhost:3000"
10−
11−wrong :: forall ctx err. Om ctx err { id :: Int, name :: String }
12−wrong = api.getUser { id: "not-an-int" }
test-compile-fail/run.shdeleted−26
@@ -1,26 +0,0 @@
1−#!/usr/bin/env bash
2−set -euo pipefail
3−
4−# Compile-fail tests for client derivation type safety.
5−# Each .purs file in cases/ must FAIL to compile.
6−
7−SOURCES=$(bunx spago sources 2>/dev/null | grep -v 'test/')
8−OUTPUT_DIR="output-compile-fail"
9−CASES_DIR="test-compile-fail/cases"
10−PASS=0
11−FAIL=0
12−
13−for f in "$CASES_DIR"/*.purs; do
14− name=$(basename "$f" .purs)
15− if bunx purs compile --output "$OUTPUT_DIR" $SOURCES "$f" >/dev/null 2>&1; then
16− echo "FAIL: $name compiled but should not have"
17− FAIL=$((FAIL + 1))
18− else
19− echo " ok: $name correctly rejected"
20− PASS=$((PASS + 1))
21− fi
22−done
23−
24−echo ""
25−echo "$PASS passed, $FAIL failed"
26−[ "$FAIL" -eq 0 ]
test/BodylessResponse.Example.pursdeleted−27
@@ -1,27 +0,0 @@
1−-- Compile-time test: bodyless responses (e.g. 304, 401) can use {} without
2−-- wrapping in { body :: {} }.
3−module BodylessResponse.Example where
4−
5−import Prelude
6−
7−import Yoga.Fetch.Om (GET, Route, type (/), client)
8−import Yoga.Om (Om)
9−
10−type Resource = { id :: Int, name :: String }
11−
12−type ResourceAPI =
13− { getResource ::
14− Route GET
15− ("resources" / "id")
16− {}
17− ( ok :: { body :: Resource }
18− , notModified :: {}
19− , unauthorized :: {}
20− )
21− }
22−
23−-- This must typecheck: the derived client exposes notModified/unauthorized as Unit errors.
24−api
25− :: forall ctx err
26− . { getResource :: Om (Record ctx) (notModified :: Unit, unauthorized :: Unit | err) Resource }
27−api = client @ResourceAPI "https://api.example.com"
test/BodylessResponse.Spec.pursdeleted−125
@@ -1,125 +0,0 @@
1−module BodylessResponse.Spec where
2−
3−import Prelude
4−
5−import Effect.Ref (Ref)
6−import Effect.Ref as Ref
7−import Effect.Aff (Aff, bracket)
8−import Effect.Aff.Class (liftAff)
9−import Effect.Class (liftEffect)
10−import Foreign (unsafeToForeign)
11−import Test.Spec (Spec, around, describe, it)
12−import Test.Spec.Assertions (shouldEqual)
13−import Yoga.Fastify.Fastify as F
14−import Yoga.Fastify.Fastify (Port(..), Host(..), RouteURL(..), StatusCode(..))
15−import Yoga.Fastify.Om (createOmFastify, deleteOm, getOm)
16−import Yoga.Fetch.Om (DELETE, GET, Route, type (/), type (:), client)
17−import Yoga.Om (runOm)
18−
19−type Resource = { id :: Int, name :: String }
20−
21−type ResourceAPI =
22− { getResource ::
23− Route GET
24− ("bodyless-test" / "resource")
25− {}
26− ( ok :: { body :: Resource }
27− , notModified :: {}
28− , unauthorized :: {}
29− )
30− , deleteUser ::
31− Route DELETE
32− ("bodyless-test" / "users" / "id" : Int)
33− {}
34− ( noContent :: {}
35− , notFound :: { body :: { error :: String } }
36− )
37− }
38−
39−api = client @ResourceAPI "http://localhost:44932"
40−
41−spec :: Spec Unit
42−spec = around withServer $ describe "bodyless responses (notModified / unauthorized)" do
43− it "304 Not Modified triggers the notModified handler" \serverMode -> do
44− liftEffect $ Ref.write "notModified" serverMode
45− triggered <- liftEffect $ Ref.new false
46− runOm {}
47− { exception: \_ -> pure unit
48− , notModified: \_ -> liftEffect $ Ref.write true triggered
49− , unauthorized: \_ -> pure unit
50− }
51− (void api.getResource)
52− result <- liftEffect $ Ref.read triggered
53− liftAff $ result `shouldEqual` true
54−
55− it "401 Unauthorized triggers the unauthorized handler" \serverMode -> do
56− liftEffect $ Ref.write "unauthorized" serverMode
57− triggered <- liftEffect $ Ref.new false
58− runOm {}
59− { exception: \_ -> pure unit
60− , notModified: \_ -> pure unit
61− , unauthorized: \_ -> liftEffect $ Ref.write true triggered
62− }
63− (void api.getResource)
64− result <- liftEffect $ Ref.read triggered
65− liftAff $ result `shouldEqual` true
66−
67− it "200 OK returns the resource body" \serverMode -> do
68− liftEffect $ Ref.write "ok" serverMode
69− resource <- runOm {}
70− { exception: \_ -> pure { id: -1, name: "" }
71− , notModified: \_ -> pure { id: -1, name: "" }
72− , unauthorized: \_ -> pure { id: -1, name: "" }
73− }
74− api.getResource
75− liftAff $ resource `shouldEqual` { id: 1, name: "test" }
76−
77− it "204 No Content returns Unit for noContent shorthand" \serverMode -> do
78− liftEffect $ Ref.write "deleted" serverMode
79− result <- runOm {}
80− { exception: \_ -> pure false
81− , notFound: \_ -> pure false
82− }
83− (api.deleteUser { id: 1 } $> true)
84− liftAff $ result `shouldEqual` true
85−
86−withServer :: forall a. (Ref String -> Aff a) -> Aff a
87−withServer test = bracket acquire release (\{ serverMode } -> test serverMode)
88− where
89− acquire = do
90− serverMode <- liftEffect $ Ref.new "ok"
91− fastify <- liftEffect do
92− f <- F.fastify {}
93− omApp <- createOmFastify {} f
94− getOm (RouteURL "/bodyless-test/resource") (handler serverMode) omApp
95− deleteOm (RouteURL "/bodyless-test/users/:id") (deleteHandler serverMode) omApp
96− pure f
97− void $ F.listen { port: Port 44932, host: Host "0.0.0.0" } fastify
98− pure { fastify, serverMode }
99−
100− release { fastify } = F.close fastify
101−
102− handler serverMode reply = do
103− mode <- liftEffect $ Ref.read serverMode
104− case mode of
105− "notModified" -> do
106− void $ F.status (StatusCode 304) reply # liftEffect
107− F.send (unsafeToForeign "") reply # liftAff
108− "unauthorized" -> do
109− void $ F.status (StatusCode 401) reply # liftEffect
110− F.send (unsafeToForeign "") reply # liftAff
111− _ -> do
112− void $ F.status (StatusCode 200) reply # liftEffect
113− void $ F.header "content-type" "application/json" reply # liftEffect
114− F.sendJson (unsafeToForeign { id: 1, name: "test" }) reply # liftAff
115−
116− deleteHandler serverMode reply = do
117− mode <- liftEffect $ Ref.read serverMode
118− case mode of
119− "deleted" -> do
120− void $ F.status (StatusCode 204) reply # liftEffect
121− F.send (unsafeToForeign "") reply # liftAff
122− _ -> do
123− void $ F.status (StatusCode 404) reply # liftEffect
124− void $ F.header "content-type" "application/json" reply # liftEffect
125− F.sendJson (unsafeToForeign { error: "Missing user" }) reply # liftAff
test/BuildUrl.Spec.purs+105−154
@@ -3,165 +3,116 @@ module BuildUrl.Spec where
33import Prelude
44
55import Data.Maybe (Maybe(..))
6−import Test.Spec (Spec, describe, it)
7−import Test.Spec.Assertions (shouldEqual)
6+import Effect (Effect)
7+import Effect.Aff (Aff)
88import Type.Proxy (Proxy(..))
9−import Unsafe.Coerce (unsafeCoerce)
109import Yoga.Fetch.Om.BuildUrl (buildUrl, substitutePathParams, appendQueryParams)
1110import Yoga.HTTP.API.Path (Path, type (/), type (:))
11+import ViTest (ViTest, describe, test)
12+import ViTest.Expect (expectToBe)
13+
14+expectToEqual :: forall a. Eq a => a -> a -> Aff Unit
15+expectToEqual expected actual = expectToBe true (expected == actual)
1216
1317--------------------------------------------------------------------------------
1418-- SubstitutePathParams Tests
1519--------------------------------------------------------------------------------
1620
17−spec :: Spec Unit
18−spec = do
19− describe "substitutePathParams" do
20− it "substitutes single path param" do
21− let result = substitutePathParams @(id :: Int) { id: 42 } "/users/:id"
22− result `shouldEqual` "/users/42"
23−
24− it "substitutes multiple path params" do
25− let
26− result = substitutePathParams @(userId :: Int, postId :: Int)
27− { userId: 1, postId: 99 }
28− "/users/:userId/posts/:postId"
29− result `shouldEqual` "/users/1/posts/99"
30−
31− it "handles String params" do
32− let
33− result = substitutePathParams @(slug :: String)
34− { slug: "hello-world" }
35− "/posts/:slug"
36− result `shouldEqual` "/posts/hello-world"
37− it "URL-encodes reserved characters in path params" do
38− let
39− result = substitutePathParams @(slug :: String)
40− { slug: "a/b c?d#e" }
41− "/users/:slug"
42− result `shouldEqual` "/users/a%2Fb%20c%3Fd%23e"
43−
44− it "distinguishes path param names with shared prefixes" do
45− let
46− result = substitutePathParams @(id :: Int, id2 :: Int)
47− { id: 1, id2: 2 }
48− "/pairs/:id/:id2"
49− result `shouldEqual` "/pairs/1/2"
50−
51− it "handles empty params" do
52− let result = substitutePathParams @() {} "/users"
53− result `shouldEqual` "/users"
54−
55− describe "appendQueryParams" do
56− it "appends single query param" do
57− let result = appendQueryParams @(limit :: Int) { limit: 10 } "/users"
58− result `shouldEqual` "/users?limit=10"
59−
60− it "appends multiple query params" do
61− let
62− result = appendQueryParams @(limit :: Int, offset :: Int)
63− { limit: 10, offset: 20 }
64− "/users"
65− result `shouldEqual` "/users?limit=10&offset=20"
66−
67− it "handles optional params - provided via partial record" do
68− let
69− result = appendQueryParams @(limit :: Maybe Int)
70− (unsafeCoerce { limit: 10 } :: Record (limit :: Maybe Int))
71− "/users"
72− result `shouldEqual` "/users?limit=10"
73−
74− it "serializes a present explicit Maybe query param" do
75− let result = appendQueryParams @(id :: Maybe Int) { id: Just 42 } "/users"
76− result `shouldEqual` "/users?id=42"
77−
78− it "omits an explicit Nothing query param" do
79− let result = appendQueryParams @(id :: Maybe Int) { id: Nothing } "/users"
80− result `shouldEqual` "/users"
81−
82− it "handles optional params - omitted via partial record" do
83− let
84− result = appendQueryParams @(limit :: Maybe Int)
85− (unsafeCoerce {} :: Record (limit :: Maybe Int))
86− "/users"
87− result `shouldEqual` "/users"
88−
89− it "handles mixed optional and required params" do
90− let
91− result = appendQueryParams @(limit :: Maybe Int, offset :: Int)
92− (unsafeCoerce { offset: 20 } :: Record (limit :: Maybe Int, offset :: Int))
93− "/users"
94− result `shouldEqual` "/users?offset=20"
95−
96− it "handles empty query params" do
97− let result = appendQueryParams @() {} "/users"
98− result `shouldEqual` "/users"
99−
100− it "URL-encodes special characters in values" do
101− let result = appendQueryParams @(search :: String) { search: "hello world&more=yes" } "/users"
102− result `shouldEqual` "/users?search=hello%20world%26more%3Dyes"
103− it "appends query params to an existing query string" do
104− let result = appendQueryParams @(search :: String) { search: "new value" } "/users?token=old"
105− result `shouldEqual` "/users?token=old&search=new%20value"
106−
107− it "inserts query params before a URL fragment" do
108− let result = appendQueryParams @(search :: String) { search: "new value" } "/users#details"
109− result `shouldEqual` "/users?search=new%20value#details"
110−
111− it "repeats Array query params one pair per element" do
112− let
113− result = appendQueryParams @(categories :: Array Int)
114− { categories: [ 3030, 103130 ] }
115− "/users"
116− result `shouldEqual` "/users?categories=3030&categories=103130"
117−
118− it "omits empty Array query params" do
119− let
120− result = appendQueryParams @(categories :: Array Int)
121− { categories: [] }
122− "/users"
123− result `shouldEqual` "/users"
124−
125− it "mixes Array and bare query params" do
126− let
127− result = appendQueryParams @(query :: String, categories :: Array Int)
128− { query: "foo", categories: [ 1, 2 ] }
129− "/users"
130− result `shouldEqual` "/users?categories=1&categories=2&query=foo"
131−
132− describe "buildUrl" do
133− it "builds URL with path params only" do
134− let
135− result = buildUrl
136− "https://api.example.com"
137− (Proxy :: _ (Path ("users" / "id" : Int)))
138− { id: 42 }
139− {}
140− result `shouldEqual` "https://api.example.com/users/42"
141−
142− it "builds URL with query params only" do
143− let
144− result = buildUrl
145− "https://api.example.com"
146− (Proxy :: _ (Path "users"))
147− {}
148− { limit: 10, offset: 20 }
149− result `shouldEqual` "https://api.example.com/users?limit=10&offset=20"
150−
151− it "appends route paths before base URL query strings and fragments" do
152− let
153− result = buildUrl
154− "https://api.example.com/base/?token=old#details"
155− (Proxy :: _ (Path "users"))
156− {}
157− { search: "new value" }
158− result `shouldEqual` "https://api.example.com/base/users?token=old&search=new%20value#details"
159−
160− it "does not substitute path captures inside a base URL query string" do
161− let
162− result = buildUrl
163− "https://api.example.com?redirect=/:id#details"
164− (Proxy :: _ (Path ("users" / "id" : Int)))
165− { id: 42 }
166− {}
167− result `shouldEqual` "https://api.example.com/users/42?redirect=/:id#details"
21+testSubstitutePathParams :: Effect ViTest
22+testSubstitutePathParams = describe "substitutePathParams" $ do
23+ _ <- test "substitutes single path param" do
24+ let
25+ result = substitutePathParams @(id :: Int) { id: 42 } "/users/:id"
26+ expectToEqual "/users/42" result
27+
28+ _ <- test "substitutes multiple path params" do
29+ let
30+ result = substitutePathParams @(userId :: Int, postId :: Int)
31+ { userId: 1, postId: 99 }
32+ "/users/:userId/posts/:postId"
33+ expectToEqual "/users/1/posts/99" result
34+
35+ _ <- test "handles String params" do
36+ let
37+ result = substitutePathParams @(slug :: String)
38+ { slug: "hello-world" }
39+ "/posts/:slug"
40+ expectToEqual "/posts/hello-world" result
41+
42+ test "handles empty params" do
43+ let
44+ result = substitutePathParams @() {} "/users"
45+ expectToEqual "/users" result
46+
47+--------------------------------------------------------------------------------
48+-- AppendQueryParams Tests
49+--------------------------------------------------------------------------------
50+
51+testAppendQueryParams :: Effect ViTest
52+testAppendQueryParams = describe "appendQueryParams" $ do
53+ _ <- test "appends single query param" do
54+ let
55+ result = appendQueryParams @(limit :: Int) { limit: 10 } "/users"
56+ expectToEqual "/users?limit=10" result
57+
58+ _ <- test "appends multiple query params" do
59+ let
60+ result = appendQueryParams @(limit :: Int, offset :: Int)
61+ { limit: 10, offset: 20 }
62+ "/users"
63+ expectToEqual "/users?limit=10&offset=20" result
64+
65+ _ <- test "handles Maybe params - Just" do
66+ let
67+ result = appendQueryParams @(limit :: Maybe Int)
68+ { limit: Just 10 }
69+ "/users"
70+ expectToEqual "/users?limit=10" result
71+
72+ _ <- test "handles Maybe params - Nothing" do
73+ let
74+ result = appendQueryParams @(limit :: Maybe Int)
75+ { limit: Nothing }
76+ "/users"
77+ expectToEqual "/users" result
78+
79+ _ <- test "handles mixed Maybe and required params" do
80+ let
81+ result = appendQueryParams @(limit :: Maybe Int, offset :: Int)
82+ { limit: Nothing, offset: 20 }
83+ "/users"
84+ expectToEqual "/users?offset=20" result
85+
86+ _ <- test "handles empty query params" do
87+ let
88+ result = appendQueryParams @() {} "/users"
89+ expectToEqual "/users" result
90+
91+ test "URL-encodes special characters in values" do
92+ let
93+ result = appendQueryParams @(search :: String) { search: "hello world&more=yes" } "/users"
94+ expectToEqual "/users?search=hello%20world%26more%3Dyes" result
95+
96+--------------------------------------------------------------------------------
97+-- BuildUrl Tests
98+--------------------------------------------------------------------------------
99+
100+testBuildUrl :: Effect ViTest
101+testBuildUrl = describe "buildUrl" $ do
102+ _ <- test "builds URL with path params only" do
103+ let
104+ result = buildUrl
105+ "https://api.example.com"
106+ (Proxy :: _ (Path ("users" / "id" : Int)))
107+ { id: 42 }
108+ {}
109+ expectToEqual "https://api.example.com/users/42" result
110+
111+ test "builds URL with query params only" do
112+ let
113+ result = buildUrl
114+ "https://api.example.com"
115+ (Proxy :: _ (Path "users"))
116+ {}
117+ { limit: 10, offset: 20 }
118+ expectToEqual "https://api.example.com/users?limit=10&offset=20" result
test/BuildUrlProperty.Spec.pursdeleted−256
@@ -1,256 +0,0 @@
1−module BuildUrlProperty.Spec where
2−
3−import Prelude
4−
5−import Data.Array as Array
6−import Data.Array.NonEmpty as NEA
7−import Data.Enum (toEnum)
8−import Data.Foldable (all)
9−import Data.Maybe (Maybe(..), fromJust, isJust)
10−import Data.String as String
11−import Data.String.CodeUnits as CodeUnits
12−import Data.String.Gen (genAsciiString)
13−import Data.String.CodePoints (CodePoint, fromCodePointArray)
14−import Data.String.Pattern (Pattern(..))
15−import JSURI (decodeURIComponent)
16−import Test.QuickCheck ((<?>), (===))
17−import Test.QuickCheck.Arbitrary (arbitrary)
18−import Test.QuickCheck.Gen (Gen, arrayOf1, chooseInt)
19−import Test.Spec (Spec, describe, it)
20−import Test.Spec.Assertions (shouldEqual)
21−import Test.Spec.QuickCheck (quickCheck)
22−import Partial.Unsafe (unsafePartial)
23−import Unsafe.Coerce (unsafeCoerce)
24−import Yoga.Fetch.Om.BuildUrl (appendQueryParams, substitutePathParams)
25−
26−spec :: Spec Unit
27−spec = describe "URL properties" do
28− it "never substitutes path captures in the query or fragment" $ quickCheck do
29− value <- (genAsciiString :: Gen String)
30− let
31− suffix = "?next=/:slug#/:slug"
32− result = substitutePathParams @(slug :: String)
33− { slug: value }
34− ("/items/:slug" <> suffix)
35− property = case String.stripSuffix (Pattern suffix) result of
36− Nothing -> false
37− Just path -> not $ String.contains (Pattern ":slug") path
38− pure $ property <?> "unexpected path substitution result: " <> show result
39−
40− it "round-trips percent-encoded path and query values" $ quickCheck do
41− value <- (genAsciiString :: Gen String)
42− let
43− pathResult = substitutePathParams @(value :: String)
44− { value }
45− "/items/:value"
46− queryResult = appendQueryParams @(value :: String)
47− { value }
48− "/items"
49− property = case
50− String.stripPrefix (Pattern "/items/") pathResult,
51− String.stripPrefix (Pattern "/items?value=") queryResult
52− of
53− Just encodedPath, Just encodedQuery ->
54− decodeURIComponent encodedPath == Just value
55− && decodeURIComponent encodedQuery == Just value
56− _, _ -> false
57− pure $ property <?> "failed to round-trip value " <> show value
58−
59− it "preserves existing query values and fragments" $ quickCheck do
60− existing <- (genAsciiString :: Gen String)
61− added <- (genAsciiString :: Gen String)
62− let
63− base = appendQueryParams @(token :: String)
64− { token: existing }
65− "/items#details"
66− result = appendQueryParams @(search :: String)
67− { search: added }
68− base
69− property = do
70− withoutFragment <- String.stripSuffix (Pattern "#details") result
71− query <- String.stripPrefix (Pattern "/items?token=") withoutFragment
72− case String.split (Pattern "&search=") query of
73− [ encodedExisting, encodedAdded ] ->
74− pure
75− $ decodeURIComponent encodedExisting == Just existing
76− && decodeURIComponent encodedAdded == Just added
77− _ -> Nothing
78− pure
79− $ (property == Just true)
80− <?> "query or fragment changed unexpectedly: " <> show result
81−
82− it "preserves repeated query parameter order and multiplicity" $ quickCheck do
83− values <- arbitrary
84− let
85− result = appendQueryParams @(tag :: Array Int) { tag: values } "/items"
86− expected = case values of
87− [] -> "/items"
88− _ -> "/items?" <> String.joinWith "&" (map (\value -> "tag=" <> show value) values)
89− pure $ result === expected
90−
91− it "normalizes absent, Nothing, and Just optional fields" $ quickCheck do
92− missing <- arbitrary
93− value <- arbitrary
94− let
95− params :: Record (id :: Maybe Int)
96− params = if missing then unsafeCoerce {} else { id: value }
97− result = appendQueryParams @(id :: Maybe Int) params "/items"
98− expected = case missing, value of
99− true, _ -> "/items"
100− false, Nothing -> "/items"
101− false, Just id -> "/items?id=" <> show id
102− pure $ result === expected
103−
104− it "never introduces malformed query separators" $ quickCheck do
105− value <- arbitrary
106− let
107− bases =
108− [ "/items"
109− , "/items?existing=1"
110− , "/items?"
111− , "/items?existing=1&"
112− , "/items#details"
113− , "/items?existing=1#details"
114− , "/items?#details"
115− , "/items?existing=1&#details"
116− ]
117− results = map (appendQueryParams @(id :: Int) { id: value }) bases
118− pure
119− $ all isWellFormed results
120− <?> "malformed URL generated from " <> show results
121−
122− it "preserves repeated existing query keys" $ quickCheck do
123− first <- (arbitrary :: Gen Int)
124− second <- (arbitrary :: Gen Int)
125− page <- (arbitrary :: Gen Int)
126− let
127− base = "/items?id=" <> show first <> "&id=" <> show second <> "#details"
128− result = appendQueryParams @(page :: Int) { page } base
129− expected =
130− "/items?id=" <> show first <> "&id=" <> show second
131− <> "&page="
132− <> show page
133− <> "#details"
134− pure $ result === expected
135−
136− it "appends duplicate query values without coalescing existing ones" $ quickCheck do
137− first <- (arbitrary :: Gen Int)
138− second <- (arbitrary :: Gen Int)
139− added <- (arbitrary :: Gen (Array Int))
140− let
141− base = "/items?id=" <> show first <> "&id=" <> show second
142− result = appendQueryParams @(id :: Array Int) { id: added } base
143− expected = case added of
144− [] -> base
145− _ -> base <> "&" <> String.joinWith "&" (map (\value -> "id=" <> show value) added)
146− pure $ result === expected
147−
148− it "ignores query-looking text inside fragments" $ quickCheck do
149− value <- (arbitrary :: Gen Int)
150− let
151− rendered = show (value :: Int)
152− cases =
153− [ { base: "/items#section?fake=1", expected: "/items?id=" <> rendered <> "#section?fake=1" }
154− , { base: "/items?real=1#section?fake=1", expected: "/items?real=1&id=" <> rendered <> "#section?fake=1" }
155− , { base: "/items#?fake=1&other=2", expected: "/items?id=" <> rendered <> "#?fake=1&other=2" }
156− , { base: "/items#first#second?fake=1", expected: "/items?id=" <> rendered <> "#first#second?fake=1" }
157− ]
158− property = all
159− (\entry -> appendQueryParams @(id :: Int) { id: value } entry.base == entry.expected)
160− cases
161− pure $ property <?> "fragment handling failed for id " <> rendered
162−
163− it "encodes every query delimiter inside values" $ quickCheck do
164− prefix <- (genAsciiString :: Gen String)
165− suffix <- (genAsciiString :: Gen String)
166− let
167− value = prefix <> "?#&=%+ /" <> suffix
168− result = appendQueryParams @(value :: String) { value } "/items"
169− property = case String.stripPrefix (Pattern "/items?value=") result of
170− Nothing -> false
171− Just encoded ->
172− decodeURIComponent encoded == Just value
173− && all
174− (\delimiter -> not $ String.contains (Pattern delimiter) encoded)
175− [ "?", "#", "&", "=", "+", "/" ]
176− && String.contains (Pattern "%25") encoded
177− pure $ property <?> "delimiter encoding failed: " <> show result
178−
179− it "substitutes repeated captures without matching overlapping names" $ quickCheck do
180− id <- (arbitrary :: Gen Int)
181− id2 <- (arbitrary :: Gen Int)
182− let
183− result = substitutePathParams @(id :: Int, id2 :: Int)
184− { id, id2 }
185− "/:id/:id/:id2/:id20?next=/:id#/:id2"
186− expected =
187− "/" <> show id <> "/" <> show id <> "/" <> show id2
188− <> "/:id20?next=/:id#/:id2"
189− pure $ result === expected
190−
191− it "normalizes lone UTF-16 surrogates before percent-encoding" do
192− let
193− value = codeUnit 0xD800 <> "x" <> codeUnit 0xDFFF
194− result = appendQueryParams @(value :: String) { value } "/items"
195− result `shouldEqual` "/items?value=%EF%BF%BDx%EF%BF%BD"
196−
197− it "percent-encodes arbitrary UTF-16 strings without throwing" $ quickCheck do
198− value <- (arbitrary :: Gen String)
199− let
200− pathResult = substitutePathParams @(value :: String) { value } "/items/:value"
201− queryResult = appendQueryParams @(value :: String) { value } "/items"
202− property =
203− isJust (String.stripPrefix (Pattern "/items/") pathResult)
204− && isJust (String.stripPrefix (Pattern "/items?value=") queryResult)
205− pure $ property <?> "encoding failed for UTF-16 input " <> show value
206−
207− it "round-trips valid non-ASCII scalar values" $ quickCheck do
208− value <- unicodeScalarString
209− let
210− pathResult = substitutePathParams @(value :: String) { value } "/items/:value"
211− queryResult = appendQueryParams @(value :: String) { value } "/items"
212− property = case
213− String.stripPrefix (Pattern "/items/") pathResult,
214− String.stripPrefix (Pattern "/items?value=") queryResult
215− of
216− Just encodedPath, Just encodedQuery ->
217− decodeURIComponent encodedPath == Just value
218− && decodeURIComponent encodedQuery == Just value
219− _, _ -> false
220− pure $ property <?> "Unicode round-trip failed for " <> show value
221−
222−isWellFormed :: String -> Boolean
223−isWellFormed url =
224− occurrences "?" url == 1
225− && occurrences "#" url <= 1
226− && not (String.contains (Pattern "??") url)
227− && not (String.contains (Pattern "&&") url)
228− && not (String.contains (Pattern "?#") url)
229− && not (String.contains (Pattern "&#") url)
230− && queryPrecedesFragment url
231−
232−queryPrecedesFragment :: String -> Boolean
233−queryPrecedesFragment url = case String.indexOf (Pattern "#") url of
234− Nothing -> true
235− Just fragmentIndex -> case String.indexOf (Pattern "?") url of
236− Nothing -> false
237− Just queryIndex -> queryIndex < fragmentIndex
238−
239−occurrences :: String -> String -> Int
240−occurrences needle = (_ - 1) <<< Array.length <<< String.split (Pattern needle)
241−
242−unicodeScalarString :: Gen String
243−unicodeScalarString =
244− fromCodePointArray <<< NEA.toArray <$> arrayOf1 unicodeScalar
245−
246−unicodeScalar :: Gen CodePoint
247−unicodeScalar = do
248− useBasicMultilingualPlane <- arbitrary
249− value <- if useBasicMultilingualPlane then
250− chooseInt 0x80 0xD7FF
251− else
252− chooseInt 0xE000 0x10FFFF
253− pure $ unsafePartial $ fromJust $ toEnum value
254−
255−codeUnit :: Int -> String
256−codeUnit value = CodeUnits.singleton $ unsafePartial $ fromJust $ toEnum value
test/Complete.Example.purs+31−17
@@ -3,10 +3,9 @@ module Complete.Example where
33import Prelude
44
55import Data.Array (length)
6−import Justifill (justifill)
6+import Data.Maybe (Maybe(..))
77import Effect.Class.Console (log)
8−import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Route, JSON, type (/), type (:), type (:?), client)
9−import Yoga.HTTP.API.Route (BearerToken(..))
8+import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Route, JSON, Path, type (/), type (:), type (:?), client)
109import Yoga.Om (Om, handleErrors)
1110
1211type User =
@@ -31,43 +30,59 @@ type ErrorMessage =
3130
3231type UserAPI =
3332 { getUser ::
34− Route GET ("users" / "id" : Int)
33+ Route GET
34+ (Path ("users" / "id" : Int))
3535 {}
3636 ( ok :: { body :: User }
3737 , notFound :: { body :: ErrorMessage }
3838 )
3939 , listUsers ::
40− Route GET ("users" :? { limit :: Int, offset :: Int })
40+ Route GET
41+ (Path "users" :? { limit :: Int, offset :: Int })
4142 {}
4243 ( ok :: { body :: Array User }
4344 )
4445 , createUser ::
45− Route POST "users"
46+ Route POST
47+ (Path "users")
4648 { body :: JSON CreateUserRequest }
4749 ( created :: { body :: User }
4850 , badRequest :: { body :: ErrorMessage }
4951 )
5052 , updateUser ::
51− Route PUT ("users" / "id" : Int)
53+ Route PUT
54+ (Path ("users" / "id" : Int))
5255 { body :: JSON UpdateUserRequest }
5356 ( ok :: { body :: User }
5457 , notFound :: { body :: ErrorMessage }
5558 , badRequest :: { body :: ErrorMessage }
5659 )
5760 , deleteUser ::
58− Route DELETE ("users" / "id" : Int)
61+ Route DELETE
62+ (Path ("users" / "id" : Int))
5963 {}
60− ( noContent :: {}
64+ ( noContent :: { body :: {} }
6165 , notFound :: { body :: ErrorMessage }
6266 )
6367 , createUserAuth ::
64− Route POST "users"
65− { headers :: Record (authorization :: BearerToken), body :: JSON CreateUserRequest }
68+ Route POST
69+ (Path "users")
70+ { headers :: Record (authorization :: String), body :: JSON CreateUserRequest }
6671 ( created :: { body :: User }
6772 , badRequest :: { body :: ErrorMessage }
6873 )
6974 }
7075
76+-- Generated client with polymorphic type annotation
77+api
78+ :: forall ctx err
79+ . { createUser :: CreateUserRequest -> Om ctx (badRequest :: ErrorMessage | err) User
80+ , createUserAuth :: { authorization :: String } -> CreateUserRequest -> Om ctx (badRequest :: ErrorMessage | err) User
81+ , deleteUser :: { id :: Int } -> Om ctx (notFound :: ErrorMessage | err) {}
82+ , getUser :: { id :: Int } -> Om ctx (notFound :: ErrorMessage | err) User
83+ , listUsers :: { limit :: Maybe Int, offset :: Maybe Int } -> Om ctx err (Array User)
84+ , updateUser :: { id :: Int } -> UpdateUserRequest -> Om ctx (badRequest :: ErrorMessage, notFound :: ErrorMessage | err) User
85+ }
7186api = client @UserAPI "https://api.example.com"
7287
7388exampleGetUser :: Om {} (notFound :: ErrorMessage) Unit
@@ -79,15 +94,15 @@ exampleGetUserHandled :: Om {} () Unit
7994exampleGetUserHandled = do
8095 user <- api.getUser { id: 42 }
8196 # handleErrors
82− { notFound: \err -> ado
97+ { notFound: \err -> do
8398 log $ "User not found: " <> err.error
84− in { id: 0, name: "unknown", email: "" }
99+ pure { id: 0, name: "unknown", email: "" }
85100 }
86101 log $ "Found user: " <> user.name
87102
88103exampleListUsers :: Om {} () Unit
89104exampleListUsers = do
90− users <- api.listUsers (justifill { limit: 10, offset: 0 })
105+ users <- api.listUsers { limit: Just 10, offset: Just 0 }
91106 log $ "Found " <> show (length users) <> " users"
92107
93108exampleCreateUser :: Om {} (badRequest :: ErrorMessage) Unit
@@ -100,8 +115,7 @@ exampleCreateUser = do
100115
101116exampleUpdateUser :: Om {} (notFound :: ErrorMessage, badRequest :: ErrorMessage) Unit
102117exampleUpdateUser = do
103− user <- api.updateUser
104− { id: 42 }
118+ user <- api.updateUser { id: 42 }
105119 { name: "Alice Updated"
106120 , email: "alice.new@example.com"
107121 }
@@ -114,7 +128,7 @@ exampleDeleteUser = do
114128
115129exampleCreateUserAuth :: Om {} (badRequest :: ErrorMessage) Unit
116130exampleCreateUserAuth = do
117− user <- api.createUserAuth { authorization: BearerToken "abc123" }
131+ user <- api.createUserAuth { authorization: "Bearer abc123" }
118132 { name: "Alice"
119133 , email: "alice@example.com"
120134 }
test/Example.purs+10−15
@@ -1,50 +1,45 @@
11module Example where
22
3−import Yoga.HTTP.API.Route (GET, POST, DELETE, Route, BearerToken)
3+import Yoga.HTTP.API.Route (GET, POST, DELETE, Route)
44import Yoga.HTTP.API.Route.Encoding (JSON)
55import Yoga.HTTP.API.Path (Path, type (/), type (:), type (:?))
66
7−type User = { id :: UserId, name :: UserName, email :: UserMail }
8−type CreateUserRequest = { name :: String, email :: UserMail }
7+type User = { id :: Int, name :: String, email :: String }
8+type CreateUserRequest = { name :: String, email :: String }
99type ErrorMessage = { error :: String }
10−newtype UserId = UserId Int
11−newtype UserName = UserName String
12−newtype UserMail = UserMail String
13−newtype Limit = Limit Int
14−newtype Offset = Offset Int
1510
1611type UserAPI =
1712 { getUser ::
1813 Route GET
19− ("users" / "id" : UserId)
14+ (Path ("users" / "id" : Int))
2015 {}
2116 ( ok :: { body :: User }
2217 , notFound :: { body :: ErrorMessage }
2318 )
2419 , listUsers ::
2520 Route GET
26− ("users" :? { limit :: Limit, offset :: Offset })
21+ (Path "users" :? { limit :: Int, offset :: Int })
2722 {}
2823 ( ok :: { body :: Array User }
2924 )
3025 , createUser ::
3126 Route POST
32− "users"
27+ (Path "users")
3328 { body :: JSON CreateUserRequest }
3429 ( created :: { body :: User }
3530 , badRequest :: { body :: ErrorMessage }
3631 )
3732 , deleteUser ::
3833 Route DELETE
39− ("users" / "id" : UserId)
34+ (Path ("users" / "id" : Int))
4035 {}
41− ( noContent :: {}
36+ ( noContent :: { body :: {} }
4237 , notFound :: { body :: ErrorMessage }
4338 )
4439 , createUserAuth ::
4540 Route POST
46− "users"
47− { headers :: Record (authorization :: BearerToken), body :: JSON CreateUserRequest }
41+ (Path "users")
42+ { headers :: Record (authorization :: String), body :: JSON CreateUserRequest }
4843 ( created :: { body :: User }
4944 , badRequest :: { body :: ErrorMessage }
5045 )
test/MakeRequest.Spec.purs+29−46
@@ -2,53 +2,36 @@ module MakeRequest.Spec where
22
33import Prelude
44
5−import Data.HTTP.Method as HTTP
6−import Data.Maybe (Maybe(..))
75import Data.Tuple.Nested ((/\))
6+import Effect (Effect)
7+import Effect.Aff (Aff)
88import JS.Fetch.Headers as Headers
99import Prim.RowList as RL
10−import Test.Spec (Spec, describe, it)
11−import Test.Spec.Assertions (shouldEqual, shouldSatisfy)
1210import Type.Proxy (Proxy(..))
13−import Yoga.Fetch.Om (FormData, PlainText, QUERY, toHeaders)
14−import Yoga.Fetch.Om.MakeRequest (encodeBody, encodingContentType, httpMethod)
15−import Yoga.HTTP.API.Route (BearerToken(..))
16−
17−spec :: Spec Unit
18−spec = do
19− describe "MakeRequest" do
20− it "maps a QUERY route method imported from the public module" do
21− httpMethod (Proxy :: Proxy QUERY) `shouldEqual` HTTP.QUERY
22−
23− describe "BodyEncoding" do
24− it "sends PlainText request bodies without JSON quoting" do
25− encodingContentType (Proxy :: Proxy PlainText) `shouldEqual` "text/plain"
26− encodeBody @PlainText "hello" `shouldEqual` Just "hello"
27−
28− it "URL-encodes FormData request bodies" do
29− encodingContentType (Proxy :: Proxy (FormData { username :: String })) `shouldEqual` "application/x-www-form-urlencoded"
30− encodeBody @(FormData { username :: String }) { username: "Ada Lovelace" }
31− `shouldEqual` Just "username=Ada+Lovelace"
32−
33− describe "toHeaders" do
34− it "empty headers produces no entries" do
35− let hdrs = toHeaders (Proxy :: _ RL.Nil) {}
36− Headers.toArray hdrs `shouldEqual` []
37−
38− it "single header is included" do
39− let hdrs = toHeaders (Proxy :: _ (RL.Cons "authorization" String RL.Nil)) { authorization: "Bearer abc123" }
40− Headers.toArray hdrs `shouldEqual` [ "authorization" /\ "Bearer abc123" ]
41−
42− it "multiple headers are included" do
43− let
44− hdrs = toHeaders
45− (Proxy :: _ (RL.Cons "authorization" String (RL.Cons "x-custom" String RL.Nil)))
46− { authorization: "Bearer abc123", "x-custom": "value" }
47− arr = Headers.toArray hdrs
48− arr `shouldSatisfy` \a ->
49− a == [ "authorization" /\ "Bearer abc123", "x-custom" /\ "value" ]
50− || a == [ "x-custom" /\ "value", "authorization" /\ "Bearer abc123" ]
51−
52− it "typed header values are rendered with HeaderValue" do
53− let hdrs = toHeaders (Proxy :: _ (RL.Cons "authorization" BearerToken RL.Nil)) { authorization: BearerToken "abc123" }
54− Headers.toArray hdrs `shouldEqual` [ "authorization" /\ "Bearer abc123" ]
11+import Yoga.Fetch.Om (toHeaders)
12+import ViTest (ViTest, describe, test)
13+import ViTest.Expect (expectToBe)
14+
15+expectToEqual :: forall a. Eq a => a -> a -> Aff Unit
16+expectToEqual expected actual = expectToBe true (expected == actual)
17+
18+testToHeaders :: Effect ViTest
19+testToHeaders = describe "toHeaders" $ do
20+ _ <- test "empty headers produces no entries" do
21+ let hdrs = toHeaders (Proxy :: _ RL.Nil) {}
22+ expectToEqual [] (Headers.toArray hdrs)
23+
24+ _ <- test "single header is included" do
25+ let hdrs = toHeaders (Proxy :: _ (RL.Cons "authorization" String RL.Nil)) { authorization: "Bearer abc123" }
26+ expectToEqual [ "authorization" /\ "Bearer abc123" ] (Headers.toArray hdrs)
27+
28+ test "multiple headers are included" do
29+ let
30+ hdrs = toHeaders
31+ (Proxy :: _ (RL.Cons "authorization" String (RL.Cons "x-custom" String RL.Nil)))
32+ { authorization: "Bearer abc123", "x-custom": "value" }
33+ arr = Headers.toArray hdrs
34+ expectToEqual true
35+ ( arr == [ "authorization" /\ "Bearer abc123", "x-custom" /\ "value" ]
36+ || arr == [ "x-custom" /\ "value", "authorization" /\ "Bearer abc123" ]
37+ )
test/PlainTextResponse.Spec.pursdeleted−122
@@ -1,122 +0,0 @@
1−module PlainTextResponse.Spec where
2−
3−import Prelude
4−import Data.String as String
5−
6−import Effect.Aff (Aff, bracket)
7−import Effect.Aff.Class (liftAff)
8−import Effect.Class (liftEffect)
9−import Effect.Ref (Ref)
10−import Effect.Exception as Exn
11−import Effect.Ref as Ref
12−import Foreign (unsafeToForeign)
13−import Test.Spec (Spec, around, describe, it)
14−import Test.Spec.Assertions (shouldEqual)
15−import Yoga.Fastify.Fastify as F
16−import Yoga.Fastify.Fastify (Port(..), Host(..), RouteURL(..), StatusCode(..))
17−import Yoga.Fastify.Om (createOmFastify, getOm, postOm)
18−import Yoga.Fetch.Om (GET, POST, Route, type (/), JSON, PlainText, Streaming, client)
19−import Yoga.Fetch.Om.Simple as Simple
20−import Yoga.Om (Om, runOm)
21−import Yoga.Om.Strom (Strom)
22−import Yoga.Om.Strom as Strom
23−
24−type Payload = { value :: Int }
25−type Result = { doubled :: Int }
26−
27−type TestAPI =
28− { double ::
29− Route POST
30− ("plain-text-test" / "double")
31− { body :: JSON Payload }
32− ( ok :: { body :: Result }
33− , badRequest :: { body :: PlainText }
34− )
35− , health :: Route GET ("plain-text-test" / "health") {} (ok :: { body :: PlainText })
36− , stream :: Route GET ("plain-text-test" / "stream") {} (ok :: { body :: Streaming String })
37− }
38−
39−api
40− :: forall ctx err
41− . { double :: Payload -> Om (Record ctx) (badRequest :: String | err) Result
42− , health :: Om (Record ctx) err String
43− , stream :: Om (Record ctx) err (Strom {} () String)
44− }
45−api = client @TestAPI "http://localhost:44933"
46−
47−spec :: Spec Unit
48−spec = around withServer $ describe "String body on error responses" do
49− it "200 OK decodes the JSON response body" \_ -> do
50− result <- runOm {}
51− { exception: \_ -> pure { doubled: -1 }
52− , badRequest: \_ -> pure { doubled: -1 }
53− }
54− (api.double { value: 21 })
55− liftAff $ result `shouldEqual` { doubled: 42 }
56−
57− it "400 badRequest delivers the raw text body as a String" \serverMode -> do
58− liftEffect $ Ref.write "badRequest" serverMode
59− captured <- liftEffect $ Ref.new ""
60− runOm {}
61− { exception: \_ -> pure unit
62− , badRequest: \b -> liftEffect $ Ref.write b captured
63− }
64− (void $ api.double { value: 0 })
65− body <- liftEffect $ Ref.read captured
66− liftAff $ body `shouldEqual` "invalid value"
67−
68− it "Simple.get returns plain text for String responses" \_ -> do
69− result <- runOm {}
70− { exception: \e -> pure $ "exception: " <> Exn.message e
71− , fetchError: \e -> pure $ "fetch error: " <> e.body
72− }
73− (Simple.get @String "http://localhost:44933/plain-text-test/health" {})
74− liftAff $ result `shouldEqual` "healthy"
75−
76− it "derived PlainText responses decode to String" \_ -> do
77− result <- runOm {}
78− { exception: \e -> pure $ "exception: " <> Exn.message e }
79− api.health
80− liftAff $ result `shouldEqual` "healthy"
81−
82− it "derived Streaming responses decode to a usable Strom" \_ -> do
83− chunks <- runOm {}
84− { exception: \e -> pure [ "exception: " <> Exn.message e ] }
85− do
86− stream <- api.stream
87− Strom.runCollect stream
88− liftAff $ String.joinWith "" chunks `shouldEqual` "stream €"
89−
90−withServer :: forall a. (Ref String -> Aff a) -> Aff a
91−withServer test = bracket acquire release (\{ serverMode } -> test serverMode)
92− where
93− acquire = do
94− serverMode <- liftEffect $ Ref.new "ok"
95− fastify <- liftEffect do
96− f <- F.fastify {}
97− omApp <- createOmFastify {} f
98− postOm (RouteURL "/plain-text-test/double") (handler serverMode) omApp
99− getOm (RouteURL "/plain-text-test/health") (plainTextHandler "healthy") omApp
100− getOm (RouteURL "/plain-text-test/stream") (plainTextHandler "stream €") omApp
101− pure f
102− void $ F.listen { port: Port 44933, host: Host "0.0.0.0" } fastify
103− pure { fastify, serverMode }
104−
105− release { fastify } = F.close fastify
106−
107− plainTextHandler text reply = do
108− void $ F.status (StatusCode 200) reply # liftEffect
109− void $ F.header "content-type" "text/plain" reply # liftEffect
110− F.send (unsafeToForeign text) reply # liftAff
111−
112− handler serverMode reply = do
113− mode <- liftEffect $ Ref.read serverMode
114− case mode of
115− "badRequest" -> do
116− void $ F.status (StatusCode 400) reply # liftEffect
117− void $ F.header "content-type" "text/plain" reply # liftEffect
118− F.send (unsafeToForeign "invalid value") reply # liftAff
119− _ -> do
120− void $ F.status (StatusCode 200) reply # liftEffect
121− void $ F.header "content-type" "application/json" reply # liftEffect
122− F.sendJson (unsafeToForeign { doubled: 42 }) reply # liftAff
test/Polymorphic.Spec.purs+47−57
@@ -2,69 +2,59 @@ module Polymorphic.Spec where
22
33import Prelude
44
5−import Effect.Aff (bracket)
6−import Effect.Aff.Class (liftAff)
5+import Effect (Effect)
76import Effect.Class (liftEffect)
8−import Test.Spec (Spec, around, describe, it)
9−import Test.Spec.Assertions (shouldEqual)
10−import Yoga.Fastify.Fastify as F
11−import Yoga.Fastify.Fastify (Port(..), Host(..))
12−import Yoga.Fastify.Om.API (registerAPI)
13−import Yoga.Fastify.Om.Route (Handler, handle, respond)
14−import Yoga.Fetch.Om (GET, Route, type (/), type (:), client)
15−import Yoga.Om (ask, runOm)
16−import Control.Monad.Error.Class (throwError)
7+import Effect.Ref as Ref
8+import Yoga.Fetch.Om (GET, Route, Path, type (/), type (:), client)
9+import Yoga.Om (Om, ask, runOm)
10+import ViTest (ViTest, describe, test)
11+import ViTest.Expect (expectToBe)
1712
1813type User = { id :: Int, name :: String }
1914
20−type GetUserRoute = Route GET ("users" / "id" : Int) {} (ok :: { body :: User })
21−
2215type TestAPI =
23− { getUser :: GetUserRoute
16+ { getUser :: Route GET (Path ("users" / "id" : Int)) {} (ok :: { body :: User })
2417 }
2518
19+api
20+ :: forall ctx err
21+ . { getUser :: { id :: Int } -> Om ctx err User
22+ }
2623api = client @TestAPI "http://localhost:44931"
2724
28−spec :: Spec Unit
29−spec = around withServer $ describe "polymorphic client" do
30− it "fetches user with { token :: String } context" \_ ->
31− runOm { token: "secret123" } { exception: throwError } do
32− { token } <- ask
33− { name } <- api.getUser { id: 42 }
34− liftAff $ (token <> ":" <> name) `shouldEqual` "secret123:User 42"
35−
36− it "fetches user with { apiKey :: String, userId :: Int } context" \_ ->
37− runOm { apiKey: "mykey", userId: 7 } { exception: throwError } do
38− { apiKey, userId } <- ask
39− { name } <- api.getUser { id: userId }
40− liftAff $ (apiKey <> ":" <> name) `shouldEqual` "mykey:User 7"
41−
42− it "same api.getUser works in two different contexts" \_ -> do
43−
44− runOm { token: "tok1" } { exception: throwError } do
45− { token } <- ask
46− { name } <- api.getUser { id: 1 }
47− liftAff $ (token <> ":" <> name) `shouldEqual` "tok1:User 1"
48−
49− runOm { endpoint: "prod", region: "eu" } { exception: throwError } do
50− { endpoint, region } <- ask
51− { name } <- api.getUser { id: 2 }
52− liftAff $ (endpoint <> "-" <> region <> ":" <> name) `shouldEqual` "prod-eu:User 2"
53−
54−withServer :: forall a. (Unit -> _ a) -> _ a
55−withServer test = bracket acquire release (\_ -> test unit)
56− where
57− acquire = do
58− fastify <- liftEffect do
59− f <- F.fastify {}
60− registerAPI @TestAPI { getUser: getUserHandler } f
61− pure f
62− void $ F.listen { port: Port 44931, host: Host "0.0.0.0" } fastify
63− pure fastify
64−
65− release = F.close
66−
67−getUserHandler :: Handler GetUserRoute ()
68−getUserHandler = handle do
69− { path } <- ask
70− respond @"ok" { id: path.id, name: "User " <> show path.id }
25+testPolymorphic :: Effect ViTest
26+testPolymorphic = describe "polymorphic client" do
27+ _ <- test "fetches user with { token :: String } context" do
28+ tokenRef <- liftEffect $ Ref.new ""
29+ runOm { token: "secret123" } { exception: \_ -> pure unit } do
30+ ctx <- ask
31+ liftEffect $ Ref.write ctx.token tokenRef
32+ user <- api.getUser { id: 42 }
33+ liftEffect $ Ref.write (ctx.token <> ":" <> user.name) tokenRef
34+ val <- liftEffect $ Ref.read tokenRef
35+ expectToBe val "secret123:User 42"
36+
37+ _ <- test "fetches user with { apiKey :: String, userId :: Int } context" do
38+ resultRef <- liftEffect $ Ref.new ""
39+ runOm { apiKey: "mykey", userId: 7 } { exception: \_ -> pure unit } do
40+ ctx <- ask
41+ user <- api.getUser { id: ctx.userId }
42+ liftEffect $ Ref.write (ctx.apiKey <> ":" <> user.name) resultRef
43+ val <- liftEffect $ Ref.read resultRef
44+ expectToBe val "mykey:User 7"
45+
46+ test "same api.getUser works in two different contexts" do
47+ ref1 <- liftEffect $ Ref.new ""
48+ ref2 <- liftEffect $ Ref.new ""
49+ runOm { token: "tok1" } { exception: \_ -> pure unit } do
50+ ctx <- ask
51+ user <- api.getUser { id: 1 }
52+ liftEffect $ Ref.write (ctx.token <> ":" <> user.name) ref1
53+ runOm { endpoint: "prod", region: "eu" } { exception: \_ -> pure unit } do
54+ ctx <- ask
55+ user <- api.getUser { id: 2 }
56+ liftEffect $ Ref.write (ctx.endpoint <> "-" <> ctx.region <> ":" <> user.name) ref2
57+ val1 <- liftEffect $ Ref.read ref1
58+ val2 <- liftEffect $ Ref.read ref2
59+ expectToBe val1 "tok1:User 1"
60+ expectToBe val2 "prod-eu:User 2"
test/Roundtrip.Spec.pursdeleted−380
@@ -1,380 +0,0 @@
1−module Roundtrip.Spec where
2−
3−import Prelude
4−
5−import Data.Maybe (Maybe(..))
6−import Effect.Aff (bracket)
7−import Effect.Aff.Class (liftAff)
8−import Effect.Class (liftEffect)
9−import Effect.Ref as Ref
10−import Effect.Exception as Exn
11−import Justifill (justifill)
12−import Test.Spec (Spec, around, describe, it)
13−import Test.Spec.Assertions (shouldEqual)
14−import Yoga.Fastify.Fastify as F
15−import Yoga.Fastify.Fastify (Port(..), Host(..))
16−import Yoga.Fastify.Om.API (registerAPI)
17−import Yoga.Fastify.Om.Route (Handler, handle, respond, reject, BearerToken(..))
18−import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Required, Route, JSON, type (/), type (:), type (:?), client, clientWith)
19−import Yoga.Om (ask, handleErrors, runOm)
20−
21−-- Types
22−
23−type User = { id :: Int, name :: String, email :: String }
24−type CreateUserReq = { name :: String, email :: String }
25−type ErrorMsg = { error :: String }
26−
27−-- Route definitions shared by server and client
28−
29−type GetUserRoute = Route GET
30− ("users" / "id" : Int)
31− {}
32− ( ok :: { body :: User }
33− , notFound :: { body :: ErrorMsg }
34− )
35−
36−type ListUsersRoute = Route GET
37− ("users" :? { limit :: Int, offset :: Int })
38− {}
39− (ok :: { body :: Array User })
40−
41−type CreateUserRoute = Route POST
42− "users"
43− { body :: JSON CreateUserReq }
44− ( created :: { body :: User }
45− , badRequest :: { body :: ErrorMsg }
46− )
47−
48−type UpdateUserRoute = Route PUT
49− ("users" / "id" : Int)
50− { body :: JSON CreateUserReq }
51− ( ok :: { body :: User }
52− , notFound :: { body :: ErrorMsg }
53− )
54−
55−type DeleteUserRoute = Route DELETE
56− ("users" / "id" : Int)
57− {}
58− ( ok :: { body :: {} }
59− , notFound :: { body :: ErrorMsg }
60− )
61−
62−type AuthRoute = Route GET
63− "me"
64− { headers :: { authorization :: BearerToken } }
65− ( ok :: { body :: User }
66− , unauthorized :: { body :: ErrorMsg }
67− )
68−
69−type SearchRoute = Route GET
70− ("search" :? { active :: Boolean, minScore :: Number })
71− {}
72− (ok :: { body :: { active :: Boolean, minScore :: Number } })
73−
74−type FindByQueryRoute = Route GET
75− ("find-by-query" :? { id :: Int })
76− {}
77− (ok :: { body :: { id :: Int } })
78−
79−type InjectedTenantRoute = Route GET
80− ("tenant-users" / "id" : Int :? { tenant :: Required String })
81− { headers :: { authorization :: BearerToken, requestid :: String } }
82− ( ok :: { body :: User }
83− , unauthorized :: { body :: ErrorMsg }
84− )
85−
86−type InjectedAPI =
87− { getTenantUser :: InjectedTenantRoute
88− }
89−
90−injectedApi tokenRef =
91− clientWith @InjectedAPI
92− { baseUrl: pure "http://localhost:44932"
93− , provide: do
94− ctx <- ask
95− token <- liftEffect $ Ref.read tokenRef
96− pure
97− { headers: { authorization: BearerToken token, requestid: ctx.requestid }
98− , query: { tenant: ctx.tenant }
99− }
100− }
101−
102−type API =
103− { getUser :: GetUserRoute
104− , listUsers :: ListUsersRoute
105− , createUser :: CreateUserRoute
106− , updateUser :: UpdateUserRoute
107− , deleteUser :: DeleteUserRoute
108− , me :: AuthRoute
109− , search :: SearchRoute
110− , findByQuery :: FindByQueryRoute
111− }
112−
113−api = client @API "http://localhost:44932"
114−
115−-- Handlers
116−
117−getUserHandler :: Handler GetUserRoute ()
118−getUserHandler = handle do
119− { path } <- ask
120− if path.id == 42 then respond @"ok" { id: 42, name: "Alice", email: "alice@test.com" }
121− else reject @"notFound" { error: "User " <> show path.id <> " not found" }
122−
123−listUsersHandler :: Handler ListUsersRoute ()
124−listUsersHandler = handle do
125− { query } <- ask
126− let
127− users =
128− [ { id: 1, name: "Alice", email: "a@t.com" }
129− , { id: 2, name: "Bob", email: "b@t.com" }
130− , { id: 3, name: "Charlie", email: "c@t.com" }
131− ]
132− let
133− limited = case query.limit of
134− Just l -> take l users
135− Nothing -> users
136− respond @"ok" limited
137− where
138− take n xs =
139− if n <= 0 then []
140− else case xs of
141− [] -> []
142− [ a ] -> [ a ]
143− [ a, _ ] | n == 1 -> [ a ]
144− [ a, b ] -> [ a, b ]
145− _ -> xs
146−
147−createUserHandler :: Handler CreateUserRoute ()
148−createUserHandler = handle do
149− { body } <- ask
150− if body.name == "" then reject @"badRequest" { error: "Name required" }
151− else respond @"created" { id: 99, name: body.name, email: body.email }
152−
153−updateUserHandler :: Handler UpdateUserRoute ()
154−updateUserHandler = handle do
155− { path, body } <- ask
156− if path.id /= 42 then reject @"notFound" { error: "Not found" }
157− else respond @"ok" { id: path.id, name: body.name, email: body.email }
158−
159−deleteUserHandler :: Handler DeleteUserRoute ()
160−deleteUserHandler = handle do
161− { path } <- ask
162− if path.id /= 42 then reject @"notFound" { error: "Not found" }
163− else respond @"ok" {}
164−
165−meHandler :: Handler AuthRoute ()
166−meHandler = handle do
167− { headers } <- ask
168− case headers.authorization of
169− BearerToken "secret" -> respond @"ok" { id: 1, name: "Admin", email: "admin@test.com" }
170− _ -> reject @"unauthorized" { error: "Invalid token" }
171−
172−searchHandler :: Handler SearchRoute ()
173−searchHandler = handle do
174− { query } <- ask
175− let
176− active = case query.active of
177− Just a -> a
178− Nothing -> false
179− let
180− minScore = case query.minScore of
181− Just s -> s
182− Nothing -> 0.0
183− respond @"ok" { active, minScore }
184−
185−findByQueryHandler :: Handler FindByQueryRoute ()
186−findByQueryHandler = handle do
187− { query } <- ask
188− let
189− id = case query.id of
190− Just value -> value
191− Nothing -> -1
192− respond @"ok" { id }
193−
194−tenantUserHandler :: Handler InjectedTenantRoute ()
195−tenantUserHandler = handle do
196− { path, query, headers } <- ask
197− case headers.authorization of
198− BearerToken "secret" -> respond @"ok" { id: path.id, name: query.tenant <> ":" <> headers.requestid, email: "tenant@test.com" }
199− _ -> reject @"unauthorized" { error: "Invalid token" }
200−
201−-- Test suite
202−
203−spec :: Spec Unit
204−spec = around withServer $ describe "server ↔ client roundtrip" do
205−
206− describe "GET with path params" do
207− it "returns user for valid id" \_ ->
208− runOm {} { exception: \_ -> pure unit } do
209− user <- api.getUser { id: 42 }
210− # handleErrors { notFound: \e -> pure { id: 0, name: e.error, email: "" } }
211− liftAff $ user.name `shouldEqual` "Alice"
212−
213− it "returns notFound for invalid id" \_ ->
214− runOm {} { exception: \_ -> pure unit } do
215− user <- api.getUser { id: 999 }
216− # handleErrors { notFound: \e -> pure { id: 0, name: e.error, email: "" } }
217− liftAff $ user.name `shouldEqual` "User 999 not found"
218−
219− describe "GET with query params" do
220− it "passes query params correctly" \_ ->
221− runOm {} { exception: \_ -> pure unit } do
222− users <- api.listUsers (justifill { limit: 1 })
223− liftAff $ (map _.name users) `shouldEqual` [ "Alice" ]
224−
225− it "serializes a single explicit Maybe query through the derived client" \_ ->
226− runOm {} { exception: \_ -> pure unit } do
227− result <- api.findByQuery { id: Just 42 }
228− liftAff $ result.id `shouldEqual` 42
229−
230− it "handles no query params" \_ ->
231− runOm {} { exception: \_ -> pure unit } do
232− users <- api.listUsers (justifill {})
233− liftAff $ (map _.name users) `shouldEqual` [ "Alice", "Bob", "Charlie" ]
234−
235− describe "POST with JSON body" do
236− it "sends and receives JSON body" \_ ->
237− runOm {} { exception: \_ -> pure unit } do
238− user <- api.createUser { name: "Dave", email: "d@t.com" }
239− # handleErrors { badRequest: \e -> pure { id: 0, name: e.error, email: "" } }
240− liftAff do
241− user.name `shouldEqual` "Dave"
242− user.email `shouldEqual` "d@t.com"
243− user.id `shouldEqual` 99
244−
245− it "rejects with badRequest for empty name" \_ ->
246− runOm {} { exception: \_ -> pure unit } do
247− user <- api.createUser { name: "", email: "e@t.com" }
248− # handleErrors { badRequest: \e -> pure { id: 0, name: e.error, email: "" } }
249− liftAff $ user.name `shouldEqual` "Name required"
250−
251− describe "PUT with path params and JSON body" do
252− it "sends path params and body together" \_ ->
253− runOm {} { exception: \_ -> pure unit } do
254− user <-
255− api.updateUser
256− { id: 42 }
257− { name: "Updated", email: "u@t.com" }
258− # handleErrors { notFound: \e -> pure { id: 0, name: e.error, email: "" } }
259− liftAff do
260− user.id `shouldEqual` 42
261− user.name `shouldEqual` "Updated"
262−
263− it "supports partial application before the body argument" \_ ->
264− runOm {} { exception: \_ -> pure unit } do
265− let update42 = api.updateUser { id: 42 }
266− user <- update42 { name: "Curried", email: "curried@t.com" }
267− # handleErrors { notFound: \e -> pure { id: 0, name: e.error, email: "" } }
268− liftAff do
269− user.id `shouldEqual` 42
270− user.name `shouldEqual` "Curried"
271−
272− describe "DELETE with path params" do
273− it "deletes successfully" \_ ->
274− runOm {} { exception: \_ -> pure unit } do
275− result <- api.deleteUser { id: 42 }
276− # handleErrors { notFound: \_ -> pure {} }
277− liftAff $ result `shouldEqual` {}
278−
279− it "returns notFound for wrong id" \_ ->
280− runOm {} { exception: \_ -> pure unit } do
281− result <- api.deleteUser { id: 1 }
282− # handleErrors { notFound: \_ -> pure {} }
283− liftAff $ result `shouldEqual` {}
284−
285− describe "GET with authorization header" do
286− it "passes bearer token in header" \_ ->
287− runOm {} { exception: \_ -> pure unit } do
288− user <- api.me { authorization: BearerToken "secret" }
289− # handleErrors { unauthorized: \e -> pure { id: 0, name: e.error, email: "" } }
290− liftAff $ user.name `shouldEqual` "Admin"
291−
292− it "rejects unauthorized" \_ ->
293− runOm {} { exception: \_ -> pure unit } do
294− user <- api.me { authorization: BearerToken "wrong" }
295− # handleErrors { unauthorized: \e -> pure { id: 0, name: e.error, email: "" } }
296− liftAff $ user.name `shouldEqual` "Invalid token"
297−
298− describe "GET with Boolean and Number query params" do
299− it "passes Boolean and Number query params" \_ ->
300− runOm {} { exception: \_ -> pure unit } do
301− result <- api.search (justifill { active: true, minScore: 3.14 })
302− liftAff do
303− result.active `shouldEqual` true
304− result.minScore `shouldEqual` 3.14
305−
306− it "handles missing optional query params" \_ ->
307− runOm {} { exception: \_ -> pure unit } do
308− result <- api.search (justifill {})
309− liftAff do
310− result.active `shouldEqual` false
311− result.minScore `shouldEqual` 0.0
312−
313− describe "optional query params" do
314− it "accepts a selected optional query parameter" \_ ->
315− runOm {} { exception: \_ -> pure unit } do
316− users <- api.listUsers (justifill { limit: 1 })
317− liftAff $ (map _.name users) `shouldEqual` [ "Alice" ]
318−
319− it "accepts all optional query parameters" \_ ->
320− runOm {} { exception: \_ -> pure unit } do
321− users <- api.listUsers (justifill { limit: 2, offset: 0 })
322− liftAff $ (map _.name users) `shouldEqual` [ "Alice", "Bob" ]
323−
324− it "fills omitted optional query parameters with Nothing" \_ ->
325− runOm {} { exception: \_ -> pure unit } do
326− users <- api.listUsers (justifill {})
327− liftAff $ (map _.name users) `shouldEqual` [ "Alice", "Bob", "Charlie" ]
328−
329− it "passes selected Boolean query param" \_ ->
330− runOm {} { exception: \_ -> pure unit } do
331− result <- api.search (justifill { active: true })
332− liftAff do
333− result.active `shouldEqual` true
334− result.minScore `shouldEqual` 0.0
335−
336− describe "clientWith injected inputs" do
337− it "reads provided headers and query params from Om context for each call" \_ -> do
338− tokenRef <- liftEffect $ Ref.new "secret"
339− let injected = injectedApi tokenRef
340− user <- runOm { tenant: "acme", requestid: "req-1" }
341− { exception: \e -> pure { id: -1, name: Exn.message e, email: "" }
342− , unauthorized: \e -> pure { id: -2, name: e.error, email: "" }
343− }
344− (injected.getTenantUser { id: 7 })
345− liftAff do
346− user.id `shouldEqual` 7
347− user.name `shouldEqual` "acme:req-1"
348− liftEffect $ Ref.write "wrong" tokenRef
349− rejected <- runOm { tenant: "acme", requestid: "req-2" }
350− { exception: \e -> pure { id: -1, name: Exn.message e, email: "" }
351− , unauthorized: \e -> pure { id: -2, name: e.error, email: "" }
352− }
353− (injected.getTenantUser { id: 8 })
354− liftAff $ rejected.name `shouldEqual` "Invalid token"
355−
356−-- Server setup
357−
358−withServer :: forall a. (Unit -> _ a) -> _ a
359−withServer test = bracket acquire release (\_ -> test unit)
360− where
361− acquire = do
362− fastify <- liftEffect do
363− f <- F.fastify {}
364− registerAPI @API
365− { getUser: getUserHandler
366− , listUsers: listUsersHandler
367− , createUser: createUserHandler
368− , updateUser: updateUserHandler
369− , deleteUser: deleteUserHandler
370− , me: meHandler
371− , search: searchHandler
372− , findByQuery: findByQueryHandler
373− }
374− f
375− registerAPI @InjectedAPI { getTenantUser: tenantUserHandler } f
376− pure f
377− void $ F.listen { port: Port 44932, host: Host "0.0.0.0" } fastify
378− pure fastify
379−
380− release = F.close
test/SplitParams.Spec.purs+31−22
@@ -2,13 +2,22 @@ module SplitParams.Spec where
22
33import Prelude
44
5−import Test.Spec (Spec, describe, it)
6−import Test.Spec.Assertions (shouldEqual)
5+import Effect (Effect)
6+import Effect.Aff (Aff)
77import Yoga.Fetch.Om.SplitParams (splitParams)
8+import ViTest (ViTest, describe, test)
9+import ViTest.Expect (expectToBe)
810
9−spec :: Spec Unit
10−spec = describe "splitParams" do
11− it "splits path and query params" do
11+expectToEqual :: forall a. Eq a => a -> a -> Aff Unit
12+expectToEqual expected actual = expectToBe true (expected == actual)
13+
14+--------------------------------------------------------------------------------
15+-- SplitParams Tests
16+--------------------------------------------------------------------------------
17+
18+testSplitParams :: Effect ViTest
19+testSplitParams = describe "splitParams" $ do
20+ _ <- test "splits path and query params" do
1221 let
1322 allParams = { id: 42, limit: 10, offset: 20 }
1423 result =
@@ -17,12 +26,12 @@ spec = describe "splitParams" do
1726 , query :: { limit :: Int, offset :: Int }
1827 , body :: {}
1928 }
20− result.path.id `shouldEqual` 42
21− result.query.limit `shouldEqual` 10
22− result.query.offset `shouldEqual` 20
23− result.body `shouldEqual` {}
29+ expectToEqual 42 result.path.id
30+ expectToEqual 10 result.query.limit
31+ expectToEqual 20 result.query.offset
32+ expectToEqual {} result.body
2433
25− it "splits path and body params" do
34+ _ <- test "splits path and body params" do
2635 let
2736 allParams = { id: 42, name: "Alice", email: "alice@example.com" }
2837 result =
@@ -31,12 +40,12 @@ spec = describe "splitParams" do
3140 , query :: {}
3241 , body :: { name :: String, email :: String }
3342 }
34− result.path.id `shouldEqual` 42
35− result.query `shouldEqual` {}
36− result.body.name `shouldEqual` "Alice"
37− result.body.email `shouldEqual` "alice@example.com"
43+ expectToEqual 42 result.path.id
44+ expectToEqual {} result.query
45+ expectToEqual "Alice" result.body.name
46+ expectToEqual "alice@example.com" result.body.email
3847
39− it "splits all three param types" do
48+ _ <- test "splits all three param types" do
4049 let
4150 allParams =
4251 { id: 42
@@ -49,11 +58,11 @@ spec = describe "splitParams" do
4958 , query :: { limit :: Int }
5059 , body :: { name :: String }
5160 }
52− result.path.id `shouldEqual` 42
53− result.query.limit `shouldEqual` 10
54− result.body.name `shouldEqual` "Bob"
61+ expectToEqual 42 result.path.id
62+ expectToEqual 10 result.query.limit
63+ expectToEqual "Bob" result.body.name
5564
56− it "handles empty splits" do
65+ test "handles empty splits" do
5766 let
5867 allParams = { id: 42 }
5968 result =
@@ -62,6 +71,6 @@ spec = describe "splitParams" do
6271 , query :: {}
6372 , body :: {}
6473 }
65− result.path.id `shouldEqual` 42
66− result.query `shouldEqual` {}
67− result.body `shouldEqual` {}
74+ expectToEqual 42 result.path.id
75+ expectToEqual {} result.query
76+ expectToEqual {} result.body
test/StreamDecode.Spec.jsdeleted−1
@@ -1 +0,0 @@
1−export const uint8Array = (values) => new Uint8Array(values);
test/StreamDecode.Spec.pursdeleted−32
@@ -1,32 +0,0 @@
1−module StreamDecode.Spec where
2−
3−import Prelude
4−
5−import Data.ArrayBuffer.Types (Uint8Array)
6−import Data.String as String
7−import Effect.Aff (Aff)
8−import Effect.Class (liftEffect)
9−import Effect.Exception (throwException)
10−import Test.Spec (Spec, describe, it)
11−import Test.Spec.Assertions (shouldEqual)
12−import Yoga.Fetch.Om.StreamDecode (decodeStream)
13−import Yoga.Om (Om)
14−import Yoga.Om as Om
15−import Yoga.Om.Strom as Strom
16−
17−foreign import uint8Array :: Array Int -> Uint8Array
18−
19−runOm :: forall a. Om {} () a -> Aff a
20−runOm om = Om.runOm {} { exception: \err -> liftEffect (throwException err) } om
21−
22−spec :: Spec Unit
23−spec = describe "StreamDecode" do
24− it "preserves UTF-8 code points split across chunks" do
25− chunks <- runOm $
26− Strom.fromArray
27− [ uint8Array [ 0xe2 ]
28− , uint8Array [ 0x82, 0xac ]
29− ]
30− # decodeStream @String
31− # Strom.runCollect
32− String.joinWith "" chunks `shouldEqual` "€"
test/Test.Main.pursnew file+24
@@ -0,0 +1,24 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import BuildUrl.Spec as BuildUrl
6+import MakeRequest.Spec as MakeRequest
7+import Polymorphic.Spec as Polymorphic
8+import SplitParams.Spec as SplitParams
9+import Variant.Spec as Variant
10+import Effect (Effect)
11+import ViTest (ViTest, viTest)
12+
13+spec :: Effect ViTest
14+spec = do
15+ _ <- BuildUrl.testSubstitutePathParams
16+ _ <- BuildUrl.testAppendQueryParams
17+ _ <- BuildUrl.testBuildUrl
18+ _ <- SplitParams.testSplitParams
19+ _ <- Variant.testOnly
20+ _ <- MakeRequest.testToHeaders
21+ Polymorphic.testPolymorphic
22+
23+main :: ViTest
24+main = viTest spec
test/Test/Main.pursdeleted−14
@@ -1,14 +0,0 @@
1−module Test.Main where
2−
3−import Prelude
4−
5−import Effect (Effect)
6−import Effect.Aff (launchAff_)
7−import Test.Spec.Discovery (discover)
8−import Test.Spec.Reporter (consoleReporter)
9−import Test.Spec.Runner (runSpec)
10−
11−main :: Effect Unit
12−main = launchAff_ do
13− specs <- discover ".*\\.Spec"
14− runSpec [ consoleReporter ] specs
test/Variant.Spec.purs+11−6
@@ -4,16 +4,21 @@ import Prelude
44
55import Data.Variant (Variant)
66import Data.Variant as Variant
7−import Test.Spec (Spec, describe, it)
8−import Test.Spec.Assertions (shouldEqual)
7+import Effect (Effect)
8+import Effect.Aff (Aff)
99import Type.Proxy (Proxy(..))
10+import ViTest (ViTest, describe, test)
11+import ViTest.Expect (expectToBe)
1012import Yoga.Fetch.Om.Variant (only)
1113
12−spec :: Spec Unit
13−spec = describe "only" do
14− it "extracts single variant value - record" do
14+expectToEqual :: forall a. Eq a => a -> a -> Aff Unit
15+expectToEqual expected actual = expectToBe true (expected == actual)
16+
17+testOnly :: Effect ViTest
18+testOnly = describe "only" do
19+ test "extracts single variant value - record" do
1520 let
1621 variant :: Variant (ok :: { id :: Int, name :: String })
1722 variant = Variant.inj (Proxy :: _ "ok") { id: 42, name: "Alice" }
1823 result = only variant
19− result `shouldEqual` { id: 42, name: "Alice" }
24+ expectToEqual { id: 42, name: "Alice" } result
test/setup.jsnew file+16
@@ -0,0 +1,16 @@
1+import {
2+ mkServer,
3+ listenServer,
4+ closeServer,
5+} from "../output/TestServer/index.js";
6+
7+let fastify;
8+
9+export async function setup() {
10+ fastify = mkServer();
11+ await listenServer(fastify)();
12+}
13+
14+export async function teardown() {
15+ await closeServer(fastify)();
16+}
vitest.config.jsnew file+10
@@ -0,0 +1,10 @@
1+import { defineConfig } from "vitest/config";
2+
3+export default defineConfig({
4+ test: {
5+ include: ["output/Test.Main/index.js"],
6+ environment: "jsdom",
7+ globals: true,
8+ globalSetup: ["test/setup.js"],
9+ },
10+});