@stacksjs/bun-router 0.0.16 → 0.0.17
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/dist/auth.d.ts +22 -8
- package/dist/chunk-1ahs68ys.js +18 -0
- package/dist/{chunk-0145g1me.js → chunk-cgptvjdf.js} +147 -75
- package/dist/chunk-g3ybefhg.js +1923 -0
- package/dist/cli.js +3 -2
- package/dist/container/container.d.ts +1 -0
- package/dist/container/contextual-binding.d.ts +0 -1
- package/dist/container/decorators.d.ts +2 -5
- package/dist/container/index.d.ts +16 -0
- package/dist/container/index.js +322 -0
- package/dist/file-serving/static-files.d.ts +16 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +1159 -1380
- package/dist/middleware/cors.d.ts +9 -0
- package/dist/middleware/csrf.d.ts +7 -0
- package/dist/middleware/ddos_protection.d.ts +16 -1
- package/dist/middleware/rate_limit.d.ts +7 -1
- package/dist/middleware/request_tracer.d.ts +15 -9
- package/dist/middleware/response_cache.d.ts +2 -1
- package/dist/request/context.d.ts +12 -1
- package/dist/request/macros.d.ts +40 -2
- package/dist/response/macros.d.ts +3 -3
- package/dist/router/fluent-routing.d.ts +50 -13
- package/dist/router/handler-resolver.d.ts +8 -0
- package/dist/router/index.d.ts +2 -0
- package/dist/router/route-compiler.d.ts +8 -10
- package/dist/router/route-trie.d.ts +7 -1
- package/dist/router/router.d.ts +13 -2
- package/dist/router/validation-integration.d.ts +10 -4
- package/dist/types/middleware-types.d.ts +1 -1
- package/dist/types/route-inference.d.ts +9 -2
- package/dist/types.d.ts +62 -9
- package/dist/utils.d.ts +1 -1
- package/package.json +11 -1
- package/src/auth.ts +32 -12
- package/src/container/container.ts +58 -12
- package/src/container/contextual-binding.ts +4 -1
- package/src/container/decorators.ts +16 -7
- package/src/container/index.ts +49 -0
- package/src/errors/circuit-breaker.ts +3 -2
- package/src/errors/graceful-degradation.ts +2 -0
- package/src/file-serving/static-files.ts +144 -7
- package/src/index.ts +53 -1
- package/src/middleware/cors.ts +76 -8
- package/src/middleware/csrf.ts +70 -22
- package/src/middleware/ddos_protection.ts +27 -3
- package/src/middleware/input_validation.ts +10 -6
- package/src/middleware/performance_alerting.ts +2 -0
- package/src/middleware/performance_monitor.ts +2 -0
- package/src/middleware/rate_limit.ts +7 -1
- package/src/middleware/request_signing.ts +2 -0
- package/src/middleware/request_tracer.ts +34 -17
- package/src/middleware/response_cache.ts +48 -13
- package/src/middleware/session.ts +3 -3
- package/src/observability/metrics.ts +1 -1
- package/src/request/context.ts +50 -3
- package/src/request/enhanced-request.ts +20 -1
- package/src/request/macros.ts +183 -49
- package/src/response/macros.ts +3 -3
- package/src/router/file-based-routing.ts +17 -10
- package/src/router/fluent-routing.ts +117 -48
- package/src/router/group-organization.ts +17 -1
- package/src/router/handler-resolver.ts +36 -0
- package/src/router/http-methods.ts +33 -3
- package/src/router/index.ts +6 -0
- package/src/router/middleware.ts +3 -0
- package/src/router/optimized-route-matching.ts +58 -9
- package/src/router/route-compiler.ts +62 -73
- package/src/router/route-matching.ts +19 -0
- package/src/router/route-trie.ts +34 -48
- package/src/router/router.ts +72 -60
- package/src/router/server.ts +361 -139
- package/src/router/validation-integration.ts +11 -5
- package/src/testing/auth-testing.ts +6 -4
- package/src/types/middleware-types.ts +7 -4
- package/src/types/route-inference.ts +29 -14
- package/src/types.ts +63 -7
- package/src/url.ts +7 -2
- package/src/utils.ts +9 -6
- package/src/validation/validator.ts +7 -4
- package/src/websocket/clustering.ts +8 -1
- package/dist/router/fluent-router.d.ts +0 -315
- package/src/router/fluent-router.ts +0 -927
|
@@ -4,16 +4,29 @@
|
|
|
4
4
|
* Advanced TypeScript utilities for inferring route parameter types from URL patterns
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
/** Strip an inline constraint from a brace parameter: `id:[0-9]+` → `id` */
|
|
8
|
+
type StripInlineConstraint<T extends string> = T extends `${infer Name}:${string}` ? Name : T
|
|
9
|
+
|
|
10
|
+
// Base types for route parameter extraction.
|
|
11
|
+
//
|
|
12
|
+
// The router's canonical parameter syntax is `{param}` (with `{param?}` for
|
|
13
|
+
// optional and `{param:regex}` for constrained parameters) — that form is
|
|
14
|
+
// handled first. The legacy Express-style `:param` clauses are kept for
|
|
15
|
+
// backward compatibility with code written against the colon syntax.
|
|
16
|
+
export type ExtractRouteParams<T extends string> =
|
|
17
|
+
T extends `${string}{${infer Param}}${infer Rest}`
|
|
18
|
+
? (Param extends `${infer Inner}?`
|
|
19
|
+
? { [K in StripInlineConstraint<Inner>]?: string }
|
|
20
|
+
: { [K in StripInlineConstraint<Param>]: string }) & ExtractRouteParams<Rest>
|
|
21
|
+
: T extends `${infer _Start}:${infer Param}/${infer Rest}`
|
|
22
|
+
? { [K in Param]: string } & ExtractRouteParams<`/${Rest}`>
|
|
23
|
+
: T extends `${infer _Start}:${infer Param}?${infer Rest}`
|
|
24
|
+
? { [K in Param]?: string } & ExtractRouteParams<Rest>
|
|
25
|
+
: T extends `${infer _Start}:${infer Param}`
|
|
26
|
+
? { [K in Param]: string }
|
|
27
|
+
: T extends `${infer _Start}*${infer Rest}`
|
|
28
|
+
? { '*': string } & ExtractRouteParams<Rest>
|
|
29
|
+
: Record<string, never>
|
|
17
30
|
|
|
18
31
|
// Advanced parameter extraction with type constraints
|
|
19
32
|
export type ExtractTypedParams<T extends string> = T extends `${infer _Start}:${infer Param}<${infer Type}>${infer Rest}`
|
|
@@ -214,11 +227,13 @@ export interface RouteGroup<TPrefix extends string = '', TContext = object> {
|
|
|
214
227
|
routes: TypedRoute<any, any, any, TContext>[]
|
|
215
228
|
}
|
|
216
229
|
|
|
217
|
-
// Middleware type (forward declaration
|
|
230
|
+
// Middleware type (forward declaration — the canonical definition lives in
|
|
231
|
+
// middleware-types.ts; this one mirrors its narrow defaults so the two
|
|
232
|
+
// can't drift apart silently)
|
|
218
233
|
export interface TypedMiddleware<
|
|
219
|
-
TRequest =
|
|
220
|
-
TResponse =
|
|
221
|
-
TNext =
|
|
234
|
+
TRequest = Request,
|
|
235
|
+
TResponse = Response,
|
|
236
|
+
TNext = () => Promise<TResponse>,
|
|
222
237
|
_TContext = Record<string, never>,
|
|
223
238
|
> {
|
|
224
239
|
(request: TRequest, next: TNext): Promise<TResponse> | TResponse
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Server } from 'bun'
|
|
2
2
|
import type { Router } from './router/router'
|
|
3
|
+
import type { SessionManager } from './session/index'
|
|
3
4
|
import type { QueryPreservationConfig } from './utils/query-preservation'
|
|
4
5
|
import type {
|
|
5
6
|
AuthContext,
|
|
@@ -807,6 +808,12 @@ export interface EnhancedRequest extends Request, Omit<RequestMacroMethods, 'ip'
|
|
|
807
808
|
* Parsed JSON body (if Content-Type is application/json)
|
|
808
809
|
*/
|
|
809
810
|
jsonBody?: Record<string, unknown>
|
|
811
|
+
/**
|
|
812
|
+
* Raw unparsed request body cache, populated on first `rawBody()` read (or up
|
|
813
|
+
* front by a framework body parser). Kept so webhook signature checks get the
|
|
814
|
+
* exact bytes the client sent.
|
|
815
|
+
*/
|
|
816
|
+
_rawBody?: string
|
|
810
817
|
/**
|
|
811
818
|
* Form body data (if Content-Type is multipart/form-data or application/x-www-form-urlencoded)
|
|
812
819
|
*/
|
|
@@ -816,9 +823,11 @@ export interface EnhancedRequest extends Request, Omit<RequestMacroMethods, 'ip'
|
|
|
816
823
|
*/
|
|
817
824
|
files?: UploadedFile[]
|
|
818
825
|
/**
|
|
819
|
-
* Session
|
|
826
|
+
* Session for the current request. The built-in session middleware
|
|
827
|
+
* attaches a `SessionManager` (with `get`/`set`/`flash`/… methods);
|
|
828
|
+
* testing utilities may attach plain `SessionData`.
|
|
820
829
|
*/
|
|
821
|
-
session?: SessionData
|
|
830
|
+
session?: SessionManager | SessionData
|
|
822
831
|
/**
|
|
823
832
|
* User data (if authentication middleware is used)
|
|
824
833
|
*/
|
|
@@ -1057,6 +1066,13 @@ export interface Route {
|
|
|
1057
1066
|
pattern?: {
|
|
1058
1067
|
exec: (url: URL) => PatternMatchResult | null
|
|
1059
1068
|
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Set to `false` (via `router.withoutNativeDispatch()`) to keep this
|
|
1071
|
+
* route on the fetch-handler matcher even when the server runs with
|
|
1072
|
+
* `nativeRoutes: true` — e.g. when it must respect registration-order
|
|
1073
|
+
* precedence against an overlapping pattern.
|
|
1074
|
+
*/
|
|
1075
|
+
nativeDispatch?: boolean
|
|
1060
1076
|
}
|
|
1061
1077
|
|
|
1062
1078
|
/**
|
|
@@ -1102,6 +1118,22 @@ export interface ServerOptions<T extends WebSocketData = WebSocketData> extends
|
|
|
1102
1118
|
* These bypass the fetch handler entirely for zero-allocation serving (~15% faster).
|
|
1103
1119
|
*/
|
|
1104
1120
|
static?: Record<string, Response>
|
|
1121
|
+
/**
|
|
1122
|
+
* Opt-in: register compatible routes with `Bun.serve`'s native router
|
|
1123
|
+
* (`routes`), skipping the fetch handler's URL parsing and route
|
|
1124
|
+
* matching for them. Compatible means: standard HTTP method, no
|
|
1125
|
+
* domain scoping, no `where()`/inline constraints, no optional
|
|
1126
|
+
* params, and `{param}`s spanning whole segments. Everything else —
|
|
1127
|
+
* incompatible routes, 404/405 handling, HEAD fallback, the generic
|
|
1128
|
+
* OPTIONS preflight — still flows through the fetch handler with
|
|
1129
|
+
* identical semantics.
|
|
1130
|
+
*
|
|
1131
|
+
* Caveat: Bun matches by specificity (exact > param > wildcard).
|
|
1132
|
+
* Apps that rely on registration order between same-shape overlapping
|
|
1133
|
+
* patterns should leave this off. Routes registered after `serve()`
|
|
1134
|
+
* only join the native table after `reload()`.
|
|
1135
|
+
*/
|
|
1136
|
+
nativeRoutes?: boolean
|
|
1105
1137
|
/**
|
|
1106
1138
|
* Development mode options for Bun's dev server.
|
|
1107
1139
|
* When true, enables HMR and development features.
|
|
@@ -1273,12 +1305,33 @@ export type MiddlewareWithParams<T extends string = string> =
|
|
|
1273
1305
|
export type RouteParam<T extends string> = T extends `{${infer P}}` ? P : never
|
|
1274
1306
|
|
|
1275
1307
|
/**
|
|
1276
|
-
*
|
|
1308
|
+
* Strip an inline constraint from a parameter token:
|
|
1309
|
+
* `id:[0-9]+` → `id`, `slug` → `slug`
|
|
1310
|
+
*/
|
|
1311
|
+
export type ExtractParamName<T extends string> =
|
|
1312
|
+
T extends `${infer Name}:${string}` ? Name : T
|
|
1313
|
+
|
|
1314
|
+
/**
|
|
1315
|
+
* Extract parameters from a route path into a narrowly-typed object.
|
|
1316
|
+
*
|
|
1317
|
+
* Handles the full parameter grammar:
|
|
1318
|
+
* - `{id}` → `{ id: string }`
|
|
1319
|
+
* - `{id?}` (optional) → `{ id?: string }`
|
|
1320
|
+
* - `{id:[0-9]+}` (inline constraint) → `{ id: string }`
|
|
1321
|
+
* - trailing `*` wildcard → `{ wildcard: string }`
|
|
1322
|
+
*
|
|
1323
|
+
* @example
|
|
1324
|
+
* type P = ExtractRouteParams<'/users/{userId}/posts/{slug?}'>
|
|
1325
|
+
* // ^? { userId: string } & { slug?: string }
|
|
1277
1326
|
*/
|
|
1278
1327
|
export type ExtractRouteParams<T extends string> =
|
|
1279
1328
|
T extends `${string}{${infer Param}}${infer Rest}`
|
|
1280
|
-
?
|
|
1281
|
-
|
|
1329
|
+
? (Param extends `${infer Inner}?`
|
|
1330
|
+
? { [K in ExtractParamName<Inner>]?: string }
|
|
1331
|
+
: { [K in ExtractParamName<Param>]: string }) & ExtractRouteParams<Rest>
|
|
1332
|
+
: T extends `${string}*${string}`
|
|
1333
|
+
? { wildcard: string }
|
|
1334
|
+
: object
|
|
1282
1335
|
|
|
1283
1336
|
/**
|
|
1284
1337
|
* Route method constraints
|
|
@@ -1735,8 +1788,8 @@ export interface StreamingOptions {
|
|
|
1735
1788
|
/**
|
|
1736
1789
|
* SSE (Server-Sent Events) data structure
|
|
1737
1790
|
*/
|
|
1738
|
-
export interface SSEData {
|
|
1739
|
-
data:
|
|
1791
|
+
export interface SSEData<T = unknown> {
|
|
1792
|
+
data: T
|
|
1740
1793
|
event?: string
|
|
1741
1794
|
id?: string
|
|
1742
1795
|
retry?: number
|
|
@@ -1933,6 +1986,9 @@ export interface RequestMacroMethods {
|
|
|
1933
1986
|
bearerToken: () => string | null
|
|
1934
1987
|
basicAuth: () => { username: string, password: string } | null
|
|
1935
1988
|
|
|
1989
|
+
/** Raw unparsed request body as a string (cached). Needed for webhook signature verification (Stripe/GitHub/Slack), where a re-serialized jsonBody won't match the HMAC. */
|
|
1990
|
+
rawBody: () => Promise<string>
|
|
1991
|
+
|
|
1936
1992
|
// Headers
|
|
1937
1993
|
hasHeader: (name: string) => boolean
|
|
1938
1994
|
header: (name: string, defaultValue?: string) => string | null
|
package/src/url.ts
CHANGED
|
@@ -100,8 +100,13 @@ export function url(
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
// Strip any unfilled optional placeholders
|
|
104
|
-
|
|
103
|
+
// Strip any unfilled optional placeholders and tidy the slashes they
|
|
104
|
+
// leave behind (`/posts/{slug?}` → `/posts`, `/a/{v?}/b` → `/a/b`)
|
|
105
|
+
if (resolved.includes('?}')) {
|
|
106
|
+
resolved = resolved.replace(/\{[^}]+\?\}/g, '').replace(/\/{2,}/g, '/')
|
|
107
|
+
if (resolved.length > 1 && resolved.endsWith('/'))
|
|
108
|
+
resolved = resolved.slice(0, -1)
|
|
109
|
+
}
|
|
105
110
|
|
|
106
111
|
// Throw on unfilled required placeholders so the caller sees the bug.
|
|
107
112
|
const missing = resolved.match(/\{([^}?]+)\}/g)
|
package/src/utils.ts
CHANGED
|
@@ -319,7 +319,7 @@ export async function resolveViewPath(
|
|
|
319
319
|
* @param template The HTML template string
|
|
320
320
|
* @param data The data to inject into the template
|
|
321
321
|
*/
|
|
322
|
-
export function processHtmlTemplate(template: string, data: Record<string,
|
|
322
|
+
export function processHtmlTemplate(template: string, data: Record<string, unknown>): string {
|
|
323
323
|
// Process general variables {{ varName }}
|
|
324
324
|
let result = template.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
|
325
325
|
const trimmedKey = key.trim()
|
|
@@ -339,7 +339,7 @@ export function processHtmlTemplate(template: string, data: Record<string, any>)
|
|
|
339
339
|
/**
|
|
340
340
|
* Process conditional statements in templates
|
|
341
341
|
*/
|
|
342
|
-
function processConditionals(template: string, data: Record<string,
|
|
342
|
+
function processConditionals(template: string, data: Record<string, unknown>): string {
|
|
343
343
|
// Match {{#if condition}} content {{/if}} or {{#if condition}} content {{else}} alternative {{/if}}
|
|
344
344
|
return template.replace(
|
|
345
345
|
/\{\{#if ([^}]+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}/g,
|
|
@@ -355,7 +355,7 @@ function processConditionals(template: string, data: Record<string, any>): strin
|
|
|
355
355
|
/**
|
|
356
356
|
* Process loop statements in templates
|
|
357
357
|
*/
|
|
358
|
-
function processLoops(template: string, data: Record<string,
|
|
358
|
+
function processLoops(template: string, data: Record<string, unknown>): string {
|
|
359
359
|
// Match {{#each items}} content {{/each}}
|
|
360
360
|
return template.replace(
|
|
361
361
|
/\{\{#each ([^}]+)\}\}([\s\S]*?)\{\{\/each\}\}/g,
|
|
@@ -385,8 +385,11 @@ function processLoops(template: string, data: Record<string, any>): string {
|
|
|
385
385
|
* @param obj The object to extract value from
|
|
386
386
|
* @param path The path to the value (e.g., 'user.profile.name')
|
|
387
387
|
*/
|
|
388
|
-
function getNestedValue(obj: Record<string,
|
|
389
|
-
return path.split('.').reduce((prev, curr) => {
|
|
390
|
-
|
|
388
|
+
function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
|
|
389
|
+
return path.split('.').reduce<unknown>((prev, curr) => {
|
|
390
|
+
if (prev && typeof prev === 'object' && curr in prev) {
|
|
391
|
+
return (prev as Record<string, unknown>)[curr]
|
|
392
|
+
}
|
|
393
|
+
return undefined
|
|
391
394
|
}, obj)
|
|
392
395
|
}
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
import type { EnhancedRequest, NextFunction } from '../types'
|
|
8
8
|
import { ValidationException } from '../errors/exceptions'
|
|
9
9
|
|
|
10
|
+
// Hoisted email check. Pragmatic RFC 5322 approximation: requires one `@`,
|
|
11
|
+
// a non-empty local part without surrounding/consecutive dots, and a domain
|
|
12
|
+
// with at least one label dot and a 2+ char TLD. The previous regex
|
|
13
|
+
// accepted addresses like `a@.com` and consecutive dots.
|
|
14
|
+
const EMAIL_REGEX = /^[A-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?)*\.[A-Z]{2,}$/i
|
|
15
|
+
|
|
10
16
|
export interface ValidationRule {
|
|
11
17
|
name: string
|
|
12
18
|
validate: (value: any, parameters: string[], field: string, data: Record<string, any>) => Promise<boolean> | boolean
|
|
@@ -74,10 +80,7 @@ export const BuiltInRules: Record<string, ValidationRule> = {
|
|
|
74
80
|
|
|
75
81
|
email: {
|
|
76
82
|
name: 'email',
|
|
77
|
-
validate:
|
|
78
|
-
const emailRegex = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/
|
|
79
|
-
return typeof value === 'string' && emailRegex.test(value)
|
|
80
|
-
},
|
|
83
|
+
validate: value => typeof value === 'string' && EMAIL_REGEX.test(value),
|
|
81
84
|
message: 'The :field field must be a valid email address.',
|
|
82
85
|
},
|
|
83
86
|
|
|
@@ -539,8 +539,13 @@ export class WebSocketCluster {
|
|
|
539
539
|
break
|
|
540
540
|
|
|
541
541
|
case 'stats':
|
|
542
|
-
// Handle stats updates from other workers
|
|
542
|
+
// Handle stats updates from other workers. Bounded: with worker
|
|
543
|
+
// churn the per-worker entries would otherwise accumulate forever.
|
|
543
544
|
if (message.data && message.workerId) {
|
|
545
|
+
const workerIds = Object.keys(this.stats.workerStats)
|
|
546
|
+
if (workerIds.length >= 1000 && !(message.workerId in this.stats.workerStats)) {
|
|
547
|
+
delete this.stats.workerStats[workerIds[0]]
|
|
548
|
+
}
|
|
544
549
|
this.stats.workerStats[message.workerId] = message.data
|
|
545
550
|
}
|
|
546
551
|
break
|
|
@@ -623,6 +628,8 @@ export class WebSocketCluster {
|
|
|
623
628
|
timestamp: now,
|
|
624
629
|
})
|
|
625
630
|
}, this.config.heartbeatInterval)
|
|
631
|
+
// The heartbeat must not keep the process alive on shutdown
|
|
632
|
+
this.heartbeatTimer.unref?.()
|
|
626
633
|
}
|
|
627
634
|
|
|
628
635
|
/**
|
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fluent Routing Extensions
|
|
3
|
-
*
|
|
4
|
-
* Implements fluent routing patterns with full TypeScript support
|
|
5
|
-
*/
|
|
6
|
-
import type { EnhancedRequest, MiddlewareHandler, Route, RouteHandler } from '../types';
|
|
7
|
-
import type { Router } from './router';
|
|
8
|
-
/**
|
|
9
|
-
* Controller class interface
|
|
10
|
-
*/
|
|
11
|
-
export interface ControllerClass {
|
|
12
|
-
new (): Controller;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Base controller interface
|
|
16
|
-
*/
|
|
17
|
-
export interface Controller {
|
|
18
|
-
[key: string]: RouteHandler | unknown;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Resource controller methods
|
|
22
|
-
*/
|
|
23
|
-
export type ResourceMethod = 'index' | 'create' | 'store' | 'show' | 'edit' | 'update' | 'destroy';
|
|
24
|
-
/**
|
|
25
|
-
* API resource methods (no create/edit views)
|
|
26
|
-
*/
|
|
27
|
-
export type ApiResourceMethod = 'index' | 'store' | 'show' | 'update' | 'destroy';
|
|
28
|
-
/**
|
|
29
|
-
* Singleton resource methods
|
|
30
|
-
*/
|
|
31
|
-
export type SingletonMethod = 'show' | 'edit' | 'update';
|
|
32
|
-
/**
|
|
33
|
-
* Resource configuration
|
|
34
|
-
*/
|
|
35
|
-
export interface ResourceOptions {
|
|
36
|
-
only?: ResourceMethod[];
|
|
37
|
-
except?: ResourceMethod[];
|
|
38
|
-
middleware?: (string | MiddlewareHandler)[];
|
|
39
|
-
names?: Partial<Record<ResourceMethod, string>>;
|
|
40
|
-
parameters?: Record<string, string>;
|
|
41
|
-
shallow?: boolean;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Route builder for fluent API
|
|
45
|
-
*/
|
|
46
|
-
export interface FluentRoute {
|
|
47
|
-
name: (name: string) => FluentRoute;
|
|
48
|
-
middleware: (...middleware: (string | MiddlewareHandler)[]) => FluentRoute;
|
|
49
|
-
withoutMiddleware: (...middleware: string[]) => FluentRoute;
|
|
50
|
-
where: (param: string, pattern: string | RegExp) => FluentRoute;
|
|
51
|
-
whereNumber: (param: string) => FluentRoute;
|
|
52
|
-
whereAlpha: (param: string) => FluentRoute;
|
|
53
|
-
whereAlphaNumeric: (param: string) => FluentRoute;
|
|
54
|
-
whereUuid: (param: string) => FluentRoute;
|
|
55
|
-
whereSlug: (param: string) => FluentRoute;
|
|
56
|
-
can: (ability: string, model?: string) => FluentRoute;
|
|
57
|
-
missing: (handler: (req: EnhancedRequest) => Response) => FluentRoute;
|
|
58
|
-
withTrashed: () => FluentRoute;
|
|
59
|
-
scopeBindings: () => FluentRoute;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Group builder for fluent API
|
|
63
|
-
*/
|
|
64
|
-
export interface GroupBuilder {
|
|
65
|
-
prefix: (prefix: string) => GroupBuilder;
|
|
66
|
-
name: (name: string) => GroupBuilder;
|
|
67
|
-
middleware: (...middleware: (string | MiddlewareHandler)[]) => GroupBuilder;
|
|
68
|
-
domain: (domain: string) => GroupBuilder;
|
|
69
|
-
controller: (controller: ControllerClass) => GroupBuilder;
|
|
70
|
-
routes: (callback: (router: FluentRouter) => void) => void;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Fluent router with chainable API
|
|
74
|
-
*/
|
|
75
|
-
export declare class FluentRouter {
|
|
76
|
-
private router;
|
|
77
|
-
private currentPrefix;
|
|
78
|
-
private currentNamePrefix;
|
|
79
|
-
private currentMiddleware;
|
|
80
|
-
private currentDomain;
|
|
81
|
-
private currentController;
|
|
82
|
-
private excludedMiddleware;
|
|
83
|
-
constructor(router: Router);
|
|
84
|
-
/**
|
|
85
|
-
* Register a GET route
|
|
86
|
-
*/
|
|
87
|
-
get(path: string, handler: RouteHandler | string | Response): FluentRouteBuilder;
|
|
88
|
-
/**
|
|
89
|
-
* Register a POST route
|
|
90
|
-
*/
|
|
91
|
-
post(path: string, handler: RouteHandler | string): FluentRouteBuilder;
|
|
92
|
-
/**
|
|
93
|
-
* Register a PUT route
|
|
94
|
-
*/
|
|
95
|
-
put(path: string, handler: RouteHandler | string): FluentRouteBuilder;
|
|
96
|
-
/**
|
|
97
|
-
* Register a PATCH route
|
|
98
|
-
*/
|
|
99
|
-
patch(path: string, handler: RouteHandler | string): FluentRouteBuilder;
|
|
100
|
-
/**
|
|
101
|
-
* Register a DELETE route
|
|
102
|
-
*/
|
|
103
|
-
delete(path: string, handler: RouteHandler | string): FluentRouteBuilder;
|
|
104
|
-
/**
|
|
105
|
-
* Register an OPTIONS route
|
|
106
|
-
*/
|
|
107
|
-
options(path: string, handler: RouteHandler | string): FluentRouteBuilder;
|
|
108
|
-
/**
|
|
109
|
-
* Register a route for any HTTP method
|
|
110
|
-
*/
|
|
111
|
-
any(path: string, handler: RouteHandler | string): FluentRouteBuilder;
|
|
112
|
-
/**
|
|
113
|
-
* Register a route for multiple HTTP methods
|
|
114
|
-
*/
|
|
115
|
-
match(methods: string[], path: string, handler: RouteHandler | string): FluentRouteBuilder;
|
|
116
|
-
/**
|
|
117
|
-
* Register a resource controller
|
|
118
|
-
*/
|
|
119
|
-
resource(name: string, Controller: ControllerClass, options?: ResourceOptions): this;
|
|
120
|
-
/**
|
|
121
|
-
* Register an API resource controller (no create/edit views)
|
|
122
|
-
*/
|
|
123
|
-
apiResource(name: string, controller: ControllerClass, options?: ResourceOptions): this;
|
|
124
|
-
/**
|
|
125
|
-
* Register a singleton resource (no index, create, store, destroy)
|
|
126
|
-
*/
|
|
127
|
-
singleton(name: string, Controller: ControllerClass, options?: ResourceOptions): this;
|
|
128
|
-
/**
|
|
129
|
-
* Create a route group with prefix
|
|
130
|
-
*/
|
|
131
|
-
prefix(prefix: string): GroupBuilderImpl;
|
|
132
|
-
/**
|
|
133
|
-
* Create a route group with name prefix
|
|
134
|
-
*/
|
|
135
|
-
name(namePrefix: string): GroupBuilderImpl;
|
|
136
|
-
/**
|
|
137
|
-
* Create a route group with middleware
|
|
138
|
-
*/
|
|
139
|
-
middleware(...middleware: (string | MiddlewareHandler)[]): GroupBuilderImpl;
|
|
140
|
-
/**
|
|
141
|
-
* Create a route group for a domain
|
|
142
|
-
*/
|
|
143
|
-
domain(domain: string): GroupBuilderImpl;
|
|
144
|
-
/**
|
|
145
|
-
* Create a route group with a controller
|
|
146
|
-
*/
|
|
147
|
-
controller(controller: ControllerClass): GroupBuilderImpl;
|
|
148
|
-
/**
|
|
149
|
-
* Create a route group
|
|
150
|
-
*/
|
|
151
|
-
group(options: GroupOptions, callback: (router: FluentRouter) => void): this;
|
|
152
|
-
/**
|
|
153
|
-
* Check if a named route exists
|
|
154
|
-
*/
|
|
155
|
-
has(name: string): boolean;
|
|
156
|
-
/**
|
|
157
|
-
* Get the current route (from request context)
|
|
158
|
-
*/
|
|
159
|
-
current(): Route | null;
|
|
160
|
-
/**
|
|
161
|
-
* Get the current route name
|
|
162
|
-
*/
|
|
163
|
-
currentRouteName(): string | null;
|
|
164
|
-
/**
|
|
165
|
-
* Get URL for a named route
|
|
166
|
-
*/
|
|
167
|
-
route(name: string, params?: Record<string, string | number>): string;
|
|
168
|
-
/**
|
|
169
|
-
* Register a fallback route
|
|
170
|
-
*/
|
|
171
|
-
fallback(handler: RouteHandler): this;
|
|
172
|
-
private addRoute;
|
|
173
|
-
/**
|
|
174
|
-
* Create a pattern object for route matching
|
|
175
|
-
*/
|
|
176
|
-
private createPattern;
|
|
177
|
-
private buildPath;
|
|
178
|
-
private buildMiddleware;
|
|
179
|
-
private resolveHandler;
|
|
180
|
-
private resolveMiddleware;
|
|
181
|
-
private getResourceMethods;
|
|
182
|
-
private getResourceRoute;
|
|
183
|
-
private getSingletonRoute;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Fluent route builder for chaining route configuration
|
|
187
|
-
*/
|
|
188
|
-
export declare class FluentRouteBuilder implements FluentRoute {
|
|
189
|
-
private router;
|
|
190
|
-
private path;
|
|
191
|
-
private method;
|
|
192
|
-
private routeName;
|
|
193
|
-
private routeMiddleware;
|
|
194
|
-
private excludedMiddleware;
|
|
195
|
-
private constraints;
|
|
196
|
-
private missingHandler;
|
|
197
|
-
private includeTrashed;
|
|
198
|
-
private scopeBindingsEnabled;
|
|
199
|
-
private ability;
|
|
200
|
-
private abilityModel;
|
|
201
|
-
constructor(router: Router, path: string, method: string);
|
|
202
|
-
/**
|
|
203
|
-
* Set the route name
|
|
204
|
-
*/
|
|
205
|
-
name(name: string): this;
|
|
206
|
-
/**
|
|
207
|
-
* Add middleware to the route
|
|
208
|
-
*/
|
|
209
|
-
middleware(...middleware: (string | MiddlewareHandler)[]): this;
|
|
210
|
-
/**
|
|
211
|
-
* Exclude middleware from the route
|
|
212
|
-
*/
|
|
213
|
-
withoutMiddleware(...middleware: string[]): this;
|
|
214
|
-
/**
|
|
215
|
-
* Add a parameter constraint
|
|
216
|
-
*/
|
|
217
|
-
where(param: string, pattern: string | RegExp): this;
|
|
218
|
-
/**
|
|
219
|
-
* Constrain parameter to numbers
|
|
220
|
-
*/
|
|
221
|
-
whereNumber(param: string): this;
|
|
222
|
-
/**
|
|
223
|
-
* Constrain parameter to letters
|
|
224
|
-
*/
|
|
225
|
-
whereAlpha(param: string): this;
|
|
226
|
-
/**
|
|
227
|
-
* Constrain parameter to alphanumeric
|
|
228
|
-
*/
|
|
229
|
-
whereAlphaNumeric(param: string): this;
|
|
230
|
-
/**
|
|
231
|
-
* Constrain parameter to UUID
|
|
232
|
-
*/
|
|
233
|
-
whereUuid(param: string): this;
|
|
234
|
-
/**
|
|
235
|
-
* Constrain parameter to slug format
|
|
236
|
-
*/
|
|
237
|
-
whereSlug(param: string): this;
|
|
238
|
-
/**
|
|
239
|
-
* Add authorization check
|
|
240
|
-
*/
|
|
241
|
-
can(ability: string, model?: string): this;
|
|
242
|
-
/**
|
|
243
|
-
* Handle missing model binding
|
|
244
|
-
*/
|
|
245
|
-
missing(handler: (req: EnhancedRequest) => Response): this;
|
|
246
|
-
/**
|
|
247
|
-
* Include soft-deleted models
|
|
248
|
-
*/
|
|
249
|
-
withTrashed(): this;
|
|
250
|
-
/**
|
|
251
|
-
* Enable scoped bindings
|
|
252
|
-
*/
|
|
253
|
-
scopeBindings(): this;
|
|
254
|
-
private findRoute;
|
|
255
|
-
}
|
|
256
|
-
interface GroupOptions {
|
|
257
|
-
prefix?: string;
|
|
258
|
-
as?: string;
|
|
259
|
-
middleware?: (string | MiddlewareHandler)[];
|
|
260
|
-
domain?: string;
|
|
261
|
-
controller?: ControllerClass;
|
|
262
|
-
}
|
|
263
|
-
declare class GroupBuilderImpl implements GroupBuilder {
|
|
264
|
-
private fluentRouter;
|
|
265
|
-
private options;
|
|
266
|
-
constructor(router: FluentRouter);
|
|
267
|
-
prefix(prefix: string): this;
|
|
268
|
-
name(name: string): this;
|
|
269
|
-
middleware(...middleware: (string | MiddlewareHandler)[]): this;
|
|
270
|
-
domain(domain: string): this;
|
|
271
|
-
controller(controller: ControllerClass): this;
|
|
272
|
-
routes(callback: (router: FluentRouter) => void): void;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Extend Router with fluent routing methods
|
|
276
|
-
*/
|
|
277
|
-
export declare function registerFluentRouting(RouterClass: typeof import('./router').Router): void;
|
|
278
|
-
declare module './router' {
|
|
279
|
-
interface Router {
|
|
280
|
-
/**
|
|
281
|
-
* Get fluent router instance
|
|
282
|
-
*/
|
|
283
|
-
fluent: () => FluentRouter;
|
|
284
|
-
/**
|
|
285
|
-
* Register a controller-based resource
|
|
286
|
-
* Note: Use `resourceController` to avoid conflict with existing `resource` method
|
|
287
|
-
*/
|
|
288
|
-
resourceController: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router;
|
|
289
|
-
/**
|
|
290
|
-
* Register an API resource controller (no create/edit views)
|
|
291
|
-
*/
|
|
292
|
-
apiResourceController: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router;
|
|
293
|
-
/**
|
|
294
|
-
* Register a singleton resource
|
|
295
|
-
*/
|
|
296
|
-
singletonResource: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router;
|
|
297
|
-
/**
|
|
298
|
-
* Check if a named route exists
|
|
299
|
-
*/
|
|
300
|
-
hasRoute: (name: string) => boolean;
|
|
301
|
-
/**
|
|
302
|
-
* Get the current route
|
|
303
|
-
*/
|
|
304
|
-
currentRoute: () => Route | null;
|
|
305
|
-
/**
|
|
306
|
-
* Get the current route name
|
|
307
|
-
*/
|
|
308
|
-
currentRouteName: () => string | null;
|
|
309
|
-
/**
|
|
310
|
-
* Get URL for a named route
|
|
311
|
-
*/
|
|
312
|
-
routeUrl: (name: string, params?: Record<string, string | number>) => string;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
export {};
|