@shaferllc/keel 0.36.0 → 0.59.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/README.md +14 -2
- package/dist/core/application.d.ts +58 -2
- package/dist/core/application.js +99 -3
- package/dist/core/auth.d.ts +21 -2
- package/dist/core/auth.js +38 -3
- package/dist/core/authorization.d.ts +52 -0
- package/dist/core/authorization.js +97 -0
- package/dist/core/broadcasting.d.ts +49 -0
- package/dist/core/broadcasting.js +84 -0
- package/dist/core/broker.d.ts +398 -0
- package/dist/core/broker.js +602 -0
- package/dist/core/crypto.d.ts +51 -1
- package/dist/core/crypto.js +96 -3
- package/dist/core/database.d.ts +26 -1
- package/dist/core/database.js +65 -2
- package/dist/core/decorators.d.ts +39 -0
- package/dist/core/decorators.js +72 -0
- package/dist/core/exceptions.d.ts +77 -6
- package/dist/core/exceptions.js +168 -10
- package/dist/core/helpers.d.ts +6 -0
- package/dist/core/helpers.js +12 -0
- package/dist/core/http/kernel.js +14 -2
- package/dist/core/http/router.d.ts +14 -0
- package/dist/core/http/router.js +30 -1
- package/dist/core/index.d.ts +31 -8
- package/dist/core/index.js +17 -5
- package/dist/core/logger.d.ts +5 -0
- package/dist/core/logger.js +24 -2
- package/dist/core/migrations.js +3 -3
- package/dist/core/model.d.ts +19 -1
- package/dist/core/model.js +72 -4
- package/dist/core/provider.d.ts +13 -4
- package/dist/core/provider.js +12 -2
- package/dist/core/redis.d.ts +78 -0
- package/dist/core/redis.js +176 -0
- package/dist/core/request-logger.d.ts +26 -0
- package/dist/core/request-logger.js +48 -0
- package/dist/core/request.d.ts +17 -1
- package/dist/core/request.js +27 -1
- package/dist/core/scheduler.d.ts +60 -0
- package/dist/core/scheduler.js +166 -0
- package/dist/core/session.js +17 -2
- package/dist/core/storage.d.ts +57 -0
- package/dist/core/storage.js +98 -0
- package/dist/core/template.d.ts +50 -0
- package/dist/core/template.js +753 -0
- package/dist/core/testing.d.ts +54 -0
- package/dist/core/testing.js +141 -0
- package/dist/core/transformer.d.ts +89 -0
- package/dist/core/transformer.js +152 -0
- package/dist/core/validation.d.ts +20 -0
- package/dist/core/validation.js +52 -1
- package/dist/core/vite.d.ts +117 -0
- package/dist/core/vite.js +258 -0
- package/dist/vite/index.d.ts +40 -0
- package/dist/vite/index.js +146 -0
- package/package.json +16 -1
package/dist/core/exceptions.js
CHANGED
|
@@ -6,14 +6,20 @@
|
|
|
6
6
|
export const STATUS_TEXT = {
|
|
7
7
|
400: "Bad Request",
|
|
8
8
|
401: "Unauthorized",
|
|
9
|
+
402: "Payment Required",
|
|
9
10
|
403: "Forbidden",
|
|
10
11
|
404: "Not Found",
|
|
11
12
|
405: "Method Not Allowed",
|
|
13
|
+
406: "Not Acceptable",
|
|
14
|
+
408: "Request Timeout",
|
|
12
15
|
409: "Conflict",
|
|
16
|
+
411: "Length Required",
|
|
13
17
|
419: "Page Expired",
|
|
14
18
|
422: "Unprocessable Entity",
|
|
15
19
|
429: "Too Many Requests",
|
|
16
20
|
500: "Internal Server Error",
|
|
21
|
+
501: "Not Implemented",
|
|
22
|
+
502: "Bad Gateway",
|
|
17
23
|
503: "Service Unavailable",
|
|
18
24
|
};
|
|
19
25
|
/**
|
|
@@ -29,37 +35,189 @@ export class HttpException extends Error {
|
|
|
29
35
|
headers;
|
|
30
36
|
/** A machine-readable error code, e.g. "E_VALIDATION". */
|
|
31
37
|
code;
|
|
32
|
-
|
|
38
|
+
/** Structured context attached to the error; surfaced in the JSON body as `data`. */
|
|
39
|
+
data;
|
|
40
|
+
constructor(status, message, headers, data) {
|
|
33
41
|
super(message ?? STATUS_TEXT[status] ?? "Error");
|
|
34
42
|
this.name = "HttpException";
|
|
35
43
|
this.status = status;
|
|
36
44
|
this.headers = headers;
|
|
45
|
+
this.data = data;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The error as a plain object, matching the JSON body the HTTP kernel renders:
|
|
49
|
+
* `{ error, status }` plus `code` and `data` when present. Subclasses that add
|
|
50
|
+
* fields (e.g. `ValidationException.errors`) override this to include them.
|
|
51
|
+
*/
|
|
52
|
+
toJSON() {
|
|
53
|
+
const body = { error: this.message, status: this.status };
|
|
54
|
+
if (this.code)
|
|
55
|
+
body.code = this.code;
|
|
56
|
+
if (this.data !== undefined)
|
|
57
|
+
body.data = this.data;
|
|
58
|
+
return body;
|
|
37
59
|
}
|
|
38
60
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
61
|
+
/*
|
|
62
|
+
* The standard HTTP error family. Each carries a fixed status and a stable
|
|
63
|
+
* `code`, and takes an optional `data` bag that lands in the JSON body. Throw
|
|
64
|
+
* any of them from a handler, middleware, or service to short-circuit with the
|
|
65
|
+
* right status. Ordered by status code.
|
|
66
|
+
*/
|
|
67
|
+
/** 400 — the request was malformed or failed validation. */
|
|
68
|
+
export class BadRequestException extends HttpException {
|
|
69
|
+
constructor(message = "Bad Request", data) {
|
|
70
|
+
super(400, message, undefined, data);
|
|
71
|
+
this.name = "BadRequestException";
|
|
72
|
+
this.code = "E_BAD_REQUEST";
|
|
43
73
|
}
|
|
44
74
|
}
|
|
45
75
|
export class UnauthorizedException extends HttpException {
|
|
46
|
-
constructor(message = "Unauthorized") {
|
|
47
|
-
super(401, message);
|
|
76
|
+
constructor(message = "Unauthorized", data) {
|
|
77
|
+
super(401, message, undefined, data);
|
|
48
78
|
this.name = "UnauthorizedException";
|
|
79
|
+
this.code = "E_UNAUTHORIZED";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** 402 — payment is required to access the resource. */
|
|
83
|
+
export class PaymentRequiredException extends HttpException {
|
|
84
|
+
constructor(message = "Payment Required", data) {
|
|
85
|
+
super(402, message, undefined, data);
|
|
86
|
+
this.name = "PaymentRequiredException";
|
|
87
|
+
this.code = "E_PAYMENT_REQUIRED";
|
|
49
88
|
}
|
|
50
89
|
}
|
|
51
90
|
export class ForbiddenException extends HttpException {
|
|
52
|
-
constructor(message = "Forbidden") {
|
|
53
|
-
super(403, message);
|
|
91
|
+
constructor(message = "Forbidden", data) {
|
|
92
|
+
super(403, message, undefined, data);
|
|
54
93
|
this.name = "ForbiddenException";
|
|
94
|
+
this.code = "E_FORBIDDEN";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export class NotFoundException extends HttpException {
|
|
98
|
+
constructor(message = "Not Found", data) {
|
|
99
|
+
super(404, message, undefined, data);
|
|
100
|
+
this.name = "NotFoundException";
|
|
101
|
+
this.code = "E_NOT_FOUND";
|
|
55
102
|
}
|
|
56
103
|
}
|
|
57
|
-
/**
|
|
104
|
+
/** 405 — the HTTP method isn't allowed for this resource. */
|
|
105
|
+
export class MethodNotAllowedException extends HttpException {
|
|
106
|
+
constructor(message = "Method Not Allowed", data) {
|
|
107
|
+
super(405, message, undefined, data);
|
|
108
|
+
this.name = "MethodNotAllowedException";
|
|
109
|
+
this.code = "E_METHOD_NOT_ALLOWED";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** 406 — no representation matches the request's `Accept` header. */
|
|
113
|
+
export class NotAcceptableException extends HttpException {
|
|
114
|
+
constructor(message = "Not Acceptable", data) {
|
|
115
|
+
super(406, message, undefined, data);
|
|
116
|
+
this.name = "NotAcceptableException";
|
|
117
|
+
this.code = "E_NOT_ACCEPTABLE";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** 408 — the client took too long to send the request. */
|
|
121
|
+
export class RequestTimeoutException extends HttpException {
|
|
122
|
+
constructor(message = "Request Timeout", data) {
|
|
123
|
+
super(408, message, undefined, data);
|
|
124
|
+
this.name = "RequestTimeoutException";
|
|
125
|
+
this.code = "E_REQUEST_TIMEOUT";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** 409 — the request conflicts with the current state (e.g. a duplicate). */
|
|
129
|
+
export class ConflictException extends HttpException {
|
|
130
|
+
constructor(message = "Conflict", data) {
|
|
131
|
+
super(409, message, undefined, data);
|
|
132
|
+
this.name = "ConflictException";
|
|
133
|
+
this.code = "E_CONFLICT";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** 411 — a `Content-Length` header is required and was missing. */
|
|
137
|
+
export class LengthRequiredException extends HttpException {
|
|
138
|
+
constructor(message = "Length Required", data) {
|
|
139
|
+
super(411, message, undefined, data);
|
|
140
|
+
this.name = "LengthRequiredException";
|
|
141
|
+
this.code = "E_LENGTH_REQUIRED";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** 422 with per-field messages. Pairs with the validation layer. */
|
|
58
145
|
export class ValidationException extends HttpException {
|
|
59
146
|
errors;
|
|
60
147
|
constructor(errors, message = "The given data was invalid.") {
|
|
61
148
|
super(422, message);
|
|
62
149
|
this.name = "ValidationException";
|
|
150
|
+
this.code = "E_VALIDATION";
|
|
63
151
|
this.errors = errors;
|
|
64
152
|
}
|
|
153
|
+
toJSON() {
|
|
154
|
+
return { ...super.toJSON(), errors: this.errors };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** 429 — the client has sent too many requests in a given window. */
|
|
158
|
+
export class TooManyRequestsException extends HttpException {
|
|
159
|
+
constructor(message = "Too Many Requests", data) {
|
|
160
|
+
super(429, message, undefined, data);
|
|
161
|
+
this.name = "TooManyRequestsException";
|
|
162
|
+
this.code = "E_TOO_MANY_REQUESTS";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** 500 — a catch-all server error you want to raise deliberately. */
|
|
166
|
+
export class ServerErrorException extends HttpException {
|
|
167
|
+
constructor(message = "Internal Server Error", data) {
|
|
168
|
+
super(500, message, undefined, data);
|
|
169
|
+
this.name = "ServerErrorException";
|
|
170
|
+
this.code = "E_SERVER_ERROR";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** 501 — the requested functionality isn't implemented. */
|
|
174
|
+
export class NotImplementedException extends HttpException {
|
|
175
|
+
constructor(message = "Not Implemented", data) {
|
|
176
|
+
super(501, message, undefined, data);
|
|
177
|
+
this.name = "NotImplementedException";
|
|
178
|
+
this.code = "E_NOT_IMPLEMENTED";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** 502 — an upstream server returned an invalid response. */
|
|
182
|
+
export class BadGatewayException extends HttpException {
|
|
183
|
+
constructor(message = "Bad Gateway", data) {
|
|
184
|
+
super(502, message, undefined, data);
|
|
185
|
+
this.name = "BadGatewayException";
|
|
186
|
+
this.code = "E_BAD_GATEWAY";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** 503 — the service is temporarily unavailable (down, overloaded). */
|
|
190
|
+
export class ServiceUnavailableException extends HttpException {
|
|
191
|
+
constructor(message = "Service Unavailable", data) {
|
|
192
|
+
super(503, message, undefined, data);
|
|
193
|
+
this.name = "ServiceUnavailableException";
|
|
194
|
+
this.code = "E_SERVICE_UNAVAILABLE";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Mint a reusable, coded `HttpException` subclass — the ergonomic way to define
|
|
199
|
+
* app-specific errors with a stable, machine-readable `code`. Inspired by
|
|
200
|
+
* `@fastify/error`. The `message` may carry `%s` placeholders, filled in order
|
|
201
|
+
* from the constructor arguments.
|
|
202
|
+
*
|
|
203
|
+
* const InsufficientFunds = createError("E_FUNDS", "Balance too low: need %s", 402);
|
|
204
|
+
* throw new InsufficientFunds("$40");
|
|
205
|
+
* // → 402 { error: "Balance too low: need $40", status: 402, code: "E_FUNDS" }
|
|
206
|
+
*
|
|
207
|
+
* The returned class extends `HttpException`, so it renders through the same
|
|
208
|
+
* path — `code` lands in the JSON body — and passes `instanceof HttpException`.
|
|
209
|
+
*/
|
|
210
|
+
export function createError(code, message, status = 500) {
|
|
211
|
+
return class extends HttpException {
|
|
212
|
+
constructor(...args) {
|
|
213
|
+
super(status, formatMessage(message, args));
|
|
214
|
+
this.name = code;
|
|
215
|
+
this.code = code;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/** printf-lite: replace each `%s` with the next argument, in order. */
|
|
220
|
+
function formatMessage(template, args) {
|
|
221
|
+
let index = 0;
|
|
222
|
+
return template.replace(/%s/g, () => (index < args.length ? String(args[index++]) : "%s"));
|
|
65
223
|
}
|
package/dist/core/helpers.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ import { Logger } from "./logger.js";
|
|
|
17
17
|
export declare function setApplication(app: Application): void;
|
|
18
18
|
/** The active application container. Throws if none has been created. */
|
|
19
19
|
export declare function app(): Application;
|
|
20
|
+
/** Run a callback once the active application has booted (see `Application.onReady`). */
|
|
21
|
+
export declare function onReady(hook: (app: Application) => void | Promise<void>): void;
|
|
22
|
+
/** Register a graceful-shutdown hook on the active application (see `Application.onShutdown`). */
|
|
23
|
+
export declare function onShutdown(hook: (app: Application) => void | Promise<void>): void;
|
|
24
|
+
/** Gracefully terminate the active application, running its shutdown hooks. */
|
|
25
|
+
export declare function terminate(): Promise<void>;
|
|
20
26
|
/**
|
|
21
27
|
* Read configuration with dot notation: `config('app.name')`, or with a
|
|
22
28
|
* fallback: `config('app.port', 3000)`.
|
package/dist/core/helpers.js
CHANGED
|
@@ -24,6 +24,18 @@ export function app() {
|
|
|
24
24
|
}
|
|
25
25
|
return current;
|
|
26
26
|
}
|
|
27
|
+
/** Run a callback once the active application has booted (see `Application.onReady`). */
|
|
28
|
+
export function onReady(hook) {
|
|
29
|
+
app().onReady(hook);
|
|
30
|
+
}
|
|
31
|
+
/** Register a graceful-shutdown hook on the active application (see `Application.onShutdown`). */
|
|
32
|
+
export function onShutdown(hook) {
|
|
33
|
+
app().onShutdown(hook);
|
|
34
|
+
}
|
|
35
|
+
/** Gracefully terminate the active application, running its shutdown hooks. */
|
|
36
|
+
export function terminate() {
|
|
37
|
+
return app().terminate();
|
|
38
|
+
}
|
|
27
39
|
/**
|
|
28
40
|
* Read configuration with dot notation: `config('app.name')`, or with a
|
|
29
41
|
* fallback: `config('app.port', 3000)`.
|
package/dist/core/http/kernel.js
CHANGED
|
@@ -101,8 +101,18 @@ export class HttpKernel {
|
|
|
101
101
|
}
|
|
102
102
|
for (const route of routes) {
|
|
103
103
|
const fn = router.resolve(route.handler);
|
|
104
|
+
// Set the matched-route context *before* this route's middleware, so both
|
|
105
|
+
// route middleware and the handler can read `request.route` (incl. config).
|
|
106
|
+
const setRoute = async (c, next) => {
|
|
107
|
+
c.set("route", {
|
|
108
|
+
name: route.name,
|
|
109
|
+
pattern: route.path,
|
|
110
|
+
methods: route.methods,
|
|
111
|
+
config: route.config,
|
|
112
|
+
});
|
|
113
|
+
await next();
|
|
114
|
+
};
|
|
104
115
|
const honoHandler = async (c) => {
|
|
105
|
-
c.set("route", { name: route.name, pattern: route.path, methods: route.methods });
|
|
106
116
|
const result = await fn(c);
|
|
107
117
|
return typeof result === "string" ? c.html(result) : result;
|
|
108
118
|
};
|
|
@@ -111,7 +121,7 @@ export class HttpKernel {
|
|
|
111
121
|
for (const [param, rgx] of Object.entries(route.wheres)) {
|
|
112
122
|
path = path.replace(new RegExp(`:${param}(\\??)`), `:${param}{${rgx}}$1`);
|
|
113
123
|
}
|
|
114
|
-
const middleware = route.middleware.map((m) => router.resolveMiddleware(m));
|
|
124
|
+
const middleware = [setRoute, ...route.middleware.map((m) => router.resolveMiddleware(m))];
|
|
115
125
|
hono.on(route.methods, [path], ...middleware, honoHandler);
|
|
116
126
|
}
|
|
117
127
|
hono.notFound((c) => this.handle(new NotFoundException(`No route for ${c.req.method} ${c.req.path}`), c));
|
|
@@ -159,6 +169,8 @@ export class HttpKernel {
|
|
|
159
169
|
const body = { error: message, status };
|
|
160
170
|
if (isHttp && err.code)
|
|
161
171
|
body.code = err.code;
|
|
172
|
+
if (isHttp && err.data !== undefined)
|
|
173
|
+
body.data = err.data;
|
|
162
174
|
if (err instanceof ValidationException)
|
|
163
175
|
body.errors = err.errors;
|
|
164
176
|
if (debug && !isHttp && err instanceof Error) {
|
|
@@ -53,6 +53,8 @@ export interface RouteDefinition {
|
|
|
53
53
|
wheres: Record<string, string>;
|
|
54
54
|
/** Host pattern this route is bound to, e.g. ":tenant.example.com". */
|
|
55
55
|
domain?: string;
|
|
56
|
+
/** Arbitrary per-route metadata, readable in the handler via `request.route.config`. */
|
|
57
|
+
config: Record<string, unknown>;
|
|
56
58
|
}
|
|
57
59
|
/** A single registered route — chain to name it, guard it, or constrain params. */
|
|
58
60
|
export declare class Route {
|
|
@@ -70,6 +72,8 @@ export declare class Route {
|
|
|
70
72
|
where(param: string, matcher: Matcher): this;
|
|
71
73
|
/** Bind this route to a host pattern (supports `:subdomain` segments). */
|
|
72
74
|
domain(pattern: string): this;
|
|
75
|
+
/** Attach arbitrary metadata to the route — read it via `request.route.config`. */
|
|
76
|
+
config(data: Record<string, unknown>): this;
|
|
73
77
|
}
|
|
74
78
|
/** A group of routes sharing a prefix, middleware, and/or name prefix. */
|
|
75
79
|
export declare class RouteGroup {
|
|
@@ -79,6 +83,8 @@ export declare class RouteGroup {
|
|
|
79
83
|
middleware(mw: MiddlewareRef | MiddlewareRef[]): this;
|
|
80
84
|
/** Alias for middleware(), matching AdonisJS. */
|
|
81
85
|
use(mw: MiddlewareRef | MiddlewareRef[]): this;
|
|
86
|
+
/** Attach metadata to every route in the group (a route's own config wins). */
|
|
87
|
+
config(data: Record<string, unknown>): this;
|
|
82
88
|
/** Constrain a parameter across every route in the group. */
|
|
83
89
|
where(param: string, matcher: Matcher): this;
|
|
84
90
|
as(namePrefix: string): this;
|
|
@@ -165,6 +171,14 @@ export declare class Router {
|
|
|
165
171
|
*/
|
|
166
172
|
resource(name: string, controller: ControllerRef): RouteResource;
|
|
167
173
|
private add;
|
|
174
|
+
private routeHooks;
|
|
175
|
+
/**
|
|
176
|
+
* Observe route registration — called with each route's definition as it's
|
|
177
|
+
* added (and replayed for routes already registered). Handy for logging or
|
|
178
|
+
* building an API map. The `def` is live, so reading it later reflects fluent
|
|
179
|
+
* config (`.name()`, `.middleware()`) applied after registration.
|
|
180
|
+
*/
|
|
181
|
+
onRoute(hook: (def: RouteDefinition) => void): this;
|
|
168
182
|
/** Register a global parameter constraint, applied to every matching route. */
|
|
169
183
|
where(param: string, matcher: Matcher): this;
|
|
170
184
|
/** All registered routes (excluding those trimmed to zero methods). */
|
package/dist/core/http/router.js
CHANGED
|
@@ -70,6 +70,11 @@ export class Route {
|
|
|
70
70
|
this.def.domain = pattern;
|
|
71
71
|
return this;
|
|
72
72
|
}
|
|
73
|
+
/** Attach arbitrary metadata to the route — read it via `request.route.config`. */
|
|
74
|
+
config(data) {
|
|
75
|
+
Object.assign(this.def.config, data);
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
/** A group of routes sharing a prefix, middleware, and/or name prefix. */
|
|
75
80
|
export class RouteGroup {
|
|
@@ -93,6 +98,12 @@ export class RouteGroup {
|
|
|
93
98
|
use(mw) {
|
|
94
99
|
return this.middleware(mw);
|
|
95
100
|
}
|
|
101
|
+
/** Attach metadata to every route in the group (a route's own config wins). */
|
|
102
|
+
config(data) {
|
|
103
|
+
for (const r of this.routes)
|
|
104
|
+
r.config = { ...data, ...r.config };
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
96
107
|
/** Constrain a parameter across every route in the group. */
|
|
97
108
|
where(param, matcher) {
|
|
98
109
|
for (const r of this.routes) {
|
|
@@ -317,10 +328,26 @@ export class Router {
|
|
|
317
328
|
handler,
|
|
318
329
|
middleware: [...this.group_mw],
|
|
319
330
|
wheres: {},
|
|
331
|
+
config: {},
|
|
320
332
|
};
|
|
321
333
|
this.routes.push(def);
|
|
334
|
+
for (const hook of this.routeHooks)
|
|
335
|
+
hook(def);
|
|
322
336
|
return new Route(def);
|
|
323
337
|
}
|
|
338
|
+
routeHooks = [];
|
|
339
|
+
/**
|
|
340
|
+
* Observe route registration — called with each route's definition as it's
|
|
341
|
+
* added (and replayed for routes already registered). Handy for logging or
|
|
342
|
+
* building an API map. The `def` is live, so reading it later reflects fluent
|
|
343
|
+
* config (`.name()`, `.middleware()`) applied after registration.
|
|
344
|
+
*/
|
|
345
|
+
onRoute(hook) {
|
|
346
|
+
for (const def of this.routes)
|
|
347
|
+
hook(def);
|
|
348
|
+
this.routeHooks.push(hook);
|
|
349
|
+
return this;
|
|
350
|
+
}
|
|
324
351
|
/** Register a global parameter constraint, applied to every matching route. */
|
|
325
352
|
where(param, matcher) {
|
|
326
353
|
this.globalWheres[param] = matcherSource(matcher);
|
|
@@ -344,7 +371,9 @@ export class Router {
|
|
|
344
371
|
throw new Error(`No route named [${name}].`);
|
|
345
372
|
let path = def.path;
|
|
346
373
|
for (const [k, v] of Object.entries(params)) {
|
|
347
|
-
|
|
374
|
+
// Global + word-boundary: replace every `:k` occurrence, and don't let
|
|
375
|
+
// `:id` match inside `:idx`.
|
|
376
|
+
path = path.replace(new RegExp(`:${k}\\b\\??`, "g"), encodeURIComponent(String(v)));
|
|
348
377
|
}
|
|
349
378
|
path = path.replace(/\/:[^/]+\?/g, "").replace(/:[^/]+/g, "");
|
|
350
379
|
if (options.qs && Object.keys(options.qs).length) {
|
package/dist/core/index.d.ts
CHANGED
|
@@ -2,23 +2,28 @@
|
|
|
2
2
|
export { Container } from "./container.js";
|
|
3
3
|
export type { Token, Constructor, Factory } from "./container.js";
|
|
4
4
|
export { Application } from "./application.js";
|
|
5
|
-
export type { BootOptions } from "./application.js";
|
|
5
|
+
export type { BootOptions, LifecycleHook, Configurator } from "./application.js";
|
|
6
6
|
export { Config, env } from "./config.js";
|
|
7
|
-
export { app, config, view, bind, singleton, instance, make, bound, events, emit, listen, cache, logger, } from "./helpers.js";
|
|
7
|
+
export { app, config, view, bind, singleton, instance, make, bound, events, emit, listen, cache, logger, onReady, onShutdown, terminate, } from "./helpers.js";
|
|
8
8
|
export { Logger } from "./logger.js";
|
|
9
9
|
export type { LogLevel, LoggerOptions } from "./logger.js";
|
|
10
|
+
export { requestLogger, requestLog } from "./request-logger.js";
|
|
11
|
+
export type { RequestLoggerOptions } from "./request-logger.js";
|
|
10
12
|
export { Events } from "./events.js";
|
|
11
13
|
export type { Listener } from "./events.js";
|
|
12
14
|
export { Cache, MemoryStore } from "./cache.js";
|
|
13
15
|
export type { CacheStore } from "./cache.js";
|
|
14
16
|
export { serveStatic } from "./static.js";
|
|
15
17
|
export type { StaticOptions } from "./static.js";
|
|
18
|
+
export { Storage, MemoryDisk, storage, setDisk } from "./storage.js";
|
|
19
|
+
export type { Disk, Contents } from "./storage.js";
|
|
16
20
|
export { dump, dd } from "./debug.js";
|
|
17
|
-
export { hash, encryption } from "./crypto.js";
|
|
21
|
+
export { hash, encryption, jwt } from "./crypto.js";
|
|
22
|
+
export type { JwtPayload, JwtSignOptions, JwtVerifyOptions } from "./crypto.js";
|
|
18
23
|
export { rateLimiter } from "./rate-limit.js";
|
|
19
24
|
export type { RateLimiterOptions } from "./rate-limit.js";
|
|
20
25
|
export { db, setConnection, QueryBuilder } from "./database.js";
|
|
21
|
-
export type { Connection, WriteResult, Row, Dialect, Operator } from "./database.js";
|
|
26
|
+
export type { Connection, WriteResult, Row, Dialect, Operator, Paginated } from "./database.js";
|
|
22
27
|
export { Model } from "./model.js";
|
|
23
28
|
export type { CastType, Casts } from "./casts.js";
|
|
24
29
|
export { Relation, HasOne, HasMany, BelongsTo, BelongsToMany } from "./relations.js";
|
|
@@ -28,14 +33,23 @@ export { Mailer, PendingMail, ArrayTransport, LogTransport, fetchTransport, mail
|
|
|
28
33
|
export type { Message, Transport, MailerOptions, FetchTransportOptions } from "./mail.js";
|
|
29
34
|
export { Job, Queue, SyncDriver, MemoryDriver, dispatch, work, setQueue, getQueue, } from "./queue.js";
|
|
30
35
|
export type { Dispatchable, JobOptions, QueueDriver, Drainable, QueuedJob } from "./queue.js";
|
|
36
|
+
export { Scheduler, ScheduledTask, scheduler, setScheduler, schedule, cronMatches } from "./scheduler.js";
|
|
37
|
+
export { MemoryBroadcaster, broadcast, setBroadcaster, getBroadcaster, channelAuth, authorizeChannel, clearChannels, } from "./broadcasting.js";
|
|
38
|
+
export type { Broadcaster, Subscriber, ChannelAuthorizer } from "./broadcasting.js";
|
|
39
|
+
export { Redis, MemoryRedis, redis, setRedis, redisStore } from "./redis.js";
|
|
40
|
+
export type { RedisConnection, SetOptions } from "./redis.js";
|
|
31
41
|
export { Notification, Notifier, MailChannel, DatabaseChannel, ArrayChannel, routeFor, notify, setNotifier, getNotifier, } from "./notification.js";
|
|
32
42
|
export type { Notifiable, MailContent, Channel } from "./notification.js";
|
|
33
43
|
export { SchemaBuilder, Migrator, TableBuilder, Column } from "./migrations.js";
|
|
34
44
|
export type { Migration } from "./migrations.js";
|
|
35
45
|
export { ctx, json, text, html, redirect, param, query, header, body, request, response, } from "./request.js";
|
|
46
|
+
export { decorateRequest, hasRequestDecorator, decorated, setRequestValue, clearRequestDecorators, } from "./decorators.js";
|
|
47
|
+
export type { RequestResolver } from "./decorators.js";
|
|
36
48
|
export type { ConfigData } from "./config.js";
|
|
37
49
|
export { View } from "./view.js";
|
|
38
50
|
export type { Renderable, ViewConfig } from "./view.js";
|
|
51
|
+
export { TemplateEngine, escapeHtml, templates, setTemplateEngine, render, } from "./template.js";
|
|
52
|
+
export type { Filter, RenderContext } from "./template.js";
|
|
39
53
|
export { ServiceProvider } from "./provider.js";
|
|
40
54
|
export type { ProviderClass } from "./provider.js";
|
|
41
55
|
export { Router, Route, RouteGroup, RouteResource, matchers } from "./http/router.js";
|
|
@@ -43,10 +57,19 @@ export type { Ctx, RouteHandler, RouteDefinition, Method, Matcher, MiddlewareRef
|
|
|
43
57
|
export { Inertia, inertia, inertiaPageAttr } from "./inertia.js";
|
|
44
58
|
export type { InertiaPage, InertiaOptions } from "./inertia.js";
|
|
45
59
|
export { HttpKernel } from "./http/kernel.js";
|
|
46
|
-
export {
|
|
47
|
-
export {
|
|
48
|
-
export
|
|
60
|
+
export { TestClient, TestResponse, testClient } from "./testing.js";
|
|
61
|
+
export { HttpException, BadRequestException, UnauthorizedException, PaymentRequiredException, ForbiddenException, NotFoundException, MethodNotAllowedException, NotAcceptableException, RequestTimeoutException, ConflictException, LengthRequiredException, ValidationException, TooManyRequestsException, ServerErrorException, NotImplementedException, BadGatewayException, ServiceUnavailableException, createError, STATUS_TEXT, } from "./exceptions.js";
|
|
62
|
+
export { validate, validateRequest, validated } from "./validation.js";
|
|
63
|
+
export type { Schema, RequestSchemas } from "./validation.js";
|
|
49
64
|
export { Session, session, sessionMiddleware } from "./session.js";
|
|
50
65
|
export type { SessionOptions } from "./session.js";
|
|
51
|
-
export { Auth, auth, authGuard, setUserProvider } from "./auth.js";
|
|
66
|
+
export { Auth, auth, authGuard, bearerAuth, setUserProvider } from "./auth.js";
|
|
52
67
|
export type { UserProvider } from "./auth.js";
|
|
68
|
+
export { define, policy, gateBefore, setUserResolver, clearAuthorization, can, cannot, canFor, authorize, authorizeFor, } from "./authorization.js";
|
|
69
|
+
export type { GateCallback, BeforeCallback } from "./authorization.js";
|
|
70
|
+
export { Transformer } from "./transformer.js";
|
|
71
|
+
export type { Attributes, DocumentOptions } from "./transformer.js";
|
|
72
|
+
export { Broker, Service, LocalTransporter, ServiceNotFoundError, RequestTimeoutError, broker, setBroker, } from "./broker.js";
|
|
73
|
+
export type { ServiceSchema, Context, ActionHandler, EventHandler, ActionDef, ActionSchema, ActionHooks, EventSchema, ServiceHooks, Visibility, EventType, BeforeHook, AfterHook, ErrorHook, Transporter, BrokerOptions, BrokerMiddleware, CallOptions, EmitOptions, MCallDefs, MCallOptions, } from "./broker.js";
|
|
74
|
+
export { Vite, viteTags, viteAsset, viteReactRefresh } from "./vite.js";
|
|
75
|
+
export type { ViteOptions, Manifest, ManifestChunk, Attributes as ViteAttributes, AttrValue, } from "./vite.js";
|
package/dist/core/index.js
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
export { Container } from "./container.js";
|
|
3
3
|
export { Application } from "./application.js";
|
|
4
4
|
export { Config, env } from "./config.js";
|
|
5
|
-
export { app, config, view, bind, singleton, instance, make, bound, events, emit, listen, cache, logger, } from "./helpers.js";
|
|
5
|
+
export { app, config, view, bind, singleton, instance, make, bound, events, emit, listen, cache, logger, onReady, onShutdown, terminate, } from "./helpers.js";
|
|
6
6
|
export { Logger } from "./logger.js";
|
|
7
|
+
export { requestLogger, requestLog } from "./request-logger.js";
|
|
7
8
|
export { Events } from "./events.js";
|
|
8
9
|
export { Cache, MemoryStore } from "./cache.js";
|
|
9
10
|
export { serveStatic } from "./static.js";
|
|
11
|
+
export { Storage, MemoryDisk, storage, setDisk } from "./storage.js";
|
|
10
12
|
export { dump, dd } from "./debug.js";
|
|
11
|
-
export { hash, encryption } from "./crypto.js";
|
|
13
|
+
export { hash, encryption, jwt } from "./crypto.js";
|
|
12
14
|
export { rateLimiter } from "./rate-limit.js";
|
|
13
15
|
export { db, setConnection, QueryBuilder } from "./database.js";
|
|
14
16
|
export { Model } from "./model.js";
|
|
@@ -16,15 +18,25 @@ export { Relation, HasOne, HasMany, BelongsTo, BelongsToMany } from "./relations
|
|
|
16
18
|
export { Faker, Factory as ModelFactory, factory, Seeder, seed } from "./factory.js";
|
|
17
19
|
export { Mailer, PendingMail, ArrayTransport, LogTransport, fetchTransport, mail, setMailer, getMailer, } from "./mail.js";
|
|
18
20
|
export { Job, Queue, SyncDriver, MemoryDriver, dispatch, work, setQueue, getQueue, } from "./queue.js";
|
|
21
|
+
export { Scheduler, ScheduledTask, scheduler, setScheduler, schedule, cronMatches } from "./scheduler.js";
|
|
22
|
+
export { MemoryBroadcaster, broadcast, setBroadcaster, getBroadcaster, channelAuth, authorizeChannel, clearChannels, } from "./broadcasting.js";
|
|
23
|
+
export { Redis, MemoryRedis, redis, setRedis, redisStore } from "./redis.js";
|
|
19
24
|
export { Notification, Notifier, MailChannel, DatabaseChannel, ArrayChannel, routeFor, notify, setNotifier, getNotifier, } from "./notification.js";
|
|
20
25
|
export { SchemaBuilder, Migrator, TableBuilder, Column } from "./migrations.js";
|
|
21
26
|
export { ctx, json, text, html, redirect, param, query, header, body, request, response, } from "./request.js";
|
|
27
|
+
export { decorateRequest, hasRequestDecorator, decorated, setRequestValue, clearRequestDecorators, } from "./decorators.js";
|
|
22
28
|
export { View } from "./view.js";
|
|
29
|
+
export { TemplateEngine, escapeHtml, templates, setTemplateEngine, render, } from "./template.js";
|
|
23
30
|
export { ServiceProvider } from "./provider.js";
|
|
24
31
|
export { Router, Route, RouteGroup, RouteResource, matchers } from "./http/router.js";
|
|
25
32
|
export { Inertia, inertia, inertiaPageAttr } from "./inertia.js";
|
|
26
33
|
export { HttpKernel } from "./http/kernel.js";
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
34
|
+
export { TestClient, TestResponse, testClient } from "./testing.js";
|
|
35
|
+
export { HttpException, BadRequestException, UnauthorizedException, PaymentRequiredException, ForbiddenException, NotFoundException, MethodNotAllowedException, NotAcceptableException, RequestTimeoutException, ConflictException, LengthRequiredException, ValidationException, TooManyRequestsException, ServerErrorException, NotImplementedException, BadGatewayException, ServiceUnavailableException, createError, STATUS_TEXT, } from "./exceptions.js";
|
|
36
|
+
export { validate, validateRequest, validated } from "./validation.js";
|
|
29
37
|
export { Session, session, sessionMiddleware } from "./session.js";
|
|
30
|
-
export { Auth, auth, authGuard, setUserProvider } from "./auth.js";
|
|
38
|
+
export { Auth, auth, authGuard, bearerAuth, setUserProvider } from "./auth.js";
|
|
39
|
+
export { define, policy, gateBefore, setUserResolver, clearAuthorization, can, cannot, canFor, authorize, authorizeFor, } from "./authorization.js";
|
|
40
|
+
export { Transformer } from "./transformer.js";
|
|
41
|
+
export { Broker, Service, LocalTransporter, ServiceNotFoundError, RequestTimeoutError, broker, setBroker, } from "./broker.js";
|
|
42
|
+
export { Vite, viteTags, viteAsset, viteReactRefresh } from "./vite.js";
|
package/dist/core/logger.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export interface LoggerOptions {
|
|
|
14
14
|
pretty?: boolean;
|
|
15
15
|
/** Fields merged into every log line. */
|
|
16
16
|
bindings?: Record<string, unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Field paths to redact from log output — top-level keys (`"password"`) or dot
|
|
19
|
+
* paths (`"req.headers.authorization"`). Matched values become `"[redacted]"`.
|
|
20
|
+
*/
|
|
21
|
+
redact?: string[];
|
|
17
22
|
}
|
|
18
23
|
export declare class Logger {
|
|
19
24
|
private options;
|
package/dist/core/logger.js
CHANGED
|
@@ -7,6 +7,24 @@
|
|
|
7
7
|
* logger().error("payment failed", { orderId, error });
|
|
8
8
|
*/
|
|
9
9
|
const LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
10
|
+
const REDACTED = "[redacted]";
|
|
11
|
+
/** Return a copy of `obj` with `path` (dot-separated) redacted — clones only along the path. */
|
|
12
|
+
function redactPath(obj, keys) {
|
|
13
|
+
const [head, ...rest] = keys;
|
|
14
|
+
if (head === undefined || !(head in obj))
|
|
15
|
+
return obj;
|
|
16
|
+
const clone = { ...obj };
|
|
17
|
+
if (rest.length === 0) {
|
|
18
|
+
clone[head] = REDACTED;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const child = clone[head];
|
|
22
|
+
if (child && typeof child === "object") {
|
|
23
|
+
clone[head] = redactPath(child, rest);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return clone;
|
|
27
|
+
}
|
|
10
28
|
export class Logger {
|
|
11
29
|
options;
|
|
12
30
|
threshold;
|
|
@@ -18,13 +36,17 @@ export class Logger {
|
|
|
18
36
|
if (LEVELS[level] < this.threshold)
|
|
19
37
|
return;
|
|
20
38
|
const time = new Date().toISOString();
|
|
39
|
+
let fields = { ...this.options.bindings, ...context };
|
|
40
|
+
for (const path of this.options.redact ?? []) {
|
|
41
|
+
fields = redactPath(fields, path.split("."));
|
|
42
|
+
}
|
|
21
43
|
if (this.options.pretty) {
|
|
22
|
-
const extra =
|
|
44
|
+
const extra = Object.keys(fields).length ? " " + JSON.stringify(fields) : "";
|
|
23
45
|
const fn = level === "warn" ? console.warn : level === "error" ? console.error : console.log;
|
|
24
46
|
fn(`[${time}] ${level.toUpperCase().padEnd(5)} ${message}${extra}`);
|
|
25
47
|
}
|
|
26
48
|
else {
|
|
27
|
-
console.log(JSON.stringify({ level, time, msg: message, ...
|
|
49
|
+
console.log(JSON.stringify({ level, time, msg: message, ...fields }));
|
|
28
50
|
}
|
|
29
51
|
}
|
|
30
52
|
debug(message, context) {
|
package/dist/core/migrations.js
CHANGED
|
@@ -165,11 +165,11 @@ export class Migrator {
|
|
|
165
165
|
/** Names of migrations already applied. */
|
|
166
166
|
async ran() {
|
|
167
167
|
await this.ensure();
|
|
168
|
-
const rows = await this.conn.select("SELECT name FROM migrations", []);
|
|
168
|
+
const rows = (await this.conn.select("SELECT name FROM migrations", []));
|
|
169
169
|
return rows.map((r) => String(r.name));
|
|
170
170
|
}
|
|
171
171
|
async maxBatch() {
|
|
172
|
-
const rows = await this.conn.select("SELECT MAX(batch) AS b FROM migrations", []);
|
|
172
|
+
const rows = (await this.conn.select("SELECT MAX(batch) AS b FROM migrations", []));
|
|
173
173
|
return Number(rows[0]?.b ?? 0);
|
|
174
174
|
}
|
|
175
175
|
/** Run all pending migrations. Returns the names applied. */
|
|
@@ -196,7 +196,7 @@ export class Migrator {
|
|
|
196
196
|
const batch = await this.maxBatch();
|
|
197
197
|
if (!batch)
|
|
198
198
|
return [];
|
|
199
|
-
const rows = await this.conn.select(this.ph("SELECT name FROM migrations WHERE batch = ?"), [batch]);
|
|
199
|
+
const rows = (await this.conn.select(this.ph("SELECT name FROM migrations WHERE batch = ?"), [batch]));
|
|
200
200
|
const schema = new SchemaBuilder(this.conn, this.dialect);
|
|
201
201
|
const rolled = [];
|
|
202
202
|
for (const name of rows.map((r) => String(r.name)).reverse()) {
|