@nifrajs/core 5.9.0 → 5.11.0
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/CHANGELOG.md +30 -0
- package/dist/edge-kit.d.ts +2 -1
- package/dist/edge-kit.d.ts.map +1 -1
- package/dist/edge-kit.js +2 -1
- package/dist/edge-kit.js.map +1 -1
- package/dist/guard-deny.d.ts +38 -0
- package/dist/guard-deny.d.ts.map +1 -0
- package/dist/guard-deny.js +43 -0
- package/dist/guard-deny.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/router/path-codec.d.ts +22 -0
- package/dist/router/path-codec.d.ts.map +1 -0
- package/dist/router/path-codec.js +87 -0
- package/dist/router/path-codec.js.map +1 -0
- package/dist/router/pattern.d.ts +21 -1
- package/dist/router/pattern.d.ts.map +1 -1
- package/dist/router/pattern.js +117 -0
- package/dist/router/pattern.js.map +1 -1
- package/dist/router/same-origin-path.d.ts +22 -0
- package/dist/router/same-origin-path.d.ts.map +1 -0
- package/dist/router/same-origin-path.js +37 -0
- package/dist/router/same-origin-path.js.map +1 -0
- package/dist/server/core-copies.js +1 -1
- package/dist/server/core-copies.js.map +1 -1
- package/dist/server/http.d.ts +1 -18
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +1 -33
- package/dist/server/http.js.map +1 -1
- package/dist/server/idle-timeout.d.ts +1 -1
- package/dist/server/idle-timeout.d.ts.map +1 -1
- package/dist/server/idle-timeout.js.map +1 -1
- package/dist/server/json-response.d.ts +14 -0
- package/dist/server/json-response.d.ts.map +1 -1
- package/dist/server/json-response.js +205 -0
- package/dist/server/json-response.js.map +1 -1
- package/dist/single-copy-register.js +5 -3
- package/dist/single-copy-register.js.map +1 -1
- package/dist/single-copy.d.ts +1 -1
- package/dist/single-copy.d.ts.map +1 -1
- package/dist/single-copy.js +49 -2
- package/dist/single-copy.js.map +1 -1
- package/package.json +6 -1
- package/src/edge-kit.ts +2 -1
- package/src/guard-deny.ts +61 -0
- package/src/index.ts +1 -1
- package/src/router/path-codec.ts +93 -0
- package/src/router/pattern.ts +127 -0
- package/src/router/same-origin-path.ts +36 -0
- package/src/server/core-copies.ts +1 -1
- package/src/server/http.ts +1 -33
- package/src/server/idle-timeout.ts +2 -4
- package/src/server/json-response.ts +209 -0
- package/src/single-copy-register.ts +5 -3
- package/src/single-copy.ts +51 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @nifrajs/core
|
|
2
2
|
|
|
3
|
+
## 5.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d76f9d7: The three auth packages' guards deny a missing session through one owner: `@nifrajs/core/guard-deny` now holds `UnauthenticatedResponse`, the default `401` it builds (`unauthenticatedResult`), and the fail-loud check `assertUnauthenticated`, which refuses a denial that both redirects and answers, and a status outside 400-599. A new argument carries the package name, so every error keeps its prefix (`[nifra/auth]`, `[nifra/authjs]`, `[nifra/better-auth]`). Each package re-exports `UnauthenticatedResponse` and keeps its own guard options and its redirect rejection; no behavior and no message changed. The new subpath imports only the status lane, so it costs nothing beyond the grammar itself.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 9a0f0a0: `withIdleTimeout(c, seconds, work)` accepts the context of any route, including one that declares a `body`, `query` or `headers` schema.
|
|
12
|
+
- abf5a0b: Resolve the single-copy declaration from the app root when Bun runs a filtered workspace script.
|
|
13
|
+
|
|
14
|
+
## 5.10.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- a6af4c9: A path segment has one spelling in every place a framework writes one. `@nifrajs/core/pattern` exports `encodePathSegment(value)`, `decodePathSegment(raw)`, `browserPathSegment(value)` and `fillRoutePath(pattern, params)`. `encodePathSegment` leaves `A-Za-z0-9 - . _ ~ ! $ & ' ( ) * + , : = @` as written and percent-encodes everything else in upper-case hex, so `;`, `[`, `]` and `|` are escaped (a proxy that reads `;` as a path parameter cannot change the path). A value no segment can carry (`.`, `..`, a lone surrogate) throws `RangeError`. `fillRoutePath` reports a value the router would read back as different params, such as `name` = `a.b` in `:name.:ext`, as invalid.
|
|
19
|
+
|
|
20
|
+
`getStaticPaths` output, the prerender report and written files, `enumerateStaticRoutes`, the typed client's path params and the contract fuzzer's request paths all use it. A `getStaticPaths` entry that leaves out a param or holds one no path can carry is skipped, and the prerender report names the param. Files in `public/` are served under the spelling a page links and the one a browser sends for the same name.
|
|
21
|
+
- 1faf86c: `href({ to, params, search, hash })` builds an in-app URL from a route pattern and returns an `Href`; `rawHref(path)` returns one for a same-origin path built elsewhere (a CMS link, `/llms.txt`). `to` is a key of the generated `RouteTable`, so a misspelt route, a missing, misspelt or extra param, and a `search` that breaks the route's schema are compile errors. A route without a `searchSchema` takes any `search`. Each param is encoded as one path segment, so a value such as `a/b` or `//evil.com` cannot change where the URL goes; a param that is missing, empty, `.`, `..` or an unpaired surrogate throws a `TypeError` naming it and the pattern. `rawHref` throws a `TypeError` unless `isSameOriginPath` accepts the path. Both are exported by `@nifrajs/web` and by the `/router` entry of every adapter.
|
|
22
|
+
|
|
23
|
+
`navigate` takes an `Href` or a history delta. A string, a template literal and the `{ to, search }` object form are compile errors, and a value forged past the types is still held to the same-origin check the browser navigate makes. `<Navigate to>` and the `useSearchParams` setter in `@nifrajs/web-react` pass their path through `rawHref`, so an off-origin `to` throws instead of doing nothing. `isSameOriginPath` is exported from `@nifrajs/core/pattern` as well as `@nifrajs/core/server`.
|
|
24
|
+
|
|
25
|
+
Upgrade: `navigate("/users/7")` becomes `navigate(href({ to: "/users/:id", params: { id: "7" } }))`, `navigate({ to, search, replace: true })` becomes `navigate(href({ to, search }), { replace: true })`, and a path built elsewhere is `navigate(rawHref(path))`. `resolveNavigate`, `NavigateTargetInput` and `NavigateSearchOf` are removed from `@nifrajs/web`.
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- 7e292dd: Reject adjacent mixed-route parameter values that the router would split differently, including percent-encoded values that would become malformed captures.
|
|
30
|
+
|
|
31
|
+
Make internal-link diagnostics account for stripped backend mounts, ignore commented-out and script-string anchors in Vue/Svelte components, and honor template-comment suppressions. Detect navigation imports from browser-safe router and client subpaths when checking route-table coverage.
|
|
32
|
+
|
|
3
33
|
## 5.9.0
|
|
4
34
|
|
|
5
35
|
### Minor Changes
|
package/dist/edge-kit.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* - {@link readBodyFramed} read + cap + proto-guard + content-type framing (JSON / urlencoded / 415)
|
|
11
11
|
* - {@link capBodyBytes} the same byte cap on a handler's own reads of the raw request
|
|
12
|
+
* - {@link boundedJsonText} preflight JSON output against an exact UTF-8 byte budget
|
|
12
13
|
* - {@link renderResult} render a {@link ResponseResult} (e.g. a 413 / 415 rejection) to a `Response`
|
|
13
14
|
* - {@link toResponse} render any handler result, a handler's own `Response` included
|
|
14
15
|
* - {@link plainError} the structured rejection envelope the lane returns
|
|
@@ -23,7 +24,7 @@ export { type ErrorLogDetail, emitRequestErrorLog } from "./server/bare-error-la
|
|
|
23
24
|
export { assertByteLimit, capBodyBytes } from "./server/body.js";
|
|
24
25
|
export { type FormOrigins, readBodyFramed } from "./server/body-lane.js";
|
|
25
26
|
export { plainError } from "./server/http.js";
|
|
26
|
-
export { jsonResponse } from "./server/json-response.js";
|
|
27
|
+
export { boundedJsonText, JsonByteLimitError, jsonResponse } from "./server/json-response.js";
|
|
27
28
|
export { answerNotFound, type NotFoundHandler, type NotFoundInput, notFoundInput, } from "./server/not-found-answer.js";
|
|
28
29
|
export type { ProtoPoisoning } from "./server/proto-guard.js";
|
|
29
30
|
export { type QueryValue, queryObjectOf, searchOf } from "./server/query.js";
|
package/dist/edge-kit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-kit.d.ts","sourceRoot":"","sources":["../src/edge-kit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"edge-kit.d.ts","sourceRoot":"","sources":["../src/edge-kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,KAAK,cAAc,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACtF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAE,KAAK,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7F,OAAO,EACL,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,aAAa,GACd,MAAM,8BAA8B,CAAA;AACrC,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAC9D,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAClE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA"}
|
package/dist/edge-kit.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* - {@link readBodyFramed} read + cap + proto-guard + content-type framing (JSON / urlencoded / 415)
|
|
11
11
|
* - {@link capBodyBytes} the same byte cap on a handler's own reads of the raw request
|
|
12
|
+
* - {@link boundedJsonText} preflight JSON output against an exact UTF-8 byte budget
|
|
12
13
|
* - {@link renderResult} render a {@link ResponseResult} (e.g. a 413 / 415 rejection) to a `Response`
|
|
13
14
|
* - {@link toResponse} render any handler result, a handler's own `Response` included
|
|
14
15
|
* - {@link plainError} the structured rejection envelope the lane returns
|
|
@@ -23,7 +24,7 @@ export { emitRequestErrorLog } from "./server/bare-error-lane.js";
|
|
|
23
24
|
export { assertByteLimit, capBodyBytes } from "./server/body.js";
|
|
24
25
|
export { readBodyFramed } from "./server/body-lane.js";
|
|
25
26
|
export { plainError } from "./server/http.js";
|
|
26
|
-
export { jsonResponse } from "./server/json-response.js";
|
|
27
|
+
export { boundedJsonText, JsonByteLimitError, jsonResponse } from "./server/json-response.js";
|
|
27
28
|
export { answerNotFound, notFoundInput, } from "./server/not-found-answer.js";
|
|
28
29
|
export { queryObjectOf, searchOf } from "./server/query.js";
|
|
29
30
|
export { renderResult, toResponse } from "./server/respond.js";
|
package/dist/edge-kit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-kit.js","sourceRoot":"","sources":["../src/edge-kit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"edge-kit.js","sourceRoot":"","sources":["../src/edge-kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAuB,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACtF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAoB,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7F,OAAO,EACL,cAAc,EAGd,aAAa,GACd,MAAM,8BAA8B,CAAA;AAErC,OAAO,EAAmB,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAE9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAElE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The denial grammar the three auth packages' route guards share: what a guard rejects a missing
|
|
3
|
+
* session with. `@nifrajs/auth`, `@nifrajs/authjs` and `@nifrajs/better-auth` each keep their own
|
|
4
|
+
* options shape and their own redirect rejection; this subpath owns the `unauthenticated` response
|
|
5
|
+
* type, its construction, and the two fail-loud dev-config checks, so the three cannot drift: a
|
|
6
|
+
* fix made here holds for every guard family at once.
|
|
7
|
+
*
|
|
8
|
+
* The module imports only the status lane, so an app whose guards use it pays nothing besides the
|
|
9
|
+
* grammar itself.
|
|
10
|
+
*/
|
|
11
|
+
import { type ResponseResult } from "./server/runtime-core.js";
|
|
12
|
+
/**
|
|
13
|
+
* The response a guard denies a missing session with, in place of the default `401` JSON. Every
|
|
14
|
+
* nifra auth package's guard options named `unauthenticated` takes this shape.
|
|
15
|
+
*/
|
|
16
|
+
export interface UnauthenticatedResponse {
|
|
17
|
+
/** An error status, 400-599. Default `401`. */
|
|
18
|
+
readonly status?: number;
|
|
19
|
+
/** The body; present but `undefined` sends none. Default `{ ok: false, error: "unauthorized" }`. */
|
|
20
|
+
readonly body?: unknown;
|
|
21
|
+
/** Response headers, such as a `www-authenticate` challenge. */
|
|
22
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A dev-authored denial that is not an error status, or that competes with a redirect, fails loud.
|
|
26
|
+
* A mutation-taking guard must say one thing, and `unauthenticated.status` must stay an HTTP error
|
|
27
|
+
* status. `owner` names the package in the message: `[nifra/<owner>]`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function assertUnauthenticated(options: {
|
|
30
|
+
readonly redirectTo?: string;
|
|
31
|
+
readonly unauthenticated?: UnauthenticatedResponse;
|
|
32
|
+
}, owner: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* The response one guard rejection carries: what the options declared, or the default `401` JSON
|
|
35
|
+
* (`{ ok: false, error: "unauthorized" }`). An explicit `body` of `undefined` sends no body.
|
|
36
|
+
*/
|
|
37
|
+
export declare function unauthenticatedResult(custom: UnauthenticatedResponse | undefined): ResponseResult;
|
|
38
|
+
//# sourceMappingURL=guard-deny.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard-deny.d.ts","sourceRoot":"","sources":["../src/guard-deny.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,cAAc,EAAU,MAAM,0BAA0B,CAAA;AAKtE;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,oGAAoG;IACpG,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,gEAAgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACpD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,uBAAuB,CAAA;CAAE,EAC7F,KAAK,EAAE,MAAM,GACZ,IAAI,CAYN;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,uBAAuB,GAAG,SAAS,GAAG,cAAc,CAMjG"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The denial grammar the three auth packages' route guards share: what a guard rejects a missing
|
|
3
|
+
* session with. `@nifrajs/auth`, `@nifrajs/authjs` and `@nifrajs/better-auth` each keep their own
|
|
4
|
+
* options shape and their own redirect rejection; this subpath owns the `unauthenticated` response
|
|
5
|
+
* type, its construction, and the two fail-loud dev-config checks, so the three cannot drift: a
|
|
6
|
+
* fix made here holds for every guard family at once.
|
|
7
|
+
*
|
|
8
|
+
* The module imports only the status lane, so an app whose guards use it pays nothing besides the
|
|
9
|
+
* grammar itself.
|
|
10
|
+
*/
|
|
11
|
+
import { status } from "./server/runtime-core.js";
|
|
12
|
+
/** The body a guard sends when the app did not declare its own. */
|
|
13
|
+
const UNAUTHORIZED = { ok: false, error: "unauthorized" };
|
|
14
|
+
/**
|
|
15
|
+
* A dev-authored denial that is not an error status, or that competes with a redirect, fails loud.
|
|
16
|
+
* A mutation-taking guard must say one thing, and `unauthenticated.status` must stay an HTTP error
|
|
17
|
+
* status. `owner` names the package in the message: `[nifra/<owner>]`.
|
|
18
|
+
*/
|
|
19
|
+
export function assertUnauthenticated(options, owner) {
|
|
20
|
+
const custom = options.unauthenticated;
|
|
21
|
+
if (custom === undefined)
|
|
22
|
+
return;
|
|
23
|
+
if (options.redirectTo !== undefined) {
|
|
24
|
+
throw new Error(`[nifra/${owner}] a guard takes redirectTo or unauthenticated, not both`);
|
|
25
|
+
}
|
|
26
|
+
const code = custom.status;
|
|
27
|
+
if (code !== undefined && (!Number.isInteger(code) || code < 400 || code > 599)) {
|
|
28
|
+
throw new RangeError(`[nifra/${owner}] guard unauthenticated.status must be an integer 400-599 (got ${String(code)})`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The response one guard rejection carries: what the options declared, or the default `401` JSON
|
|
33
|
+
* (`{ ok: false, error: "unauthorized" }`). An explicit `body` of `undefined` sends no body.
|
|
34
|
+
*/
|
|
35
|
+
export function unauthenticatedResult(custom) {
|
|
36
|
+
if (custom === undefined)
|
|
37
|
+
return status(401, UNAUTHORIZED);
|
|
38
|
+
const body = "body" in custom ? custom.body : UNAUTHORIZED;
|
|
39
|
+
return custom.headers === undefined
|
|
40
|
+
? status(custom.status ?? 401, body)
|
|
41
|
+
: status(custom.status ?? 401, body, { headers: custom.headers });
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=guard-deny.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard-deny.js","sourceRoot":"","sources":["../src/guard-deny.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAuB,MAAM,EAAE,MAAM,0BAA0B,CAAA;AAEtE,mEAAmE;AACnE,MAAM,YAAY,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAW,CAAA;AAelE;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAA6F,EAC7F,KAAa;IAEb,MAAM,MAAM,GAAG,OAAO,CAAC,eAAe,CAAA;IACtC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAM;IAChC,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,yDAAyD,CAAC,CAAA;IAC3F,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAA;IAC1B,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,UAAU,CAClB,UAAU,KAAK,kEAAkE,MAAM,CAAC,IAAI,CAAC,GAAG,CACjG,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAA2C;IAC/E,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;IAC1D,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;IAC1D,OAAO,MAAM,CAAC,OAAO,KAAK,SAAS;QACjC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,IAAI,CAAC;QACpC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;AACrE,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* read its own package.json at runtime. `scripts/version.ts` rewrites it on every release bump and
|
|
16
16
|
* `check:publish` asserts it equals `@nifrajs/core`'s package version.
|
|
17
17
|
*/
|
|
18
|
-
export declare const VERSION: "5.
|
|
18
|
+
export declare const VERSION: "5.11.0";
|
|
19
19
|
export type Version = typeof VERSION;
|
|
20
20
|
export { FrameworkError, RouteConfigError, type RouteConfigErrorCode } from "./errors.js";
|
|
21
21
|
export { FRAMEWORK_NAME, type FrameworkName } from "./internal/brand.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAG,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* read its own package.json at runtime. `scripts/version.ts` rewrites it on every release bump and
|
|
16
16
|
* `check:publish` asserts it equals `@nifrajs/core`'s package version.
|
|
17
17
|
*/
|
|
18
|
-
export const VERSION = "5.
|
|
18
|
+
export const VERSION = "5.11.0";
|
|
19
19
|
export { FrameworkError, RouteConfigError } from "./errors.js";
|
|
20
20
|
export { FRAMEWORK_NAME } from "./internal/brand.js";
|
|
21
21
|
export { isSameOriginForm, isSameOriginRequest } from "./internal/same-origin.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,QAAiB,CAAA;AAIxC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAA6B,MAAM,aAAa,CAAA;AACzF,OAAO,EAAE,cAAc,EAAsB,MAAM,qBAAqB,CAAA;AACxE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AACjF,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,mBAAmB,GACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,OAAO,EAGP,MAAM,GAEP,MAAM,oBAAoB,CAAA;AAU3B,OAAO,EASL,aAAa,EACb,sBAAsB,EACtB,wBAAwB,EACxB,QAAQ,GACT,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAU1D,OAAO,EAGL,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,SAAS,EACT,WAAW,GACZ,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAKL,cAAc,GACf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,gBAAgB,EAChB,UAAU,EAEV,UAAU,EACV,MAAM,GACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,oBAAoB,EACpB,UAAU,EAIV,eAAe,EACf,YAAY,GACb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAUL,eAAe,EACf,gBAAgB,EAChB,eAAe,GAEhB,MAAM,+BAA+B,CAAA;AAStC,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EACL,kBAAkB,EAElB,YAAY,GACb,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC5D,OAAO,EAIL,MAAM,GACP,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAOL,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,EA8BlB,MAAM,EAEN,MAAM,GAEP,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `value` as one path segment. Left as written: letters, digits, the at sign and `- . _ ~ ! $ & ' ( ) * + , : =`.
|
|
3
|
+
* Everything else is percent-encoded, UTF-8 and upper-case hex, including `; [ ] | / ? # % \`.
|
|
4
|
+
*
|
|
5
|
+
* Throws a `RangeError` for a value that no segment can carry: exactly `.` or `..` (a URL parser reads
|
|
6
|
+
* `%2E` and `%2E%2E` as dot segments too) and a lone surrogate (substituting U+FFFD would map distinct
|
|
7
|
+
* values to one URL). The empty string is returned as is; a caller that cannot use it checks for it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function encodePathSegment(value: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* `value` as a browser spells it in a request URL: {@link encodePathSegment} with `; [ ] |` left as
|
|
12
|
+
* written. Used for one thing, registering a public file under the spelling its page links request;
|
|
13
|
+
* it differs from the canonical form on those four characters only.
|
|
14
|
+
*/
|
|
15
|
+
export declare function browserPathSegment(value: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* `raw` with its percent-escapes decoded, or `undefined` when one is malformed. The inverse of
|
|
18
|
+
* {@link encodePathSegment} and of {@link browserPathSegment}; a segment in any other spelling decodes
|
|
19
|
+
* too. What a malformed escape means is the caller's policy.
|
|
20
|
+
*/
|
|
21
|
+
export declare function decodePathSegment(raw: string): string | undefined;
|
|
22
|
+
//# sourceMappingURL=path-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-codec.d.ts","sourceRoot":"","sources":["../../src/router/path-codec.ts"],"names":[],"mappings":"AA4DA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMjE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// One URL path segment, encoded and decoded. Every place that spells a value into a path calls this
|
|
2
|
+
// module, so a link, a prerendered file, a client request and a public asset agree on one spelling.
|
|
3
|
+
const HEX = "0123456789ABCDEF";
|
|
4
|
+
const UNRESERVED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
5
|
+
function rawTable(chars) {
|
|
6
|
+
const table = new Uint8Array(128);
|
|
7
|
+
for (let i = 0; i < chars.length; i++)
|
|
8
|
+
table[chars.charCodeAt(i)] = 1;
|
|
9
|
+
return table;
|
|
10
|
+
}
|
|
11
|
+
// RFC 3986 `pchar` minus `;`: the unreserved set, the sub-delimiters, `:` and `@`. `;` delimits path
|
|
12
|
+
// parameters (RFC 3986 section 3.3) and servlet containers strip them; `[`, `]` and `|` are not valid in
|
|
13
|
+
// a path at all and Tomcat rejects them unencoded by default.
|
|
14
|
+
const CANONICAL = /* @__PURE__ */ rawTable(`${UNRESERVED}!$&'()*+,:=@`);
|
|
15
|
+
// What a browser leaves as written in a path: the canonical set plus `;`, `[`, `]` and `|`.
|
|
16
|
+
const BROWSER = /* @__PURE__ */ rawTable(`${UNRESERVED}!$&'()*+,:=@;[]|`);
|
|
17
|
+
function encodeNonAscii(run) {
|
|
18
|
+
try {
|
|
19
|
+
return encodeURIComponent(run);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new RangeError("[nifra/core] a path segment cannot hold a lone surrogate");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function encodeWith(raw, value) {
|
|
26
|
+
if (value === "." || value === "..") {
|
|
27
|
+
throw new RangeError(`[nifra/core] a path segment cannot be ${JSON.stringify(value)}: every spelling of it is a dot segment to a URL parser`);
|
|
28
|
+
}
|
|
29
|
+
const length = value.length;
|
|
30
|
+
let out = "";
|
|
31
|
+
let start = 0;
|
|
32
|
+
let i = 0;
|
|
33
|
+
while (i < length) {
|
|
34
|
+
const code = value.charCodeAt(i);
|
|
35
|
+
if (code < 128 && raw[code] === 1) {
|
|
36
|
+
i++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
out += value.slice(start, i);
|
|
40
|
+
if (code < 128) {
|
|
41
|
+
out += `%${HEX[code >> 4]}${HEX[code & 15]}`;
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
let end = i + 1;
|
|
46
|
+
while (end < length && value.charCodeAt(end) >= 128)
|
|
47
|
+
end++;
|
|
48
|
+
out += encodeNonAscii(value.slice(i, end));
|
|
49
|
+
i = end;
|
|
50
|
+
}
|
|
51
|
+
start = i;
|
|
52
|
+
}
|
|
53
|
+
return start === 0 ? value : out + value.slice(start);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `value` as one path segment. Left as written: letters, digits, the at sign and `- . _ ~ ! $ & ' ( ) * + , : =`.
|
|
57
|
+
* Everything else is percent-encoded, UTF-8 and upper-case hex, including `; [ ] | / ? # % \`.
|
|
58
|
+
*
|
|
59
|
+
* Throws a `RangeError` for a value that no segment can carry: exactly `.` or `..` (a URL parser reads
|
|
60
|
+
* `%2E` and `%2E%2E` as dot segments too) and a lone surrogate (substituting U+FFFD would map distinct
|
|
61
|
+
* values to one URL). The empty string is returned as is; a caller that cannot use it checks for it.
|
|
62
|
+
*/
|
|
63
|
+
export function encodePathSegment(value) {
|
|
64
|
+
return encodeWith(CANONICAL, value);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* `value` as a browser spells it in a request URL: {@link encodePathSegment} with `; [ ] |` left as
|
|
68
|
+
* written. Used for one thing, registering a public file under the spelling its page links request;
|
|
69
|
+
* it differs from the canonical form on those four characters only.
|
|
70
|
+
*/
|
|
71
|
+
export function browserPathSegment(value) {
|
|
72
|
+
return encodeWith(BROWSER, value);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* `raw` with its percent-escapes decoded, or `undefined` when one is malformed. The inverse of
|
|
76
|
+
* {@link encodePathSegment} and of {@link browserPathSegment}; a segment in any other spelling decodes
|
|
77
|
+
* too. What a malformed escape means is the caller's policy.
|
|
78
|
+
*/
|
|
79
|
+
export function decodePathSegment(raw) {
|
|
80
|
+
try {
|
|
81
|
+
return decodeURIComponent(raw);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=path-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-codec.js","sourceRoot":"","sources":["../../src/router/path-codec.ts"],"names":[],"mappings":"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"}
|
package/dist/router/pattern.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { browserPathSegment, decodePathSegment, encodePathSegment } from "./path-codec.js";
|
|
2
|
+
export { isSameOriginPath } from "./same-origin-path.js";
|
|
1
3
|
declare const COMPILED_ROUTE_PATTERN: unique symbol;
|
|
2
4
|
export type RoutePatternSegment = {
|
|
3
5
|
readonly kind: "static";
|
|
@@ -185,7 +187,25 @@ export declare function sortRoutesBySpecificity<T extends {
|
|
|
185
187
|
/** Decode router captures under one rule. Plain values take the zero-allocation path; malformed
|
|
186
188
|
* escapes return `null`, allowing HTTP to emit 400 while client navigation declines the match. */
|
|
187
189
|
export declare function decodeRouteParams(raw: Record<string, string>): Record<string, string> | null;
|
|
190
|
+
export type FilledRoutePath = {
|
|
191
|
+
readonly ok: true;
|
|
192
|
+
readonly path: string;
|
|
193
|
+
} | {
|
|
194
|
+
readonly ok: false;
|
|
195
|
+
/** Params the pattern names that `params` leaves out or holds as the empty string. */
|
|
196
|
+
readonly missing: readonly string[];
|
|
197
|
+
/** Params whose value cannot be carried or would be split differently by this pattern. */
|
|
198
|
+
readonly invalid: readonly string[];
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* The path `pattern` names for `params`, every value spelled by {@link encodePathSegment}. A wildcard
|
|
202
|
+
* value is split on `/` and each part encoded, empty parts dropped; a `.` or `..` part is invalid, as
|
|
203
|
+
* a whole-segment value is. Nothing is thrown for a bad value: the caller decides what a missing or
|
|
204
|
+
* invalid param means. A param inside a mixed segment is invalid when its value holds the literal that ends it (`name` = `a.b` in
|
|
205
|
+
* `:name.:ext`), or when it touches the next param and encodes to more than one character, because the
|
|
206
|
+
* router would split it differently. A param's constraint (`:id{[0-9]+}`) is not checked here.
|
|
207
|
+
*/
|
|
208
|
+
export declare function fillRoutePath(pattern: string | CompiledRoutePattern, params: Readonly<Record<string, string | undefined>>): FilledRoutePath;
|
|
188
209
|
/** Match one compiled pattern and return decoded captures. The caller decides cross-pattern order. */
|
|
189
210
|
export declare function matchRoutePattern(compiled: CompiledRoutePattern, pathname: string): RoutePatternMatch;
|
|
190
|
-
export {};
|
|
191
211
|
//# sourceMappingURL=pattern.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/router/pattern.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/router/pattern.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIxD,QAAA,MAAM,sBAAsB,EAAE,OAAO,MAA+C,CAAA;AAEpF,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACtD,oGAAoG;GAClG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAA;CAAE,CAAA;AAEpE;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;CAAE,CAAA;AAE5F;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IAC9C,yGAAyG;IACzG,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;IACxD;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACjD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;CACvC;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACnE;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAAA;CAAE,CAAA;AAsD3E;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAsDzE;AA8FD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAetE;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,MAAM,EAAE,CAAA;AAEjD,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,iBAAiB,CAQhF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,SAAS,CAE9F;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EAAE,EACb,OAAO,CAAC,EAAE,SAAS,SAAS,EAAE,GAC7B,OAAO,CAqCT;AASD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAYvE;AAED,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CA+FtE;AAWD;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,SAAS,SAAS,EAAE,EAC1B,KAAK,EAAE,SAAS,SAAS,EAAE,GAC1B,MAAM,CAoCR;AAED;yBACyB;AACzB,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,oBAAoB,EAC1B,KAAK,EAAE,oBAAoB,GAC1B,MAAM,CAeR;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,EAC1F,MAAM,EAAE,CAAC,EAAE,GACV,CAAC,EAAE,CAEL;AAED;kGACkG;AAClG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAa5F;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5C;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;IAClB,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC,CAAA;AAEL;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,GAAG,oBAAoB,EACtC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GACnD,eAAe,CA2EjB;AA2BD,sGAAsG;AACtG,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,GACf,iBAAiB,CA8BnB"}
|
package/dist/router/pattern.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { RouteConfigError } from "../errors.js";
|
|
2
|
+
import { encodePathSegment } from "./path-codec.js";
|
|
3
|
+
export { browserPathSegment, decodePathSegment, encodePathSegment } from "./path-codec.js";
|
|
4
|
+
export { isSameOriginPath } from "./same-origin-path.js";
|
|
2
5
|
const PARAM_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3
6
|
const RESERVED_PARAM_NAMES = new Set(["__proto__", "constructor", "prototype"]);
|
|
4
7
|
const COMPILED_ROUTE_PATTERN = Symbol("nifra.compiled-route-pattern");
|
|
@@ -517,6 +520,120 @@ export function decodeRouteParams(raw) {
|
|
|
517
520
|
}
|
|
518
521
|
return out ?? raw;
|
|
519
522
|
}
|
|
523
|
+
/**
|
|
524
|
+
* The path `pattern` names for `params`, every value spelled by {@link encodePathSegment}. A wildcard
|
|
525
|
+
* value is split on `/` and each part encoded, empty parts dropped; a `.` or `..` part is invalid, as
|
|
526
|
+
* a whole-segment value is. Nothing is thrown for a bad value: the caller decides what a missing or
|
|
527
|
+
* invalid param means. A param inside a mixed segment is invalid when its value holds the literal that ends it (`name` = `a.b` in
|
|
528
|
+
* `:name.:ext`), or when it touches the next param and encodes to more than one character, because the
|
|
529
|
+
* router would split it differently. A param's constraint (`:id{[0-9]+}`) is not checked here.
|
|
530
|
+
*/
|
|
531
|
+
export function fillRoutePath(pattern, params) {
|
|
532
|
+
const compiled = typeof pattern === "string" ? compileRoutePattern(pattern) : pattern;
|
|
533
|
+
// Built as one string, with the two problem lists made only when a param is bad: a path that fills
|
|
534
|
+
// allocates nothing else.
|
|
535
|
+
let path = "";
|
|
536
|
+
let missing;
|
|
537
|
+
let invalid;
|
|
538
|
+
const segments = compiled.segments;
|
|
539
|
+
for (let s = 0; s < segments.length; s++) {
|
|
540
|
+
const segment = segments[s];
|
|
541
|
+
if (segment.kind === "static") {
|
|
542
|
+
path += `/${segment.value}`;
|
|
543
|
+
}
|
|
544
|
+
else if (segment.kind === "param") {
|
|
545
|
+
const value = fillValue(params, segment.name);
|
|
546
|
+
if (value === undefined) {
|
|
547
|
+
missing = noted(missing, segment.name);
|
|
548
|
+
path += "/";
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
const encoded = spell(value);
|
|
552
|
+
if (encoded === undefined)
|
|
553
|
+
invalid = noted(invalid, segment.name);
|
|
554
|
+
path += `/${encoded ?? ""}`;
|
|
555
|
+
}
|
|
556
|
+
else if (segment.kind === "wildcard") {
|
|
557
|
+
const value = fillValue(params, segment.name);
|
|
558
|
+
let joined = "";
|
|
559
|
+
let any = false;
|
|
560
|
+
if (value !== undefined) {
|
|
561
|
+
for (const piece of value.split("/")) {
|
|
562
|
+
if (piece === "")
|
|
563
|
+
continue;
|
|
564
|
+
const encoded = spell(piece);
|
|
565
|
+
if (encoded === undefined)
|
|
566
|
+
invalid = noted(invalid, segment.name);
|
|
567
|
+
joined += any ? `/${encoded ?? ""}` : (encoded ?? "");
|
|
568
|
+
any = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (!any)
|
|
572
|
+
missing = noted(missing, segment.name);
|
|
573
|
+
path += `/${joined}`;
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
let text = "";
|
|
577
|
+
const shape = segment.parts;
|
|
578
|
+
for (let i = 0; i < shape.length; i++) {
|
|
579
|
+
const part = shape[i];
|
|
580
|
+
if (part.t === "lit") {
|
|
581
|
+
text += part.v;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const value = fillValue(params, part.name);
|
|
585
|
+
if (value === undefined) {
|
|
586
|
+
missing = noted(missing, part.name);
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
const encoded = spell(value);
|
|
590
|
+
if (encoded === undefined) {
|
|
591
|
+
invalid = noted(invalid, part.name);
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
// matchMixedSegment ends a parameter at the first occurrence of the literal after it, one
|
|
595
|
+
// character in or later. A value holding that literal earlier would come back as different
|
|
596
|
+
// params. A literal in the last place is the segment's tail, which is anchored to its end.
|
|
597
|
+
const next = shape[i + 1];
|
|
598
|
+
// With no separating literal the matcher gives this parameter exactly one raw character.
|
|
599
|
+
// Splitting a percent-escape here would also make the captured parameters malformed.
|
|
600
|
+
if (next?.t === "param" && encoded.length !== 1)
|
|
601
|
+
invalid = noted(invalid, part.name);
|
|
602
|
+
else if (next?.t === "lit" && shape[i + 2] !== undefined) {
|
|
603
|
+
if ((encoded + next.v).indexOf(next.v, 1) !== encoded.length)
|
|
604
|
+
invalid = noted(invalid, part.name);
|
|
605
|
+
}
|
|
606
|
+
text += encoded;
|
|
607
|
+
}
|
|
608
|
+
path += `/${text}`;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return missing !== undefined || invalid !== undefined
|
|
612
|
+
? { ok: false, missing: missing ?? [], invalid: invalid ?? [] }
|
|
613
|
+
: { ok: true, path: path === "" ? "/" : path };
|
|
614
|
+
}
|
|
615
|
+
/** A param's value when it is a non-empty string the caller gave, not one inherited from the prototype. */
|
|
616
|
+
function fillValue(params, name) {
|
|
617
|
+
const value = Object.hasOwn(params, name) ? params[name] : undefined;
|
|
618
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
619
|
+
}
|
|
620
|
+
/** `value` spelled as one path segment, or undefined when no segment can carry it. */
|
|
621
|
+
function spell(value) {
|
|
622
|
+
try {
|
|
623
|
+
return encodePathSegment(value);
|
|
624
|
+
}
|
|
625
|
+
catch {
|
|
626
|
+
return undefined;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/** `list` with `name` in it once, made when there was none. */
|
|
630
|
+
function noted(list, name) {
|
|
631
|
+
if (list === undefined)
|
|
632
|
+
return [name];
|
|
633
|
+
if (!list.includes(name))
|
|
634
|
+
list.push(name);
|
|
635
|
+
return list;
|
|
636
|
+
}
|
|
520
637
|
/** Match one compiled pattern and return decoded captures. The caller decides cross-pattern order. */
|
|
521
638
|
export function matchRoutePattern(compiled, pathname) {
|
|
522
639
|
const matcher = matcherOf(compiled);
|