clear-router 2.7.5 → 2.7.7
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/ClearRequest.cjs +23 -0
- package/dist/ClearRequest.d.cts +28 -0
- package/dist/ClearRequest.d.mts +28 -0
- package/dist/ClearRequest.mjs +22 -0
- package/dist/Contracts.d.cts +12 -0
- package/dist/Contracts.d.mts +12 -0
- package/dist/Controller.cjs +12 -0
- package/dist/Controller.d.cts +14 -0
- package/dist/Controller.d.mts +14 -0
- package/dist/Controller.mjs +11 -0
- package/dist/{Request-DKXwa_W0.d.mts → Request-BhTJDR_5.d.mts} +5 -5
- package/dist/Route.cjs +58 -0
- package/dist/Route.d.cts +35 -0
- package/dist/Route.d.mts +35 -0
- package/dist/Route.mjs +57 -0
- package/dist/core/Request.cjs +35 -0
- package/dist/core/Request.d.cts +25 -0
- package/dist/core/Request.d.mts +25 -0
- package/dist/core/Request.mjs +35 -0
- package/dist/core/Response.cjs +59 -0
- package/dist/core/Response.d.cts +24 -0
- package/dist/core/Response.d.mts +24 -0
- package/dist/core/Response.mjs +58 -0
- package/dist/{bindings-CLsZjOEy.cjs → core/bindings.cjs} +10 -160
- package/dist/{bindings-CNL7bpz5.d.mts → core/bindings.d.cts} +1 -1
- package/dist/{bindings-CxvtC8XS.d.cts → core/bindings.d.mts} +1 -1
- package/dist/{bindings-XLDXFpHZ.mjs → core/bindings.mjs} +3 -110
- package/dist/core/index.cjs +4 -20
- package/dist/core/index.d.cts +4 -1
- package/dist/core/index.d.mts +4 -1
- package/dist/core/index.mjs +4 -14
- package/dist/core/plugins.cjs +14 -0
- package/dist/core/plugins.d.cts +109 -0
- package/dist/core/plugins.d.mts +109 -0
- package/dist/core/plugins.mjs +13 -0
- package/dist/{responses-Bvnk0uvc.cjs → core/responses.cjs} +5 -20
- package/dist/{responses-BvETUeDL.mjs → core/responses.mjs} +2 -2
- package/dist/{router-C6W-k6sS.cjs → core/router.cjs} +11 -69
- package/dist/core/router.d.cts +274 -0
- package/dist/core/router.d.mts +274 -0
- package/dist/{router-Dc9w86Wn.mjs → core/router.mjs} +5 -58
- package/dist/decorators/index.cjs +1 -1
- package/dist/decorators/index.d.cts +1 -1
- package/dist/decorators/index.d.mts +1 -1
- package/dist/decorators/index.mjs +1 -1
- package/dist/decorators/setup.cjs +2 -2
- package/dist/decorators/setup.d.cts +1 -1
- package/dist/decorators/setup.d.mts +1 -1
- package/dist/decorators/setup.mjs +2 -2
- package/dist/express/index.cjs +2 -265
- package/dist/express/index.d.cts +1 -127
- package/dist/express/index.d.mts +1 -127
- package/dist/express/index.mjs +1 -264
- package/dist/express/router.cjs +265 -0
- package/dist/express/router.d.cts +131 -0
- package/dist/express/router.d.mts +131 -0
- package/dist/express/router.mjs +265 -0
- package/dist/fastify/index.cjs +2 -254
- package/dist/fastify/index.d.cts +1 -125
- package/dist/fastify/index.d.mts +1 -125
- package/dist/fastify/index.mjs +1 -253
- package/dist/fastify/router.cjs +254 -0
- package/dist/fastify/router.d.cts +129 -0
- package/dist/fastify/router.d.mts +129 -0
- package/dist/fastify/router.mjs +254 -0
- package/dist/h3/index.cjs +2 -260
- package/dist/h3/index.d.cts +1 -128
- package/dist/h3/index.d.mts +1 -128
- package/dist/h3/index.mjs +1 -259
- package/dist/h3/router.cjs +260 -0
- package/dist/h3/router.d.cts +132 -0
- package/dist/h3/router.d.mts +132 -0
- package/dist/h3/router.mjs +260 -0
- package/dist/hono/index.cjs +2 -251
- package/dist/hono/index.d.cts +1 -130
- package/dist/hono/index.d.mts +1 -130
- package/dist/hono/index.mjs +1 -250
- package/dist/hono/router.cjs +251 -0
- package/dist/hono/router.d.cts +134 -0
- package/dist/hono/router.d.mts +134 -0
- package/dist/hono/router.mjs +251 -0
- package/dist/index.cjs +16 -1097
- package/dist/index.d.cts +9 -563
- package/dist/index.d.mts +9 -563
- package/dist/index.mjs +8 -1089
- package/dist/koa/index.cjs +2 -261
- package/dist/koa/index.d.cts +1 -131
- package/dist/koa/index.d.mts +1 -131
- package/dist/koa/index.mjs +1 -260
- package/dist/koa/router.cjs +261 -0
- package/dist/koa/router.d.cts +135 -0
- package/dist/koa/router.d.mts +135 -0
- package/dist/koa/router.mjs +261 -0
- package/dist/types/basic.d.cts +43 -0
- package/dist/types/express.d.cts +44 -0
- package/dist/types/express.d.mts +4 -2
- package/dist/types/fastify.d.cts +23 -0
- package/dist/types/fastify.d.mts +4 -2
- package/dist/types/h3.d.cts +41 -0
- package/dist/types/h3.d.mts +5 -6
- package/dist/types/hono.d.cts +21 -0
- package/dist/types/hono.d.mts +5 -5
- package/dist/types/koa.d.cts +25 -0
- package/dist/types/koa.d.mts +5 -5
- package/package.json +1 -1
- package/dist/router-CYBshGMl.d.mts +0 -652
- package/dist/router-tpnSi_Y7.d.cts +0 -652
package/dist/index.mjs
CHANGED
|
@@ -1,1091 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ClearRequest } from "./ClearRequest.mjs";
|
|
2
|
+
import { Controller } from "./Controller.mjs";
|
|
3
|
+
import { Route } from "./Route.mjs";
|
|
4
|
+
import { Request } from "./core/Request.mjs";
|
|
5
|
+
import { Response } from "./core/Response.mjs";
|
|
6
|
+
import { definePlugin } from "./core/plugins.mjs";
|
|
7
|
+
import { CoreRouter } from "./core/router.mjs";
|
|
8
|
+
import "./core/index.mjs";
|
|
3
9
|
|
|
4
|
-
//#region src/ClearRequest.ts
|
|
5
|
-
var ClearRequest = class {
|
|
6
|
-
/**
|
|
7
|
-
* @param body - Parsed request body
|
|
8
|
-
*/
|
|
9
|
-
body;
|
|
10
|
-
/**
|
|
11
|
-
* @param query - Parsed query parameters
|
|
12
|
-
*/
|
|
13
|
-
query;
|
|
14
|
-
/**
|
|
15
|
-
* @param params - Parsed route parameters
|
|
16
|
-
*/
|
|
17
|
-
params;
|
|
18
|
-
route;
|
|
19
|
-
constructor(init) {
|
|
20
|
-
Object.assign(this, init);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/Controller.ts
|
|
26
|
-
var Controller = class {
|
|
27
|
-
ctx;
|
|
28
|
-
body;
|
|
29
|
-
query;
|
|
30
|
-
params;
|
|
31
|
-
clearRequest;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/Route.ts
|
|
36
|
-
var Route = class {
|
|
37
|
-
ctx;
|
|
38
|
-
body = {};
|
|
39
|
-
query = {};
|
|
40
|
-
params = {};
|
|
41
|
-
clearRequest;
|
|
42
|
-
methods;
|
|
43
|
-
path;
|
|
44
|
-
registrationPaths;
|
|
45
|
-
parameters;
|
|
46
|
-
routeName;
|
|
47
|
-
handler;
|
|
48
|
-
middlewares;
|
|
49
|
-
controllerName;
|
|
50
|
-
actionName;
|
|
51
|
-
handlerType;
|
|
52
|
-
middlewareCount;
|
|
53
|
-
constructor(methods, path, handler, middlewares = [], options = {}) {
|
|
54
|
-
this.methods = methods;
|
|
55
|
-
this.path = path;
|
|
56
|
-
this.registrationPaths = options.registrationPaths || [path];
|
|
57
|
-
this.parameters = options.parameters || [];
|
|
58
|
-
this.handler = handler;
|
|
59
|
-
this.middlewares = middlewares;
|
|
60
|
-
this.handlerType = Array.isArray(handler) ? "controller" : "function";
|
|
61
|
-
this.middlewareCount = middlewares.length;
|
|
62
|
-
this.controllerName = Array.isArray(handler) ? handler[0]?.name : void 0;
|
|
63
|
-
this.actionName = Array.isArray(handler) ? handler[1] : typeof handler === "function" ? handler.constructor.name ?? handler.name : void 0;
|
|
64
|
-
this.onName = options.onName;
|
|
65
|
-
}
|
|
66
|
-
onName;
|
|
67
|
-
name(name) {
|
|
68
|
-
const previousName = this.routeName;
|
|
69
|
-
this.routeName = name;
|
|
70
|
-
this.onName?.(name, this, previousName);
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
toPath(params = {}) {
|
|
74
|
-
return this.path.replace(/\/?\{([^{}]+)\}/g, (segment, raw) => {
|
|
75
|
-
const optional = raw.endsWith("?");
|
|
76
|
-
const [rawName, rawField] = (optional ? raw.slice(0, -1) : raw).split(":", 2);
|
|
77
|
-
const name = rawName.trim();
|
|
78
|
-
const field = rawField?.trim();
|
|
79
|
-
const value = params[name];
|
|
80
|
-
const resolved = field && value && typeof value === "object" ? value[field] : value;
|
|
81
|
-
if (typeof resolved === "undefined" || resolved === null || resolved === "") {
|
|
82
|
-
if (optional) return "";
|
|
83
|
-
throw new Error(`Missing required route parameter: ${name}`);
|
|
84
|
-
}
|
|
85
|
-
return `${segment.startsWith("/") ? "/" : ""}${encodeURIComponent(String(resolved))}`;
|
|
86
|
-
}) || "/";
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
//#endregion
|
|
91
|
-
//#region src/core/Request.ts
|
|
92
|
-
var Request = class extends ClearRequest {
|
|
93
|
-
original;
|
|
94
|
-
method = "GET";
|
|
95
|
-
path = "/";
|
|
96
|
-
url = "/";
|
|
97
|
-
headers = {};
|
|
98
|
-
constructor(init) {
|
|
99
|
-
super(init);
|
|
100
|
-
Object.assign(this, init);
|
|
101
|
-
}
|
|
102
|
-
getBody() {
|
|
103
|
-
return this.body ?? {};
|
|
104
|
-
}
|
|
105
|
-
header(name) {
|
|
106
|
-
if (typeof this.headers.get === "function") return this.headers.get(name) || "";
|
|
107
|
-
const headers = this.headers;
|
|
108
|
-
const value = headers[name] ?? headers[name.toLowerCase()];
|
|
109
|
-
return Array.isArray(value) ? String(value[0] ?? "") : String(value ?? "");
|
|
110
|
-
}
|
|
111
|
-
param(name) {
|
|
112
|
-
return this.params?.[name];
|
|
113
|
-
}
|
|
114
|
-
input(name) {
|
|
115
|
-
return this.body?.[name] ?? this.query?.[name] ?? this.params?.[name];
|
|
116
|
-
}
|
|
117
|
-
is(method) {
|
|
118
|
-
return this.method.toLowerCase() === String(method).toLowerCase();
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
//#endregion
|
|
123
|
-
//#region src/core/Response.ts
|
|
124
|
-
var Response = class {
|
|
125
|
-
body;
|
|
126
|
-
headers = new Headers();
|
|
127
|
-
sent = false;
|
|
128
|
-
statusCode = 200;
|
|
129
|
-
statusText = "OK";
|
|
130
|
-
constructor(init) {
|
|
131
|
-
const { status: _, ...rest } = init ?? {};
|
|
132
|
-
Object.assign(this, rest);
|
|
133
|
-
if (init?.headers && !(init.headers instanceof Headers)) this.headers = new Headers(init.headers);
|
|
134
|
-
if (init?.status && typeof init.status === "number") this.statusCode = init?.status;
|
|
135
|
-
}
|
|
136
|
-
status(code) {
|
|
137
|
-
this.statusCode = code;
|
|
138
|
-
return this;
|
|
139
|
-
}
|
|
140
|
-
setStatusText(text) {
|
|
141
|
-
this.statusText = text;
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
|
-
code(code) {
|
|
145
|
-
return this.status(code);
|
|
146
|
-
}
|
|
147
|
-
setHeader(name, value) {
|
|
148
|
-
this.headers.set(name, value);
|
|
149
|
-
return this;
|
|
150
|
-
}
|
|
151
|
-
header(name, value) {
|
|
152
|
-
return this.setHeader(name, value);
|
|
153
|
-
}
|
|
154
|
-
set(name, value) {
|
|
155
|
-
return this.setHeader(name, value);
|
|
156
|
-
}
|
|
157
|
-
type(contentType) {
|
|
158
|
-
return this.setHeader("Content-Type", contentType);
|
|
159
|
-
}
|
|
160
|
-
send(body) {
|
|
161
|
-
this.body = body;
|
|
162
|
-
this.sent = true;
|
|
163
|
-
return this;
|
|
164
|
-
}
|
|
165
|
-
json(body) {
|
|
166
|
-
return this.type("application/json; charset=utf-8").send(body);
|
|
167
|
-
}
|
|
168
|
-
html(body) {
|
|
169
|
-
return this.type("text/html; charset=utf-8").send(body);
|
|
170
|
-
}
|
|
171
|
-
text(body) {
|
|
172
|
-
return this.type("text/plain; charset=utf-8").send(body);
|
|
173
|
-
}
|
|
174
|
-
noContent() {
|
|
175
|
-
return this.status(204).send(null);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region src/core/plugins.ts
|
|
181
|
-
/**
|
|
182
|
-
* Creates a new plugin
|
|
183
|
-
*
|
|
184
|
-
* @param plugin
|
|
185
|
-
* @returns
|
|
186
|
-
*/
|
|
187
|
-
function definePlugin(plugin) {
|
|
188
|
-
return plugin;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
//#endregion
|
|
192
|
-
//#region src/core/bindings.ts
|
|
193
|
-
const metadataKey = Symbol.for("clear-router:binding-metadata");
|
|
194
|
-
const bindings = /* @__PURE__ */ new WeakMap();
|
|
195
|
-
var Container = class {
|
|
196
|
-
static registry = /* @__PURE__ */ new Map();
|
|
197
|
-
static bind(token, value) {
|
|
198
|
-
this.registry.set(token, value);
|
|
199
|
-
}
|
|
200
|
-
static unbind(token) {
|
|
201
|
-
this.registry.delete(token);
|
|
202
|
-
}
|
|
203
|
-
static clear() {
|
|
204
|
-
this.registry.clear();
|
|
205
|
-
}
|
|
206
|
-
static has(token) {
|
|
207
|
-
return this.registry.has(token) || Boolean(this.findEquivalentToken(token));
|
|
208
|
-
}
|
|
209
|
-
static bindings() {
|
|
210
|
-
return Object.fromEntries(this.registry.entries());
|
|
211
|
-
}
|
|
212
|
-
static async resolve(token, ctx, autoDiscover = false) {
|
|
213
|
-
if (token === Request) return ctx.clearRequest;
|
|
214
|
-
if (token === Response) return ctx.clearResponse;
|
|
215
|
-
const binding = this.getBinding(token);
|
|
216
|
-
if (binding) return this.resolveBinding(binding, ctx, autoDiscover);
|
|
217
|
-
if (autoDiscover && typeof token === "function") return new token();
|
|
218
|
-
}
|
|
219
|
-
static getBinding(token) {
|
|
220
|
-
if (this.registry.has(token)) return this.registry.get(token);
|
|
221
|
-
const equivalent = this.findEquivalentToken(token);
|
|
222
|
-
return equivalent ? this.registry.get(equivalent) : void 0;
|
|
223
|
-
}
|
|
224
|
-
static findEquivalentToken(token) {
|
|
225
|
-
const name = token.name;
|
|
226
|
-
if (!name) return;
|
|
227
|
-
const tokenParent = Object.getPrototypeOf(token);
|
|
228
|
-
const tokenProps = this.getComparableStaticProps(token);
|
|
229
|
-
for (const registered of this.registry.keys()) {
|
|
230
|
-
if (registered === token) continue;
|
|
231
|
-
if (registered.name !== name) continue;
|
|
232
|
-
const registeredParent = Object.getPrototypeOf(registered);
|
|
233
|
-
if (tokenParent && registeredParent && tokenParent.name !== registeredParent.name) continue;
|
|
234
|
-
const registeredProps = this.getComparableStaticProps(registered);
|
|
235
|
-
if (!this.staticPropsMatch(token, registered, tokenProps, registeredProps)) continue;
|
|
236
|
-
return registered;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
static getComparableStaticProps(token) {
|
|
240
|
-
return Object.getOwnPropertyNames(token).filter((prop) => {
|
|
241
|
-
return ![
|
|
242
|
-
"length",
|
|
243
|
-
"name",
|
|
244
|
-
"prototype",
|
|
245
|
-
"arguments",
|
|
246
|
-
"caller"
|
|
247
|
-
].includes(prop);
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
static staticPropsMatch(token, registered, tokenProps, registeredProps) {
|
|
251
|
-
if (tokenProps.length !== registeredProps.length) return false;
|
|
252
|
-
for (const prop of tokenProps) {
|
|
253
|
-
if (!registeredProps.includes(prop)) return false;
|
|
254
|
-
if (Reflect.get(token, prop) !== Reflect.get(registered, prop)) return false;
|
|
255
|
-
}
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
static async resolveBinding(binding, ctx, autoDiscover) {
|
|
259
|
-
if (!binding) return void 0;
|
|
260
|
-
if (typeof binding !== "function") return binding;
|
|
261
|
-
if (isClass(binding)) return new binding();
|
|
262
|
-
const resolved = await binding(ctx);
|
|
263
|
-
if (typeof resolved === "function" && autoDiscover && isClass(resolved)) return new resolved();
|
|
264
|
-
return resolved;
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
function getStandardMetadata(metadata, propertyKey) {
|
|
268
|
-
const store = metadata && metadata[metadataKey];
|
|
269
|
-
if (!store) return void 0;
|
|
270
|
-
return propertyKey ? store[propertyKey] : void 0;
|
|
271
|
-
}
|
|
272
|
-
function getBindingMetadataFromTargets(targets) {
|
|
273
|
-
for (const { target, propertyKey } of targets) {
|
|
274
|
-
if (!target) continue;
|
|
275
|
-
const metadata = getBindingMetadata(target, propertyKey);
|
|
276
|
-
if (metadata) return metadata;
|
|
277
|
-
const standardMetadata = getStandardMetadata(target[Symbol.metadata], propertyKey);
|
|
278
|
-
if (standardMetadata) return standardMetadata;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function getBindingMetadata(target, propertyKey) {
|
|
282
|
-
if (propertyKey) return bindings.get(target)?.get(propertyKey);
|
|
283
|
-
return bindings.get(target)?.get("__route_handler__");
|
|
284
|
-
}
|
|
285
|
-
function getDesignParamTypes(target, propertyKey) {
|
|
286
|
-
return Reflect.getMetadata?.("design:paramtypes", target, propertyKey) ?? [];
|
|
287
|
-
}
|
|
288
|
-
function isClass(value) {
|
|
289
|
-
return typeof value === "function" && /^class\s/.test(Function.prototype.toString.call(value));
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
//#endregion
|
|
293
|
-
//#region src/core/router.ts
|
|
294
|
-
/**
|
|
295
|
-
* @class clear-router CoreRouter
|
|
296
|
-
* @description Core routing logic for clear-router, shared between all supported adapters (Express.js, H3, etc.)
|
|
297
|
-
* @author 3m1n3nc3
|
|
298
|
-
* @repository https://github.com/arkstack-tmp/clear-router
|
|
299
|
-
*/
|
|
300
|
-
var CoreRouter = class {
|
|
301
|
-
static routerStateNamespace = "clear-router:core";
|
|
302
|
-
static stateStoreKey = Symbol.for("clear-router:router-state");
|
|
303
|
-
static stateBoundKey = Symbol.for("clear-router:router-state-bound");
|
|
304
|
-
static defaultConfigKey = Symbol.for("clear-router:default-config");
|
|
305
|
-
static pluginStoreKey = Symbol.for("clear-router:plugins");
|
|
306
|
-
static pluginPendingKey = Symbol.for("clear-router:plugin-promises");
|
|
307
|
-
static pluginHttpCtxResolversKey = Symbol.for("clear-router:plugin-http-ctx");
|
|
308
|
-
static pluginArgumentResolversKey = Symbol.for("clear-router:plugin-argument-resolvers");
|
|
309
|
-
static requestProvider;
|
|
310
|
-
static responseProvider;
|
|
311
|
-
static config = {
|
|
312
|
-
inferParamName: false,
|
|
313
|
-
methodOverride: {
|
|
314
|
-
enabled: true,
|
|
315
|
-
bodyKeys: ["_method"],
|
|
316
|
-
headerKeys: ["x-http-method"]
|
|
317
|
-
},
|
|
318
|
-
container: {
|
|
319
|
-
enabled: false,
|
|
320
|
-
autoDiscover: false
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
static groupContext = new AsyncLocalStorage();
|
|
324
|
-
static pluginRequestContext = new AsyncLocalStorage();
|
|
325
|
-
static routes = /* @__PURE__ */ new Set([]);
|
|
326
|
-
static routesByPathMethod = /* @__PURE__ */ new Map();
|
|
327
|
-
static routesByMethod = /* @__PURE__ */ new Map();
|
|
328
|
-
static routesByName = /* @__PURE__ */ new Map();
|
|
329
|
-
static prefix = "";
|
|
330
|
-
static groupMiddlewares = [];
|
|
331
|
-
static globalMiddlewares = [];
|
|
332
|
-
/**
|
|
333
|
-
* Resets the router to it's default state
|
|
334
|
-
*/
|
|
335
|
-
static reset() {
|
|
336
|
-
this.routes.clear();
|
|
337
|
-
this.prefix = "";
|
|
338
|
-
this.groupMiddlewares = [];
|
|
339
|
-
this.globalMiddlewares = [];
|
|
340
|
-
this.routesByPathMethod.clear();
|
|
341
|
-
this.routesByMethod.clear();
|
|
342
|
-
this.routesByName.clear();
|
|
343
|
-
return this;
|
|
344
|
-
}
|
|
345
|
-
static createBaseConfig() {
|
|
346
|
-
return {
|
|
347
|
-
inferParamName: false,
|
|
348
|
-
methodOverride: {
|
|
349
|
-
enabled: true,
|
|
350
|
-
bodyKeys: ["_method"],
|
|
351
|
-
headerKeys: ["x-http-method"]
|
|
352
|
-
},
|
|
353
|
-
container: {
|
|
354
|
-
enabled: false,
|
|
355
|
-
autoDiscover: false
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
static mergeConfig(target, source) {
|
|
360
|
-
if (!source) return target;
|
|
361
|
-
if (source.methodOverride) target.methodOverride = {
|
|
362
|
-
...target.methodOverride || {},
|
|
363
|
-
...source.methodOverride
|
|
364
|
-
};
|
|
365
|
-
if (source.container) target.container = {
|
|
366
|
-
...target.container || {},
|
|
367
|
-
...source.container
|
|
368
|
-
};
|
|
369
|
-
return target;
|
|
370
|
-
}
|
|
371
|
-
static getDefaultConfig() {
|
|
372
|
-
const g = globalThis;
|
|
373
|
-
if (!g[this.defaultConfigKey]) g[this.defaultConfigKey] = this.createBaseConfig();
|
|
374
|
-
return {
|
|
375
|
-
inferParamName: g[this.defaultConfigKey].inferParamName,
|
|
376
|
-
methodOverride: { ...g[this.defaultConfigKey].methodOverride },
|
|
377
|
-
container: { ...g[this.defaultConfigKey].container }
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
static resolveStateNamespace() {
|
|
381
|
-
return String(this.routerStateNamespace || this.name || "clear-router:core");
|
|
382
|
-
}
|
|
383
|
-
static getStateStore() {
|
|
384
|
-
const g = globalThis;
|
|
385
|
-
if (!g[this.stateStoreKey]) g[this.stateStoreKey] = Object.create(null);
|
|
386
|
-
return g[this.stateStoreKey];
|
|
387
|
-
}
|
|
388
|
-
static getPluginStore() {
|
|
389
|
-
const g = globalThis;
|
|
390
|
-
if (!g[this.pluginStoreKey]) g[this.pluginStoreKey] = /* @__PURE__ */ new Set();
|
|
391
|
-
return g[this.pluginStoreKey];
|
|
392
|
-
}
|
|
393
|
-
static getPluginPendingStore() {
|
|
394
|
-
const g = globalThis;
|
|
395
|
-
if (!g[this.pluginPendingKey]) g[this.pluginPendingKey] = /* @__PURE__ */ new Set();
|
|
396
|
-
return g[this.pluginPendingKey];
|
|
397
|
-
}
|
|
398
|
-
static getPluginArgumentResolvers() {
|
|
399
|
-
const g = globalThis;
|
|
400
|
-
if (!g[this.pluginArgumentResolversKey]) g[this.pluginArgumentResolversKey] = /* @__PURE__ */ new Set();
|
|
401
|
-
return g[this.pluginArgumentResolversKey];
|
|
402
|
-
}
|
|
403
|
-
static getPluginHttpCtxResolvers() {
|
|
404
|
-
const g = globalThis;
|
|
405
|
-
if (!g[this.pluginHttpCtxResolversKey]) g[this.pluginHttpCtxResolversKey] = /* @__PURE__ */ new Set();
|
|
406
|
-
return g[this.pluginHttpCtxResolversKey];
|
|
407
|
-
}
|
|
408
|
-
static createDefaultState() {
|
|
409
|
-
return {
|
|
410
|
-
config: this.getDefaultConfig(),
|
|
411
|
-
groupContext: new AsyncLocalStorage(),
|
|
412
|
-
routes: /* @__PURE__ */ new Set([]),
|
|
413
|
-
routesByPathMethod: /* @__PURE__ */ new Map(),
|
|
414
|
-
routesByMethod: /* @__PURE__ */ new Map(),
|
|
415
|
-
routesByName: /* @__PURE__ */ new Map(),
|
|
416
|
-
prefix: "",
|
|
417
|
-
groupMiddlewares: [],
|
|
418
|
-
globalMiddlewares: []
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
static bindStateAccessors() {
|
|
422
|
-
if (Object.prototype.hasOwnProperty.call(this, this.stateBoundKey)) return;
|
|
423
|
-
const namespace = this.resolveStateNamespace();
|
|
424
|
-
const store = this.getStateStore();
|
|
425
|
-
if (!store[namespace]) store[namespace] = this.createDefaultState();
|
|
426
|
-
for (const key of [
|
|
427
|
-
"config",
|
|
428
|
-
"groupContext",
|
|
429
|
-
"routes",
|
|
430
|
-
"routesByPathMethod",
|
|
431
|
-
"routesByMethod",
|
|
432
|
-
"routesByName",
|
|
433
|
-
"prefix",
|
|
434
|
-
"groupMiddlewares",
|
|
435
|
-
"globalMiddlewares"
|
|
436
|
-
]) Object.defineProperty(this, key, {
|
|
437
|
-
get() {
|
|
438
|
-
const ns = this.resolveStateNamespace();
|
|
439
|
-
const registry = this.getStateStore();
|
|
440
|
-
if (!registry[ns]) registry[ns] = this.createDefaultState();
|
|
441
|
-
return registry[ns][key];
|
|
442
|
-
},
|
|
443
|
-
set(value) {
|
|
444
|
-
const ns = this.resolveStateNamespace();
|
|
445
|
-
const registry = this.getStateStore();
|
|
446
|
-
if (!registry[ns]) registry[ns] = this.createDefaultState();
|
|
447
|
-
registry[ns][key] = value;
|
|
448
|
-
},
|
|
449
|
-
configurable: true,
|
|
450
|
-
enumerable: true
|
|
451
|
-
});
|
|
452
|
-
Object.defineProperty(this, this.stateBoundKey, {
|
|
453
|
-
value: true,
|
|
454
|
-
configurable: false,
|
|
455
|
-
enumerable: false,
|
|
456
|
-
writable: false
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
static createDefaultOptionsHandler() {
|
|
460
|
-
return (ctx) => {
|
|
461
|
-
const allow = "GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD";
|
|
462
|
-
if (ctx?.header && ctx?.status && ctx?.body) {
|
|
463
|
-
ctx.header("Allow", allow);
|
|
464
|
-
ctx.status(204);
|
|
465
|
-
return ctx.body(null);
|
|
466
|
-
}
|
|
467
|
-
if (ctx?.res?.headers?.set) {
|
|
468
|
-
ctx.res.headers.set("Allow", allow);
|
|
469
|
-
ctx.res.status = 204;
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
if (ctx?.res?.set) {
|
|
473
|
-
ctx.res.set("Allow", allow);
|
|
474
|
-
ctx.res.sendStatus(204);
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
if (ctx?.reply?.header) {
|
|
478
|
-
ctx.reply.header("Allow", allow);
|
|
479
|
-
ctx.reply.code(204).send();
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
if (ctx?.set && "status" in ctx) {
|
|
483
|
-
ctx.set("Allow", allow);
|
|
484
|
-
ctx.status = 204;
|
|
485
|
-
ctx.body = null;
|
|
486
|
-
}
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Default configuration used for everytime the router is reset
|
|
491
|
-
*
|
|
492
|
-
* @param options
|
|
493
|
-
*/
|
|
494
|
-
static configureDefaults(options) {
|
|
495
|
-
const g = globalThis;
|
|
496
|
-
const defaults = this.mergeConfig(g[this.defaultConfigKey] || this.createBaseConfig(), options);
|
|
497
|
-
g[this.defaultConfigKey] = defaults;
|
|
498
|
-
const store = this.getStateStore();
|
|
499
|
-
for (const state of Object.values(store)) state.config = this.mergeConfig(state.config || this.createBaseConfig(), options);
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Use a registered plugin
|
|
503
|
-
*
|
|
504
|
-
* @param this
|
|
505
|
-
* @param plugin
|
|
506
|
-
* @param options
|
|
507
|
-
* @returns
|
|
508
|
-
*/
|
|
509
|
-
static async use(plugin, options) {
|
|
510
|
-
const name = typeof plugin === "function" ? plugin.name : plugin.name;
|
|
511
|
-
const store = this.getPluginStore();
|
|
512
|
-
if (name && store.has(name)) return;
|
|
513
|
-
if (name) store.add(name);
|
|
514
|
-
const setup = async () => {
|
|
515
|
-
const ctx = {
|
|
516
|
-
container: Container,
|
|
517
|
-
bind: this.createPluginBind(),
|
|
518
|
-
resolveArguments: (resolver) => {
|
|
519
|
-
this.getPluginArgumentResolvers().add(resolver);
|
|
520
|
-
},
|
|
521
|
-
useHttpContext: (resolver) => {
|
|
522
|
-
this.getPluginHttpCtxResolvers().add(resolver);
|
|
523
|
-
},
|
|
524
|
-
bindings: Container.bindings(),
|
|
525
|
-
configure: this.configure.bind(this),
|
|
526
|
-
configureDefaults: this.configureDefaults.bind(this),
|
|
527
|
-
get request() {
|
|
528
|
-
return this.getRequest();
|
|
529
|
-
},
|
|
530
|
-
get response() {
|
|
531
|
-
return this.getResponse();
|
|
532
|
-
},
|
|
533
|
-
getRequest: () => this.getCurrentPluginRequestContext()?.request,
|
|
534
|
-
getResponse: () => this.getCurrentPluginRequestContext()?.response,
|
|
535
|
-
options
|
|
536
|
-
};
|
|
537
|
-
if (typeof plugin === "function") await plugin(ctx);
|
|
538
|
-
else await plugin.setup(ctx);
|
|
539
|
-
};
|
|
540
|
-
const pending = this.getPluginPendingStore();
|
|
541
|
-
const promise = setup();
|
|
542
|
-
pending.add(promise);
|
|
543
|
-
try {
|
|
544
|
-
await promise;
|
|
545
|
-
} catch (error) {
|
|
546
|
-
if (name) store.delete(name);
|
|
547
|
-
throw error;
|
|
548
|
-
} finally {
|
|
549
|
-
pending.delete(promise);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
static async pluginsReady() {
|
|
553
|
-
const pending = Array.from(this.getPluginPendingStore());
|
|
554
|
-
if (!pending.length) return;
|
|
555
|
-
await Promise.all(pending);
|
|
556
|
-
}
|
|
557
|
-
static getCurrentPluginRequestContext() {
|
|
558
|
-
return this.pluginRequestContext.getStore();
|
|
559
|
-
}
|
|
560
|
-
static createPluginRequestContext(ctx) {
|
|
561
|
-
const request = ctx.clearRequest;
|
|
562
|
-
const response = ctx.clearResponse;
|
|
563
|
-
return {
|
|
564
|
-
...ctx,
|
|
565
|
-
ctx,
|
|
566
|
-
request,
|
|
567
|
-
response,
|
|
568
|
-
getBindings: () => Container.bindings()
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
static createPluginBind() {
|
|
572
|
-
const bind = (token, value) => {
|
|
573
|
-
if (typeof value === "function" && !isClass(value)) {
|
|
574
|
-
const factory = value;
|
|
575
|
-
Container.bind(token, (ctx) => factory(this.createPluginRequestContext(ctx)));
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
Container.bind(token, value);
|
|
579
|
-
};
|
|
580
|
-
return bind;
|
|
581
|
-
}
|
|
582
|
-
static async resolvePluginArguments(ctx, routeContext) {
|
|
583
|
-
const resolvers = Array.from(this.getPluginArgumentResolvers());
|
|
584
|
-
if (!resolvers.length) return void 0;
|
|
585
|
-
const pluginContext = {
|
|
586
|
-
...this.createPluginRequestContext(ctx),
|
|
587
|
-
...routeContext
|
|
588
|
-
};
|
|
589
|
-
for (const resolver of resolvers) {
|
|
590
|
-
const args = await resolver(pluginContext);
|
|
591
|
-
if (Array.isArray(args)) return args;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
static async resolvePluginHttpCtx(ctx) {
|
|
595
|
-
const resolvers = Array.from(this.getPluginHttpCtxResolvers());
|
|
596
|
-
if (!resolvers.length) return void 0;
|
|
597
|
-
const pluginContext = this.createPluginRequestContext(ctx);
|
|
598
|
-
for (const resolver of resolvers) await resolver(pluginContext);
|
|
599
|
-
}
|
|
600
|
-
static ensureState() {
|
|
601
|
-
this.bindStateAccessors();
|
|
602
|
-
if (!this.config) this.config = { methodOverride: {
|
|
603
|
-
enabled: true,
|
|
604
|
-
bodyKeys: ["_method"],
|
|
605
|
-
headerKeys: ["x-http-method"]
|
|
606
|
-
} };
|
|
607
|
-
if (!this.groupContext) this.groupContext = new AsyncLocalStorage();
|
|
608
|
-
if (!this.routes || Array.isArray(this.routes)) this.routes = new Set(this.routes ?? []);
|
|
609
|
-
if (!this.routesByPathMethod) this.routesByPathMethod = /* @__PURE__ */ new Map();
|
|
610
|
-
if (!this.routesByMethod) this.routesByMethod = /* @__PURE__ */ new Map();
|
|
611
|
-
if (!this.routesByName) this.routesByName = /* @__PURE__ */ new Map();
|
|
612
|
-
if (typeof this.prefix !== "string") this.prefix = "";
|
|
613
|
-
if (!Array.isArray(this.groupMiddlewares)) this.groupMiddlewares = [];
|
|
614
|
-
if (!Array.isArray(this.globalMiddlewares)) this.globalMiddlewares = [];
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Normalizes a path by ensuring it starts with a single slash and does not have trailing
|
|
618
|
-
* slashes, while preserving dynamic segments and parameters.
|
|
619
|
-
*
|
|
620
|
-
* @param path The path to normalize.
|
|
621
|
-
* @returns The normalized path.
|
|
622
|
-
*/
|
|
623
|
-
static normalizePath(path) {
|
|
624
|
-
return "/" + path.split("/").filter(Boolean).join("/");
|
|
625
|
-
}
|
|
626
|
-
static parseRouteParameters(path) {
|
|
627
|
-
const parameters = [];
|
|
628
|
-
const seen = /* @__PURE__ */ new Set();
|
|
629
|
-
const pattern = /\{([^{}]+)\}/g;
|
|
630
|
-
let match;
|
|
631
|
-
while ((match = pattern.exec(path)) !== null) {
|
|
632
|
-
const raw = match[1].trim();
|
|
633
|
-
const optional = raw.endsWith("?");
|
|
634
|
-
const [name, field] = (optional ? raw.slice(0, -1) : raw).split(":", 2).map((part) => part.trim());
|
|
635
|
-
if (!name || seen.has(name)) continue;
|
|
636
|
-
seen.add(name);
|
|
637
|
-
parameters.push({
|
|
638
|
-
name,
|
|
639
|
-
field: field || void 0,
|
|
640
|
-
optional
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
return parameters;
|
|
644
|
-
}
|
|
645
|
-
static expandRoutePath(path) {
|
|
646
|
-
let paths = [""];
|
|
647
|
-
const segments = this.normalizePath(path).split("/").filter(Boolean);
|
|
648
|
-
for (const segment of segments) {
|
|
649
|
-
const match = segment.match(/^\{([^{}]+)\}$/);
|
|
650
|
-
if (!match) {
|
|
651
|
-
paths = paths.map((current) => `${current}/${segment}`);
|
|
652
|
-
continue;
|
|
653
|
-
}
|
|
654
|
-
const raw = match[1].trim();
|
|
655
|
-
const optional = raw.endsWith("?");
|
|
656
|
-
const [rawName] = (optional ? raw.slice(0, -1) : raw).split(":", 2);
|
|
657
|
-
const name = rawName.trim();
|
|
658
|
-
if (!name) continue;
|
|
659
|
-
const parameterSegment = `/:${name}`;
|
|
660
|
-
paths = optional ? paths.flatMap((current) => [current, `${current}${parameterSegment}`]) : paths.map((current) => `${current}${parameterSegment}`);
|
|
661
|
-
}
|
|
662
|
-
return paths.map((path) => path || "/");
|
|
663
|
-
}
|
|
664
|
-
static routeRegistrationPaths(path) {
|
|
665
|
-
return this.expandRoutePath(path);
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Configures the router with the given options, such as method override settings.
|
|
669
|
-
*
|
|
670
|
-
* @param this
|
|
671
|
-
* @param options
|
|
672
|
-
* @returns
|
|
673
|
-
*/
|
|
674
|
-
static configure(options) {
|
|
675
|
-
this.ensureState();
|
|
676
|
-
this.config = this.mergeConfig(this.getDefaultConfig(), this.config);
|
|
677
|
-
const container = options?.container;
|
|
678
|
-
if (container) {
|
|
679
|
-
if (typeof container.enabled === "boolean") this.config.container.enabled = container.enabled;
|
|
680
|
-
if (typeof container.autoDiscover === "boolean") this.config.container.autoDiscover = container.autoDiscover;
|
|
681
|
-
}
|
|
682
|
-
if (options?.inferParamName) this.config.inferParamName = options?.inferParamName;
|
|
683
|
-
const override = options?.methodOverride;
|
|
684
|
-
if (override) {
|
|
685
|
-
if (typeof override.enabled === "boolean") this.config.methodOverride.enabled = override.enabled;
|
|
686
|
-
const bodyKeys = override.bodyKeys;
|
|
687
|
-
if (typeof bodyKeys !== "undefined") this.config.methodOverride.bodyKeys = (Array.isArray(bodyKeys) ? bodyKeys : [bodyKeys]).map((e) => String(e).trim()).filter(Boolean);
|
|
688
|
-
const headerKeys = override.headerKeys;
|
|
689
|
-
if (typeof headerKeys !== "undefined") this.config.methodOverride.headerKeys = (Array.isArray(headerKeys) ? headerKeys : [headerKeys]).map((e) => String(e).trim().toLowerCase()).filter(Boolean);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
static resolveMethodOverride(method, headers, body) {
|
|
693
|
-
this.ensureState();
|
|
694
|
-
if (!this.config.methodOverride?.enabled || method.toLowerCase() !== "post") return null;
|
|
695
|
-
let override;
|
|
696
|
-
const headerValueFor = (key) => {
|
|
697
|
-
if (typeof headers.get === "function") return headers.get(key);
|
|
698
|
-
const value = headers?.[key];
|
|
699
|
-
return Array.isArray(value) ? value[0] : value;
|
|
700
|
-
};
|
|
701
|
-
for (const key of this.config.methodOverride?.headerKeys || []) {
|
|
702
|
-
const value = headerValueFor(key);
|
|
703
|
-
if (value) {
|
|
704
|
-
override = value;
|
|
705
|
-
break;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
if (!override && body && typeof body === "object") for (const key of this.config.methodOverride?.bodyKeys || []) {
|
|
709
|
-
const value = body[key];
|
|
710
|
-
if (typeof value !== "undefined" && value !== null && value !== "") {
|
|
711
|
-
override = value;
|
|
712
|
-
break;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
const normalized = String(override || "").trim().toLowerCase();
|
|
716
|
-
if (!normalized) return null;
|
|
717
|
-
if ([
|
|
718
|
-
"put",
|
|
719
|
-
"patch",
|
|
720
|
-
"delete",
|
|
721
|
-
"post"
|
|
722
|
-
].includes(normalized)) return normalized;
|
|
723
|
-
return null;
|
|
724
|
-
}
|
|
725
|
-
/**
|
|
726
|
-
* Adds a new route to the router.
|
|
727
|
-
*
|
|
728
|
-
* @param this
|
|
729
|
-
* @param methods
|
|
730
|
-
* @param path
|
|
731
|
-
* @param handler
|
|
732
|
-
* @param middlewares
|
|
733
|
-
*/
|
|
734
|
-
static add(methods, path, handler, middlewares) {
|
|
735
|
-
this.ensureState();
|
|
736
|
-
const context = this.groupContext.getStore();
|
|
737
|
-
const activePrefix = context?.prefix ?? this.prefix;
|
|
738
|
-
const activeGroupMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
|
|
739
|
-
methods = Array.isArray(methods) ? methods : [methods];
|
|
740
|
-
middlewares = middlewares ? Array.isArray(middlewares) ? middlewares : [middlewares] : void 0;
|
|
741
|
-
const fullPath = this.normalizePath(`${activePrefix}/${path}`);
|
|
742
|
-
const registrationPaths = this.routeRegistrationPaths(fullPath);
|
|
743
|
-
const parameters = this.parseRouteParameters(fullPath);
|
|
744
|
-
const route = new Route(methods.includes("options") ? methods : methods.concat("options"), fullPath, handler, [
|
|
745
|
-
...this.globalMiddlewares,
|
|
746
|
-
...activeGroupMiddlewares,
|
|
747
|
-
...middlewares || []
|
|
748
|
-
], {
|
|
749
|
-
registrationPaths,
|
|
750
|
-
parameters,
|
|
751
|
-
onName: (name, route, previousName) => {
|
|
752
|
-
if (previousName && this.routesByName.get(previousName) === route) this.routesByName.delete(previousName);
|
|
753
|
-
this.routesByName.set(name, route);
|
|
754
|
-
}
|
|
755
|
-
});
|
|
756
|
-
if (!methods.includes("options") && !this.routesByPathMethod.get(`OPTIONS ${fullPath}`)) this.options(path, this.createDefaultOptionsHandler());
|
|
757
|
-
this.routes.add(route);
|
|
758
|
-
for (const method of methods.map((m) => m.toUpperCase())) {
|
|
759
|
-
this.routesByPathMethod.set(`${method} ${fullPath}`, route);
|
|
760
|
-
if (!this.routesByMethod.has(method)) this.routesByMethod.set(method, []);
|
|
761
|
-
this.routesByMethod.get(method)?.push(route);
|
|
762
|
-
}
|
|
763
|
-
return route;
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Define a resourceful API controller with standard CRUD routes.
|
|
767
|
-
*
|
|
768
|
-
* @param this
|
|
769
|
-
* @param basePath
|
|
770
|
-
* @param controller
|
|
771
|
-
* @param options
|
|
772
|
-
*/
|
|
773
|
-
static apiResource(basePath, controller, options) {
|
|
774
|
-
let paramName = "id";
|
|
775
|
-
if (!!this.config.inferParamName && this.hasPackageInstalled("@h3ravel/support")) {
|
|
776
|
-
const { str } = createRequire(import.meta.url)("@h3ravel/support");
|
|
777
|
-
paramName = str(basePath).singular().afterLast("/").toString();
|
|
778
|
-
}
|
|
779
|
-
const actions = {
|
|
780
|
-
index: {
|
|
781
|
-
method: "get",
|
|
782
|
-
path: "/"
|
|
783
|
-
},
|
|
784
|
-
show: {
|
|
785
|
-
method: "get",
|
|
786
|
-
path: `/:${paramName}`
|
|
787
|
-
},
|
|
788
|
-
create: {
|
|
789
|
-
method: "post",
|
|
790
|
-
path: "/"
|
|
791
|
-
},
|
|
792
|
-
update: {
|
|
793
|
-
method: "put",
|
|
794
|
-
path: `/:${paramName}`
|
|
795
|
-
},
|
|
796
|
-
destroy: {
|
|
797
|
-
method: "delete",
|
|
798
|
-
path: `/:${paramName}`
|
|
799
|
-
}
|
|
800
|
-
};
|
|
801
|
-
const only = options?.only || Object.keys(actions);
|
|
802
|
-
const except = options?.except || [];
|
|
803
|
-
const preController = typeof controller === "function" ? new controller() : controller;
|
|
804
|
-
for (const action of only) {
|
|
805
|
-
if (except.includes(action)) continue;
|
|
806
|
-
if (typeof preController[action] === "function") {
|
|
807
|
-
const { method, path } = actions[action];
|
|
808
|
-
const actionMiddlewares = typeof options?.middlewares === "object" && !Array.isArray(options.middlewares) ? options.middlewares[action] : options?.middlewares;
|
|
809
|
-
const name = `${basePath}${path}`.replace(/\/:[^/]+|\/\{[^}]+\}/g, "").replace(/\{(\w+):[^}]+\}/g, "$1").replace(/\/|:|[{}]/g, ".").replace(/\.{2,}/g, ".").replace(/^\.|\.$/g, "");
|
|
810
|
-
this.add(method, `${basePath}${path}`, [controller, action], Array.isArray(actionMiddlewares) ? actionMiddlewares : actionMiddlewares ? [actionMiddlewares] : void 0).name(name + "." + action.toLowerCase());
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
/**
|
|
815
|
-
* Adds a new GET route to the router.
|
|
816
|
-
*
|
|
817
|
-
* @param this The router instance.
|
|
818
|
-
* @param path The path for the GET route.
|
|
819
|
-
* @param handler The handler function for the GET route.
|
|
820
|
-
* @param middlewares Optional middlewares to apply to the GET route.
|
|
821
|
-
*/
|
|
822
|
-
static get(path, handler, middlewares) {
|
|
823
|
-
return this.add("get", path, handler, middlewares);
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* Adds a new POST route to the router.
|
|
827
|
-
*
|
|
828
|
-
* @param this
|
|
829
|
-
* @param path
|
|
830
|
-
* @param handler
|
|
831
|
-
* @param middlewares
|
|
832
|
-
*/
|
|
833
|
-
static post(path, handler, middlewares) {
|
|
834
|
-
return this.add("post", path, handler, middlewares);
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Adds a new PUT route to the router.
|
|
838
|
-
*
|
|
839
|
-
* @param this
|
|
840
|
-
* @param path
|
|
841
|
-
* @param handler
|
|
842
|
-
* @param middlewares
|
|
843
|
-
*/
|
|
844
|
-
static put(path, handler, middlewares) {
|
|
845
|
-
return this.add("put", path, handler, middlewares);
|
|
846
|
-
}
|
|
847
|
-
/**
|
|
848
|
-
* Adds a new DELETE route to the router.
|
|
849
|
-
*
|
|
850
|
-
* @param this
|
|
851
|
-
* @param path
|
|
852
|
-
* @param handler
|
|
853
|
-
* @param middlewares
|
|
854
|
-
*/
|
|
855
|
-
static delete(path, handler, middlewares) {
|
|
856
|
-
return this.add("delete", path, handler, middlewares);
|
|
857
|
-
}
|
|
858
|
-
/**
|
|
859
|
-
* Adds a new PATCH route to the router.
|
|
860
|
-
*
|
|
861
|
-
* @param this
|
|
862
|
-
* @param path
|
|
863
|
-
* @param handler
|
|
864
|
-
* @param middlewares
|
|
865
|
-
*/
|
|
866
|
-
static patch(path, handler, middlewares) {
|
|
867
|
-
return this.add("patch", path, handler, middlewares);
|
|
868
|
-
}
|
|
869
|
-
/**
|
|
870
|
-
* Adds a new OPTIONS route to the router.
|
|
871
|
-
*
|
|
872
|
-
* @param this
|
|
873
|
-
* @param path
|
|
874
|
-
* @param handler
|
|
875
|
-
* @param middlewares
|
|
876
|
-
*/
|
|
877
|
-
static options(path, handler, middlewares) {
|
|
878
|
-
return this.add("options", path, handler, middlewares);
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* Adds a new HEAD route to the router.
|
|
882
|
-
*
|
|
883
|
-
* @param this
|
|
884
|
-
* @param path
|
|
885
|
-
* @param handler
|
|
886
|
-
* @param middlewares
|
|
887
|
-
*/
|
|
888
|
-
static head(path, handler, middlewares) {
|
|
889
|
-
return this.add("head", path, handler, middlewares);
|
|
890
|
-
}
|
|
891
|
-
/**
|
|
892
|
-
* Defines a group of routes with a common prefix.
|
|
893
|
-
*
|
|
894
|
-
* @param this
|
|
895
|
-
* @param prefix
|
|
896
|
-
* @param callback
|
|
897
|
-
* @param middlewares
|
|
898
|
-
*/
|
|
899
|
-
static async group(prefix, callback, middlewares) {
|
|
900
|
-
this.ensureState();
|
|
901
|
-
const context = this.groupContext.getStore();
|
|
902
|
-
const previousPrefix = context?.prefix ?? this.prefix;
|
|
903
|
-
const previousMiddlewares = context?.groupMiddlewares ?? this.groupMiddlewares;
|
|
904
|
-
const fullPrefix = [previousPrefix, prefix].filter(Boolean).join("/");
|
|
905
|
-
const nextContext = {
|
|
906
|
-
prefix: this.normalizePath(fullPrefix),
|
|
907
|
-
groupMiddlewares: [...previousMiddlewares, ...middlewares || []]
|
|
908
|
-
};
|
|
909
|
-
await this.groupContext.run(nextContext, async () => {
|
|
910
|
-
await Promise.resolve(callback());
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
/**
|
|
914
|
-
* Adds global middlewares to the router, which will be applied to all routes.
|
|
915
|
-
*
|
|
916
|
-
* @param this
|
|
917
|
-
* @param middlewares
|
|
918
|
-
* @param callback
|
|
919
|
-
*/
|
|
920
|
-
static middleware(middlewares, callback) {
|
|
921
|
-
this.ensureState();
|
|
922
|
-
const prevMiddlewares = this.globalMiddlewares;
|
|
923
|
-
this.globalMiddlewares = [...prevMiddlewares, ...middlewares || []];
|
|
924
|
-
callback();
|
|
925
|
-
this.globalMiddlewares = prevMiddlewares;
|
|
926
|
-
}
|
|
927
|
-
static allRoutes(type) {
|
|
928
|
-
this.ensureState();
|
|
929
|
-
if (type === "method") return Object.fromEntries(this.routesByMethod.entries());
|
|
930
|
-
if (type === "path") return Object.fromEntries(this.routesByPathMethod.entries());
|
|
931
|
-
if (type === "name") return Object.fromEntries(this.routesByName.entries());
|
|
932
|
-
return Array.from(this.routes).filter((e) => e.methods.length > 1 || e.methods[0] !== "options");
|
|
933
|
-
}
|
|
934
|
-
static route(name) {
|
|
935
|
-
this.ensureState();
|
|
936
|
-
return this.routesByName.get(name);
|
|
937
|
-
}
|
|
938
|
-
static url(name, params) {
|
|
939
|
-
return this.route(name)?.toPath(params);
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Provide a class that will overide the base Request instance
|
|
943
|
-
*
|
|
944
|
-
* @param provider
|
|
945
|
-
*/
|
|
946
|
-
static setRequestProvider(provider) {
|
|
947
|
-
this.requestProvider = provider;
|
|
948
|
-
}
|
|
949
|
-
/**
|
|
950
|
-
* Provide a class that will overide the base Response instance
|
|
951
|
-
*
|
|
952
|
-
* @param provider
|
|
953
|
-
*/
|
|
954
|
-
static setResponseProvider(provider) {
|
|
955
|
-
this.responseProvider = provider;
|
|
956
|
-
}
|
|
957
|
-
static hasPackageInstalled(name) {
|
|
958
|
-
try {
|
|
959
|
-
createRequire(import.meta.url).resolve(name, { paths: [process.cwd()] });
|
|
960
|
-
return true;
|
|
961
|
-
} catch {
|
|
962
|
-
return false;
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
static initializeInstance(provider, args) {
|
|
966
|
-
const isRequest = [
|
|
967
|
-
"CoreRequest",
|
|
968
|
-
"Request",
|
|
969
|
-
"ClearRequest"
|
|
970
|
-
].includes(provider.name);
|
|
971
|
-
const isResponse = [
|
|
972
|
-
"CoreResponse",
|
|
973
|
-
"Response",
|
|
974
|
-
"ClearResponse"
|
|
975
|
-
].includes(provider.name);
|
|
976
|
-
if (isRequest && this.requestProvider) return new this.requestProvider(args);
|
|
977
|
-
else if (isResponse && this.responseProvider) return new this.responseProvider(args);
|
|
978
|
-
return new provider(args);
|
|
979
|
-
}
|
|
980
|
-
static resolveHandler(route) {
|
|
981
|
-
let handlerFunction;
|
|
982
|
-
let instance = null;
|
|
983
|
-
let bindingTarget;
|
|
984
|
-
let bindingMethod;
|
|
985
|
-
let bindingHandler;
|
|
986
|
-
let bindingMetadata;
|
|
987
|
-
if (typeof route.handler === "function") {
|
|
988
|
-
handlerFunction = route.handler.bind(route);
|
|
989
|
-
bindingTarget = route.handler;
|
|
990
|
-
bindingHandler = route.handler;
|
|
991
|
-
} else if (Array.isArray(route.handler) && route.handler.length === 2) {
|
|
992
|
-
const [ControllerType, method] = route.handler;
|
|
993
|
-
if (["function", "object"].includes(typeof ControllerType) && typeof ControllerType[method] === "function") {
|
|
994
|
-
instance = ControllerType;
|
|
995
|
-
handlerFunction = ControllerType[method].bind(ControllerType);
|
|
996
|
-
bindingTarget = ControllerType;
|
|
997
|
-
bindingMethod = method;
|
|
998
|
-
bindingHandler = ControllerType[method];
|
|
999
|
-
bindingMetadata = ControllerType[Symbol.metadata];
|
|
1000
|
-
} else if (typeof ControllerType === "function") {
|
|
1001
|
-
instance = new ControllerType();
|
|
1002
|
-
if (typeof instance[method] === "function") {
|
|
1003
|
-
handlerFunction = instance[method].bind(instance);
|
|
1004
|
-
bindingTarget = ControllerType.prototype;
|
|
1005
|
-
bindingMethod = method;
|
|
1006
|
-
bindingHandler = instance[method];
|
|
1007
|
-
bindingMetadata = ControllerType[Symbol.metadata];
|
|
1008
|
-
} else throw new Error(`Method "${method}" not found in controller instance "${ControllerType.name}"`);
|
|
1009
|
-
} else throw new Error(`Invalid controller type for route: ${route.path}`);
|
|
1010
|
-
} else throw new Error(`Invalid handler format for route: ${route.path}`);
|
|
1011
|
-
return {
|
|
1012
|
-
handlerFunction,
|
|
1013
|
-
instance,
|
|
1014
|
-
bindingTarget,
|
|
1015
|
-
bindingMethod,
|
|
1016
|
-
bindingHandler,
|
|
1017
|
-
bindingMetadata
|
|
1018
|
-
};
|
|
1019
|
-
}
|
|
1020
|
-
static async callHandler(handlerFunction, ctx, bindingTarget, bindingMethod, bindingHandler, bindingMetadata) {
|
|
1021
|
-
return this.pluginRequestContext.run(this.createPluginRequestContext(ctx), async () => {
|
|
1022
|
-
await this.pluginsReady();
|
|
1023
|
-
await this.resolvePluginHttpCtx(ctx);
|
|
1024
|
-
if (!this.config.container?.enabled) return handlerFunction(ctx, ctx.clearRequest);
|
|
1025
|
-
const designTokens = [...bindingTarget ? getDesignParamTypes(bindingTarget, bindingMethod) : [], ...bindingHandler ? getDesignParamTypes(bindingHandler) : []];
|
|
1026
|
-
const metadata = getBindingMetadataFromTargets([
|
|
1027
|
-
{
|
|
1028
|
-
target: bindingTarget,
|
|
1029
|
-
propertyKey: bindingMethod
|
|
1030
|
-
},
|
|
1031
|
-
{ target: bindingHandler },
|
|
1032
|
-
{
|
|
1033
|
-
target: bindingTarget,
|
|
1034
|
-
propertyKey: "__class__"
|
|
1035
|
-
}
|
|
1036
|
-
]) ?? getStandardMetadata(bindingMetadata, bindingMethod) ?? getStandardMetadata(bindingMetadata, "__class__");
|
|
1037
|
-
const tokens = metadata?.tokens?.length ? metadata.tokens : designTokens;
|
|
1038
|
-
const pluginArgs = await this.resolvePluginArguments(ctx, {
|
|
1039
|
-
target: bindingTarget,
|
|
1040
|
-
method: bindingMethod,
|
|
1041
|
-
handler: bindingHandler,
|
|
1042
|
-
metadata: bindingMetadata,
|
|
1043
|
-
tokens,
|
|
1044
|
-
designTokens
|
|
1045
|
-
});
|
|
1046
|
-
if (pluginArgs?.length) return handlerFunction(...pluginArgs);
|
|
1047
|
-
if (!metadata || !tokens.length) return handlerFunction(ctx, ctx.clearRequest);
|
|
1048
|
-
const args = [];
|
|
1049
|
-
for (const token of tokens) {
|
|
1050
|
-
const resolved = await Container.resolve(token, ctx, Boolean(this.config.container?.autoDiscover));
|
|
1051
|
-
if (typeof resolved === "undefined") return handlerFunction(ctx, ctx.clearRequest);
|
|
1052
|
-
args.push(resolved);
|
|
1053
|
-
}
|
|
1054
|
-
return handlerFunction(...args);
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
static bindRequestToInstance(ctx, instance, route, payload) {
|
|
1058
|
-
const clearRequest = ctx.clearRequest instanceof Request ? ctx.clearRequest : this.initializeInstance(Request, {
|
|
1059
|
-
ctx,
|
|
1060
|
-
route,
|
|
1061
|
-
body: payload.body,
|
|
1062
|
-
query: payload.query,
|
|
1063
|
-
params: payload.params,
|
|
1064
|
-
method: String(payload.method || ctx.req?.method || ctx.method || "GET").toUpperCase(),
|
|
1065
|
-
path: String(ctx.path || ctx.req?.path || ctx.req?.url || route.path),
|
|
1066
|
-
url: String(ctx.url || ctx.req?.url || ctx.req?.originalUrl || route.path),
|
|
1067
|
-
headers: ctx.req?.headers || ctx.headers || {},
|
|
1068
|
-
original: ctx.req || ctx.request || ctx
|
|
1069
|
-
});
|
|
1070
|
-
clearRequest.ctx = ctx;
|
|
1071
|
-
clearRequest.route = route;
|
|
1072
|
-
clearRequest.body = payload.body;
|
|
1073
|
-
clearRequest.query = payload.query;
|
|
1074
|
-
clearRequest.params = payload.params;
|
|
1075
|
-
ctx.clearRequest = clearRequest;
|
|
1076
|
-
Container.bind(Request, ctx.clearRequest);
|
|
1077
|
-
if (!(ctx.clearResponse instanceof Response)) {
|
|
1078
|
-
ctx.clearResponse = this.initializeInstance(Response, ctx.response ?? ctx.reply ?? ctx.res);
|
|
1079
|
-
Container.bind(Response, ctx.clearResponse);
|
|
1080
|
-
}
|
|
1081
|
-
if (!instance) return;
|
|
1082
|
-
instance.ctx = ctx;
|
|
1083
|
-
instance.body = payload.body;
|
|
1084
|
-
instance.query = payload.query;
|
|
1085
|
-
instance.params = payload.params;
|
|
1086
|
-
instance.clearRequest = clearRequest;
|
|
1087
|
-
}
|
|
1088
|
-
};
|
|
1089
|
-
|
|
1090
|
-
//#endregion
|
|
1091
10
|
export { ClearRequest, Controller, CoreRouter, Request, Response, Route, definePlugin };
|