git.iko.soy/rowtype-yoga/purescript-yoga-fetch-om
on 180a528 help sign in
51 files changed+500−2130
since splitsnapshots
unifiedsplit
.c8rc.json−22 .github/workflows/ci.yml+2−7 .gitignore−2 LICENSE−21 README.md+17−73 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+3−15 spago.lock+189−111 spago.yaml+6−18 src/Yoga/Fetch/Om.js−1 src/Yoga/Fetch/Om.purs+8−135 src/Yoga/Fetch/Om/BuildUrl.js+2−86 src/Yoga/Fetch/Om/BuildUrl.purs+13−43 src/Yoga/Fetch/Om/ClientFunction.purs+15−24 src/Yoga/Fetch/Om/MakeRequest.purs+13−6 src/Yoga/Fetch/Om/ParseResponse.purs+30−11 src/Yoga/Fetch/Om/ReadOptional.js−18 src/Yoga/Fetch/Om/ReadOptional.purs−56 src/Yoga/Fetch/Om/Simple.purs+15−6 src/Yoga/Fetch/Om/SplitResponses.purs+2−12 src/Yoga/Fetch/Om/StreamDecode.js+1−12 src/Yoga/Fetch/Om/StreamDecode.purs+2−43 test/BodylessResponse.Example.purs+1−1 test/BodylessResponse.Spec.purs+4−31 test/BuildUrl.Spec.purs+6−74 test/BuildUrlProperty.Spec.purs−256 test/Complete.Example.purs+19−14 test/Example.purs+8−13 test/MakeRequest.Spec.purs+5−15 test/PlainTextResponse.Spec.purs+8−49 test/Polymorphic.Spec.purs+18−21 test/Roundtrip.Spec.purs−380 test/StreamDecode.Spec.js−1 test/StreamDecode.Spec.purs−32
.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
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−73
@@ -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
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+3−15
@@ -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",
88 "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",
9+ "clean": "rm -rf output .spago",
1210 "watch": "spago build --watch",
1311 "repl": "spago repl",
1412 "docs": "spago docs",
@@ -26,20 +24,10 @@
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 },
4533 "dependencies": {
spago.lock+189−111
@@ -44,9 +44,6 @@
4444 {
4545 "strings": ">=6.0.1 <7.0.0"
4646 },
47− {
48− "tailrec": ">=6.1.0 <7.0.0"
49− },
5047 {
5148 "transformers": ">=6.1.0 <7.0.0"
5249 },
@@ -63,7 +60,7 @@
6360 "variant": ">=8.0.0 <9.0.0"
6461 },
6562 {
66− "yoga-http-api": ">=0.3.4 <0.4.0"
63+ "yoga-http-api": ">=0.3.2 <0.4.0"
6764 },
6865 {
6966 "yoga-json": ">=5.2.1 <6.0.0"
@@ -74,28 +71,97 @@
7471 {
7572 "yoga-om-strom": ">=0.4.3 <0.5.0"
7673 }
77− ]
78− },
79− "test": {
80− "dependencies": [
81− {
82− "console": ">=6.0.0 <7.0.0"
83− },
74+ ],
75+ "build_plan": [
76+ "aff",
77+ "aff-retry",
78+ "ansi",
79+ "arraybuffer-types",
80+ "arrays",
81+ "avar",
82+ "bifunctors",
83+ "catenable-lists",
84+ "console",
85+ "const",
86+ "contravariant",
87+ "control",
88+ "datetime",
89+ "distributive",
90+ "effect",
91+ "either",
8492 "enums",
93+ "exceptions",
94+ "exists",
8595 "foldable-traversable",
8696 "foreign",
8797 "foreign-object",
98+ "fork",
99+ "free",
100+ "functions",
101+ "functors",
102+ "gen",
103+ "heterogeneous",
104+ "http-methods",
105+ "identity",
88106 "integers",
107+ "invariant",
108+ "js-bigints",
109+ "js-date",
110+ "js-fetch",
89111 "js-promise",
90− {
91− "js-uri": ">=3.1.0 <4.0.0"
92− },
93− {
94− "justifill": ">=0.5.0 <0.6.0"
95− },
112+ "js-promise-aff",
113+ "lazy",
114+ "lists",
115+ "literals",
116+ "maybe",
117+ "media-types",
118+ "mmorph",
119+ "newtype",
120+ "nonempty",
121+ "now",
122+ "nullable",
123+ "numbers",
124+ "ordered-collections",
125+ "orders",
126+ "parallel",
96127 "partial",
128+ "pipes",
129+ "prelude",
130+ "profunctor",
131+ "random",
132+ "record",
133+ "record-studio",
134+ "refs",
135+ "safe-coerce",
136+ "spec",
137+ "st",
138+ "strings",
139+ "tailrec",
140+ "transformers",
141+ "tuples",
142+ "type-equality",
143+ "typelevel-prelude",
144+ "uncurried-transformers",
145+ "undefined-is-not-a-problem",
146+ "unfoldable",
147+ "unsafe-coerce",
148+ "untagged-union",
149+ "variant",
150+ "web-events",
151+ "web-file",
152+ "web-streams",
153+ "yoga-http-api",
154+ "yoga-json",
155+ "yoga-om",
156+ "yoga-om-strom",
157+ "yoga-options",
158+ "yoga-tree"
159+ ]
160+ },
161+ "test": {
162+ "dependencies": [
97163 {
98− "quickcheck": ">=8.0.1 <9.0.0"
164+ "console": ">=6.0.0 <7.0.0"
99165 },
100166 {
101167 "refs": ">=6.0.0 <7.0.0"
@@ -107,12 +173,106 @@
107173 "spec-discovery": ">=8.0.0 <9.0.0"
108174 },
109175 {
110− "spec-quickcheck": ">=5.0.2 <6.0.0"
111− },
112− "yoga-fastify",
113− {
114− "yoga-fastify-om": ">=0.4.7 <0.5.0"
176+ "yoga-fastify-om": ">=0.4.5 <0.5.0"
115177 }
178+ ],
179+ "build_plan": [
180+ "aff",
181+ "aff-retry",
182+ "ansi",
183+ "argonaut-codecs",
184+ "argonaut-core",
185+ "arraybuffer-types",
186+ "arrays",
187+ "avar",
188+ "bifunctors",
189+ "catenable-lists",
190+ "console",
191+ "const",
192+ "contravariant",
193+ "control",
194+ "datetime",
195+ "distributive",
196+ "effect",
197+ "either",
198+ "enums",
199+ "exceptions",
200+ "exists",
201+ "exitcodes",
202+ "foldable-traversable",
203+ "foreign",
204+ "foreign-object",
205+ "fork",
206+ "free",
207+ "functions",
208+ "functors",
209+ "gen",
210+ "heterogeneous",
211+ "identity",
212+ "integers",
213+ "invariant",
214+ "js-bigints",
215+ "js-date",
216+ "js-promise",
217+ "js-promise-aff",
218+ "js-uri",
219+ "lazy",
220+ "lists",
221+ "literals",
222+ "maybe",
223+ "mmorph",
224+ "newtype",
225+ "node-buffer",
226+ "node-event-emitter",
227+ "node-fs",
228+ "node-path",
229+ "node-process",
230+ "node-streams",
231+ "nonempty",
232+ "now",
233+ "nullable",
234+ "numbers",
235+ "open-memoize",
236+ "optparse",
237+ "ordered-collections",
238+ "orders",
239+ "parallel",
240+ "partial",
241+ "pipes",
242+ "posix-types",
243+ "prelude",
244+ "profunctor",
245+ "random",
246+ "record",
247+ "record-studio",
248+ "refs",
249+ "routing-duplex",
250+ "safe-coerce",
251+ "spec",
252+ "spec-discovery",
253+ "spec-node",
254+ "st",
255+ "strings",
256+ "tailrec",
257+ "transformers",
258+ "tuples",
259+ "type-equality",
260+ "typelevel-prelude",
261+ "uncurried-transformers",
262+ "undefined-is-not-a-problem",
263+ "unfoldable",
264+ "unsafe-coerce",
265+ "untagged-union",
266+ "variant",
267+ "yoga-fastify",
268+ "yoga-fastify-om",
269+ "yoga-http-api",
270+ "yoga-json",
271+ "yoga-om",
272+ "yoga-om-layer",
273+ "yoga-om-strom",
274+ "yoga-options",
275+ "yoga-tree"
116276 ]
117277 }
118278 }
@@ -694,17 +854,6 @@
694854 "prelude"
695855 ]
696856 },
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− },
708857 "lazy": {
709858 "type": "registry",
710859 "version": "6.0.0",
@@ -716,20 +865,6 @@
716865 "prelude"
717866 ]
718867 },
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− },
733868 "lists": {
734869 "type": "registry",
735870 "version": "7.0.0",
@@ -1109,39 +1244,6 @@
11091244 "tuples"
11101245 ]
11111246 },
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− },
11451247 "random": {
11461248 "type": "registry",
11471249 "version": "6.0.0",
@@ -1292,22 +1394,6 @@
12921394 "tuples"
12931395 ]
12941396 },
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− },
13111397 "st": {
13121398 "type": "registry",
13131399 "version": "6.2.0",
@@ -1544,8 +1630,8 @@
15441630 },
15451631 "yoga-fastify": {
15461632 "type": "registry",
1547− "version": "0.5.4",
1548− "integrity": "sha256-YenjePRdiApMPpcvkXlzre7c4fIuvk5T19Z2uEAWhR4=",
1633+ "version": "0.5.3",
1634+ "integrity": "sha256-KeAoEYXXNH2gHEl5Y7j9uMo5ipByLZp/p7u6pgQn6KU=",
15491635 "dependencies": [
15501636 "aff",
15511637 "arrays",
@@ -1571,8 +1657,8 @@
15711657 },
15721658 "yoga-fastify-om": {
15731659 "type": "registry",
1574− "version": "0.4.7",
1575− "integrity": "sha256-KGQ7lHI7pcSiEQoEq6j6K+3XilcPy8wQ/M9C1y+uaEk=",
1660+ "version": "0.4.5",
1661+ "integrity": "sha256-fPDRni+HjddpI7GPad5U2kVHxMPOZIsvTPFZtOPpiWI=",
15761662 "dependencies": [
15771663 "aff",
15781664 "console",
@@ -1584,12 +1670,10 @@
15841670 "integers",
15851671 "maybe",
15861672 "node-buffer",
1587− "numbers",
15881673 "prelude",
15891674 "record",
15901675 "refs",
15911676 "routing-duplex",
1592− "strings",
15931677 "transformers",
15941678 "typelevel-prelude",
15951679 "unsafe-coerce",
@@ -1604,25 +1688,19 @@
16041688 },
16051689 "yoga-http-api": {
16061690 "type": "registry",
1607− "version": "0.3.4",
1608− "integrity": "sha256-DSIJ8YWr5vB5IMpyaBALM+fx57lz3AAAR0MGtfHeWxo=",
1691+ "version": "0.3.2",
1692+ "integrity": "sha256-SiL07TYVjHPiQjMhnkYdoI3KrL6gNn1W1LigCN+80jA=",
16091693 "dependencies": [
16101694 "aff",
1611− "arraybuffer-types",
16121695 "arrays",
1613− "effect",
16141696 "either",
16151697 "foldable-traversable",
16161698 "foreign",
16171699 "foreign-object",
16181700 "identity",
16191701 "integers",
1620− "js-promise",
1621− "js-promise-aff",
16221702 "maybe",
16231703 "newtype",
1624− "nullable",
1625− "numbers",
16261704 "prelude",
16271705 "record",
16281706 "strings",
spago.yaml+6−18
@@ -2,7 +2,7 @@ workspace: {}
22package:
33 name: yoga-fetch-om
44 publish:
5− version: 0.8.1
5+ version: 0.7.0
66 license: MIT
77 location:
88 githubOwner: rowtype-yoga
@@ -10,6 +10,7 @@ package:
1010 include:
1111 - LICENSE
1212 - README.md
13+ - CHANGELOG.md
1314 exclude:
1415 - .github
1516 dependencies:
@@ -26,33 +27,20 @@ package:
2627 - prelude: ">=6.0.2 <7.0.0"
2728 - record: ">=4.0.0 <5.0.0"
2829 - strings: ">=6.0.1 <7.0.0"
29− - tailrec: ">=6.1.0 <7.0.0"
3030 - transformers: ">=6.1.0 <7.0.0"
3131 - tuples: ">=7.0.0 <8.0.0"
3232 - typelevel-prelude: ">=7.0.0 <8.0.0"
3333 - unsafe-coerce: ">=6.0.0 <7.0.0"
3434 - variant: ">=8.0.0 <9.0.0"
35− - yoga-http-api: ">=0.3.4 <0.4.0"
35+ - yoga-http-api: ">=0.3.2 <0.4.0"
3636 - yoga-json: ">=5.2.1 <6.0.0"
3737 - yoga-om: ">=1.5.2 <2.0.0"
3838 - yoga-om-strom: ">=0.4.3 <0.5.0"
3939 test:
4040 main: Test.Main
4141 dependencies:
42− - enums
43− - foldable-traversable
44− - foreign
45− - foreign-object
46− - integers
47− - js-promise
48− - partial
49− - yoga-fastify
50− - 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− - refs: ">=6.0.0 <7.0.0"
5542 - spec: ">=8.0.0 <9.0.0"
5643 - 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"
44+ - console: ">=6.0.0 <7.0.0"
45+ - yoga-fastify-om: ">=0.4.5 <0.5.0"
46+ - refs: ">=6.0.0 <7.0.0"
src/Yoga/Fetch/Om.jsdeleted−1
@@ -1 +0,0 @@
1−export const mergeRecords = (left) => (right) => ({ ...right, ...left });
src/Yoga/Fetch/Om.purs+8−135
@@ -1,8 +1,5 @@
11module Yoga.Fetch.Om
22 ( client
3− , clientWith
4− , ClientInputs
5− , ClientWithConfig
63 , deriveClient
74 , class DeriveClient
85 , deriveClientImpl
@@ -10,17 +7,12 @@ 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
15+ , plainText
2416 , module Yoga.Fetch.Om.StreamDecode
2517 , module Yoga.Fetch.Om.Simple
2618 ) where
@@ -48,21 +40,9 @@ import Yoga.Fetch.Om.ParseResponse (class ParseResponse, parseResponse)
4840import Yoga.Fetch.Om.SplitResponses (class SplitResponses)
4941import Yoga.Fetch.Om.Variant (class VariantOrValue, variantOrValue)
5042import 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)
43+import Yoga.HTTP.API.Route (Route(..), GET, POST, PUT, DELETE, PATCH, Response(..), JSON, FormData, PlainText, Streaming, NoBody, class HeaderValue, printHeader)
5244import 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
45+import Yoga.Om (class ToOm, Om, toOm)
6646
6747-- | Extract row type from Record type
6848class RecordRow :: Type -> Row Type -> Constraint
@@ -120,11 +100,11 @@ instance
120100 , ToHeaders headersRL headers
121101 , CheckBodyIsUnit body bodyFlag
122102 , Row.Union routeErrors extraErr errRow
123− , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers (Record pathQuery) { | ctx } errRow result fn
103+ , BuildClientFn pathQueryRL headersRL bodyFlag body pathQuery headers { | ctx } errRow result fn
124104 ) =>
125105 DeriveClientFn method segments request response ctx extraErr routeErrors result fn where
126106 deriveClientFn baseUrl _ =
127− buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) identity impl
107+ buildClientFn (Proxy :: _ pathQueryRL) (Proxy :: _ headersRL) (Proxy :: _ bodyFlag) impl
128108 where
129109 impl :: Record pathQuery -> Record headers -> body -> Om { | ctx } errRow result
130110 impl pathQueryRec headersRec bodyVal = widenOm do
@@ -137,88 +117,18 @@ instance
137117 where
138118 pathParamsRec = unsafeCoerce pathQueryRec :: Record pathParams
139119 queryParamsRec = unsafeCoerce pathQueryRec :: Record queryParams
140−
141120 widenOm :: Om (Record ()) routeErrors ~> Om { | ctx } errRow
142121 widenOm = unsafeCoerce
143122
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−
208123class DeriveClient :: Row Type -> Row Type -> Row Type -> Row Type -> Constraint
209124class DeriveClient ctx extraErr routesRow clientsRow | routesRow ctx extraErr -> clientsRow where
210125 deriveClientImpl :: String -> Proxy (Record routesRow) -> Record clientsRow
211126
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.
127+-- | Derive API client functions from route definitions using VTA
215128-- |
216129-- | ```purescript
217130-- | type UserAPI = { getUser :: Route ... }
218131-- | 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
222132-- | ```
223133client :: forall @routes routesRow ctx extraErr clientsRow. RecordRow routes routesRow => DeriveClient ctx extraErr routesRow clientsRow => String -> Record clientsRow
224134client baseUrl = deriveClientImpl @ctx @extraErr baseUrl (Proxy :: _ { | routesRow })
@@ -231,13 +141,8 @@ client baseUrl = deriveClientImpl @ctx @extraErr baseUrl (Proxy :: _ { | routesR
231141deriveClient :: forall @routesRow ctx extraErr clientsRow. DeriveClient ctx extraErr routesRow clientsRow => String -> Record clientsRow
232142deriveClient baseUrl = deriveClientImpl @ctx @extraErr baseUrl (Proxy :: _ { | routesRow })
233143
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 })
144+plainText :: PlainText -> String
145+plainText = unsafeCoerce
241146
242147instance
243148 ( RowToList routesRow rl
@@ -246,38 +151,6 @@ instance
246151 DeriveClient ctx extraErr routesRow clientsRow where
247152 deriveClientImpl baseUrl _ = deriveClientRL @ctx @extraErr baseUrl (Proxy :: _ rl) {}
248153
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
252−
253−instance
254− ( 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
274− ) =>
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
280−
281154class DeriveClientRL :: Row Type -> Row Type -> RowList Type -> Row Type -> Row Type -> Constraint
282155class DeriveClientRL ctx extraErr rl acc out | ctx extraErr rl acc -> out where
283156 deriveClientRL :: String -> Proxy rl -> Record acc -> Record out
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+13−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,10 @@ instance
5046 pattern = pathPattern segmentsProxy
5147 withBase = case baseUrl of
5248 "" -> pattern
53− base -> appendPath_ base pattern
49+ base -> dropTrailingSlash base <> pattern
50+ dropTrailingSlash s = case String.stripSuffix (Pattern "/") s of
51+ Just s' -> dropTrailingSlash s'
52+ Nothing -> s
5453 withPathParams = substitutePathParamsImpl (Proxy :: _ pathParams) pathParamsRec withBase
5554 withQueryParams = appendQueryParamsImpl (Proxy :: _ queryParams) queryParamsRec withPathParams
5655
@@ -85,8 +84,8 @@ instance
8584 substitutePathParamsRL _ rec url = substitutePathParamsRL (Proxy :: _ tail) rec replaced
8685 where
8786 value = Record.get (Proxy :: _ name) rec
88− name = reflectSymbol (Proxy :: _ name)
89− replaced = substitutePathParam_ name (serializeParam value) url
87+ pattern = Pattern (":" <> reflectSymbol (Proxy :: _ name))
88+ replaced = String.replaceAll pattern (Replacement (serializeParam value)) url
9089
9190class AppendQueryParams :: Row Type -> Constraint
9291class AppendQueryParams params where
@@ -100,11 +99,12 @@ instance
10099 , AppendQueryParamsRL rl params
101100 ) =>
102101 AppendQueryParams params where
103− appendQueryParamsImpl _ rec url = case pairs of
104− [] -> url
105− _ -> appendQueryString_ url (String.joinWith "&" pairs)
102+ appendQueryParamsImpl _ rec url = url <> queryString
106103 where
107104 pairs = appendQueryParamsRL (Proxy :: _ rl) rec []
105+ queryString = case pairs of
106+ [] -> ""
107+ _ -> "?" <> String.joinWith "&" pairs
108108
109109class AppendQueryParamsRL :: RowList Type -> Row Type -> Constraint
110110class AppendQueryParamsRL rl r where
@@ -113,49 +113,19 @@ class AppendQueryParamsRL rl r where
113113instance AppendQueryParamsRL RL.Nil r where
114114 appendQueryParamsRL _ _ acc = acc
115115
116−-- Maybe (Array ty): optional repeated query param — missing/empty field → no pairs
117116instance
118117 ( IsSymbol name
119118 , SerializeParam ty
120119 , 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
120+ , Row.Cons name (Maybe ty) tailRow r
135121 ) =>
136122 AppendQueryParamsRL (RL.Cons name (Maybe ty) tail) r where
137123 appendQueryParamsRL _ rec acc = appendQueryParamsRL (Proxy :: _ tail) rec acc'
138124 where
139− acc' = case (readOptional @name rec :: Maybe ty) of
125+ acc' = case Record.get (Proxy :: _ name) rec of
140126 Nothing -> acc
141127 Just value -> Array.snoc acc (encodeURIComponent_ (reflectSymbol (Proxy :: _ name)) <> "=" <> encodeURIComponent_ (serializeParam value))
142128
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
159129else instance
160130 ( IsSymbol name
161131 , SerializeParam ty
src/Yoga/Fetch/Om/ClientFunction.purs+15−24
@@ -22,17 +22,16 @@ 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 -> 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 ctx 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 ctx errorRow result -> fn where
3131 buildClientFn
3232 :: Proxy pathQueryRL
3333 -> Proxy headersRL
3434 -> Proxy bodyFlag
35− -> (arg -> Record pathQuery)
3635 -> (Record pathQuery -> Record headers -> body -> Om ctx errorRow result)
3736 -> fn
3837
@@ -44,12 +43,11 @@ instance
4443 Unit
4544 ()
4645 ()
47− arg
4846 ctx
4947 errorRow
5048 result
5149 (Om ctx errorRow result) where
52− buildClientFn _ _ _ _ f = f {} {} unit
50+ buildClientFn _ _ _ f = f {} {} unit
5351
5452-- No path/query, no headers, with body
5553instance
@@ -59,12 +57,11 @@ instance
5957 body
6058 ()
6159 ()
62− arg
6360 ctx
6461 errorRow
6562 result
6663 (body -> Om ctx errorRow result) where
67− buildClientFn _ _ _ _ f b = f {} {} b
64+ buildClientFn _ _ _ f b = f {} {} b
6865
6966-- No path/query, with headers, no body
7067instance
@@ -74,12 +71,11 @@ instance
7471 Unit
7572 ()
7673 h
77− arg
7874 ctx
7975 errorRow
8076 result
8177 (Record h -> Om ctx errorRow result) where
82− buildClientFn _ _ _ _ f hdrs = f {} hdrs unit
78+ buildClientFn _ _ _ f hdrs = f {} hdrs unit
8379
8480-- No path/query, with headers, with body
8581instance
@@ -89,12 +85,11 @@ instance
8985 body
9086 ()
9187 h
92− arg
9388 ctx
9489 errorRow
9590 result
9691 (Record h -> body -> Om ctx errorRow result) where
97− buildClientFn _ _ _ _ f hdrs b = f {} hdrs b
92+ buildClientFn _ _ _ f hdrs b = f {} hdrs b
9893
9994-- With path/query, no headers, no body
10095instance
@@ -104,12 +99,11 @@ instance
10499 Unit
105100 pq
106101 ()
107− arg
108102 ctx
109103 errorRow
110104 result
111− (arg -> Om ctx errorRow result) where
112− buildClientFn _ _ _ convert f pqr = f (convert pqr) {} unit
105+ (Record pq -> Om ctx errorRow result) where
106+ buildClientFn _ _ _ f pqr = f pqr {} unit
113107
114108-- With path/query, no headers, with body
115109instance
@@ -119,12 +113,11 @@ instance
119113 body
120114 pq
121115 ()
122− arg
123116 ctx
124117 errorRow
125118 result
126− (arg -> body -> Om ctx errorRow result) where
127− buildClientFn _ _ _ convert f pqr b = f (convert pqr) {} b
119+ (Record pq -> body -> Om ctx errorRow result) where
120+ buildClientFn _ _ _ f pqr b = f pqr {} b
128121
129122-- With path/query, with headers, no body
130123instance
@@ -134,12 +127,11 @@ instance
134127 Unit
135128 pq
136129 h
137− arg
138130 ctx
139131 errorRow
140132 result
141− (arg -> Record h -> Om ctx errorRow result) where
142− buildClientFn _ _ _ convert f pqr hdrs = f (convert pqr) hdrs unit
133+ (Record pq -> Record h -> Om ctx errorRow result) where
134+ buildClientFn _ _ _ f pqr hdrs = f pqr hdrs unit
143135
144136-- With path/query, with headers, with body
145137instance
@@ -149,9 +141,8 @@ instance
149141 body
150142 pq
151143 h
152− arg
153144 ctx
154145 errorRow
155146 result
156− (arg -> Record h -> body -> Om ctx errorRow result) where
157− buildClientFn _ _ _ convert f pqr hdrs b = f (convert pqr) hdrs b
147+ (Record pq -> Record h -> body -> Om ctx errorRow result) where
148+ buildClientFn _ _ _ f = f
src/Yoga/Fetch/Om/MakeRequest.purs+13−6
@@ -2,6 +2,8 @@ module Yoga.Fetch.Om.MakeRequest
22 ( class MakeRequest
33 , httpMethod
44 , makeRequest
5+ , class SerializeBody
6+ , serializeBody
57 , class BodyEncoding
68 , encodingContentType
79 , encodeBody
@@ -30,8 +32,8 @@ import JS.Fetch.Integrity (Integrity(..))
3032import JS.Fetch.Duplex as Duplex
3133import JS.Fetch.Response (Response) as FetchResponse
3234import Promise.Aff as Promise
33−import Type.Proxy (Proxy)
34−import Yoga.HTTP.API.Route.Encoding (JSON, FormData, PlainText, NoBody)
35+import Type.Proxy (Proxy(..))
36+import Yoga.HTTP.API.Route.Encoding (JSON, FormData, NoBody)
3537import Yoga.HTTP.API.Route.Method as Method
3638import Yoga.JSON (class WriteForeign, writeJSON)
3739
@@ -94,6 +96,15 @@ makeRequest proxy url customHeaders bodyContentType maybeBody = do
9496 , cache: Cache.Default
9597 }
9698
99+class SerializeBody :: Type -> Constraint
100+class SerializeBody body where
101+ serializeBody :: body -> Maybe String
102+
103+instance SerializeBody Unit where
104+ serializeBody _ = Nothing
105+else instance WriteForeign body => SerializeBody body where
106+ serializeBody b = Just (writeJSON b)
107+
97108class BodyEncoding :: Type -> Type -> Constraint
98109class BodyEncoding encoding body | encoding -> body where
99110 encodingContentType :: Proxy encoding -> String
@@ -103,10 +114,6 @@ instance WriteForeign ty => BodyEncoding (JSON ty) ty where
103114 encodingContentType _ = "application/json"
104115 encodeBody b = Just (writeJSON b)
105116
106−else instance BodyEncoding PlainText String where
107− encodingContentType _ = "text/plain"
108− encodeBody = Just
109−
110117else instance BodyEncoding (FormData ty) ty where
111118 encodingContentType _ = "application/x-www-form-urlencoded"
112119 encodeBody b = Just (toUrlEncoded b)
src/Yoga/Fetch/Om/ParseResponse.purs+30−11
@@ -29,10 +29,11 @@ import Promise.Aff as Promise
2929import Type.Proxy (Proxy(..))
3030import Unsafe.Coerce (unsafeCoerce)
3131import Yoga.Fetch.Om.StreamDecode (class StreamDecode, decodeStream)
32+import Yoga.HTTP.API.Route.Encoding (PlainText, Streaming)
3233import Yoga.HTTP.API.Route.StatusCode (class StatusCodeMap, statusCodeFor, StatusCode(..))
3334import Yoga.JSON (class ReadForeign, readJSON)
3435import Yoga.JSON.Error (withStringErrors)
35−import Yoga.Om (Om, toOm)
36+import Yoga.Om (class ToOm, Om, toOm)
3637import Yoga.Om.Error (Exception)
3738import Yoga.Om.Strom (Strom)
3839import Yoga.Om.Strom.WebStream as WebStream
@@ -74,35 +75,37 @@ instance ParseSuccessRL RL.Nil successRow where
7475else instance
7576 ( IsSymbol label
7677 , StatusCodeMap label
77− , StreamDecode a
78− , Row.Cons label (Strom streamContext streamError a) tailRow successRow
78+ , Row.Cons label PlainText tailRow successRow
7979 , Row.Lacks label tailRow
8080 , ParseSuccessRL tailRL successRow
8181 ) =>
82− ParseSuccessRL (RL.Cons label (Strom streamContext streamError a) tailRL) successRow where
82+ ParseSuccessRL (RL.Cons label PlainText tailRL) successRow where
8383 parseSuccessRL _ actualStatus fetchResp = do
8484 let StatusCode expected = statusCodeFor (Proxy :: _ label)
8585 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))
86+ text <- liftEffect (Fetch.text fetchResp) >>= Promise.toAff
87+ pure (Variant.inj (Proxy :: _ label) (unsafeCoerce text))
8988 else
9089 parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
9190
9291else instance
9392 ( IsSymbol label
9493 , StatusCodeMap label
95− , Row.Cons label Unit tailRow successRow
94+ , StreamDecode a
95+ , Row.Cons label (Streaming a) tailRow successRow
9696 , Row.Lacks label tailRow
9797 , ParseSuccessRL tailRL successRow
9898 ) =>
99− ParseSuccessRL (RL.Cons label Unit tailRL) successRow where
99+ ParseSuccessRL (RL.Cons label (Streaming a) tailRL) successRow where
100100 parseSuccessRL _ actualStatus fetchResp = do
101101 let StatusCode expected = statusCodeFor (Proxy :: _ label)
102− if actualStatus == expected then
103− pure (Variant.inj (Proxy :: _ label) unit)
102+ if actualStatus == expected then do
103+ webStream <- liftEffect (Fetch.body fetchResp)
104+ let strom = decodeStream (WebStream.fromReadableStream (unsafeCoerce webStream)) :: Strom {} () a
105+ pure (Variant.inj (Proxy :: _ label) (unsafeCoerce strom))
104106 else
105107 parseSuccessRL (Proxy :: _ tailRL) actualStatus fetchResp
108+
106109else instance
107110 ( IsSymbol label
108111 , StatusCodeMap label
@@ -135,6 +138,22 @@ instance ParseErrorRL RL.Nil errorRow where
135138 parseErrorRL _ _ status _ =
136139 throwError (Variant.inj (Proxy :: _ "exception") (Exception.error ("Unexpected error status code: " <> show status)))
137140
141+else instance
142+ ( IsSymbol label
143+ , StatusCodeMap label
144+ , Row.Cons label PlainText tailRow errorRow
145+ , Row.Lacks label tailRow
146+ , Row.Cons label PlainText tailExc (Exception errorRow)
147+ , ParseErrorRL tailRL errorRow
148+ ) =>
149+ ParseErrorRL (RL.Cons label PlainText tailRL) errorRow where
150+ parseErrorRL _ errorProxy actualStatus text = do
151+ let StatusCode expected = statusCodeFor (Proxy :: _ label)
152+ if actualStatus == expected then
153+ throwError (Variant.inj (Proxy :: _ label) (unsafeCoerce text))
154+ else
155+ parseErrorRL (Proxy :: _ tailRL) errorProxy actualStatus text
156+
138157else instance
139158 ( IsSymbol label
140159 , StatusCodeMap label
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+15−6
@@ -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 (class ToOm, Om, toOm, 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
@@ -138,7 +147,7 @@ delete_
138147 => String
139148 -> Record headers
140149 -> Om ctx (fetchError :: FetchError | err) Unit
141−delete_ url headers = void $ simpleFetch @String DELETE headers url Nothing
150+delete_ url headers = void $ simpleFetch @PlainTextResponse DELETE headers url Nothing
142151
143152post
144153 :: forall @a result headers body ctx err
@@ -170,7 +179,7 @@ post_
170179 -> Record headers
171180 -> body
172181 -> Om ctx (fetchError :: FetchError | err) Unit
173−post_ url headers body = void $ simpleFetch @String POST headers url (Just (writeJSON body))
182+post_ url headers body = void $ simpleFetch @PlainTextResponse POST headers url (Just (writeJSON body))
174183
175184put
176185 :: forall @a result headers body ctx err
@@ -202,7 +211,7 @@ put_
202211 -> Record headers
203212 -> body
204213 -> Om ctx (fetchError :: FetchError | err) Unit
205−put_ url headers body = void $ simpleFetch @String PUT headers url (Just (writeJSON body))
214+put_ url headers body = void $ simpleFetch @PlainTextResponse PUT headers url (Just (writeJSON body))
206215
207216patch
208217 :: forall @a result headers body ctx err
@@ -234,4 +243,4 @@ patch_
234243 -> Record headers
235244 -> body
236245 -> Om ctx (fetchError :: FetchError | err) Unit
237−patch_ url headers body = void $ simpleFetch @String PATCH headers url (Just (writeJSON body))
246+patch_ url headers body = void $ simpleFetch @PlainTextResponse PATCH headers url (Just (writeJSON body))
src/Yoga/Fetch/Om/SplitResponses.purs+2−12
@@ -2,7 +2,6 @@ module Yoga.Fetch.Om.SplitResponses
22 ( class IsSuccessStatus
33 , class ExtractBodyRL
44 , class ExtractBody
5− , class ClientResponseBody
65 , class SplitResponses
76 , class SplitResponsesRL
87 , class DispatchResponse
@@ -14,8 +13,6 @@ import Prim.RowList (RowList)
1413import Prim.RowList as RL
1514import Data.Unit (Unit)
1615import Yoga.HTTP.API.Route (Response)
17−import Yoga.HTTP.API.Route.Encoding (PlainText, Streaming)
18−import Yoga.Om.Strom (Strom)
1916
2017class IsSuccessStatus (label :: Symbol) (isSuccess :: Boolean) | label -> isSuccess
2118
@@ -40,20 +37,13 @@ instance ExtractBodyRL (RL.Cons "body" body tail) body
4037
4138else instance ExtractBodyRL tail body => ExtractBodyRL (RL.Cons label ty tail) body
4239
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−
4940class ExtractBody (responseType :: Type) (bodyType :: Type) | responseType -> bodyType
5041
51−instance ClientResponseBody encoded body => ExtractBody (Response headers encoded) body
42+instance ExtractBody (Response headers body) body
5243
5344else instance
5445 ( RL.RowToList row rl
55− , ExtractBodyRL rl encoded
56− , ClientResponseBody encoded body
46+ , ExtractBodyRL rl body
5747 ) =>
5848 ExtractBody (Record row) body
5949
src/Yoga/Fetch/Om/StreamDecode.js+1−12
@@ -1,12 +1 @@
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();
1+export const decodeUtf8 = (uint8array) => new TextDecoder().decode(uint8array);
src/Yoga/Fetch/Om/StreamDecode.purs+2−43
@@ -4,15 +4,7 @@ module Yoga.Fetch.Om.StreamDecode
44 ) where
55
66import Prelude
7−
8−import Control.Monad.Rec.Class (Step(..))
9−import Data.Array as Array
107import 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)
168import Yoga.Om.Strom (Strom)
179import Yoga.Om.Strom as Strom
1810
@@ -23,39 +15,6 @@ instance StreamDecode Uint8Array where
2315 decodeStream = identity
2416
2517else 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
18+ decodeStream = Strom.mapStrom decodeUtf8
5719
58−nonEmpty :: forall a. Array a -> Maybe (Array a)
59−nonEmpty values
60− | Array.null values = Nothing
61− | otherwise = Just values
20+foreign import decodeUtf8 :: Uint8Array -> String
test/BodylessResponse.Example.purs+1−1
@@ -23,5 +23,5 @@ type ResourceAPI =
2323-- This must typecheck: the derived client exposes notModified/unauthorized as Unit errors.
2424api
2525 :: forall ctx err
26− . { getResource :: Om (Record ctx) (notModified :: Unit, unauthorized :: Unit | err) Resource }
26+ . { getResource :: Om ctx (notModified :: Unit, unauthorized :: Unit | err) Resource }
2727api = client @ResourceAPI "https://api.example.com"
test/BodylessResponse.Spec.purs+4−31
@@ -12,9 +12,9 @@ import Test.Spec (Spec, around, describe, it)
1212import Test.Spec.Assertions (shouldEqual)
1313import Yoga.Fastify.Fastify as F
1414import 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)
15+import Yoga.Fastify.Om (createOmFastify, getOm)
16+import Yoga.Fetch.Om (GET, Route, type (/), client)
17+import Yoga.Om (Om, runOm)
1818
1919type Resource = { id :: Int, name :: String }
2020
@@ -27,15 +27,9 @@ type ResourceAPI =
2727 , notModified :: {}
2828 , unauthorized :: {}
2929 )
30− , deleteUser ::
31− Route DELETE
32− ("bodyless-test" / "users" / "id" : Int)
33− {}
34− ( noContent :: {}
35− , notFound :: { body :: { error :: String } }
36− )
3730 }
3831
32+api :: forall ctx err. { getResource :: Om ctx (notModified :: Unit, unauthorized :: Unit | err) Resource }
3933api = client @ResourceAPI "http://localhost:44932"
4034
4135spec :: Spec Unit
@@ -74,15 +68,6 @@ spec = around withServer $ describe "bodyless responses (notModified / unauthori
7468 api.getResource
7569 liftAff $ resource `shouldEqual` { id: 1, name: "test" }
7670
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−
8671withServer :: forall a. (Ref String -> Aff a) -> Aff a
8772withServer test = bracket acquire release (\{ serverMode } -> test serverMode)
8873 where
@@ -92,7 +77,6 @@ withServer test = bracket acquire release (\{ serverMode } -> test serverMode)
9277 f <- F.fastify {}
9378 omApp <- createOmFastify {} f
9479 getOm (RouteURL "/bodyless-test/resource") (handler serverMode) omApp
95− deleteOm (RouteURL "/bodyless-test/users/:id") (deleteHandler serverMode) omApp
9680 pure f
9781 void $ F.listen { port: Port 44932, host: Host "0.0.0.0" } fastify
9882 pure { fastify, serverMode }
@@ -112,14 +96,3 @@ withServer test = bracket acquire release (\{ serverMode } -> test serverMode)
11296 void $ F.status (StatusCode 200) reply # liftEffect
11397 void $ F.header "content-type" "application/json" reply # liftEffect
11498 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+6−74
@@ -6,7 +6,6 @@ import Data.Maybe (Maybe(..))
66import Test.Spec (Spec, describe, it)
77import Test.Spec.Assertions (shouldEqual)
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 (:))
1211
@@ -34,19 +33,6 @@ spec = do
3433 { slug: "hello-world" }
3534 "/posts/:slug"
3635 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"
5036
5137 it "handles empty params" do
5238 let result = substitutePathParams @() {} "/users"
@@ -64,32 +50,24 @@ spec = do
6450 "/users"
6551 result `shouldEqual` "/users?limit=10&offset=20"
6652
67− it "handles optional params - provided via partial record" do
53+ it "handles Maybe params - Just" do
6854 let
6955 result = appendQueryParams @(limit :: Maybe Int)
70− (unsafeCoerce { limit: 10 } :: Record (limit :: Maybe Int))
56+ { limit: Just 10 }
7157 "/users"
7258 result `shouldEqual` "/users?limit=10"
7359
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
60+ it "handles Maybe params - Nothing" do
8361 let
8462 result = appendQueryParams @(limit :: Maybe Int)
85− (unsafeCoerce {} :: Record (limit :: Maybe Int))
63+ { limit: Nothing }
8664 "/users"
8765 result `shouldEqual` "/users"
8866
89− it "handles mixed optional and required params" do
67+ it "handles mixed Maybe and required params" do
9068 let
9169 result = appendQueryParams @(limit :: Maybe Int, offset :: Int)
92− (unsafeCoerce { offset: 20 } :: Record (limit :: Maybe Int, offset :: Int))
70+ { limit: Nothing, offset: 20 }
9371 "/users"
9472 result `shouldEqual` "/users?offset=20"
9573
@@ -100,34 +78,6 @@ spec = do
10078 it "URL-encodes special characters in values" do
10179 let result = appendQueryParams @(search :: String) { search: "hello world&more=yes" } "/users"
10280 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"
13181
13282 describe "buildUrl" do
13383 it "builds URL with path params only" do
@@ -147,21 +97,3 @@ spec = do
14797 {}
14898 { limit: 10, offset: 20 }
14999 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"
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+19−14
@@ -3,9 +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)
8+import Yoga.Fetch.Om (GET, POST, PUT, DELETE, Route, JSON, Path, type (/), type (:), type (:?), client)
99import Yoga.HTTP.API.Route (BearerToken(..))
1010import Yoga.Om (Om, handleErrors)
1111
@@ -31,37 +31,43 @@ type ErrorMessage =
3131
3232type UserAPI =
3333 { getUser ::
34− Route GET ("users" / "id" : Int)
34+ Route GET
35+ (Path ("users" / "id" : Int))
3536 {}
3637 ( ok :: { body :: User }
3738 , notFound :: { body :: ErrorMessage }
3839 )
3940 , listUsers ::
40− Route GET ("users" :? { limit :: Int, offset :: Int })
41+ Route GET
42+ (Path "users" :? { limit :: Int, offset :: Int })
4143 {}
4244 ( ok :: { body :: Array User }
4345 )
4446 , createUser ::
45− Route POST "users"
47+ Route POST
48+ (Path "users")
4649 { body :: JSON CreateUserRequest }
4750 ( created :: { body :: User }
4851 , badRequest :: { body :: ErrorMessage }
4952 )
5053 , updateUser ::
51− Route PUT ("users" / "id" : Int)
54+ Route PUT
55+ (Path ("users" / "id" : Int))
5256 { body :: JSON UpdateUserRequest }
5357 ( ok :: { body :: User }
5458 , notFound :: { body :: ErrorMessage }
5559 , badRequest :: { body :: ErrorMessage }
5660 )
5761 , deleteUser ::
58− Route DELETE ("users" / "id" : Int)
62+ Route DELETE
63+ (Path ("users" / "id" : Int))
5964 {}
60− ( noContent :: {}
65+ ( noContent :: { body :: {} }
6166 , notFound :: { body :: ErrorMessage }
6267 )
6368 , createUserAuth ::
64− Route POST "users"
69+ Route POST
70+ (Path "users")
6571 { headers :: Record (authorization :: BearerToken), body :: JSON CreateUserRequest }
6672 ( created :: { body :: User }
6773 , badRequest :: { body :: ErrorMessage }
@@ -79,15 +85,15 @@ exampleGetUserHandled :: Om {} () Unit
7985exampleGetUserHandled = do
8086 user <- api.getUser { id: 42 }
8187 # handleErrors
82− { notFound: \err -> ado
88+ { notFound: \err -> do
8389 log $ "User not found: " <> err.error
84− in { id: 0, name: "unknown", email: "" }
90+ pure { id: 0, name: "unknown", email: "" }
8591 }
8692 log $ "Found user: " <> user.name
8793
8894exampleListUsers :: Om {} () Unit
8995exampleListUsers = do
90− users <- api.listUsers (justifill { limit: 10, offset: 0 })
96+ users <- api.listUsers { limit: Just 10, offset: Just 0 }
9197 log $ "Found " <> show (length users) <> " users"
9298
9399exampleCreateUser :: Om {} (badRequest :: ErrorMessage) Unit
@@ -100,8 +106,7 @@ exampleCreateUser = do
100106
101107exampleUpdateUser :: Om {} (notFound :: ErrorMessage, badRequest :: ErrorMessage) Unit
102108exampleUpdateUser = do
103− user <- api.updateUser
104− { id: 42 }
109+ user <- api.updateUser { id: 42 }
105110 { name: "Alice Updated"
106111 , email: "alice.new@example.com"
107112 }
test/Example.purs+8−13
@@ -4,46 +4,41 @@ import Yoga.HTTP.API.Route (GET, POST, DELETE, Route, BearerToken)
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"
41+ (Path "users")
4742 { headers :: Record (authorization :: BearerToken), body :: JSON CreateUserRequest }
4843 ( created :: { body :: User }
4944 , badRequest :: { body :: ErrorMessage }
test/MakeRequest.Spec.purs+5−15
@@ -3,32 +3,22 @@ module MakeRequest.Spec where
33import Prelude
44
55import Data.HTTP.Method as HTTP
6−import Data.Maybe (Maybe(..))
76import Data.Tuple.Nested ((/\))
87import JS.Fetch.Headers as Headers
98import Prim.RowList as RL
109import Test.Spec (Spec, describe, it)
1110import Test.Spec.Assertions (shouldEqual, shouldSatisfy)
1211import Type.Proxy (Proxy(..))
13−import Yoga.Fetch.Om (FormData, PlainText, QUERY, toHeaders)
14−import Yoga.Fetch.Om.MakeRequest (encodeBody, encodingContentType, httpMethod)
12+import Yoga.Fetch.Om (toHeaders)
13+import Yoga.Fetch.Om.MakeRequest (httpMethod)
1514import Yoga.HTTP.API.Route (BearerToken(..))
15+import Yoga.HTTP.API.Route.Method as Route
1616
1717spec :: Spec Unit
1818spec = do
1919 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"
20+ it "maps a QUERY route method to the QUERY runtime method" do
21+ httpMethod (Proxy :: Proxy Route.QUERY) `shouldEqual` HTTP.QUERY
3222
3323 describe "toHeaders" do
3424 it "empty headers produces no entries" do
test/PlainTextResponse.Spec.purs+8−49
@@ -1,54 +1,42 @@
11module PlainTextResponse.Spec where
22
33import Prelude
4−import Data.String as String
54
65import Effect.Aff (Aff, bracket)
76import Effect.Aff.Class (liftAff)
87import Effect.Class (liftEffect)
98import Effect.Ref (Ref)
10−import Effect.Exception as Exn
119import Effect.Ref as Ref
1210import Foreign (unsafeToForeign)
1311import Test.Spec (Spec, around, describe, it)
1412import Test.Spec.Assertions (shouldEqual)
1513import Yoga.Fastify.Fastify as F
1614import 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
15+import Yoga.Fastify.Om (createOmFastify, postOm)
16+import Yoga.Fetch.Om (POST, Route, type (/), JSON, client)
2017import Yoga.Om (Om, runOm)
21−import Yoga.Om.Strom (Strom)
22−import Yoga.Om.Strom as Strom
2318
2419type Payload = { value :: Int }
25−type Result = { doubled :: Int }
20+type Result = { doubled :: Int }
2621
2722type TestAPI =
2823 { double ::
2924 Route POST
3025 ("plain-text-test" / "double")
3126 { body :: JSON Payload }
32− ( ok :: { body :: Result }
33− , badRequest :: { body :: PlainText }
27+ ( ok :: { body :: Result }
28+ , badRequest :: { body :: String }
3429 )
35− , health :: Route GET ("plain-text-test" / "health") {} (ok :: { body :: PlainText })
36− , stream :: Route GET ("plain-text-test" / "stream") {} (ok :: { body :: Streaming String })
3730 }
3831
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− }
32+api :: { double :: forall ctx err. Payload -> Om ctx (badRequest :: String | err) Result }
4533api = client @TestAPI "http://localhost:44933"
4634
4735spec :: Spec Unit
4836spec = around withServer $ describe "String body on error responses" do
4937 it "200 OK decodes the JSON response body" \_ -> do
5038 result <- runOm {}
51− { exception: \_ -> pure { doubled: -1 }
39+ { exception: \_ -> pure { doubled: -1 }
5240 , badRequest: \_ -> pure { doubled: -1 }
5341 }
5442 (api.double { value: 21 })
@@ -58,35 +46,13 @@ spec = around withServer $ describe "String body on error responses" do
5846 liftEffect $ Ref.write "badRequest" serverMode
5947 captured <- liftEffect $ Ref.new ""
6048 runOm {}
61− { exception: \_ -> pure unit
49+ { exception: \_ -> pure unit
6250 , badRequest: \b -> liftEffect $ Ref.write b captured
6351 }
6452 (void $ api.double { value: 0 })
6553 body <- liftEffect $ Ref.read captured
6654 liftAff $ body `shouldEqual` "invalid value"
6755
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−
9056withServer :: forall a. (Ref String -> Aff a) -> Aff a
9157withServer test = bracket acquire release (\{ serverMode } -> test serverMode)
9258 where
@@ -96,19 +62,12 @@ withServer test = bracket acquire release (\{ serverMode } -> test serverMode)
9662 f <- F.fastify {}
9763 omApp <- createOmFastify {} f
9864 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
10165 pure f
10266 void $ F.listen { port: Port 44933, host: Host "0.0.0.0" } fastify
10367 pure { fastify, serverMode }
10468
10569 release { fastify } = F.close fastify
10670
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−
11271 handler serverMode reply = do
11372 mode <- liftEffect $ Ref.read serverMode
11473 case mode of
test/Polymorphic.Spec.purs+18−21
@@ -12,8 +12,7 @@ import Yoga.Fastify.Fastify (Port(..), Host(..))
1212import Yoga.Fastify.Om.API (registerAPI)
1313import Yoga.Fastify.Om.Route (Handler, handle, respond)
1414import Yoga.Fetch.Om (GET, Route, type (/), type (:), client)
15−import Yoga.Om (ask, runOm)
16−import Control.Monad.Error.Class (throwError)
15+import Yoga.Om (Om, ask, runOm)
1716
1817type User = { id :: Int, name :: String }
1918
@@ -28,28 +27,26 @@ api = client @TestAPI "http://localhost:44931"
2827spec :: Spec Unit
2928spec = around withServer $ describe "polymorphic client" do
3029 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"
30+ runOm { token: "secret123" } { exception: \_ -> pure unit } do
31+ ctx <- ask
32+ user <- api.getUser { id: 42 }
33+ liftAff $ (ctx.token <> ":" <> user.name) `shouldEqual` "secret123:User 42"
3534
3635 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"
36+ runOm { apiKey: "mykey", userId: 7 } { exception: \_ -> pure unit } do
37+ ctx <- ask
38+ user <- api.getUser { id: ctx.userId }
39+ liftAff $ (ctx.apiKey <> ":" <> user.name) `shouldEqual` "mykey:User 7"
4140
4241 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"
42+ runOm { token: "tok1" } { exception: \_ -> pure unit } do
43+ ctx <- ask
44+ user <- api.getUser { id: 1 }
45+ liftAff $ (ctx.token <> ":" <> user.name) `shouldEqual` "tok1:User 1"
46+ runOm { endpoint: "prod", region: "eu" } { exception: \_ -> pure unit } do
47+ ctx <- ask
48+ user <- api.getUser { id: 2 }
49+ liftAff $ (ctx.endpoint <> "-" <> ctx.region <> ":" <> user.name) `shouldEqual` "prod-eu:User 2"
5350
5451withServer :: forall a. (Unit -> _ a) -> _ a
5552withServer test = bracket acquire release (\_ -> test unit)
@@ -57,7 +54,7 @@ withServer test = bracket acquire release (\_ -> test unit)
5754 acquire = do
5855 fastify <- liftEffect do
5956 f <- F.fastify {}
60− registerAPI @TestAPI { getUser: getUserHandler } f
57+ registerAPI { getUser: getUserHandler } f
6158 pure f
6259 void $ F.listen { port: Port 44931, host: Host "0.0.0.0" } fastify
6360 pure fastify
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/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` "€"