@znight/http 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -32
- package/dist/compiler.mjs +82 -13
- package/dist/compiler.mjs.map +1 -1
- package/dist/express.mjs +3 -2
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.mjs +200 -23
- package/dist/fastify.mjs.map +1 -1
- package/dist/hono.mjs +174 -43
- package/dist/hono.mjs.map +1 -1
- package/dist/index.d.mts +9 -1
- package/dist/index.mjs +813 -255
- package/dist/index.mjs.map +1 -1
- package/dist/{node-Clm1Ia7T.mjs → node-DnYv_AMI.mjs} +4 -3
- package/dist/{node-Clm1Ia7T.mjs.map → node-DnYv_AMI.mjs.map} +1 -1
- package/dist/{node-io-CVnqpVNS.mjs → node-io-BkdRSQeg.mjs} +99 -60
- package/dist/{node-io-CVnqpVNS.mjs.map → node-io-BkdRSQeg.mjs.map} +1 -1
- package/dist/node.mjs +1 -1
- package/dist/utils-DIWdzVht.mjs +46 -0
- package/dist/utils-DIWdzVht.mjs.map +1 -0
- package/package.json +4 -4
- package/src/adapters/express.ts +4 -1
- package/src/adapters/fastify.ts +265 -25
- package/src/adapters/hono.ts +225 -58
- package/src/adapters/node-io.ts +128 -58
- package/src/adapters/node.ts +4 -1
- package/src/body.ts +31 -2
- package/src/coerce.ts +31 -0
- package/src/compiler/generate.ts +127 -22
- package/src/context.ts +148 -78
- package/src/index.ts +1 -1
- package/src/pipeline.ts +714 -176
- package/src/plugin.ts +1 -2
- package/src/response.ts +6 -8
- package/src/serialize.ts +48 -21
- package/src/utils.ts +25 -2
- package/src/validate.ts +28 -12
- package/dist/utils-DCbu3a4w.mjs +0 -27
- package/dist/utils-DCbu3a4w.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1493,6 +1493,14 @@ app.hooks.wrap('http:request', async (event, next) => {
|
|
|
1493
1493
|
stream, `run` resolves when the stream opens (`status: 200`): durations measure the time to open.
|
|
1494
1494
|
A stream-lifetime hook is not provided.
|
|
1495
1495
|
|
|
1496
|
+
When no `http:request` wrapper or listener is registered (and CORS is off), routes (other than
|
|
1497
|
+
`@All`) without guards, interceptors, `@Sse`, `@Res()` or `@Header` take a fast lane: the plugin
|
|
1498
|
+
decides at boot what the pipeline would do for them, runs a synchronous handler without creating a
|
|
1499
|
+
promise and builds the headers of a JSON reply from a template. The replies are the same as through
|
|
1500
|
+
the full pipeline (a thrown error, an `HttpResponse` or anything unusual continues there, in the
|
|
1501
|
+
same request scope; a promise is awaited first); registering a hook simply sends every request
|
|
1502
|
+
through the full pipeline again.
|
|
1503
|
+
|
|
1496
1504
|
## Plugin options
|
|
1497
1505
|
|
|
1498
1506
|
`HttpPlugin.configure(options)` — `options` may also be a function of the container, evaluated
|
|
@@ -1638,8 +1646,18 @@ HttpPlugin.configure({ adapter: fastifyAdapter({ instance }) })
|
|
|
1638
1646
|
|
|
1639
1647
|
Znight routes are registered in an encapsulated context with their own body handling and 404
|
|
1640
1648
|
handler, so your other routes keep Fastify's parsers. A `content-type` Fastify cannot parse (`foo`,
|
|
1641
|
-
an empty one)
|
|
1642
|
-
instead of Fastify's `415`.
|
|
1649
|
+
an empty one) never reaches Fastify's parsers for Znight routes, which then answer like on the other
|
|
1650
|
+
adapters instead of Fastify's `415`.
|
|
1651
|
+
|
|
1652
|
+
Znight parses bodies itself, so a Znight route that no other hook needs is served straight from the
|
|
1653
|
+
`onRequest` hook of its context: Fastify skips its content type parsing and the steps before the
|
|
1654
|
+
handler, and a text reply is written on the raw response exactly as `reply.send()` writes it. Your
|
|
1655
|
+
hooks always run: with a `preParsing`, `preValidation` or `preHandler` hook (or an `onRoute` hook
|
|
1656
|
+
wrapping the handler, a schema, a subscriber of the `fastify.request.handler` tracing channel), the
|
|
1657
|
+
request goes through Fastify's whole lifecycle; with an `onSend` or `onResponse` hook, or headers
|
|
1658
|
+
and trailers set by an `onRequest` hook, the reply goes through `reply.send()`. The adapter reads
|
|
1659
|
+
these hooks on Fastify's internal route and reply state: should a Fastify release change it, every
|
|
1660
|
+
request goes through the lifecycle (slower, same responses). Created instances use
|
|
1643
1661
|
`exposeHeadRoutes: false` (Znight serves HEAD), a `maxParamLength` of 8 KiB and a
|
|
1644
1662
|
`frameworkErrors` handler rendering Znight errors (with your own instance, malformed URLs are
|
|
1645
1663
|
answered by Fastify). `inject()` uses `fastify.inject()`. Event streams are sent through
|
|
@@ -1695,10 +1713,13 @@ handler returning its own `Response`.
|
|
|
1695
1713
|
| `serveOptions` | Options of `Bun.serve()` (`tls`, `idleTimeout`, `development`, `error`...; Znight sets `port`, `hostname`, `fetch`). Bun only |
|
|
1696
1714
|
|
|
1697
1715
|
On Bun, the body limits are Znight's (`bodyLimit`, `@BodyLimit`): `maxRequestBodySize` defaults to
|
|
1698
|
-
unlimited instead of Bun's 128 MiB.
|
|
1716
|
+
unlimited instead of Bun's 128 MiB. A body with a `Content-Length` is read at once
|
|
1717
|
+
(`request.bytes()`, refused with 413 before reading when the length is above the limit), a chunked
|
|
1718
|
+
one as a stream (413 as soon as it passes the limit). Bun's `idleTimeout` (10 s by default) still closes idle
|
|
1699
1719
|
connections and stalled requests, but is lifted while a streamed response is sent (SSE, slow
|
|
1700
1720
|
producers), then restored. In your own `Bun.serve({ fetch: adapter.fetch })` the adapter cannot
|
|
1701
|
-
lift it: keep the SSE `heartbeat` below `idleTimeout` (the 8 s default does).
|
|
1721
|
+
lift it: keep the SSE `heartbeat` below `idleTimeout` (the 8 s default does). Bun checks idle timeouts
|
|
1722
|
+
every 4 seconds, so an `idleTimeout` of 4 s or less closes a connection even while it is written to.
|
|
1702
1723
|
|
|
1703
1724
|
`Bun.serve()` sends streamed bodies with chunked transfer encoding even when a `Content-Length`
|
|
1704
1725
|
header is set (unless the whole stream is available at once): files sent with
|
|
@@ -2084,7 +2105,8 @@ Rules:
|
|
|
2084
2105
|
boot.
|
|
2085
2106
|
- `@Validate(S)` applies `S` to the `@Body()` parameter (`@Validate(S)` + `@Body()` ≡ `@Body(S)`);
|
|
2086
2107
|
`@Validate(S)` + `@Body(S2)` is ZNIGHT005. Without a `@Body()` parameter the body is still
|
|
2087
|
-
validated (and discarded): `invoke: async (c, ctx) => { await validate(S, await ctx.parseBody(), 'body'); return c.m() }
|
|
2108
|
+
validated (and discarded): `invoke: async (c, ctx) => { await validate(S, await ctx.parseBody(), 'body'); return c.m() }`
|
|
2109
|
+
(this `async` arrow `await`s its other validations too).
|
|
2088
2110
|
- The plugin parses the body before the interceptors when `params` contains a `source: 'body'`
|
|
2089
2111
|
entry or `uploads` is not empty; `ctx.body` is `undefined` otherwise (generated code then uses
|
|
2090
2112
|
`await ctx.parseBody()`).
|
|
@@ -2117,31 +2139,53 @@ Rules:
|
|
|
2117
2139
|
|
|
2118
2140
|
### `invoke` generation
|
|
2119
2141
|
|
|
2120
|
-
`invoke(controller, ctx)` extracts every argument in parameter order and calls the handler. It
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2142
|
+
`invoke(controller, ctx)` extracts every argument in parameter order and calls the handler. It
|
|
2143
|
+
returns the handler result as is (promises included); the plugin awaits it only when it is a
|
|
2144
|
+
thenable. Without schema it is a synchronous arrow:
|
|
2145
|
+
`(c: UserController, ctx: HttpContext) => c.find(coerce.number(ctx.params['id'], 'param "id"'))`.
|
|
2146
|
+
|
|
2147
|
+
With schemas it stays synchronous as long as every `validate()` returns a value (Zod, Valibot or
|
|
2148
|
+
ArkType schemas without async refinements): no promise, no `await` per request. The arguments up to
|
|
2149
|
+
the last validated one are evaluated in order into `a<index>` constants (`@Ctx()` stays `ctx`), the
|
|
2150
|
+
later ones are inlined in the call, and each validated argument is followed by a fallback for
|
|
2151
|
+
asynchronous schemas:
|
|
2152
|
+
|
|
2153
|
+
```ts
|
|
2154
|
+
invoke: (c: UserController, ctx: HttpContext) => {
|
|
2155
|
+
const a0 = coerce.number(ctx.params['id'], 'param "id"')
|
|
2156
|
+
const a1 = validate(UpdateUser, ctx.body, 'body')
|
|
2157
|
+
if (isThenable(a1)) return (async () => c.update(a0, await a1, ctx))()
|
|
2158
|
+
return c.update(a0, a1, ctx)
|
|
2159
|
+
},
|
|
2160
|
+
```
|
|
2161
|
+
|
|
2162
|
+
When `validate()` returns a thenable (an async refinement, or an output that is itself a promise),
|
|
2163
|
+
the `async` arrow awaits it, then evaluates the remaining arguments (`await`ing their validations)
|
|
2164
|
+
and calls the handler: the order of evaluation, the error of the first invalid argument and the
|
|
2165
|
+
values received by the handler are those of an `async` invoke `await`ing every `validate()`. The
|
|
2166
|
+
expressions of the table below are `validate(S, ...)` calls in both forms.
|
|
2167
|
+
|
|
2168
|
+
| Parameter | Generated expression |
|
|
2169
|
+
| --------------------------------------- | ----------------------------------------------------------------------------- |
|
|
2170
|
+
| `@Param('id') id: T` | `coerce.X(ctx.params['id'], 'param "id"')` (see below) |
|
|
2171
|
+
| `@Param('id', S)` | `validate(S, ctx.params['id'], 'param "id"')` |
|
|
2172
|
+
| `@Param()` | `ctx.params as never` · with schema: `validate(S, ctx.params, 'params')` |
|
|
2173
|
+
| `@Query('page') page: T` | `coerce.X(ctx.query['page'], 'query "page"')` |
|
|
2174
|
+
| `@Query('page', S)` | `validate(S, ctx.query['page'], 'query "page"')` |
|
|
2175
|
+
| `@Query()` / `@Query(S)` | `ctx.query as never` / `validate(S, ctx.query, 'query')` |
|
|
2176
|
+
| `@Body()` / `@Body(S)` / `@Validate(S)` | `ctx.body as never` / `validate(S, ctx.body, 'body')` |
|
|
2177
|
+
| `@Headers('X-Api-Key') key: T` | `coerce.X(ctx.headers['x-api-key'], 'header "x-api-key"')` (name lower-cased) |
|
|
2178
|
+
| `@Headers()` | `ctx.headers as never` |
|
|
2179
|
+
| `@Cookie('sid') sid: T` | `coerce.X(ctx.cookies['sid'], 'cookie "sid"')` (or `coerce.optional(...)`) |
|
|
2180
|
+
| `@Cookie('sid', S)` | `validate(S, ctx.cookies['sid'], 'cookie "sid"')` |
|
|
2181
|
+
| `@Cookie()` / `@Cookie(S)` | `ctx.cookies as never` / `validate(S, ctx.cookies, 'cookies')` |
|
|
2182
|
+
| `@SignedCookie('sid') sid: T` | `coerce.X(ctx.signedCookie('sid'), 'cookie "sid"')` |
|
|
2183
|
+
| `@SignedCookie('sid', S)` | `validate(S, ctx.signedCookie('sid'), 'cookie "sid"')` |
|
|
2184
|
+
| `@Req()` / `@Res()` | `ctx.request as never` / `ctx.response as never` (+ `rawResponse: true`) |
|
|
2185
|
+
| `@UploadedFile('f') f: File` | `uploads.file(ctx, 'f')` · optional: `uploads.optionalFile(ctx, 'f')` |
|
|
2186
|
+
| `@UploadedFiles('f') f: File[]` | `uploads.files(ctx, 'f')` · optional: `uploads.optionalFiles(ctx, 'f')` |
|
|
2187
|
+
| `@UploadedFile()` / `@UploadedFiles()` | the same helpers with `undefined` as field (any field) |
|
|
2188
|
+
| `@Ctx()` | `ctx` |
|
|
2145
2189
|
|
|
2146
2190
|
`coerce.X` by declared type (`T`):
|
|
2147
2191
|
|
|
@@ -2187,6 +2231,8 @@ function validate<S extends StandardSchemaV1>(
|
|
|
2187
2231
|
location: string,
|
|
2188
2232
|
): InferSchemaOutput<S> | Promise<InferSchemaOutput<S>>
|
|
2189
2233
|
|
|
2234
|
+
function isThenable(value: unknown): value is PromiseLike<unknown> // a `then` method: validate() result to await
|
|
2235
|
+
|
|
2190
2236
|
const uploads: {
|
|
2191
2237
|
file(ctx: HttpContext, field: string | undefined): File // 400 `Missing file "field"` when absent
|
|
2192
2238
|
optionalFile(ctx: HttpContext, field: string | undefined): File | undefined
|
|
@@ -2199,8 +2245,9 @@ Every coercion failure throws `BadRequestException` (400); `validate` throws `Va
|
|
|
2199
2245
|
(400, `details: [{ path, message }]`) and `TypeError` when `schema` is not a Standard Schema; the
|
|
2200
2246
|
`uploads` helpers read `ctx.files` (`undefined` field: every field) and throw `BadRequestException`
|
|
2201
2247
|
for missing required files (`Expected a file in field "f", received a text value` when the field
|
|
2202
|
-
was sent as text).
|
|
2203
|
-
|
|
2248
|
+
was sent as text). `validate` returns the output synchronously when the schema validates
|
|
2249
|
+
synchronously, a promise otherwise. Generated modules import them with
|
|
2250
|
+
`import { coerce, isThenable, uploads, validate } from '@znight/http'` and the context type with
|
|
2204
2251
|
`import type { HttpContext } from '@znight/http'`.
|
|
2205
2252
|
|
|
2206
2253
|
### Manifest
|
package/dist/compiler.mjs
CHANGED
|
@@ -1695,6 +1695,7 @@ function groupsNode(ctx, controllers) {
|
|
|
1695
1695
|
function createHelpers(ctx) {
|
|
1696
1696
|
let coerce;
|
|
1697
1697
|
let validate;
|
|
1698
|
+
let isThenable;
|
|
1698
1699
|
let uploads;
|
|
1699
1700
|
let context;
|
|
1700
1701
|
return {
|
|
@@ -1706,6 +1707,10 @@ function createHelpers(ctx) {
|
|
|
1706
1707
|
validate ??= ctx.importFrom(HTTP_PACKAGE, "validate");
|
|
1707
1708
|
return validate;
|
|
1708
1709
|
},
|
|
1710
|
+
isThenable: () => {
|
|
1711
|
+
isThenable ??= ctx.importFrom(HTTP_PACKAGE, "isThenable");
|
|
1712
|
+
return isThenable;
|
|
1713
|
+
},
|
|
1709
1714
|
uploads: () => {
|
|
1710
1715
|
uploads ??= ctx.importFrom(HTTP_PACKAGE, "uploads");
|
|
1711
1716
|
return uploads;
|
|
@@ -1778,22 +1783,86 @@ function tokens(ctx, refs) {
|
|
|
1778
1783
|
return ctx.arr(refs.map((ref) => ctx.tokenOf(ref) ?? ref.token));
|
|
1779
1784
|
}
|
|
1780
1785
|
/**
|
|
1781
|
-
* `(c: Controller, ctx: HttpContext) => c.handler(...args)
|
|
1782
|
-
*
|
|
1786
|
+
* `(c: Controller, ctx: HttpContext) => c.handler(...args)`, synchronous when no argument is
|
|
1787
|
+
* validated with a schema. A route validating arguments gets a block that stays synchronous as long
|
|
1788
|
+
* as every `validate()` returns a value (see {@link validatingBlock}). `@Validate(S)` without
|
|
1789
|
+
* `@Body()` parameter reads the body in `invoke`: that arrow is `async` and `await`s every
|
|
1790
|
+
* `validate()`.
|
|
1783
1791
|
*/
|
|
1784
1792
|
function invokeNode(ctx, helpers, controller, route) {
|
|
1785
|
-
const async = route.bodySchema !== void 0 || route.params.some((param) => param.schema !== void 0);
|
|
1786
1793
|
const params = [["c", ctx.importType(controller.identity)]];
|
|
1787
1794
|
if (route.params.length > 0 || route.bodySchema !== void 0) params.push(["ctx", helpers.context()]);
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1795
|
+
const slots = [];
|
|
1796
|
+
for (const param of route.params) slots[param.index] = param;
|
|
1797
|
+
const bodySchema = route.bodySchema;
|
|
1798
|
+
if (bodySchema !== void 0) return ctx.arrow(params, (c, http) => {
|
|
1799
|
+
const call = handlerCall(ctx, c, route, Array.from(slots, (param) => awaitedArgument(ctx, helpers, http, param)));
|
|
1793
1800
|
const body = ctx.code`await ${ctx.call(ctx.member(http, "parseBody"))}`;
|
|
1794
|
-
const check = validation(ctx, helpers,
|
|
1801
|
+
const check = ctx.code`await ${validation(ctx, helpers, bodySchema, body, "body")}`;
|
|
1795
1802
|
return ctx.code`{ ${check}; return ${call} }`;
|
|
1796
|
-
}, { async });
|
|
1803
|
+
}, { async: true });
|
|
1804
|
+
const last = slots.findLastIndex((param) => param?.schema !== void 0);
|
|
1805
|
+
return ctx.arrow(params, (c, http) => last === -1 ? handlerCall(ctx, c, route, Array.from(slots, (param) => plainArgument(ctx, helpers, http, param))) : validatingBlock(ctx, helpers, route, slots, last, c, http));
|
|
1806
|
+
}
|
|
1807
|
+
/** `c.handler(args)`. */
|
|
1808
|
+
function handlerCall(ctx, c, route, args) {
|
|
1809
|
+
return ctx.call(ctx.member(c, route.handler), args);
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* The body of the `invoke` of a route validating arguments with schemas: the arguments up to the
|
|
1813
|
+
* last validated one are evaluated in order into `const`s (`a<index>`), the handler is called with
|
|
1814
|
+
* them. `validate()` returns the value when the schema validates synchronously (Zod, Valibot and
|
|
1815
|
+
* ArkType without async refinements), so the request needs no promise; when one returns a
|
|
1816
|
+
* thenable, the rest runs in an `async` arrow that awaits it, then evaluates the remaining arguments
|
|
1817
|
+
* (`await`ing their validations) and calls the handler: the order of evaluation, the error reported
|
|
1818
|
+
* when several arguments are invalid and the value received by the handler are those of an `async`
|
|
1819
|
+
* `invoke` awaiting every `validate()`.
|
|
1820
|
+
*
|
|
1821
|
+
* ```ts
|
|
1822
|
+
* (c: UserController, ctx: HttpContext) => {
|
|
1823
|
+
* const a0 = coerce.number(ctx.params['id'], 'param "id"')
|
|
1824
|
+
* const a1 = validate(UpdateUser, ctx.body, 'body')
|
|
1825
|
+
* if (isThenable(a1)) return (async () => c.update(a0, await a1, ctx))()
|
|
1826
|
+
* return c.update(a0, a1, ctx)
|
|
1827
|
+
* }
|
|
1828
|
+
* ```
|
|
1829
|
+
*/
|
|
1830
|
+
function validatingBlock(ctx, helpers, route, slots, last, c, http) {
|
|
1831
|
+
const bound = [];
|
|
1832
|
+
for (let index = 0; index <= last; index++) {
|
|
1833
|
+
const param = slots[index];
|
|
1834
|
+
if (param !== void 0 && param.source !== "context") bound.push(index);
|
|
1835
|
+
}
|
|
1836
|
+
return ctx.block(bound.map((index) => `a${index}`), (...locals) => {
|
|
1837
|
+
const local = new Map(bound.map((index, position) => [index, locals[position]]));
|
|
1838
|
+
const evaluated = (index) => local.get(index) ?? plainArgument(ctx, helpers, http, slots[index]);
|
|
1839
|
+
const statements = [];
|
|
1840
|
+
for (const index of bound) {
|
|
1841
|
+
const param = slots[index];
|
|
1842
|
+
const name = local.get(index);
|
|
1843
|
+
statements.push(ctx.code`const ${name} = ${plainArgument(ctx, helpers, http, param)}`);
|
|
1844
|
+
if (param.schema !== void 0) {
|
|
1845
|
+
const rest = Array.from(slots, (other, position) => {
|
|
1846
|
+
if (position < index) return evaluated(position);
|
|
1847
|
+
return position === index ? ctx.code`await ${name}` : awaitedArgument(ctx, helpers, http, other);
|
|
1848
|
+
});
|
|
1849
|
+
const resume = ctx.arrow([], () => handlerCall(ctx, c, route, rest), { async: true });
|
|
1850
|
+
statements.push(ctx.code`if (${ctx.call(helpers.isThenable(), [name])}) return (${resume})()`);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
const args = Array.from(slots, (param, position) => position <= last ? evaluated(position) : plainArgument(ctx, helpers, http, param));
|
|
1854
|
+
statements.push(ctx.code`return ${handlerCall(ctx, c, route, args)}`);
|
|
1855
|
+
return statements;
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
/** An argument evaluated in an `async` arrow: its validation, if any, is `await`ed. */
|
|
1859
|
+
function awaitedArgument(ctx, helpers, http, param) {
|
|
1860
|
+
const node = plainArgument(ctx, helpers, http, param);
|
|
1861
|
+
return param?.schema === void 0 ? node : ctx.code`await ${node}`;
|
|
1862
|
+
}
|
|
1863
|
+
/** An argument as an expression (`validate()` not awaited); `undefined` for a hole. */
|
|
1864
|
+
function plainArgument(ctx, helpers, http, param) {
|
|
1865
|
+
return param === void 0 ? ctx.lit(void 0) : argumentNode(ctx, helpers, http, param);
|
|
1797
1866
|
}
|
|
1798
1867
|
/** The expression passed to the handler for one parameter. */
|
|
1799
1868
|
function argumentNode(ctx, helpers, http, param) {
|
|
@@ -1844,13 +1913,13 @@ function argumentNode(ctx, helpers, http, param) {
|
|
|
1844
1913
|
}
|
|
1845
1914
|
}
|
|
1846
1915
|
}
|
|
1847
|
-
/** `
|
|
1916
|
+
/** `validate(schema, value, location)` (the value, or a promise when the schema validates asynchronously). */
|
|
1848
1917
|
function validation(ctx, helpers, schema, value, location) {
|
|
1849
|
-
return ctx.
|
|
1918
|
+
return ctx.call(helpers.validate(), [
|
|
1850
1919
|
ctx.ref(schema),
|
|
1851
1920
|
value,
|
|
1852
1921
|
location
|
|
1853
|
-
])
|
|
1922
|
+
]);
|
|
1854
1923
|
}
|
|
1855
1924
|
/** `coerce.X(value, label, ...)`, cast with `as never` when the result does not have the declared type. */
|
|
1856
1925
|
function coercion(ctx, helpers, coercion, value, label) {
|