@jsango/router 1.0.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/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/path-utils.d.ts +5 -0
- package/dist/internal/path-utils.d.ts.map +1 -0
- package/dist/internal/path-utils.js +40 -0
- package/dist/internal/path-utils.js.map +1 -0
- package/dist/internal/radix-node.d.ts +20 -0
- package/dist/internal/radix-node.d.ts.map +1 -0
- package/dist/internal/radix-node.js +11 -0
- package/dist/internal/radix-node.js.map +1 -0
- package/dist/internal/radix-tree.d.ts +14 -0
- package/dist/internal/radix-tree.d.ts.map +1 -0
- package/dist/internal/radix-tree.js +272 -0
- package/dist/internal/radix-tree.js.map +1 -0
- package/dist/public/constraints.d.ts +5 -0
- package/dist/public/constraints.d.ts.map +1 -0
- package/dist/public/constraints.js +35 -0
- package/dist/public/constraints.js.map +1 -0
- package/dist/public/errors.d.ts +20 -0
- package/dist/public/errors.d.ts.map +1 -0
- package/dist/public/errors.js +48 -0
- package/dist/public/errors.js.map +1 -0
- package/dist/public/group.d.ts +29 -0
- package/dist/public/group.d.ts.map +1 -0
- package/dist/public/group.js +75 -0
- package/dist/public/group.js.map +1 -0
- package/dist/public/index.d.ts +8 -0
- package/dist/public/index.d.ts.map +1 -0
- package/dist/public/index.js +8 -0
- package/dist/public/index.js.map +1 -0
- package/dist/public/result.d.ts +24 -0
- package/dist/public/result.d.ts.map +1 -0
- package/dist/public/result.js +10 -0
- package/dist/public/result.js.map +1 -0
- package/dist/public/route.d.ts +26 -0
- package/dist/public/route.d.ts.map +1 -0
- package/dist/public/route.js +31 -0
- package/dist/public/route.js.map +1 -0
- package/dist/public/router-interface.d.ts +19 -0
- package/dist/public/router-interface.d.ts.map +1 -0
- package/dist/public/router-interface.js +2 -0
- package/dist/public/router-interface.js.map +1 -0
- package/dist/public/router.d.ts +37 -0
- package/dist/public/router.d.ts.map +1 -0
- package/dist/public/router.js +210 -0
- package/dist/public/router.js.map +1 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JSango contributors
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @jsango/router
|
|
2
|
+
|
|
3
|
+
> High-performance Segment Radix Trie router (>7.7M ops/sec), typed constraints, route groups, and RFC 7231 compliance.
|
|
4
|
+
|
|
5
|
+
Part of the **[jsango](https://github.com/jsango/jsango)** backend framework for TypeScript.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @jsango/router
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { Router } from '@jsango/router';
|
|
17
|
+
|
|
18
|
+
const router = new Router();
|
|
19
|
+
router.get('/users/:id<number>', (ctx) => ({ id: ctx.request.params.id }));
|
|
20
|
+
router.compile();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Documentation
|
|
24
|
+
|
|
25
|
+
For full architecture documentation and guides, visit the [jsango documentation](https://github.com/jsango/jsango/tree/main/docs).
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
MIT © jsango contributors
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { RouterError, DuplicateRouteError, DuplicateRouteNameError, InvalidRoutePatternError, RouterLockedError, RouteNotFoundError, type BuiltinConstraint, type RouteConstraintDefinition, type CompiledConstraint, resolveConstraint, Route, type RouteHandler, type RouteOptions, type RouteMatch, type MethodNotAllowedMatch, type NotFoundMatch, type RouteMatchResult, isRouteMatch, isMethodNotAllowedMatch, isNotFoundMatch, RouteGroup, type RouteGroupConfig, type RouteGroupOptions, type IRouter, Router, type RouterState, } from './public/index.js';
|
|
2
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,EAGlB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EACvB,iBAAiB,EAGjB,KAAK,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EAGjB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,YAAY,EACZ,uBAAuB,EACvB,eAAe,EAGf,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAGtB,KAAK,OAAO,EACZ,MAAM,EACN,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export {
|
|
2
|
+
// Errors
|
|
3
|
+
RouterError, DuplicateRouteError, DuplicateRouteNameError, InvalidRoutePatternError, RouterLockedError, RouteNotFoundError, resolveConstraint,
|
|
4
|
+
// Route & Handlers
|
|
5
|
+
Route, isRouteMatch, isMethodNotAllowedMatch, isNotFoundMatch,
|
|
6
|
+
// Groups
|
|
7
|
+
RouteGroup, Router, } from './public/index.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACL,SAAS;AACT,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,EAMlB,iBAAiB;AAEjB,mBAAmB;AACnB,KAAK,EASL,YAAY,EACZ,uBAAuB,EACvB,eAAe;AAEf,SAAS;AACT,UAAU,EAMV,MAAM,GAEP,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function normalizePath(path: string): string;
|
|
2
|
+
export declare function joinPaths(prefix: string, path: string): string;
|
|
3
|
+
export declare function splitSegments(path: string): string[];
|
|
4
|
+
export declare function safeDecodeParam(value: string): string;
|
|
5
|
+
//# sourceMappingURL=path-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/internal/path-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBlD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ9D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMpD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOrD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function normalizePath(path) {
|
|
2
|
+
if (!path || path === '/') {
|
|
3
|
+
return '/';
|
|
4
|
+
}
|
|
5
|
+
// Ensure leading slash
|
|
6
|
+
let normalized = path.startsWith('/') ? path : `/${path}`;
|
|
7
|
+
// Collapse multiple slashes (e.g. //a///b/ -> /a/b/)
|
|
8
|
+
normalized = normalized.replace(/\/+/g, '/');
|
|
9
|
+
// Strip trailing slash if length > 1
|
|
10
|
+
if (normalized.length > 1 && normalized.endsWith('/')) {
|
|
11
|
+
normalized = normalized.slice(0, -1);
|
|
12
|
+
}
|
|
13
|
+
return normalized;
|
|
14
|
+
}
|
|
15
|
+
export function joinPaths(prefix, path) {
|
|
16
|
+
const normPrefix = normalizePath(prefix);
|
|
17
|
+
const normPath = normalizePath(path);
|
|
18
|
+
if (normPrefix === '/')
|
|
19
|
+
return normPath;
|
|
20
|
+
if (normPath === '/')
|
|
21
|
+
return normPrefix;
|
|
22
|
+
return `${normPrefix}${normPath}`;
|
|
23
|
+
}
|
|
24
|
+
export function splitSegments(path) {
|
|
25
|
+
const normalized = normalizePath(path);
|
|
26
|
+
if (normalized === '/') {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
return normalized.slice(1).split('/');
|
|
30
|
+
}
|
|
31
|
+
export function safeDecodeParam(value) {
|
|
32
|
+
try {
|
|
33
|
+
return decodeURIComponent(value);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// If malformed percent encoding occurs, return raw string gracefully
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=path-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/internal/path-utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,uBAAuB;IACvB,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAE1D,qDAAqD;IACrD,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE7C,qCAAqC;IACrC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,IAAY;IACpD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAErC,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IACxC,IAAI,QAAQ,KAAK,GAAG;QAAE,OAAO,UAAU,CAAC;IAExC,OAAO,GAAG,UAAU,GAAG,QAAQ,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;QACrE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HttpMethod } from '@jsango/http';
|
|
2
|
+
import type { Route } from '../public/route.js';
|
|
3
|
+
import type { CompiledConstraint } from '../public/constraints.js';
|
|
4
|
+
export interface ParamChild {
|
|
5
|
+
readonly paramName: string;
|
|
6
|
+
readonly constraintDef?: string | undefined;
|
|
7
|
+
readonly constraint?: CompiledConstraint | undefined;
|
|
8
|
+
readonly node: RadixNode;
|
|
9
|
+
}
|
|
10
|
+
export interface WildcardChild {
|
|
11
|
+
readonly paramName: string;
|
|
12
|
+
readonly node: RadixNode;
|
|
13
|
+
}
|
|
14
|
+
export declare class RadixNode {
|
|
15
|
+
readonly staticChildren: Map<string, RadixNode>;
|
|
16
|
+
readonly paramChildren: ParamChild[];
|
|
17
|
+
wildcardChild: WildcardChild | null;
|
|
18
|
+
readonly routes: Map<HttpMethod, Route>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=radix-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radix-node.d.ts","sourceRoot":"","sources":["../../src/internal/radix-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;CAC1B;AAED,qBAAa,SAAS;IAEpB,SAAgB,cAAc,yBAAgC;IAG9D,SAAgB,aAAa,EAAE,UAAU,EAAE,CAAM;IAG1C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAQ;IAGlD,SAAgB,MAAM,yBAAgC;CACvD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class RadixNode {
|
|
2
|
+
// O(1) hash map for static string segments
|
|
3
|
+
staticChildren = new Map();
|
|
4
|
+
// Prioritized array: constrained params evaluated before unconstrained params
|
|
5
|
+
paramChildren = [];
|
|
6
|
+
// Terminal wildcard capturing the remainder of the path
|
|
7
|
+
wildcardChild = null;
|
|
8
|
+
// Registered HTTP routes at this node
|
|
9
|
+
routes = new Map();
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=radix-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radix-node.js","sourceRoot":"","sources":["../../src/internal/radix-node.ts"],"names":[],"mappings":"AAgBA,MAAM,OAAO,SAAS;IACpB,2CAA2C;IAC3B,cAAc,GAAG,IAAI,GAAG,EAAqB,CAAC;IAE9D,8EAA8E;IAC9D,aAAa,GAAiB,EAAE,CAAC;IAEjD,wDAAwD;IACjD,aAAa,GAAyB,IAAI,CAAC;IAElD,sCAAsC;IACtB,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;CACvD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HttpMethod } from '@jsango/http';
|
|
2
|
+
import type { Route } from '../public/route.js';
|
|
3
|
+
import type { RouteMatchResult } from '../public/result.js';
|
|
4
|
+
export declare class RadixTree {
|
|
5
|
+
private readonly root;
|
|
6
|
+
private readonly staticRouteMap;
|
|
7
|
+
private _isLocked;
|
|
8
|
+
get isLocked(): boolean;
|
|
9
|
+
lock(): void;
|
|
10
|
+
insert(route: Route): void;
|
|
11
|
+
private insertSegments;
|
|
12
|
+
search(method: HttpMethod, rawPath: string): RouteMatchResult;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=radix-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radix-tree.d.ts","sourceRoot":"","sources":["../../src/internal/radix-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAW5D,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6C;IAC5E,OAAO,CAAC,SAAS,CAAS;IAE1B,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAEM,IAAI,IAAI,IAAI;IAIZ,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAgDjC,OAAO,CAAC,cAAc;IAkGf,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB;CAgKrE"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { RadixNode } from './radix-node.js';
|
|
2
|
+
import { resolveConstraint } from '../public/constraints.js';
|
|
3
|
+
import { DuplicateRouteError, InvalidRoutePatternError, RouterLockedError, } from '../public/errors.js';
|
|
4
|
+
import { normalizePath, splitSegments, safeDecodeParam } from './path-utils.js';
|
|
5
|
+
const EMPTY_PARAMS = Object.freeze({});
|
|
6
|
+
export class RadixTree {
|
|
7
|
+
root = new RadixNode();
|
|
8
|
+
staticRouteMap = new Map();
|
|
9
|
+
_isLocked = false;
|
|
10
|
+
get isLocked() {
|
|
11
|
+
return this._isLocked;
|
|
12
|
+
}
|
|
13
|
+
lock() {
|
|
14
|
+
this._isLocked = true;
|
|
15
|
+
}
|
|
16
|
+
insert(route) {
|
|
17
|
+
if (this._isLocked) {
|
|
18
|
+
throw new RouterLockedError();
|
|
19
|
+
}
|
|
20
|
+
const path = normalizePath(route.path);
|
|
21
|
+
// Track pure static routes for O(1) matching fast path
|
|
22
|
+
if (!path.includes(':') && !path.includes('*') && !path.includes('?')) {
|
|
23
|
+
let methodMap = this.staticRouteMap.get(path);
|
|
24
|
+
if (!methodMap) {
|
|
25
|
+
methodMap = new Map();
|
|
26
|
+
this.staticRouteMap.set(path, methodMap);
|
|
27
|
+
}
|
|
28
|
+
if (methodMap.has(route.method)) {
|
|
29
|
+
throw new DuplicateRouteError(route.method, route.path);
|
|
30
|
+
}
|
|
31
|
+
methodMap.set(route.method, route);
|
|
32
|
+
}
|
|
33
|
+
// Check for optional parameter at the end (e.g. /users/:id? or /users/:id<number>? or /:id?)
|
|
34
|
+
if (path.includes('?')) {
|
|
35
|
+
const match = path.match(/^(.*?)\/:([a-zA-Z0-9_]+)(?:<(.+?)>)?\?$/);
|
|
36
|
+
if (match) {
|
|
37
|
+
const basePath = match[1] || '/';
|
|
38
|
+
const paramName = match[2];
|
|
39
|
+
const constraintPart = match[3] ? `<${match[3]}>` : '';
|
|
40
|
+
// Insert branch without optional param
|
|
41
|
+
this.insertSegments(splitSegments(basePath), route, []);
|
|
42
|
+
// Insert branch with optional param
|
|
43
|
+
const fullChildPath = basePath === '/'
|
|
44
|
+
? `/:${paramName}${constraintPart}`
|
|
45
|
+
: `${basePath}/:${paramName}${constraintPart}`;
|
|
46
|
+
this.insertSegments(splitSegments(fullChildPath), route, [paramName]);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
throw new InvalidRoutePatternError(path, 'Optional parameters are only supported as the trailing segment (e.g. /path/:id?).');
|
|
50
|
+
}
|
|
51
|
+
const segments = splitSegments(path);
|
|
52
|
+
const paramNames = [];
|
|
53
|
+
this.insertSegments(segments, route, paramNames);
|
|
54
|
+
}
|
|
55
|
+
insertSegments(segments, route, extractedParamNames) {
|
|
56
|
+
let current = this.root;
|
|
57
|
+
for (let i = 0; i < segments.length; i++) {
|
|
58
|
+
const segment = segments[i];
|
|
59
|
+
// Wildcard segment (*path)
|
|
60
|
+
if (segment.startsWith('*')) {
|
|
61
|
+
if (i !== segments.length - 1) {
|
|
62
|
+
throw new InvalidRoutePatternError(route.path, 'Wildcard segments must appear at the end of the route path.');
|
|
63
|
+
}
|
|
64
|
+
const wildcardName = segment.slice(1) || 'wildcard';
|
|
65
|
+
extractedParamNames.push(wildcardName);
|
|
66
|
+
if (!current.wildcardChild) {
|
|
67
|
+
current.wildcardChild = {
|
|
68
|
+
paramName: wildcardName,
|
|
69
|
+
node: new RadixNode(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
current = current.wildcardChild.node;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// Parameter segment (:param or :param<constraint>)
|
|
76
|
+
if (segment.startsWith(':')) {
|
|
77
|
+
let paramName = segment.slice(1);
|
|
78
|
+
let constraint;
|
|
79
|
+
let constraintKey;
|
|
80
|
+
// Check for inline constraint syntax :id<number>
|
|
81
|
+
const constraintMatch = paramName.match(/^([a-zA-Z0-9_]+)<(.+)>$/);
|
|
82
|
+
if (constraintMatch) {
|
|
83
|
+
paramName = constraintMatch[1];
|
|
84
|
+
const constraintDef = constraintMatch[2];
|
|
85
|
+
constraint = resolveConstraint(constraintDef);
|
|
86
|
+
constraintKey = constraintDef;
|
|
87
|
+
}
|
|
88
|
+
else if (route.constraints[paramName]) {
|
|
89
|
+
const rawConstraint = route.constraints[paramName];
|
|
90
|
+
constraint = resolveConstraint(rawConstraint);
|
|
91
|
+
constraintKey = typeof rawConstraint === 'string' ? rawConstraint : 'custom';
|
|
92
|
+
}
|
|
93
|
+
extractedParamNames.push(paramName);
|
|
94
|
+
// Check existing param child with same name and constraint
|
|
95
|
+
const existingChild = current.paramChildren.find((c) => c.paramName === paramName && c.constraintDef === constraintKey);
|
|
96
|
+
if (!existingChild) {
|
|
97
|
+
const newNode = new RadixNode();
|
|
98
|
+
const newChild = {
|
|
99
|
+
paramName,
|
|
100
|
+
constraintDef: constraintKey,
|
|
101
|
+
constraint,
|
|
102
|
+
node: newNode,
|
|
103
|
+
};
|
|
104
|
+
// Prioritize: constrained parameters come before unconstrained ones
|
|
105
|
+
if (constraint) {
|
|
106
|
+
const firstUnconstrainedIdx = current.paramChildren.findIndex((c) => !c.constraint);
|
|
107
|
+
if (firstUnconstrainedIdx === -1) {
|
|
108
|
+
current.paramChildren.push(newChild);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
current.paramChildren.splice(firstUnconstrainedIdx, 0, newChild);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
current.paramChildren.push(newChild);
|
|
116
|
+
}
|
|
117
|
+
current = newNode;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
current = existingChild.node;
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// Static segment
|
|
125
|
+
let nextNode = current.staticChildren.get(segment);
|
|
126
|
+
if (!nextNode) {
|
|
127
|
+
nextNode = new RadixNode();
|
|
128
|
+
current.staticChildren.set(segment, nextNode);
|
|
129
|
+
}
|
|
130
|
+
current = nextNode;
|
|
131
|
+
}
|
|
132
|
+
// Terminal node
|
|
133
|
+
if (current.routes.has(route.method)) {
|
|
134
|
+
throw new DuplicateRouteError(route.method, route.path);
|
|
135
|
+
}
|
|
136
|
+
current.routes.set(route.method, route);
|
|
137
|
+
}
|
|
138
|
+
search(method, rawPath) {
|
|
139
|
+
const pathname = normalizePath(rawPath);
|
|
140
|
+
// O(1) Fast path for pure static routes
|
|
141
|
+
const staticRoutes = this.staticRouteMap.get(pathname);
|
|
142
|
+
if (staticRoutes) {
|
|
143
|
+
const exactRoute = staticRoutes.get(method);
|
|
144
|
+
if (exactRoute) {
|
|
145
|
+
return {
|
|
146
|
+
type: 'MATCHED',
|
|
147
|
+
route: exactRoute,
|
|
148
|
+
handler: exactRoute.handler,
|
|
149
|
+
params: EMPTY_PARAMS,
|
|
150
|
+
metadata: exactRoute.metadata,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (method === 'HEAD') {
|
|
154
|
+
const getRoute = staticRoutes.get('GET');
|
|
155
|
+
if (getRoute) {
|
|
156
|
+
return {
|
|
157
|
+
type: 'MATCHED',
|
|
158
|
+
route: getRoute,
|
|
159
|
+
handler: getRoute.handler,
|
|
160
|
+
params: EMPTY_PARAMS,
|
|
161
|
+
metadata: getRoute.metadata,
|
|
162
|
+
isHeadFallback: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const allowed = new Set(staticRoutes.keys());
|
|
167
|
+
if (staticRoutes.has('GET')) {
|
|
168
|
+
allowed.add('HEAD');
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
type: 'METHOD_NOT_ALLOWED',
|
|
172
|
+
allowedMethods: Object.freeze(Array.from(allowed)),
|
|
173
|
+
pathname,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const segments = splitSegments(pathname);
|
|
177
|
+
// Search algorithm with strict deterministic precedence:
|
|
178
|
+
// Static > Constrained Param > Generic Param > Wildcard
|
|
179
|
+
function findNode(node, index, collectedParams) {
|
|
180
|
+
// If all segments consumed
|
|
181
|
+
if (index === segments.length) {
|
|
182
|
+
// Must have at least one route registered or wildcard
|
|
183
|
+
if (node.routes.size > 0) {
|
|
184
|
+
return { node, params: collectedParams };
|
|
185
|
+
}
|
|
186
|
+
// If node has wildcard child with empty match
|
|
187
|
+
if (node.wildcardChild && node.wildcardChild.node.routes.size > 0) {
|
|
188
|
+
return {
|
|
189
|
+
node: node.wildcardChild.node,
|
|
190
|
+
params: { ...collectedParams, [node.wildcardChild.paramName]: '' },
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const segment = segments[index];
|
|
196
|
+
// 1. Static match (Highest Priority)
|
|
197
|
+
const staticChild = node.staticChildren.get(segment);
|
|
198
|
+
if (staticChild) {
|
|
199
|
+
const res = findNode(staticChild, index + 1, collectedParams);
|
|
200
|
+
if (res)
|
|
201
|
+
return res;
|
|
202
|
+
}
|
|
203
|
+
// 2. Parameter match (Constrained params are ordered before generic params)
|
|
204
|
+
for (const paramChild of node.paramChildren) {
|
|
205
|
+
if (paramChild.constraint && !paramChild.constraint(segment)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const decoded = safeDecodeParam(segment);
|
|
209
|
+
const nextParams = { ...collectedParams, [paramChild.paramName]: decoded };
|
|
210
|
+
const res = findNode(paramChild.node, index + 1, nextParams);
|
|
211
|
+
if (res)
|
|
212
|
+
return res;
|
|
213
|
+
}
|
|
214
|
+
// 3. Wildcard match (Lowest Priority)
|
|
215
|
+
if (node.wildcardChild) {
|
|
216
|
+
const remainder = segments.slice(index).map(safeDecodeParam).join('/');
|
|
217
|
+
const nextParams = {
|
|
218
|
+
...collectedParams,
|
|
219
|
+
[node.wildcardChild.paramName]: remainder,
|
|
220
|
+
};
|
|
221
|
+
if (node.wildcardChild.node.routes.size > 0) {
|
|
222
|
+
return { node: node.wildcardChild.node, params: nextParams };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const candidate = findNode(this.root, 0, {});
|
|
228
|
+
if (!candidate) {
|
|
229
|
+
return {
|
|
230
|
+
type: 'NOT_FOUND',
|
|
231
|
+
pathname,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const { node, params } = candidate;
|
|
235
|
+
// 1. Exact HTTP method match
|
|
236
|
+
const exactRoute = node.routes.get(method);
|
|
237
|
+
if (exactRoute) {
|
|
238
|
+
return {
|
|
239
|
+
type: 'MATCHED',
|
|
240
|
+
route: exactRoute,
|
|
241
|
+
handler: exactRoute.handler,
|
|
242
|
+
params: Object.freeze(params),
|
|
243
|
+
metadata: exactRoute.metadata,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
// 2. HEAD fallback: RFC 7231 allows automatic fallback to GET handler
|
|
247
|
+
if (method === 'HEAD') {
|
|
248
|
+
const getRoute = node.routes.get('GET');
|
|
249
|
+
if (getRoute) {
|
|
250
|
+
return {
|
|
251
|
+
type: 'MATCHED',
|
|
252
|
+
route: getRoute,
|
|
253
|
+
handler: getRoute.handler,
|
|
254
|
+
params: Object.freeze(params),
|
|
255
|
+
metadata: getRoute.metadata,
|
|
256
|
+
isHeadFallback: true,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// 3. Method Not Allowed (405)
|
|
261
|
+
const allowed = new Set(node.routes.keys());
|
|
262
|
+
if (node.routes.has('GET')) {
|
|
263
|
+
allowed.add('HEAD');
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
type: 'METHOD_NOT_ALLOWED',
|
|
267
|
+
allowedMethods: Object.freeze(Array.from(allowed)),
|
|
268
|
+
pathname,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=radix-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radix-tree.js","sourceRoot":"","sources":["../../src/internal/radix-tree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type BuiltinConstraint = 'number' | 'uuid' | 'slug' | 'alpha' | 'alphanumeric';
|
|
2
|
+
export type RouteConstraintDefinition = BuiltinConstraint | string | RegExp | ((value: string) => boolean);
|
|
3
|
+
export type CompiledConstraint = (value: string) => boolean;
|
|
4
|
+
export declare function resolveConstraint(def: RouteConstraintDefinition): CompiledConstraint;
|
|
5
|
+
//# sourceMappingURL=constraints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constraints.d.ts","sourceRoot":"","sources":["../../src/public/constraints.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,cAAc,CAAC;AAEtF,MAAM,MAAM,yBAAyB,GACnC,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;AAUrE,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAE5D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,yBAAyB,GAAG,kBAAkB,CA4BpF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { InvalidRoutePatternError } from './errors.js';
|
|
2
|
+
const BUILTIN_REGEXPS = {
|
|
3
|
+
number: /^\d+$/,
|
|
4
|
+
uuid: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/,
|
|
5
|
+
slug: /^[a-z0-9_-]+$/,
|
|
6
|
+
alpha: /^[a-zA-Z]+$/,
|
|
7
|
+
alphanumeric: /^[a-zA-Z0-9]+$/,
|
|
8
|
+
};
|
|
9
|
+
export function resolveConstraint(def) {
|
|
10
|
+
if (typeof def === 'function') {
|
|
11
|
+
return def;
|
|
12
|
+
}
|
|
13
|
+
if (def instanceof RegExp) {
|
|
14
|
+
return (val) => {
|
|
15
|
+
def.lastIndex = 0;
|
|
16
|
+
return def.test(val);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (typeof def === 'string') {
|
|
20
|
+
const builtin = BUILTIN_REGEXPS[def.toLowerCase()];
|
|
21
|
+
if (builtin) {
|
|
22
|
+
return (val) => builtin.test(val);
|
|
23
|
+
}
|
|
24
|
+
// Try custom regex string (e.g. [0-9]+ or [a-z]{2,4})
|
|
25
|
+
try {
|
|
26
|
+
const customRegex = new RegExp(`^${def}$`);
|
|
27
|
+
return (val) => customRegex.test(val);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new InvalidRoutePatternError(def, `Invalid constraint pattern: "${def}"`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new InvalidRoutePatternError(String(def), 'Invalid constraint definition type.');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=constraints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constraints.js","sourceRoot":"","sources":["../../src/public/constraints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAOvD,MAAM,eAAe,GAAsC;IACzD,MAAM,EAAE,OAAO;IACf,IAAI,EAAE,+EAA+E;IACrF,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,aAAa;IACpB,YAAY,EAAE,gBAAgB;CAC/B,CAAC;AAIF,MAAM,UAAU,iBAAiB,CAAC,GAA8B;IAC9D,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,GAAG,YAAY,MAAM,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAW,EAAE,EAAE;YACrB,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC;YAClB,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,WAAW,EAAuB,CAAC,CAAC;QACxE,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC;QAED,sDAAsD;QACtD,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,wBAAwB,CAAC,GAAG,EAAE,gCAAgC,GAAG,GAAG,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IAED,MAAM,IAAI,wBAAwB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,qCAAqC,CAAC,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { JsangoError, type ErrorMetadata } from '@jsango/core';
|
|
2
|
+
export declare class RouterError extends JsangoError {
|
|
3
|
+
constructor(code: string, message: string, metadata?: ErrorMetadata);
|
|
4
|
+
}
|
|
5
|
+
export declare class DuplicateRouteError extends RouterError {
|
|
6
|
+
constructor(method: string, path: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class DuplicateRouteNameError extends RouterError {
|
|
9
|
+
constructor(name: string, existingPath: string, newPath: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class InvalidRoutePatternError extends RouterError {
|
|
12
|
+
constructor(path: string, reason: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class RouterLockedError extends RouterError {
|
|
15
|
+
constructor(action?: string);
|
|
16
|
+
}
|
|
17
|
+
export declare class RouteNotFoundError extends RouterError {
|
|
18
|
+
constructor(name: string);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/public/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAE/D,qBAAa,WAAY,SAAQ,WAAW;gBAC9B,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa;CASpE;AAED,qBAAa,mBAAoB,SAAQ,WAAW;gBACtC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAQzC;AAED,qBAAa,uBAAwB,SAAQ,WAAW;gBAC1C,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAQhE;AAED,qBAAa,wBAAyB,SAAQ,WAAW;gBAC3C,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAOzC;AAED,qBAAa,iBAAkB,SAAQ,WAAW;gBACpC,MAAM,SAAwB;CAM3C;AAED,qBAAa,kBAAmB,SAAQ,WAAW;gBACrC,IAAI,EAAE,MAAM;CAIzB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { JsangoError } from '@jsango/core';
|
|
2
|
+
export class RouterError extends JsangoError {
|
|
3
|
+
constructor(code, message, metadata) {
|
|
4
|
+
super({
|
|
5
|
+
code,
|
|
6
|
+
message,
|
|
7
|
+
metadata,
|
|
8
|
+
statusCode: 500,
|
|
9
|
+
});
|
|
10
|
+
this.name = 'RouterError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class DuplicateRouteError extends RouterError {
|
|
14
|
+
constructor(method, path) {
|
|
15
|
+
super('ERR_ROUTER_DUPLICATE_ROUTE', `Duplicate route definition: [${method}] "${path}" is already registered.`, { method, path });
|
|
16
|
+
this.name = 'DuplicateRouteError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class DuplicateRouteNameError extends RouterError {
|
|
20
|
+
constructor(name, existingPath, newPath) {
|
|
21
|
+
super('ERR_ROUTER_DUPLICATE_NAME', `Duplicate route name: "${name}" is already assigned to "${existingPath}", cannot assign to "${newPath}".`, { name, existingPath, newPath });
|
|
22
|
+
this.name = 'DuplicateRouteNameError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class InvalidRoutePatternError extends RouterError {
|
|
26
|
+
constructor(path, reason) {
|
|
27
|
+
super('ERR_ROUTER_INVALID_PATTERN', `Invalid route pattern "${path}": ${reason}`, {
|
|
28
|
+
path,
|
|
29
|
+
reason,
|
|
30
|
+
});
|
|
31
|
+
this.name = 'InvalidRoutePatternError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class RouterLockedError extends RouterError {
|
|
35
|
+
constructor(action = 'register new routes') {
|
|
36
|
+
super('ERR_ROUTER_LOCKED', `Router is locked/compiled. Cannot ${action} after compilation.`, {
|
|
37
|
+
action,
|
|
38
|
+
});
|
|
39
|
+
this.name = 'RouterLockedError';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export class RouteNotFoundError extends RouterError {
|
|
43
|
+
constructor(name) {
|
|
44
|
+
super('ERR_ROUTER_NOT_FOUND', `Route with name "${name}" was not found.`, { name });
|
|
45
|
+
this.name = 'RouteNotFoundError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/public/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAsB,MAAM,cAAc,CAAC;AAE/D,MAAM,OAAO,WAAY,SAAQ,WAAW;IAC1C,YAAY,IAAY,EAAE,OAAe,EAAE,QAAwB;QACjE,KAAK,CAAC;YACJ,IAAI;YACJ,OAAO;YACP,QAAQ;YACR,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IAClD,YAAY,MAAc,EAAE,IAAY;QACtC,KAAK,CACH,4BAA4B,EAC5B,gCAAgC,MAAM,MAAM,IAAI,0BAA0B,EAC1E,EAAE,MAAM,EAAE,IAAI,EAAE,CACjB,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,WAAW;IACtD,YAAY,IAAY,EAAE,YAAoB,EAAE,OAAe;QAC7D,KAAK,CACH,2BAA2B,EAC3B,0BAA0B,IAAI,6BAA6B,YAAY,wBAAwB,OAAO,IAAI,EAC1G,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAChC,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,WAAW;IACvD,YAAY,IAAY,EAAE,MAAc;QACtC,KAAK,CAAC,4BAA4B,EAAE,0BAA0B,IAAI,MAAM,MAAM,EAAE,EAAE;YAChF,IAAI;YACJ,MAAM;SACP,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,WAAW;IAChD,YAAY,MAAM,GAAG,qBAAqB;QACxC,KAAK,CAAC,mBAAmB,EAAE,qCAAqC,MAAM,qBAAqB,EAAE;YAC3F,MAAM;SACP,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,WAAW;IACjD,YAAY,IAAY;QACtB,KAAK,CAAC,sBAAsB,EAAE,oBAAoB,IAAI,kBAAkB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { HttpMethod } from '@jsango/http';
|
|
2
|
+
import { Route, type RouteHandler, type RouteOptions } from './route.js';
|
|
3
|
+
import type { IRouter } from './router-interface.js';
|
|
4
|
+
export interface RouteGroupConfig {
|
|
5
|
+
readonly prefix: string;
|
|
6
|
+
readonly metadata?: Record<string, unknown> | undefined;
|
|
7
|
+
readonly middleware?: readonly unknown[] | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface RouteGroupOptions {
|
|
10
|
+
readonly metadata?: Record<string, unknown> | undefined;
|
|
11
|
+
readonly middleware?: readonly unknown[] | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare class RouteGroup {
|
|
14
|
+
private readonly router;
|
|
15
|
+
readonly prefix: string;
|
|
16
|
+
readonly metadata: Readonly<Record<string, unknown>>;
|
|
17
|
+
readonly middleware: readonly unknown[];
|
|
18
|
+
constructor(router: IRouter, prefix: string, options?: RouteGroupOptions);
|
|
19
|
+
route(method: HttpMethod, path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
20
|
+
get(path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
21
|
+
post(path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
22
|
+
put(path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
23
|
+
patch(path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
24
|
+
delete(path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
25
|
+
head(path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
26
|
+
options(path: string, handler: RouteHandler, options?: RouteOptions): Route;
|
|
27
|
+
group(prefixOrConfig: string | RouteGroupConfig, callback: (group: RouteGroup) => void, options?: RouteGroupOptions): this;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=group.d.ts.map
|