@ozanarslan/corpus 0.1.4 → 0.1.6
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 +24 -25
- package/dist/index.cjs +2014 -0
- package/dist/index.d.ts +389 -226
- package/dist/index.js +1012 -527
- package/package.json +19 -6
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2014 @@
|
|
|
1
|
+
// @bun @bun-cjs
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
13
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
21
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
31
|
+
return to;
|
|
32
|
+
};
|
|
33
|
+
var __toCommonJS = (from) => {
|
|
34
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
35
|
+
if (entry)
|
|
36
|
+
return entry;
|
|
37
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
38
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
39
|
+
for (var key of __getOwnPropNames(from))
|
|
40
|
+
if (!__hasOwnProp.call(entry, key))
|
|
41
|
+
__defProp(entry, key, {
|
|
42
|
+
get: __accessProp.bind(from, key),
|
|
43
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
__moduleCache.set(from, entry);
|
|
47
|
+
return entry;
|
|
48
|
+
};
|
|
49
|
+
var __moduleCache;
|
|
50
|
+
var __returnValue = (v) => v;
|
|
51
|
+
function __exportSetter(name, newValue) {
|
|
52
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
53
|
+
}
|
|
54
|
+
var __export = (target, all) => {
|
|
55
|
+
for (var name in all)
|
|
56
|
+
__defProp(target, name, {
|
|
57
|
+
get: all[name],
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
set: __exportSetter.bind(all, name)
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/index.ts
|
|
65
|
+
var exports_src = {};
|
|
66
|
+
__export(exports_src, {
|
|
67
|
+
default: () => exports_C,
|
|
68
|
+
_routerStore: () => _routerStore,
|
|
69
|
+
_prefixStore: () => _prefixStore,
|
|
70
|
+
_corsStore: () => _corsStore,
|
|
71
|
+
X: () => exports_X,
|
|
72
|
+
Status: () => Status,
|
|
73
|
+
StaticRoute: () => StaticRoute,
|
|
74
|
+
Server: () => Server,
|
|
75
|
+
Route: () => Route,
|
|
76
|
+
Response: () => CResponse,
|
|
77
|
+
Request: () => CRequest,
|
|
78
|
+
Repository: () => RepositoryAbstract,
|
|
79
|
+
Parser: () => Parser,
|
|
80
|
+
Middleware: () => Middleware,
|
|
81
|
+
Method: () => Method,
|
|
82
|
+
MemoiristAdapter: () => MemoiristAdapter,
|
|
83
|
+
Headers: () => CHeaders,
|
|
84
|
+
File: () => XFile,
|
|
85
|
+
Extra: () => exports_X,
|
|
86
|
+
Error: () => CError,
|
|
87
|
+
Cors: () => Cors,
|
|
88
|
+
Cookies: () => Cookies,
|
|
89
|
+
Controller: () => ControllerAbstract,
|
|
90
|
+
Context: () => Context,
|
|
91
|
+
Config: () => Config,
|
|
92
|
+
CommonHeaders: () => CommonHeaders,
|
|
93
|
+
C: () => exports_C
|
|
94
|
+
});
|
|
95
|
+
module.exports = __toCommonJS(exports_src);
|
|
96
|
+
|
|
97
|
+
// src/Store/StoreAbstract.ts
|
|
98
|
+
class StoreAbstract {
|
|
99
|
+
set(value) {
|
|
100
|
+
this.value = value;
|
|
101
|
+
}
|
|
102
|
+
get() {
|
|
103
|
+
return this.value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/Store/globals/GlobalPrefixStore.ts
|
|
108
|
+
class GlobalPrefixStore extends StoreAbstract {
|
|
109
|
+
value = "";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/Store/globals/GlobalRouterStore.ts
|
|
113
|
+
class GlobalRouterStore extends StoreAbstract {
|
|
114
|
+
value = null;
|
|
115
|
+
get() {
|
|
116
|
+
if (!this.value) {
|
|
117
|
+
console.error("Router instance is not set. Please instantiate your Server before your routes.");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
return this.value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/Store/globals/GlobalCorsStore.ts
|
|
125
|
+
class GlobalCorsStore extends StoreAbstract {
|
|
126
|
+
value = null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/C.ts
|
|
130
|
+
var exports_C = {};
|
|
131
|
+
__export(exports_C, {
|
|
132
|
+
Status: () => Status,
|
|
133
|
+
StaticRoute: () => StaticRoute,
|
|
134
|
+
Server: () => Server,
|
|
135
|
+
Route: () => Route,
|
|
136
|
+
Response: () => CResponse,
|
|
137
|
+
Request: () => CRequest,
|
|
138
|
+
Middleware: () => Middleware,
|
|
139
|
+
Method: () => Method,
|
|
140
|
+
Headers: () => CHeaders,
|
|
141
|
+
Error: () => CError,
|
|
142
|
+
Cookies: () => Cookies,
|
|
143
|
+
Controller: () => ControllerAbstract,
|
|
144
|
+
Context: () => Context,
|
|
145
|
+
Config: () => Config,
|
|
146
|
+
CommonHeaders: () => CommonHeaders
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// src/Config/enums/RuntimeOptions.ts
|
|
150
|
+
var RuntimeOptions = {
|
|
151
|
+
bun: "bun",
|
|
152
|
+
node: "node"
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/utils/strIsDefined.ts
|
|
156
|
+
function strIsDefined(input) {
|
|
157
|
+
return !!input?.trim() && typeof input === "string";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/Config/Config.ts
|
|
161
|
+
var import_path = __toESM(require("path"));
|
|
162
|
+
|
|
163
|
+
class Config {
|
|
164
|
+
static get runtime() {
|
|
165
|
+
if (typeof Bun !== "undefined") {
|
|
166
|
+
return RuntimeOptions.bun;
|
|
167
|
+
}
|
|
168
|
+
if (typeof process !== "undefined" && process?.env) {
|
|
169
|
+
return RuntimeOptions.node;
|
|
170
|
+
}
|
|
171
|
+
console.warn("\u26A0\uFE0F Runtime isn't Bun or NodeJS. Features may not be available. App might not start.");
|
|
172
|
+
return "unknown";
|
|
173
|
+
}
|
|
174
|
+
static get nodeEnv() {
|
|
175
|
+
return this.env.NODE_ENV ?? "development";
|
|
176
|
+
}
|
|
177
|
+
static get env() {
|
|
178
|
+
switch (this.runtime) {
|
|
179
|
+
case RuntimeOptions.bun:
|
|
180
|
+
return Bun.env;
|
|
181
|
+
case RuntimeOptions.node:
|
|
182
|
+
return process.env;
|
|
183
|
+
default:
|
|
184
|
+
console.warn("\u26A0\uFE0F process.env wasn't available. Your environment variables are in memory.");
|
|
185
|
+
return {};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
static cwd() {
|
|
189
|
+
return process.cwd();
|
|
190
|
+
}
|
|
191
|
+
static resolvePath(...paths) {
|
|
192
|
+
return import_path.default.resolve(...paths);
|
|
193
|
+
}
|
|
194
|
+
static get(key, opts) {
|
|
195
|
+
const value = this.env[key];
|
|
196
|
+
if (strIsDefined(value)) {
|
|
197
|
+
return opts?.parser ? opts?.parser(value) : value;
|
|
198
|
+
}
|
|
199
|
+
if (opts?.fallback !== undefined) {
|
|
200
|
+
return opts?.fallback;
|
|
201
|
+
}
|
|
202
|
+
console.error(`${key} doesn't exist in env`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
static set(key, value) {
|
|
206
|
+
if (typeof value === "number") {
|
|
207
|
+
this.env[key] = value.toString();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (typeof value === "boolean") {
|
|
211
|
+
this.env[key] = value ? "true" : "false";
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.env[key] = value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// src/CResponse/enums/Status.ts
|
|
218
|
+
var Status = {
|
|
219
|
+
CONTINUE: 100,
|
|
220
|
+
SWITCHING_PROTOCOLS: 101,
|
|
221
|
+
PROCESSING: 102,
|
|
222
|
+
EARLY_HINTS: 103,
|
|
223
|
+
OK: 200,
|
|
224
|
+
CREATED: 201,
|
|
225
|
+
ACCEPTED: 202,
|
|
226
|
+
NON_AUTHORITATIVE_INFORMATION: 203,
|
|
227
|
+
NO_CONTENT: 204,
|
|
228
|
+
RESET_CONTENT: 205,
|
|
229
|
+
PARTIAL_CONTENT: 206,
|
|
230
|
+
MULTI_STATUS: 207,
|
|
231
|
+
ALREADY_REPORTED: 208,
|
|
232
|
+
IM_USED: 226,
|
|
233
|
+
MULTIPLE_CHOICES: 300,
|
|
234
|
+
MOVED_PERMANENTLY: 301,
|
|
235
|
+
FOUND: 302,
|
|
236
|
+
SEE_OTHER: 303,
|
|
237
|
+
NOT_MODIFIED: 304,
|
|
238
|
+
USE_PROXY: 305,
|
|
239
|
+
TEMPORARY_REDIRECT: 307,
|
|
240
|
+
PERMANENT_REDIRECT: 308,
|
|
241
|
+
BAD_REQUEST: 400,
|
|
242
|
+
UNAUTHORIZED: 401,
|
|
243
|
+
PAYMENT_REQUIRED: 402,
|
|
244
|
+
FORBIDDEN: 403,
|
|
245
|
+
NOT_FOUND: 404,
|
|
246
|
+
METHOD_NOT_ALLOWED: 405,
|
|
247
|
+
NOT_ACCEPTABLE: 406,
|
|
248
|
+
PROXY_AUTHENTICATION_REQUIRED: 407,
|
|
249
|
+
REQUEST_TIMEOUT: 408,
|
|
250
|
+
CONFLICT: 409,
|
|
251
|
+
GONE: 410,
|
|
252
|
+
LENGTH_REQUIRED: 411,
|
|
253
|
+
PRECONDITION_FAILED: 412,
|
|
254
|
+
PAYLOAD_TOO_LARGE: 413,
|
|
255
|
+
URI_TOO_LONG: 414,
|
|
256
|
+
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
257
|
+
RANGE_NOT_SATISFIABLE: 416,
|
|
258
|
+
EXPECTATION_FAILED: 417,
|
|
259
|
+
IM_A_TEAPOT: 418,
|
|
260
|
+
MISDIRECTED_REQUEST: 421,
|
|
261
|
+
UNPROCESSABLE_ENTITY: 422,
|
|
262
|
+
LOCKED: 423,
|
|
263
|
+
FAILED_DEPENDENCY: 424,
|
|
264
|
+
TOO_EARLY: 425,
|
|
265
|
+
UPGRADE_REQUIRED: 426,
|
|
266
|
+
PRECONDITION_REQUIRED: 428,
|
|
267
|
+
TOO_MANY_REQUESTS: 429,
|
|
268
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
|
|
269
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: 451,
|
|
270
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
271
|
+
NOT_IMPLEMENTED: 501,
|
|
272
|
+
BAD_GATEWAY: 502,
|
|
273
|
+
SERVICE_UNAVAILABLE: 503,
|
|
274
|
+
GATEWAY_TIMEOUT: 504,
|
|
275
|
+
HTTP_VERSION_NOT_SUPPORTED: 505,
|
|
276
|
+
VARIANT_ALSO_NEGOTIATES: 506,
|
|
277
|
+
INSUFFICIENT_STORAGE: 507,
|
|
278
|
+
LOOP_DETECTED: 508,
|
|
279
|
+
NOT_EXTENDED: 510,
|
|
280
|
+
NETWORK_AUTHENTICATION_REQUIRED: 511
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/CResponse/enums/DefaultStatusTexts.ts
|
|
284
|
+
var DefaultStatusTexts = {
|
|
285
|
+
[Status.OK]: "OK",
|
|
286
|
+
[Status.CREATED]: "Created",
|
|
287
|
+
[Status.NO_CONTENT]: "No Content",
|
|
288
|
+
[Status.MOVED_PERMANENTLY]: "Moved Permanently",
|
|
289
|
+
[Status.FOUND]: "Found",
|
|
290
|
+
[Status.SEE_OTHER]: "See Other",
|
|
291
|
+
[Status.TEMPORARY_REDIRECT]: "Temporary Redirect",
|
|
292
|
+
[Status.PERMANENT_REDIRECT]: "Permanent Redirect",
|
|
293
|
+
[Status.BAD_REQUEST]: "Bad Request",
|
|
294
|
+
[Status.UNAUTHORIZED]: "Unauthorized",
|
|
295
|
+
[Status.FORBIDDEN]: "Forbidden",
|
|
296
|
+
[Status.NOT_FOUND]: "Not Found",
|
|
297
|
+
[Status.INTERNAL_SERVER_ERROR]: "Internal Server Error"
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/CHeaders/enums/CommonHeaders.ts
|
|
301
|
+
var CommonHeaders = {
|
|
302
|
+
CacheControl: "Cache-Control",
|
|
303
|
+
ContentType: "Content-Type",
|
|
304
|
+
ContentLength: "Content-Length",
|
|
305
|
+
ContentDisposition: "Content-Disposition",
|
|
306
|
+
AcceptEncoding: "Accept-Encoding",
|
|
307
|
+
Accept: "Accept",
|
|
308
|
+
Authorization: "Authorization",
|
|
309
|
+
UserAgent: "User-Agent",
|
|
310
|
+
Host: "Host",
|
|
311
|
+
Referer: "Referer",
|
|
312
|
+
Connection: "Connection",
|
|
313
|
+
Pragma: "Pragma",
|
|
314
|
+
Date: "Date",
|
|
315
|
+
IfNoneMatch: "If-None-Match",
|
|
316
|
+
IfModifiedSince: "If-Modified-Since",
|
|
317
|
+
ETag: "ETag",
|
|
318
|
+
Expires: "Expires",
|
|
319
|
+
LastModified: "Last-Modified",
|
|
320
|
+
Location: "Location",
|
|
321
|
+
WWWAuthenticate: "WWW-Authenticate",
|
|
322
|
+
AccessControlAllowOrigin: "Access-Control-Allow-Origin",
|
|
323
|
+
AccessControlMaxAge: "Access-Control-Max-Age",
|
|
324
|
+
AccessControlAllowCredentials: "Access-Control-Allow-Credentials",
|
|
325
|
+
AccessControlRequestMethod: "Access-Control-Request-Method",
|
|
326
|
+
SetCookie: "Set-Cookie",
|
|
327
|
+
Cookie: "Cookie"
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/Cookies/CookiesAbstract.ts
|
|
331
|
+
class CookiesAbstract {
|
|
332
|
+
applyInit(init) {
|
|
333
|
+
if (!init)
|
|
334
|
+
return;
|
|
335
|
+
if (init instanceof CookiesAbstract) {
|
|
336
|
+
for (const name of init.keys()) {
|
|
337
|
+
const value = init.get(name) ?? "";
|
|
338
|
+
this.set({ name, value });
|
|
339
|
+
}
|
|
340
|
+
} else if (Array.isArray(init)) {
|
|
341
|
+
for (const opts of init) {
|
|
342
|
+
this.set(opts);
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
this.set(init);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/Cookies/CookiesUsingBun.ts
|
|
351
|
+
class CookiesUsingBun extends CookiesAbstract {
|
|
352
|
+
constructor(init) {
|
|
353
|
+
super();
|
|
354
|
+
this.applyInit(init);
|
|
355
|
+
}
|
|
356
|
+
map = new Bun.CookieMap;
|
|
357
|
+
toSetCookieHeaders() {
|
|
358
|
+
return this.map.toSetCookieHeaders();
|
|
359
|
+
}
|
|
360
|
+
set(opts) {
|
|
361
|
+
this.map.set(opts.name, opts.value, opts);
|
|
362
|
+
}
|
|
363
|
+
setMany(optsArr) {
|
|
364
|
+
for (const opts of optsArr) {
|
|
365
|
+
this.set(opts);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
get(name) {
|
|
369
|
+
return this.map.get(name);
|
|
370
|
+
}
|
|
371
|
+
has(name) {
|
|
372
|
+
return this.map.has(name);
|
|
373
|
+
}
|
|
374
|
+
get count() {
|
|
375
|
+
return this.values().length;
|
|
376
|
+
}
|
|
377
|
+
delete(name) {
|
|
378
|
+
this.map.delete(name);
|
|
379
|
+
}
|
|
380
|
+
entries() {
|
|
381
|
+
return this.map.entries();
|
|
382
|
+
}
|
|
383
|
+
values() {
|
|
384
|
+
return Array.from(this.map.values());
|
|
385
|
+
}
|
|
386
|
+
keys() {
|
|
387
|
+
return Array.from(this.map.keys());
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/Cookies/Cookies.ts
|
|
392
|
+
class Cookies extends CookiesUsingBun {
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/CHeaders/CHeaders.ts
|
|
396
|
+
class CHeaders extends Headers {
|
|
397
|
+
constructor(init) {
|
|
398
|
+
super(init);
|
|
399
|
+
}
|
|
400
|
+
append(name, value) {
|
|
401
|
+
super.append(name, value);
|
|
402
|
+
}
|
|
403
|
+
set(name, value) {
|
|
404
|
+
super.set(name, value);
|
|
405
|
+
}
|
|
406
|
+
get(name) {
|
|
407
|
+
return super.get(name) || super.get(name.toLowerCase());
|
|
408
|
+
}
|
|
409
|
+
has(name) {
|
|
410
|
+
return super.has(name) || super.has(name.toLowerCase());
|
|
411
|
+
}
|
|
412
|
+
delete(name) {
|
|
413
|
+
return super.delete(name);
|
|
414
|
+
}
|
|
415
|
+
static combine(source, target) {
|
|
416
|
+
source.forEach((value, key) => {
|
|
417
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
418
|
+
target.append(key, value);
|
|
419
|
+
} else {
|
|
420
|
+
target.set(key, value);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
return target;
|
|
424
|
+
}
|
|
425
|
+
innerCombine(source) {
|
|
426
|
+
CHeaders.combine(source, this);
|
|
427
|
+
}
|
|
428
|
+
setMany(init) {
|
|
429
|
+
const entries = Array.isArray(init) ? init : Object.entries(init);
|
|
430
|
+
for (const [key, value] of entries) {
|
|
431
|
+
if (!strIsDefined(value))
|
|
432
|
+
continue;
|
|
433
|
+
this.set(key, value);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
static findHeaderInInit(init, name) {
|
|
437
|
+
if (init instanceof CHeaders || init instanceof Headers) {
|
|
438
|
+
return init.get(name);
|
|
439
|
+
} else if (Array.isArray(init)) {
|
|
440
|
+
return init.find((entry) => entry[0] === name)?.[1] ?? null;
|
|
441
|
+
} else {
|
|
442
|
+
return init[name] ?? null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/utils/isNil.ts
|
|
448
|
+
function isNil(input) {
|
|
449
|
+
return input === null || input === undefined;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/utils/isPrimitive.ts
|
|
453
|
+
function isPrimitive(input) {
|
|
454
|
+
return typeof input === "string" || typeof input === "number" || typeof input === "boolean" || typeof input === "bigint";
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/utils/isPlainObject.ts
|
|
458
|
+
function isPlainObject(input) {
|
|
459
|
+
return typeof input === "object" && input !== null && input.constructor === Object;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/CError/CError.ts
|
|
463
|
+
class CError extends Error {
|
|
464
|
+
message;
|
|
465
|
+
status;
|
|
466
|
+
data;
|
|
467
|
+
constructor(message, status, data) {
|
|
468
|
+
super(message);
|
|
469
|
+
this.message = message;
|
|
470
|
+
this.status = status;
|
|
471
|
+
this.data = data;
|
|
472
|
+
}
|
|
473
|
+
toResponse() {
|
|
474
|
+
return new CResponse(this.data ? { error: this.data, message: this.message } : { error: true, message: this.message }, { status: this.status });
|
|
475
|
+
}
|
|
476
|
+
isStatusOf(status) {
|
|
477
|
+
return this.status === status;
|
|
478
|
+
}
|
|
479
|
+
static internalServerError(msg) {
|
|
480
|
+
const status = Status.INTERNAL_SERVER_ERROR;
|
|
481
|
+
return new this(msg ?? status.toString(), status);
|
|
482
|
+
}
|
|
483
|
+
static badRequest(msg) {
|
|
484
|
+
const status = Status.BAD_REQUEST;
|
|
485
|
+
return new this(msg ?? status.toString(), status);
|
|
486
|
+
}
|
|
487
|
+
static notFound(msg) {
|
|
488
|
+
const status = Status.NOT_FOUND;
|
|
489
|
+
return new this(msg ?? status.toString(), status);
|
|
490
|
+
}
|
|
491
|
+
static methodNotAllowed(msg) {
|
|
492
|
+
const status = Status.METHOD_NOT_ALLOWED;
|
|
493
|
+
return new this(msg ?? status.toString(), status);
|
|
494
|
+
}
|
|
495
|
+
static unprocessableEntity(msg) {
|
|
496
|
+
const status = Status.UNPROCESSABLE_ENTITY;
|
|
497
|
+
return new this(msg ?? status.toString(), status);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/XFile/XFileAbstract.ts
|
|
502
|
+
class XFileAbstract {
|
|
503
|
+
path;
|
|
504
|
+
fallbackExtension;
|
|
505
|
+
constructor(path2, fallbackExtension) {
|
|
506
|
+
this.path = path2;
|
|
507
|
+
this.fallbackExtension = fallbackExtension;
|
|
508
|
+
}
|
|
509
|
+
get name() {
|
|
510
|
+
return this.path.split("/").pop() ?? this.path;
|
|
511
|
+
}
|
|
512
|
+
get extension() {
|
|
513
|
+
return this.path.split(".").pop() ?? this.fallbackExtension ?? "txt";
|
|
514
|
+
}
|
|
515
|
+
get mimeType() {
|
|
516
|
+
const mimeTypes = {
|
|
517
|
+
html: "text/html",
|
|
518
|
+
htm: "text/html",
|
|
519
|
+
css: "text/css",
|
|
520
|
+
js: "application/javascript",
|
|
521
|
+
ts: "application/javascript",
|
|
522
|
+
mjs: "application/javascript",
|
|
523
|
+
json: "application/json",
|
|
524
|
+
png: "image/png",
|
|
525
|
+
jpg: "image/jpeg",
|
|
526
|
+
jpeg: "image/jpeg",
|
|
527
|
+
gif: "image/gif",
|
|
528
|
+
svg: "image/svg+xml",
|
|
529
|
+
ico: "image/x-icon",
|
|
530
|
+
txt: "text/plain",
|
|
531
|
+
xml: "application/xml",
|
|
532
|
+
pdf: "application/pdf",
|
|
533
|
+
zip: "application/zip",
|
|
534
|
+
mp3: "audio/mpeg",
|
|
535
|
+
mp4: "video/mp4",
|
|
536
|
+
webm: "video/webm",
|
|
537
|
+
woff: "font/woff",
|
|
538
|
+
woff2: "font/woff2",
|
|
539
|
+
ttf: "font/ttf"
|
|
540
|
+
};
|
|
541
|
+
return mimeTypes[this.extension] ?? "application/octet-stream";
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/XFile/XFileUsingBun.ts
|
|
546
|
+
class XFileUsingBun extends XFileAbstract {
|
|
547
|
+
constructor(...args) {
|
|
548
|
+
super(...args);
|
|
549
|
+
this.file = Bun.file(args[0]);
|
|
550
|
+
}
|
|
551
|
+
file;
|
|
552
|
+
async exists() {
|
|
553
|
+
return await this.file.exists();
|
|
554
|
+
}
|
|
555
|
+
async text() {
|
|
556
|
+
return await this.file.text();
|
|
557
|
+
}
|
|
558
|
+
stream() {
|
|
559
|
+
return this.file.stream();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/XFile/XFile.ts
|
|
564
|
+
class XFile extends XFileUsingBun {
|
|
565
|
+
}
|
|
566
|
+
// src/CResponse/CResponse.ts
|
|
567
|
+
class CResponse {
|
|
568
|
+
data;
|
|
569
|
+
init;
|
|
570
|
+
constructor(data, init) {
|
|
571
|
+
this.data = data;
|
|
572
|
+
this.init = init;
|
|
573
|
+
this.cookies = this.resolveCookies();
|
|
574
|
+
this.headers = this.resolveHeaders();
|
|
575
|
+
this.body = this.resolveBody();
|
|
576
|
+
this.status = this.resolveStatus();
|
|
577
|
+
this.statusText = this.getDefaultStatusText();
|
|
578
|
+
}
|
|
579
|
+
body;
|
|
580
|
+
headers;
|
|
581
|
+
status;
|
|
582
|
+
statusText;
|
|
583
|
+
cookies;
|
|
584
|
+
get response() {
|
|
585
|
+
return new Response(this.body, {
|
|
586
|
+
status: this.status,
|
|
587
|
+
statusText: this.statusText,
|
|
588
|
+
headers: this.headers
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
static redirect(url, init) {
|
|
592
|
+
const res = new CResponse(undefined, {
|
|
593
|
+
...init,
|
|
594
|
+
status: init?.status ?? Status.FOUND,
|
|
595
|
+
statusText: init?.statusText ?? DefaultStatusTexts[Status.FOUND]
|
|
596
|
+
});
|
|
597
|
+
const urlString = url instanceof URL ? url.toString() : url;
|
|
598
|
+
res.headers.set(CommonHeaders.Location, urlString);
|
|
599
|
+
return res;
|
|
600
|
+
}
|
|
601
|
+
static permanentRedirect(url, init) {
|
|
602
|
+
return this.redirect(url, {
|
|
603
|
+
...init,
|
|
604
|
+
status: Status.MOVED_PERMANENTLY
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
static temporaryRedirect(url, init) {
|
|
608
|
+
return this.redirect(url, { ...init, status: Status.TEMPORARY_REDIRECT });
|
|
609
|
+
}
|
|
610
|
+
static seeOther(url, init) {
|
|
611
|
+
return this.redirect(url, { ...init, status: Status.SEE_OTHER });
|
|
612
|
+
}
|
|
613
|
+
static createStream(execute) {
|
|
614
|
+
let cancelled = false;
|
|
615
|
+
let cleanup;
|
|
616
|
+
return new ReadableStream({
|
|
617
|
+
start(controller) {
|
|
618
|
+
try {
|
|
619
|
+
cleanup = execute(controller, () => cancelled);
|
|
620
|
+
if (typeof cleanup !== "function") {
|
|
621
|
+
controller.close();
|
|
622
|
+
}
|
|
623
|
+
} catch (err) {
|
|
624
|
+
controller.error(err);
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
cancel() {
|
|
628
|
+
cancelled = true;
|
|
629
|
+
cleanup?.();
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
static sse(source, init, retry) {
|
|
634
|
+
const encoder = new TextEncoder;
|
|
635
|
+
const stream = CResponse.createStream((controller, isCancelled) => {
|
|
636
|
+
return source((event) => {
|
|
637
|
+
if (isCancelled())
|
|
638
|
+
return;
|
|
639
|
+
let chunk = "";
|
|
640
|
+
if (retry !== undefined)
|
|
641
|
+
chunk += `retry: ${retry}
|
|
642
|
+
`;
|
|
643
|
+
if (event.id)
|
|
644
|
+
chunk += `id: ${event.id}
|
|
645
|
+
`;
|
|
646
|
+
if (event.event)
|
|
647
|
+
chunk += `event: ${event.event}
|
|
648
|
+
`;
|
|
649
|
+
chunk += `data: ${JSON.stringify(event.data)}
|
|
650
|
+
|
|
651
|
+
`;
|
|
652
|
+
controller.enqueue(encoder.encode(chunk));
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
const res = new CResponse(stream, { ...init, status: Status.OK });
|
|
656
|
+
res.headers.setMany({
|
|
657
|
+
[CommonHeaders.ContentType]: "text/event-stream",
|
|
658
|
+
[CommonHeaders.CacheControl]: "no-cache",
|
|
659
|
+
[CommonHeaders.Connection]: "keep-alive"
|
|
660
|
+
});
|
|
661
|
+
return res;
|
|
662
|
+
}
|
|
663
|
+
static ndjson(source, init) {
|
|
664
|
+
const encoder = new TextEncoder;
|
|
665
|
+
const stream = CResponse.createStream((controller, isCancelled) => {
|
|
666
|
+
return source((item) => {
|
|
667
|
+
if (isCancelled())
|
|
668
|
+
return;
|
|
669
|
+
controller.enqueue(encoder.encode(`${JSON.stringify(item)}
|
|
670
|
+
`));
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
const res = new CResponse(stream, { ...init, status: Status.OK });
|
|
674
|
+
res.headers.setMany({
|
|
675
|
+
[CommonHeaders.ContentType]: "application/x-ndjson",
|
|
676
|
+
[CommonHeaders.CacheControl]: "no-cache"
|
|
677
|
+
});
|
|
678
|
+
return res;
|
|
679
|
+
}
|
|
680
|
+
static async streamFile(filePath, disposition = "attachment", init) {
|
|
681
|
+
const file = new XFile(filePath);
|
|
682
|
+
if (!file) {
|
|
683
|
+
throw CError.notFound();
|
|
684
|
+
}
|
|
685
|
+
const stream = file.stream();
|
|
686
|
+
const res = new CResponse(stream, { ...init, status: Status.OK });
|
|
687
|
+
res.headers.setMany({
|
|
688
|
+
[CommonHeaders.ContentType]: file.mimeType,
|
|
689
|
+
[CommonHeaders.ContentDisposition]: `${disposition}; filename="${file.name}"`
|
|
690
|
+
});
|
|
691
|
+
return res;
|
|
692
|
+
}
|
|
693
|
+
static async file(filePath, init) {
|
|
694
|
+
const file = new XFile(filePath);
|
|
695
|
+
if (!file) {
|
|
696
|
+
throw CError.notFound();
|
|
697
|
+
}
|
|
698
|
+
const content = await file.text();
|
|
699
|
+
const res = new CResponse(content, init);
|
|
700
|
+
res.headers.setMany({
|
|
701
|
+
[CommonHeaders.ContentType]: file.mimeType,
|
|
702
|
+
[CommonHeaders.ContentLength]: content.length.toString()
|
|
703
|
+
});
|
|
704
|
+
return res;
|
|
705
|
+
}
|
|
706
|
+
resolveCookies() {
|
|
707
|
+
return new Cookies(this.init?.cookies);
|
|
708
|
+
}
|
|
709
|
+
resolveHeaders() {
|
|
710
|
+
const headers = new CHeaders(this.init?.headers);
|
|
711
|
+
const setCookieHeaders = this.cookies.toSetCookieHeaders();
|
|
712
|
+
if (setCookieHeaders.length > 0) {
|
|
713
|
+
for (const header of setCookieHeaders) {
|
|
714
|
+
headers.append(CommonHeaders.SetCookie, header);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return headers;
|
|
718
|
+
}
|
|
719
|
+
resolveStatus() {
|
|
720
|
+
if (this.init?.status)
|
|
721
|
+
return this.init.status;
|
|
722
|
+
if (this.headers.has(CommonHeaders.Location)) {
|
|
723
|
+
return Status.FOUND;
|
|
724
|
+
}
|
|
725
|
+
return Status.OK;
|
|
726
|
+
}
|
|
727
|
+
setContentType(value) {
|
|
728
|
+
if (!this.headers.has(CommonHeaders.ContentType) || this.headers.get(CommonHeaders.ContentType) === "text/plain") {
|
|
729
|
+
this.headers.set(CommonHeaders.ContentType, value);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
resolveBody() {
|
|
733
|
+
if (isNil(this.data)) {
|
|
734
|
+
this.setContentType("text/plain");
|
|
735
|
+
return "";
|
|
736
|
+
}
|
|
737
|
+
if (isPrimitive(this.data)) {
|
|
738
|
+
this.setContentType("text/plain");
|
|
739
|
+
return String(this.data);
|
|
740
|
+
}
|
|
741
|
+
if (this.data instanceof ArrayBuffer) {
|
|
742
|
+
this.setContentType("application/octet-stream");
|
|
743
|
+
return this.data;
|
|
744
|
+
}
|
|
745
|
+
if (this.data instanceof Blob) {
|
|
746
|
+
if (this.data.type)
|
|
747
|
+
this.setContentType(this.data.type);
|
|
748
|
+
return this.data;
|
|
749
|
+
}
|
|
750
|
+
if (this.data instanceof FormData) {
|
|
751
|
+
this.setContentType("multipart/form-data");
|
|
752
|
+
return this.data;
|
|
753
|
+
}
|
|
754
|
+
if (this.data instanceof URLSearchParams) {
|
|
755
|
+
this.setContentType("application/x-www-form-urlencoded");
|
|
756
|
+
return this.data;
|
|
757
|
+
}
|
|
758
|
+
if (this.data instanceof ReadableStream) {
|
|
759
|
+
return this.data;
|
|
760
|
+
}
|
|
761
|
+
if (this.data instanceof Date) {
|
|
762
|
+
this.setContentType("text/plain");
|
|
763
|
+
return this.data.toISOString();
|
|
764
|
+
}
|
|
765
|
+
if (Array.isArray(this.data) || isPlainObject(this.data)) {
|
|
766
|
+
this.setContentType("application/json");
|
|
767
|
+
return JSON.stringify(this.data);
|
|
768
|
+
}
|
|
769
|
+
this.setContentType("text/plain");
|
|
770
|
+
return String(this.data);
|
|
771
|
+
}
|
|
772
|
+
getDefaultStatusText() {
|
|
773
|
+
const key = this.status;
|
|
774
|
+
return DefaultStatusTexts[key] ?? "Unknown";
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/CRequest/enums/Method.ts
|
|
779
|
+
var Method = {
|
|
780
|
+
GET: "GET",
|
|
781
|
+
POST: "POST",
|
|
782
|
+
PUT: "PUT",
|
|
783
|
+
PATCH: "PATCH",
|
|
784
|
+
DELETE: "DELETE",
|
|
785
|
+
HEAD: "HEAD",
|
|
786
|
+
OPTIONS: "OPTIONS",
|
|
787
|
+
CONNECT: "CONNECT",
|
|
788
|
+
TRACE: "TRACE"
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
// src/utils/arrIncludes.ts
|
|
792
|
+
function arrIncludes(input, array) {
|
|
793
|
+
return array.includes(input);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// src/utils/isObjectWith.ts
|
|
797
|
+
function isObjectWith(item, key) {
|
|
798
|
+
return item !== null && item !== undefined && typeof item === "object" && key in item;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// src/utils/objAppendEntry.ts
|
|
802
|
+
function objAppendEntry(data, key, value) {
|
|
803
|
+
const existing = data[key];
|
|
804
|
+
if (existing !== undefined) {
|
|
805
|
+
data[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
806
|
+
} else {
|
|
807
|
+
data[key] = value;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// src/Model/Parser.ts
|
|
812
|
+
class Parser {
|
|
813
|
+
static async parse(data, validate) {
|
|
814
|
+
if (!validate)
|
|
815
|
+
return data;
|
|
816
|
+
const result = await validate(data);
|
|
817
|
+
if (result.issues !== undefined) {
|
|
818
|
+
const msg = this.issuesToErrorMessage(result.issues);
|
|
819
|
+
throw CError.unprocessableEntity(msg);
|
|
820
|
+
}
|
|
821
|
+
return result.value;
|
|
822
|
+
}
|
|
823
|
+
static issuesToErrorMessage(issues) {
|
|
824
|
+
if (issues.length === 0)
|
|
825
|
+
return "";
|
|
826
|
+
return issues.map((issue) => {
|
|
827
|
+
if (!issue.path || issue.path.length === 0) {
|
|
828
|
+
return issue.message;
|
|
829
|
+
}
|
|
830
|
+
const key = issue.path.map((segment) => isObjectWith(segment, "key") ? String(segment.key) : String(segment)).join(".");
|
|
831
|
+
return `${key}: ${issue.message}`;
|
|
832
|
+
}).join(`
|
|
833
|
+
`);
|
|
834
|
+
}
|
|
835
|
+
static async parseUrlData(params, validate) {
|
|
836
|
+
const data = {};
|
|
837
|
+
for (const [key, value] of Object.entries(params)) {
|
|
838
|
+
data[key] = decodeURIComponent(value);
|
|
839
|
+
}
|
|
840
|
+
return await this.parse(data, validate);
|
|
841
|
+
}
|
|
842
|
+
static async parseBody(r, validate) {
|
|
843
|
+
let data;
|
|
844
|
+
const empty = {};
|
|
845
|
+
const input = r instanceof Request ? r : r instanceof Response ? r : r.response;
|
|
846
|
+
try {
|
|
847
|
+
switch (Parser.getNormalizedContentType(input)) {
|
|
848
|
+
case "json":
|
|
849
|
+
data = await this.getJsonBody(input);
|
|
850
|
+
break;
|
|
851
|
+
case "form-urlencoded":
|
|
852
|
+
data = await this.getFormUrlEncodedBody(input);
|
|
853
|
+
break;
|
|
854
|
+
case "form-data":
|
|
855
|
+
data = await this.getFormDataBody(input);
|
|
856
|
+
break;
|
|
857
|
+
case "text":
|
|
858
|
+
data = await this.getTextBody(input);
|
|
859
|
+
break;
|
|
860
|
+
case "unknown":
|
|
861
|
+
data = await this.getUnknownBody(input);
|
|
862
|
+
break;
|
|
863
|
+
case "xml":
|
|
864
|
+
case "binary":
|
|
865
|
+
case "pdf":
|
|
866
|
+
case "image":
|
|
867
|
+
case "audio":
|
|
868
|
+
case "video":
|
|
869
|
+
throw new CError("unprocessable.contentType", Status.UNPROCESSABLE_ENTITY);
|
|
870
|
+
case "no-body-allowed":
|
|
871
|
+
default:
|
|
872
|
+
return empty;
|
|
873
|
+
}
|
|
874
|
+
return await this.parse(data, validate);
|
|
875
|
+
} catch (err) {
|
|
876
|
+
if (err instanceof SyntaxError)
|
|
877
|
+
return empty;
|
|
878
|
+
throw err;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
static async getUnknownBody(input, validate) {
|
|
882
|
+
if (!validate) {
|
|
883
|
+
return await this.getTextBody(input);
|
|
884
|
+
}
|
|
885
|
+
try {
|
|
886
|
+
return await this.getJsonBody(input);
|
|
887
|
+
} catch {
|
|
888
|
+
return await this.getTextBody(input);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
static async getJsonBody(req) {
|
|
892
|
+
return await req.json();
|
|
893
|
+
}
|
|
894
|
+
static async getFormUrlEncodedBody(input) {
|
|
895
|
+
const text = await input.text();
|
|
896
|
+
if (!text || text.trim().length === 0) {
|
|
897
|
+
throw new SyntaxError("Body is empty");
|
|
898
|
+
}
|
|
899
|
+
const params = new URLSearchParams(text);
|
|
900
|
+
const body = {};
|
|
901
|
+
for (const [key, value] of params.entries()) {
|
|
902
|
+
objAppendEntry(body, key, value);
|
|
903
|
+
}
|
|
904
|
+
return body;
|
|
905
|
+
}
|
|
906
|
+
static async getFormDataBody(input) {
|
|
907
|
+
const formData = await input.formData();
|
|
908
|
+
const entries = formData.entries();
|
|
909
|
+
const body = {};
|
|
910
|
+
for (const [key, value] of entries) {
|
|
911
|
+
if (value instanceof File) {
|
|
912
|
+
body[key] = value;
|
|
913
|
+
} else {
|
|
914
|
+
objAppendEntry(body, key, value);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return body;
|
|
918
|
+
}
|
|
919
|
+
static async getTextBody(input) {
|
|
920
|
+
const contentLength = input.headers.get(CommonHeaders.ContentLength);
|
|
921
|
+
const length = contentLength ? parseInt(contentLength) : 0;
|
|
922
|
+
if (length > 0 && length < 1024 * 1024) {
|
|
923
|
+
return await input.text();
|
|
924
|
+
}
|
|
925
|
+
const buffer = await input.arrayBuffer();
|
|
926
|
+
const contentType = input.headers.get(CommonHeaders.ContentType) || "";
|
|
927
|
+
const match = contentType.match(/charset=([^;]+)/i);
|
|
928
|
+
const charset = match?.[1] ? match[1].trim() : null;
|
|
929
|
+
const decoder = new TextDecoder(charset || "utf-8");
|
|
930
|
+
return decoder.decode(buffer);
|
|
931
|
+
}
|
|
932
|
+
static getNormalizedContentType(input) {
|
|
933
|
+
const contentTypeHeader = input.headers.get(CommonHeaders.ContentType) || "";
|
|
934
|
+
if ("method" in input && typeof input.method === "string" && !arrIncludes(input.method.toUpperCase(), [
|
|
935
|
+
Method.POST,
|
|
936
|
+
Method.PUT,
|
|
937
|
+
Method.PATCH,
|
|
938
|
+
Method.DELETE
|
|
939
|
+
])) {
|
|
940
|
+
return "no-body-allowed";
|
|
941
|
+
}
|
|
942
|
+
if (contentTypeHeader.includes("application/json")) {
|
|
943
|
+
return "json";
|
|
944
|
+
} else if (contentTypeHeader.includes("application/x-www-form-urlencoded")) {
|
|
945
|
+
return "form-urlencoded";
|
|
946
|
+
} else if (contentTypeHeader.includes("multipart/form-data")) {
|
|
947
|
+
return "form-data";
|
|
948
|
+
} else if (contentTypeHeader.includes("text/plain")) {
|
|
949
|
+
return "text";
|
|
950
|
+
} else if (contentTypeHeader.includes("application/xml")) {
|
|
951
|
+
return "xml";
|
|
952
|
+
} else if (contentTypeHeader.includes("text/xml")) {
|
|
953
|
+
return "xml";
|
|
954
|
+
} else if (contentTypeHeader.includes("application/octet-stream")) {
|
|
955
|
+
return "binary";
|
|
956
|
+
} else if (contentTypeHeader.includes("application/pdf")) {
|
|
957
|
+
return "pdf";
|
|
958
|
+
} else if (contentTypeHeader.includes("image/")) {
|
|
959
|
+
return "image";
|
|
960
|
+
} else if (contentTypeHeader.includes("audio/")) {
|
|
961
|
+
return "audio";
|
|
962
|
+
} else if (contentTypeHeader.includes("video/")) {
|
|
963
|
+
return "video";
|
|
964
|
+
}
|
|
965
|
+
return "unknown";
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// src/Context/Context.ts
|
|
970
|
+
class Context {
|
|
971
|
+
constructor(req, body, search, params, res) {
|
|
972
|
+
this.req = req;
|
|
973
|
+
this.url = req.urlObject;
|
|
974
|
+
this.headers = req.headers;
|
|
975
|
+
this.cookies = req.cookies;
|
|
976
|
+
this.body = body;
|
|
977
|
+
this.search = search;
|
|
978
|
+
this.params = params;
|
|
979
|
+
this.res = res ?? new CResponse;
|
|
980
|
+
}
|
|
981
|
+
req;
|
|
982
|
+
url;
|
|
983
|
+
headers;
|
|
984
|
+
cookies;
|
|
985
|
+
body;
|
|
986
|
+
search;
|
|
987
|
+
params;
|
|
988
|
+
res;
|
|
989
|
+
data = {};
|
|
990
|
+
static makeFromRequest(req) {
|
|
991
|
+
return new Context(req, {}, {}, {});
|
|
992
|
+
}
|
|
993
|
+
static async appendParsedData(ctx, req, params, search, model) {
|
|
994
|
+
ctx.body = await Parser.parseBody(req, model?.body);
|
|
995
|
+
ctx.params = await Parser.parseUrlData(params, model?.params);
|
|
996
|
+
ctx.search = await Parser.parseUrlData(search, model?.search);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
// src/Route/enums/RouteVariant.ts
|
|
1000
|
+
var RouteVariant = {
|
|
1001
|
+
static: "static",
|
|
1002
|
+
dynamic: "dynamic"
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
// src/utils/joinPathSegments.ts
|
|
1006
|
+
function joinPathSegments(...segments) {
|
|
1007
|
+
const joined = segments.map((segment) => typeof segment === "number" ? segment.toString() : segment).filter((segment) => segment !== undefined && segment !== null && segment.trim() !== "").map((segment) => segment.replace(/^\/+|\/+$/g, "")).filter((segment) => segment.length > 0).join("/");
|
|
1008
|
+
return `/${joined}`;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/Route/RouteAbstract.ts
|
|
1012
|
+
class RouteAbstract {
|
|
1013
|
+
resolveEndpoint(definition, variant) {
|
|
1014
|
+
const endpoint = typeof definition === "string" ? definition : definition.path;
|
|
1015
|
+
if (variant === RouteVariant.dynamic) {
|
|
1016
|
+
return joinPathSegments(_prefixStore.get(), endpoint);
|
|
1017
|
+
}
|
|
1018
|
+
return endpoint;
|
|
1019
|
+
}
|
|
1020
|
+
resolveMethod(definition) {
|
|
1021
|
+
return typeof definition === "string" ? Method.GET : definition.method;
|
|
1022
|
+
}
|
|
1023
|
+
resolvePattern(endpoint) {
|
|
1024
|
+
return Route.makeRoutePattern(endpoint);
|
|
1025
|
+
}
|
|
1026
|
+
resolveId(method, endpoint) {
|
|
1027
|
+
return Route.makeRouteId(method, endpoint);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// src/Route/Route.ts
|
|
1032
|
+
class Route extends RouteAbstract {
|
|
1033
|
+
constructor(definition, handler, model) {
|
|
1034
|
+
super();
|
|
1035
|
+
this.variant = RouteVariant.dynamic;
|
|
1036
|
+
this.endpoint = this.resolveEndpoint(definition, this.variant);
|
|
1037
|
+
this.method = this.resolveMethod(definition);
|
|
1038
|
+
this.pattern = this.resolvePattern(this.endpoint);
|
|
1039
|
+
this.id = this.resolveId(this.method, this.endpoint);
|
|
1040
|
+
this.model = model;
|
|
1041
|
+
this.handler = handler;
|
|
1042
|
+
_routerStore.get().addRoute(this);
|
|
1043
|
+
if (model) {
|
|
1044
|
+
_routerStore.get().addModel(this, model);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
variant;
|
|
1048
|
+
endpoint;
|
|
1049
|
+
method;
|
|
1050
|
+
pattern;
|
|
1051
|
+
id;
|
|
1052
|
+
handler;
|
|
1053
|
+
model;
|
|
1054
|
+
static makeRouteId(method, endpoint) {
|
|
1055
|
+
return `${method.toUpperCase()} ${endpoint}`;
|
|
1056
|
+
}
|
|
1057
|
+
static makeRoutePattern(endpoint) {
|
|
1058
|
+
const regex = endpoint.split("/").map((part) => part.startsWith(":") ? "([^\\/]+)" : part).join("/");
|
|
1059
|
+
return new RegExp(`^${regex}$`);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/Route/StaticRoute.ts
|
|
1064
|
+
class StaticRoute extends RouteAbstract {
|
|
1065
|
+
constructor(path2, definition, handler, model) {
|
|
1066
|
+
super();
|
|
1067
|
+
this.variant = RouteVariant.static;
|
|
1068
|
+
this.endpoint = this.resolveEndpoint(path2, this.variant);
|
|
1069
|
+
this.method = Method.GET;
|
|
1070
|
+
this.pattern = this.resolvePattern(this.endpoint);
|
|
1071
|
+
this.id = this.resolveId(this.method, this.endpoint);
|
|
1072
|
+
this.model = model;
|
|
1073
|
+
this.filePath = this.resolveFilePath(definition);
|
|
1074
|
+
this.handler = this.resolveHandler(definition, handler);
|
|
1075
|
+
_routerStore.get().addRoute(this);
|
|
1076
|
+
}
|
|
1077
|
+
id;
|
|
1078
|
+
variant;
|
|
1079
|
+
method;
|
|
1080
|
+
endpoint;
|
|
1081
|
+
pattern;
|
|
1082
|
+
model;
|
|
1083
|
+
handler;
|
|
1084
|
+
filePath;
|
|
1085
|
+
resolveFilePath(definition) {
|
|
1086
|
+
return typeof definition === "string" ? definition : definition.filePath;
|
|
1087
|
+
}
|
|
1088
|
+
resolveHandler(definition, customHandler) {
|
|
1089
|
+
if (customHandler !== undefined) {
|
|
1090
|
+
return async (c) => {
|
|
1091
|
+
const file = new XFile(this.filePath);
|
|
1092
|
+
if (!file) {
|
|
1093
|
+
console.error("File not found at:", this.filePath);
|
|
1094
|
+
throw CError.notFound();
|
|
1095
|
+
}
|
|
1096
|
+
const content = await file.text();
|
|
1097
|
+
c.res.headers.setMany({
|
|
1098
|
+
[CommonHeaders.ContentType]: file.mimeType,
|
|
1099
|
+
[CommonHeaders.ContentLength]: content.length.toString()
|
|
1100
|
+
});
|
|
1101
|
+
return customHandler(c, content);
|
|
1102
|
+
};
|
|
1103
|
+
} else if (typeof definition === "string") {
|
|
1104
|
+
return async () => await CResponse.file(this.filePath);
|
|
1105
|
+
} else {
|
|
1106
|
+
return async () => await CResponse.streamFile(this.filePath);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/Controller/ControllerAbstract.ts
|
|
1112
|
+
class ControllerAbstract {
|
|
1113
|
+
constructor(opts) {
|
|
1114
|
+
this.prefix = opts?.prefix;
|
|
1115
|
+
this.beforeEach = opts?.beforeEach;
|
|
1116
|
+
}
|
|
1117
|
+
routeIds = new Set;
|
|
1118
|
+
prefix;
|
|
1119
|
+
beforeEach;
|
|
1120
|
+
route(...args) {
|
|
1121
|
+
const [definition, handler, model] = args;
|
|
1122
|
+
const route = new Route(this.resolveRouteDefinition(definition), async (ctx) => {
|
|
1123
|
+
await this.beforeEach?.(ctx);
|
|
1124
|
+
return handler(ctx);
|
|
1125
|
+
}, model);
|
|
1126
|
+
this.routeIds.add(route.id);
|
|
1127
|
+
return route;
|
|
1128
|
+
}
|
|
1129
|
+
staticRoute(...args) {
|
|
1130
|
+
const [path2, filePath, handler, model] = args;
|
|
1131
|
+
const route = new StaticRoute(joinPathSegments(this.prefix, path2), filePath, handler, model);
|
|
1132
|
+
this.routeIds.add(route.id);
|
|
1133
|
+
return route;
|
|
1134
|
+
}
|
|
1135
|
+
resolveRouteDefinition(definition) {
|
|
1136
|
+
if (typeof definition === "string") {
|
|
1137
|
+
return joinPathSegments(this.prefix, definition);
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
method: definition.method,
|
|
1141
|
+
path: joinPathSegments(this.prefix, definition.path)
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
// src/Middleware/Middleware.ts
|
|
1146
|
+
class Middleware {
|
|
1147
|
+
constructor(opts) {
|
|
1148
|
+
this.useOn = opts.useOn;
|
|
1149
|
+
this.handler = opts.handler;
|
|
1150
|
+
_routerStore.get().addMiddleware(opts);
|
|
1151
|
+
}
|
|
1152
|
+
useOn;
|
|
1153
|
+
handler;
|
|
1154
|
+
}
|
|
1155
|
+
// src/utils/assert.ts
|
|
1156
|
+
function assert(condition, message) {
|
|
1157
|
+
const conditionName = String(condition);
|
|
1158
|
+
if (!condition) {
|
|
1159
|
+
if (!message) {
|
|
1160
|
+
message = `Assertion failed for ${conditionName}`;
|
|
1161
|
+
} else {
|
|
1162
|
+
message = `${conditionName}: ${message}`;
|
|
1163
|
+
}
|
|
1164
|
+
throw new Error(message);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// src/utils/strSplit.ts
|
|
1169
|
+
function strSplit(mark, input, minLength) {
|
|
1170
|
+
const parts = input.split(mark).map((part) => part.trim()).filter(Boolean);
|
|
1171
|
+
if (minLength) {
|
|
1172
|
+
assert(parts.length >= minLength);
|
|
1173
|
+
return parts;
|
|
1174
|
+
}
|
|
1175
|
+
return parts;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/CRequest/CRequest.ts
|
|
1179
|
+
class CRequest extends Request {
|
|
1180
|
+
info;
|
|
1181
|
+
init;
|
|
1182
|
+
constructor(info, init) {
|
|
1183
|
+
super(info, init);
|
|
1184
|
+
this.info = info;
|
|
1185
|
+
this.init = init;
|
|
1186
|
+
this.urlObject = this.resolveUrlObject();
|
|
1187
|
+
this.headers = this.resolveHeaders();
|
|
1188
|
+
this.cookies = this.resolveCookies();
|
|
1189
|
+
this.isPreflight = this.resolveIsPreflight();
|
|
1190
|
+
}
|
|
1191
|
+
urlObject;
|
|
1192
|
+
isPreflight;
|
|
1193
|
+
cookies;
|
|
1194
|
+
headers;
|
|
1195
|
+
resolveUrlObject() {
|
|
1196
|
+
let urlObject;
|
|
1197
|
+
switch (true) {
|
|
1198
|
+
case this.info instanceof URL:
|
|
1199
|
+
urlObject = this.info;
|
|
1200
|
+
break;
|
|
1201
|
+
case this.info instanceof CRequest:
|
|
1202
|
+
urlObject = this.info.urlObject;
|
|
1203
|
+
break;
|
|
1204
|
+
case this.info instanceof Request:
|
|
1205
|
+
urlObject = new URL(this.info.url);
|
|
1206
|
+
break;
|
|
1207
|
+
default:
|
|
1208
|
+
urlObject = new URL(this.info);
|
|
1209
|
+
break;
|
|
1210
|
+
}
|
|
1211
|
+
if (!urlObject.pathname) {
|
|
1212
|
+
urlObject.pathname += "/";
|
|
1213
|
+
}
|
|
1214
|
+
return urlObject;
|
|
1215
|
+
}
|
|
1216
|
+
resolveHeaders() {
|
|
1217
|
+
if (this.init?.headers !== undefined) {
|
|
1218
|
+
return new CHeaders(this.init.headers);
|
|
1219
|
+
}
|
|
1220
|
+
if (this.info instanceof Request || this.info instanceof CRequest) {
|
|
1221
|
+
return new CHeaders(this.info.headers);
|
|
1222
|
+
}
|
|
1223
|
+
return new CHeaders;
|
|
1224
|
+
}
|
|
1225
|
+
resolveCookies() {
|
|
1226
|
+
const jar = new Cookies;
|
|
1227
|
+
const cookieHeader = this.headers.get(CommonHeaders.Cookie);
|
|
1228
|
+
if (cookieHeader) {
|
|
1229
|
+
const pairs = strSplit(";", cookieHeader);
|
|
1230
|
+
for (const pair of pairs) {
|
|
1231
|
+
const [name, value] = strSplit("=", pair);
|
|
1232
|
+
if (!name || !value)
|
|
1233
|
+
continue;
|
|
1234
|
+
jar.set({ name, value });
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
return jar;
|
|
1238
|
+
}
|
|
1239
|
+
resolveIsPreflight() {
|
|
1240
|
+
const accessControlRequestMethodHeader = this.headers.has(CommonHeaders.AccessControlRequestMethod);
|
|
1241
|
+
return this.method === Method.OPTIONS && accessControlRequestMethodHeader;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
// src/utils/isRegexMatch.ts
|
|
1245
|
+
function isRegexMatch(source, pattern) {
|
|
1246
|
+
return pattern.test(source);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// src/utils/strIsEqual.ts
|
|
1250
|
+
function strIsEqual(source, target, modifier) {
|
|
1251
|
+
source = source.trim();
|
|
1252
|
+
target = target.trim();
|
|
1253
|
+
if (modifier === "upper") {
|
|
1254
|
+
return source.toUpperCase() === target.toUpperCase();
|
|
1255
|
+
}
|
|
1256
|
+
if (modifier === "lower") {
|
|
1257
|
+
return source.toUpperCase() === target.toUpperCase();
|
|
1258
|
+
}
|
|
1259
|
+
return source === target;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// src/Store/LazyMap.ts
|
|
1263
|
+
class LazyMap {
|
|
1264
|
+
constructor() {
|
|
1265
|
+
return new Proxy(this, {
|
|
1266
|
+
get(target, prop) {
|
|
1267
|
+
const val = Reflect.get(target.map, prop);
|
|
1268
|
+
return typeof val === "function" ? val.bind(target.map) : val;
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
_map;
|
|
1273
|
+
get map() {
|
|
1274
|
+
if (!this._map) {
|
|
1275
|
+
this._map = new Map;
|
|
1276
|
+
}
|
|
1277
|
+
return this._map;
|
|
1278
|
+
}
|
|
1279
|
+
get [Symbol.toStringTag]() {
|
|
1280
|
+
return "LazyMap";
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/utils/strRemoveWhitespace.ts
|
|
1285
|
+
function strRemoveWhitespace(str) {
|
|
1286
|
+
return str.trim().replace(/\s+/g, "");
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// src/Router/registries/ModelRegistry.ts
|
|
1290
|
+
class ModelRegistry {
|
|
1291
|
+
map = new LazyMap;
|
|
1292
|
+
add(method, endpoint, model) {
|
|
1293
|
+
const entry = ModelRegistry.toRouterModelData(model);
|
|
1294
|
+
this.map.set(Route.makeRouteId(method, endpoint), entry);
|
|
1295
|
+
}
|
|
1296
|
+
find(routeId) {
|
|
1297
|
+
return this.map.get(routeId);
|
|
1298
|
+
}
|
|
1299
|
+
static internFuncMap = new LazyMap;
|
|
1300
|
+
static toRouterModelData(model) {
|
|
1301
|
+
const entry = {};
|
|
1302
|
+
for (const k of Object.keys(model)) {
|
|
1303
|
+
const key = k;
|
|
1304
|
+
const schema = model[key];
|
|
1305
|
+
if (!schema)
|
|
1306
|
+
continue;
|
|
1307
|
+
const handler = schema["~standard"].validate;
|
|
1308
|
+
entry[key] = this.intern(handler, "model", strRemoveWhitespace(JSON.stringify(schema)));
|
|
1309
|
+
}
|
|
1310
|
+
return entry;
|
|
1311
|
+
}
|
|
1312
|
+
static intern(value, ...namespace) {
|
|
1313
|
+
const key = namespace.join("::");
|
|
1314
|
+
const existing = this.internFuncMap.get(key);
|
|
1315
|
+
if (existing)
|
|
1316
|
+
return existing;
|
|
1317
|
+
this.internFuncMap.set(key, value);
|
|
1318
|
+
return value;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// src/utils/compile.ts
|
|
1323
|
+
function compile(fns) {
|
|
1324
|
+
return async (...args) => {
|
|
1325
|
+
for (const fn of fns) {
|
|
1326
|
+
if (!fn)
|
|
1327
|
+
continue;
|
|
1328
|
+
await fn(...args);
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// src/Router/registries/MiddlewareRegistry.ts
|
|
1334
|
+
class MiddlewareRegistry {
|
|
1335
|
+
middlewares = new LazyMap;
|
|
1336
|
+
add(m) {
|
|
1337
|
+
const resolved = MiddlewareRegistry.resolveRouteIds(m);
|
|
1338
|
+
if (resolved.isGlobal) {
|
|
1339
|
+
const existing = this.middlewares.get("*") ?? [];
|
|
1340
|
+
this.middlewares.set("*", [...existing, m.handler]);
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
for (const routeId of resolved.routeIds) {
|
|
1344
|
+
const existing = this.middlewares.get(routeId) ?? [];
|
|
1345
|
+
this.middlewares.set(routeId, [...existing, m.handler]);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
find(routeId) {
|
|
1349
|
+
const globals = this.middlewares.get("*") ?? [];
|
|
1350
|
+
const locals = this.middlewares.get(routeId) ?? [];
|
|
1351
|
+
return compile([...globals, ...locals]);
|
|
1352
|
+
}
|
|
1353
|
+
static resolveRouteIds(m) {
|
|
1354
|
+
if (m.useOn === "*")
|
|
1355
|
+
return { isGlobal: true };
|
|
1356
|
+
const targets = Array.isArray(m.useOn) ? m.useOn : [m.useOn];
|
|
1357
|
+
const routeIds = [];
|
|
1358
|
+
for (const target of targets) {
|
|
1359
|
+
if (target instanceof Route) {
|
|
1360
|
+
routeIds.push(target.id);
|
|
1361
|
+
} else if (target instanceof ControllerAbstract) {
|
|
1362
|
+
routeIds.push(...target.routeIds);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
return { isGlobal: false, routeIds };
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
// src/Router/adapters/CorpusAdapter.ts
|
|
1370
|
+
class CorpusAdapter {
|
|
1371
|
+
routes = new Map;
|
|
1372
|
+
modelRegistry = new ModelRegistry;
|
|
1373
|
+
middlewareRegistry = new MiddlewareRegistry;
|
|
1374
|
+
addRoute(data) {
|
|
1375
|
+
this.checkPossibleCollision(data);
|
|
1376
|
+
this.routes.set(data.id, data);
|
|
1377
|
+
}
|
|
1378
|
+
addModel(route, model) {
|
|
1379
|
+
this.modelRegistry.add(route.method, route.endpoint, model);
|
|
1380
|
+
}
|
|
1381
|
+
addMiddleware(middleware) {
|
|
1382
|
+
this.middlewareRegistry.add(middleware);
|
|
1383
|
+
}
|
|
1384
|
+
find(req) {
|
|
1385
|
+
const method = req.method;
|
|
1386
|
+
const pathname = req.urlObject.pathname;
|
|
1387
|
+
const searchParams = req.urlObject.searchParams;
|
|
1388
|
+
let route = null;
|
|
1389
|
+
for (const data of this.routes.values()) {
|
|
1390
|
+
if (this.hasAnyParam(data.endpoint) && isRegexMatch(pathname, data.pattern)) {
|
|
1391
|
+
route = data;
|
|
1392
|
+
break;
|
|
1393
|
+
}
|
|
1394
|
+
if (this.hasLastPartParam(data.endpoint) && strIsEqual(this.removeLastParam(data.endpoint), pathname, "lower")) {
|
|
1395
|
+
route = data;
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
if (strIsEqual(data.endpoint, pathname)) {
|
|
1399
|
+
route = data;
|
|
1400
|
+
break;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
if (route === null) {
|
|
1404
|
+
throw CError.notFound();
|
|
1405
|
+
}
|
|
1406
|
+
if (!strIsEqual(method, route.method, "upper")) {
|
|
1407
|
+
throw CError.methodNotAllowed();
|
|
1408
|
+
}
|
|
1409
|
+
return {
|
|
1410
|
+
route,
|
|
1411
|
+
model: this.modelRegistry.find(route.id),
|
|
1412
|
+
middleware: this.middlewareRegistry.find(route.id),
|
|
1413
|
+
params: this.extractParams(pathname, route.endpoint),
|
|
1414
|
+
search: Object.fromEntries(searchParams)
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
list() {
|
|
1418
|
+
return Array.from(this.routes.values());
|
|
1419
|
+
}
|
|
1420
|
+
checkPossibleCollision(n) {
|
|
1421
|
+
const dupeMsg = (nId) => console.error(`Duplicate route detected. ${nId} has already been registered.`);
|
|
1422
|
+
const dynamicPatternMsg = (nId, oId) => console.error(`Ambiguous dynamic routes. ${nId} and ${oId} match the same URL patterns.`);
|
|
1423
|
+
const baseDupeMsg = (nId, oId) => console.error(`Dynamic route overlaps existing route. ${nId} \u2014 dropping the last param segment matches ${oId}.`);
|
|
1424
|
+
const shadowMsg = (nId, oId) => console.error(`Route shadowed by existing dynamic route. ${nId} will be unreachable \u2014 ${oId} captures the same URL space.`);
|
|
1425
|
+
const existing = this.routes.get(n.id);
|
|
1426
|
+
if (existing) {
|
|
1427
|
+
dupeMsg(n.id);
|
|
1428
|
+
return true;
|
|
1429
|
+
}
|
|
1430
|
+
const nHasAnyParam = this.hasAnyParam(n.endpoint);
|
|
1431
|
+
const nHasLastPartParam = this.hasLastPartParam(n.endpoint);
|
|
1432
|
+
for (const o of this.routes.values()) {
|
|
1433
|
+
if (o.method !== n.method)
|
|
1434
|
+
continue;
|
|
1435
|
+
if (nHasAnyParam) {
|
|
1436
|
+
if (isRegexMatch(n.endpoint, o.pattern) || isRegexMatch(o.endpoint, n.pattern)) {
|
|
1437
|
+
dynamicPatternMsg(n.id, o.id);
|
|
1438
|
+
return true;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
if (nHasLastPartParam) {
|
|
1442
|
+
if (isRegexMatch(this.removeLastParam(n.endpoint), o.pattern)) {
|
|
1443
|
+
baseDupeMsg(n.id, o.id);
|
|
1444
|
+
return true;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
const oHasLastPartParam = this.hasLastPartParam(o.endpoint);
|
|
1448
|
+
if (oHasLastPartParam) {
|
|
1449
|
+
if (isRegexMatch(n.endpoint, Route.makeRoutePattern(this.removeLastParam(o.endpoint)))) {
|
|
1450
|
+
shadowMsg(n.id, o.id);
|
|
1451
|
+
return true;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
extractParams(pathname, endpoint) {
|
|
1458
|
+
const data = {};
|
|
1459
|
+
if (!this.hasAnyParam(endpoint))
|
|
1460
|
+
return data;
|
|
1461
|
+
const defParts = endpoint.split("/");
|
|
1462
|
+
const reqParts = pathname.split("/");
|
|
1463
|
+
for (const [i, defPart] of defParts.entries()) {
|
|
1464
|
+
const reqPart = reqParts[i];
|
|
1465
|
+
if (defPart.startsWith(":") && reqPart !== undefined) {
|
|
1466
|
+
data[defPart.slice(1)] = decodeURIComponent(reqPart);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return data;
|
|
1470
|
+
}
|
|
1471
|
+
hasLastPartParam(endpoint) {
|
|
1472
|
+
if (!this.hasAnyParam(endpoint))
|
|
1473
|
+
return false;
|
|
1474
|
+
const parts = endpoint.split("/");
|
|
1475
|
+
return parts[parts.length - 1]?.startsWith(":") ?? false;
|
|
1476
|
+
}
|
|
1477
|
+
removeLastParam(endpoint) {
|
|
1478
|
+
return endpoint.split("/").slice(0, -1).join("/");
|
|
1479
|
+
}
|
|
1480
|
+
hasAnyParam(endpoint) {
|
|
1481
|
+
if (endpoint.includes("/:"))
|
|
1482
|
+
return true;
|
|
1483
|
+
if (!endpoint.includes(":"))
|
|
1484
|
+
return false;
|
|
1485
|
+
return endpoint.split("/").some((p) => p.startsWith(":"));
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// src/Router/Router.ts
|
|
1490
|
+
class Router {
|
|
1491
|
+
constructor(adapter) {
|
|
1492
|
+
this._adapter = adapter ?? new CorpusAdapter;
|
|
1493
|
+
}
|
|
1494
|
+
models = [];
|
|
1495
|
+
_adapter;
|
|
1496
|
+
cache = new WeakMap;
|
|
1497
|
+
checkPossibleCollision(n) {
|
|
1498
|
+
if (this._adapter instanceof CorpusAdapter) {
|
|
1499
|
+
return this._adapter.checkPossibleCollision(n);
|
|
1500
|
+
}
|
|
1501
|
+
return false;
|
|
1502
|
+
}
|
|
1503
|
+
addModel(route, model) {
|
|
1504
|
+
this.models.push(model);
|
|
1505
|
+
this._adapter.addModel(route, model);
|
|
1506
|
+
}
|
|
1507
|
+
addMiddleware(middleware) {
|
|
1508
|
+
this._adapter.addMiddleware(middleware);
|
|
1509
|
+
}
|
|
1510
|
+
addRoute(r) {
|
|
1511
|
+
this._adapter.addRoute({
|
|
1512
|
+
id: r.id,
|
|
1513
|
+
endpoint: r.endpoint,
|
|
1514
|
+
method: r.method,
|
|
1515
|
+
handler: r.handler,
|
|
1516
|
+
pattern: r.pattern
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
findRouteHandler(req) {
|
|
1520
|
+
const cached = this.cache.get(req);
|
|
1521
|
+
if (cached)
|
|
1522
|
+
return cached;
|
|
1523
|
+
const match = this._adapter.find(req);
|
|
1524
|
+
if (!match)
|
|
1525
|
+
throw CError.notFound();
|
|
1526
|
+
const ctx = Context.makeFromRequest(req);
|
|
1527
|
+
const handler = async () => {
|
|
1528
|
+
await match.middleware?.(ctx);
|
|
1529
|
+
await Context.appendParsedData(ctx, req, match.params, match.search, match.model);
|
|
1530
|
+
const res = await match.route.handler(ctx);
|
|
1531
|
+
return res instanceof CResponse ? res : new CResponse(res, {
|
|
1532
|
+
cookies: ctx.res.cookies,
|
|
1533
|
+
headers: ctx.res.headers,
|
|
1534
|
+
status: ctx.res.status,
|
|
1535
|
+
statusText: ctx.res.statusText
|
|
1536
|
+
});
|
|
1537
|
+
};
|
|
1538
|
+
this.cache.set(req, handler);
|
|
1539
|
+
return handler;
|
|
1540
|
+
}
|
|
1541
|
+
getRouteList() {
|
|
1542
|
+
return this._adapter.list().map((v) => [v.method, v.endpoint]);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
// src/Server/ServerAbstract.ts
|
|
1547
|
+
class ServerAbstract {
|
|
1548
|
+
opts;
|
|
1549
|
+
constructor(opts) {
|
|
1550
|
+
this.opts = opts;
|
|
1551
|
+
_routerStore.set(new Router(opts?.adapter));
|
|
1552
|
+
}
|
|
1553
|
+
get routes() {
|
|
1554
|
+
return _routerStore.get().getRouteList();
|
|
1555
|
+
}
|
|
1556
|
+
setGlobalPrefix(value) {
|
|
1557
|
+
_prefixStore.set(value);
|
|
1558
|
+
}
|
|
1559
|
+
async listen(port, hostname = "0.0.0.0") {
|
|
1560
|
+
try {
|
|
1561
|
+
process.on("SIGINT", () => this.close());
|
|
1562
|
+
process.on("SIGTERM", () => this.close());
|
|
1563
|
+
console.log(`Listening on ${hostname}:${port}`);
|
|
1564
|
+
await this.handleBeforeListen?.();
|
|
1565
|
+
this.serve({
|
|
1566
|
+
port,
|
|
1567
|
+
hostname,
|
|
1568
|
+
fetch: (r) => this.handle(r)
|
|
1569
|
+
});
|
|
1570
|
+
} catch (err) {
|
|
1571
|
+
console.error("Server unable to start:", err);
|
|
1572
|
+
await this.close();
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
async handle(request) {
|
|
1576
|
+
const req = new CRequest(request);
|
|
1577
|
+
let res = await this.getResponse(req);
|
|
1578
|
+
const cors = _corsStore.get();
|
|
1579
|
+
if (cors !== null) {
|
|
1580
|
+
cors.apply(req, res);
|
|
1581
|
+
}
|
|
1582
|
+
if (this.handleAfterResponse) {
|
|
1583
|
+
res = await this.handleAfterResponse(res);
|
|
1584
|
+
}
|
|
1585
|
+
return res.response;
|
|
1586
|
+
}
|
|
1587
|
+
async getResponse(req) {
|
|
1588
|
+
try {
|
|
1589
|
+
if (req.isPreflight) {
|
|
1590
|
+
return new CResponse("Departed");
|
|
1591
|
+
}
|
|
1592
|
+
const handler = _routerStore.get().findRouteHandler(req);
|
|
1593
|
+
return await handler();
|
|
1594
|
+
} catch (err) {
|
|
1595
|
+
if (err instanceof CError) {
|
|
1596
|
+
if (err.isStatusOf(Status.NOT_FOUND)) {
|
|
1597
|
+
return await this.handleNotFound(req);
|
|
1598
|
+
}
|
|
1599
|
+
if (err.isStatusOf(Status.METHOD_NOT_ALLOWED)) {
|
|
1600
|
+
return await this.handleMethodNotAllowed(req);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return await this.handleError(err);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
handleBeforeListen;
|
|
1607
|
+
setOnBeforeListen(handler) {
|
|
1608
|
+
this.handleBeforeListen = handler;
|
|
1609
|
+
}
|
|
1610
|
+
defaultOnBeforeListen = undefined;
|
|
1611
|
+
handleBeforeClose;
|
|
1612
|
+
setOnBeforeClose(handler) {
|
|
1613
|
+
this.handleBeforeClose = handler;
|
|
1614
|
+
}
|
|
1615
|
+
defaultOnBeforeClose = undefined;
|
|
1616
|
+
handleAfterResponse;
|
|
1617
|
+
setOnAfterResponse(handler) {
|
|
1618
|
+
this.handleAfterResponse = handler;
|
|
1619
|
+
}
|
|
1620
|
+
defaultOnAfterResponse = undefined;
|
|
1621
|
+
handleError = (err) => this.defaultErrorHandler(err);
|
|
1622
|
+
setOnError(handler) {
|
|
1623
|
+
this.handleError = handler;
|
|
1624
|
+
}
|
|
1625
|
+
defaultErrorHandler = (err) => {
|
|
1626
|
+
if (!(err instanceof Error)) {
|
|
1627
|
+
return new CResponse({ error: err, message: "Unknown" }, { status: Status.INTERNAL_SERVER_ERROR });
|
|
1628
|
+
}
|
|
1629
|
+
if (err instanceof CError) {
|
|
1630
|
+
return err.toResponse();
|
|
1631
|
+
}
|
|
1632
|
+
return new CResponse({ error: err, message: err.message }, { status: Status.INTERNAL_SERVER_ERROR });
|
|
1633
|
+
};
|
|
1634
|
+
handleNotFound = (req) => this.defaultNotFoundHandler(req);
|
|
1635
|
+
setOnNotFound(handler) {
|
|
1636
|
+
this.handleNotFound = handler;
|
|
1637
|
+
}
|
|
1638
|
+
defaultNotFoundHandler = (req) => {
|
|
1639
|
+
return new CResponse({ error: true, message: `${req.method} on ${req.url} does not exist.` }, { status: Status.NOT_FOUND });
|
|
1640
|
+
};
|
|
1641
|
+
handleMethodNotAllowed = (req) => this.defaultMethodNotFoundHandler(req);
|
|
1642
|
+
defaultMethodNotFoundHandler = (req) => {
|
|
1643
|
+
return new CResponse({ error: `${req.method} ${req.url} does not exist.` }, { status: Status.METHOD_NOT_ALLOWED });
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// src/Server/ServerUsingBun.ts
|
|
1648
|
+
class ServerUsingBun extends ServerAbstract {
|
|
1649
|
+
app;
|
|
1650
|
+
serve(args) {
|
|
1651
|
+
this.app = this.createApp(args);
|
|
1652
|
+
}
|
|
1653
|
+
async close() {
|
|
1654
|
+
await this.handleBeforeClose?.();
|
|
1655
|
+
console.log("Closing...");
|
|
1656
|
+
await this.app?.stop(true);
|
|
1657
|
+
if (Config.nodeEnv !== "test") {
|
|
1658
|
+
process.exit(0);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
createApp(options) {
|
|
1662
|
+
return Bun.serve({
|
|
1663
|
+
port: options.port,
|
|
1664
|
+
hostname: options.hostname,
|
|
1665
|
+
fetch: options.fetch,
|
|
1666
|
+
idleTimeout: this.opts?.idleTimeout,
|
|
1667
|
+
tls: this.opts?.tls
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
// src/Server/Server.ts
|
|
1673
|
+
class Server extends ServerUsingBun {
|
|
1674
|
+
}
|
|
1675
|
+
// src/X.ts
|
|
1676
|
+
var exports_X = {};
|
|
1677
|
+
__export(exports_X, {
|
|
1678
|
+
Repository: () => RepositoryAbstract,
|
|
1679
|
+
Parser: () => Parser,
|
|
1680
|
+
MemoiristAdapter: () => MemoiristAdapter,
|
|
1681
|
+
File: () => XFile,
|
|
1682
|
+
Cors: () => Cors
|
|
1683
|
+
});
|
|
1684
|
+
// src/utils/boolToString.ts
|
|
1685
|
+
function boolToString(arg) {
|
|
1686
|
+
return arg ? "true" : "false";
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// src/utils/isSomeArray.ts
|
|
1690
|
+
function isSomeArray(arg) {
|
|
1691
|
+
return arg !== undefined && Array.isArray(arg) && arg.length > 0 && arg.every((a) => a !== null && a !== undefined);
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// src/Cors/Cors.ts
|
|
1695
|
+
class Cors {
|
|
1696
|
+
opts;
|
|
1697
|
+
constructor(opts) {
|
|
1698
|
+
this.opts = opts;
|
|
1699
|
+
if (opts === undefined) {
|
|
1700
|
+
_corsStore.set(null);
|
|
1701
|
+
} else {
|
|
1702
|
+
_corsStore.set(this);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
originKey = "Access-Control-Allow-Origin";
|
|
1706
|
+
methodsKey = "Access-Control-Allow-Methods";
|
|
1707
|
+
headersKey = "Access-Control-Allow-Headers";
|
|
1708
|
+
credentialsKey = "Access-Control-Allow-Credentials";
|
|
1709
|
+
getCorsHeaders(req, res) {
|
|
1710
|
+
const reqOrigin = req.headers.get("origin") ?? "";
|
|
1711
|
+
const { allowedOrigins, allowedMethods, allowedHeaders, credentials } = this.opts ?? {};
|
|
1712
|
+
if (isSomeArray(allowedOrigins) && allowedOrigins.includes(reqOrigin)) {
|
|
1713
|
+
res.headers.set(this.originKey, reqOrigin);
|
|
1714
|
+
}
|
|
1715
|
+
if (isSomeArray(allowedMethods)) {
|
|
1716
|
+
res.headers.set(this.methodsKey, allowedMethods.join(", "));
|
|
1717
|
+
}
|
|
1718
|
+
if (isSomeArray(allowedHeaders)) {
|
|
1719
|
+
res.headers.set(this.headersKey, allowedHeaders.join(", "));
|
|
1720
|
+
}
|
|
1721
|
+
res.headers.set(this.credentialsKey, boolToString(credentials));
|
|
1722
|
+
return res.headers;
|
|
1723
|
+
}
|
|
1724
|
+
apply(req, res) {
|
|
1725
|
+
const headers = this.getCorsHeaders(req, res);
|
|
1726
|
+
res.headers.innerCombine(headers);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
// src/Repository/RepositoryAbstract.ts
|
|
1730
|
+
class RepositoryAbstract {
|
|
1731
|
+
db;
|
|
1732
|
+
constructor(db) {
|
|
1733
|
+
this.db = db;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
// node_modules/memoirist/dist/bun/index.js
|
|
1737
|
+
var Y = (v, b) => {
|
|
1738
|
+
let A = b?.length ? {} : null;
|
|
1739
|
+
if (A)
|
|
1740
|
+
for (let Q of b)
|
|
1741
|
+
A[Q.part.charCodeAt(0)] = Q;
|
|
1742
|
+
return { part: v, store: null, inert: A, params: null, wildcardStore: null };
|
|
1743
|
+
};
|
|
1744
|
+
var k = (v, b) => ({ ...v, part: b });
|
|
1745
|
+
var T = (v) => ({ name: v, store: null, inert: null });
|
|
1746
|
+
|
|
1747
|
+
class _ {
|
|
1748
|
+
config;
|
|
1749
|
+
root = {};
|
|
1750
|
+
history = [];
|
|
1751
|
+
deferred = [];
|
|
1752
|
+
constructor(v = {}) {
|
|
1753
|
+
this.config = v;
|
|
1754
|
+
if (v.lazy)
|
|
1755
|
+
this.find = this.lazyFind;
|
|
1756
|
+
if (v.onParam && !Array.isArray(v.onParam))
|
|
1757
|
+
this.config.onParam = [this.config.onParam];
|
|
1758
|
+
}
|
|
1759
|
+
static regex = { static: /:.+?(?=\/|$)/, params: /:.+?(?=\/|$)/g, optionalParams: /(\/:\w+\?)/g };
|
|
1760
|
+
lazyFind = (v, b) => {
|
|
1761
|
+
if (!this.config.lazy)
|
|
1762
|
+
return this.find;
|
|
1763
|
+
return this.build(), this.find(v, b);
|
|
1764
|
+
};
|
|
1765
|
+
build() {
|
|
1766
|
+
if (!this.config.lazy)
|
|
1767
|
+
return;
|
|
1768
|
+
for (let [v, b, A] of this.deferred)
|
|
1769
|
+
this.add(v, b, A, { lazy: false, ignoreHistory: true });
|
|
1770
|
+
this.deferred = [], this.find = (v, b) => {
|
|
1771
|
+
let A = this.root[v];
|
|
1772
|
+
if (!A)
|
|
1773
|
+
return null;
|
|
1774
|
+
return $(b, b.length, A, 0, this.config.onParam);
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
add(v, b, A, { ignoreError: Q = false, ignoreHistory: O = false, lazy: V = this.config.lazy } = {}) {
|
|
1778
|
+
if (V)
|
|
1779
|
+
return this.find = this.lazyFind, this.deferred.push([v, b, A]), A;
|
|
1780
|
+
if (typeof b !== "string")
|
|
1781
|
+
throw new TypeError("Route path must be a string");
|
|
1782
|
+
if (b === "")
|
|
1783
|
+
b = "/";
|
|
1784
|
+
else if (b[0] !== "/")
|
|
1785
|
+
b = `/${b}`;
|
|
1786
|
+
let X = b[b.length - 1] === "*", J = b.match(_.regex.optionalParams);
|
|
1787
|
+
if (J) {
|
|
1788
|
+
let F = b.replaceAll("?", "");
|
|
1789
|
+
this.add(v, F, A, { ignoreError: Q, ignoreHistory: O, lazy: V });
|
|
1790
|
+
for (let B = 0;B < J.length; B++) {
|
|
1791
|
+
let D = b.replace(J[B], "");
|
|
1792
|
+
this.add(v, D, A, { ignoreError: true, ignoreHistory: O, lazy: V });
|
|
1793
|
+
}
|
|
1794
|
+
return A;
|
|
1795
|
+
}
|
|
1796
|
+
if (J)
|
|
1797
|
+
b = b.replaceAll("?", "");
|
|
1798
|
+
if (this.history.find(([F, B, D]) => F === v && B === b))
|
|
1799
|
+
return A;
|
|
1800
|
+
if (X || J && b.charCodeAt(b.length - 1) === 63)
|
|
1801
|
+
b = b.slice(0, -1);
|
|
1802
|
+
if (!O)
|
|
1803
|
+
this.history.push([v, b, A]);
|
|
1804
|
+
let K = b.split(_.regex.static), G = b.match(_.regex.params) || [];
|
|
1805
|
+
if (K[K.length - 1] === "")
|
|
1806
|
+
K.pop();
|
|
1807
|
+
let q;
|
|
1808
|
+
if (!this.root[v])
|
|
1809
|
+
q = this.root[v] = Y("/");
|
|
1810
|
+
else
|
|
1811
|
+
q = this.root[v];
|
|
1812
|
+
let U = 0;
|
|
1813
|
+
for (let F = 0;F < K.length; ++F) {
|
|
1814
|
+
let B = K[F];
|
|
1815
|
+
if (F > 0) {
|
|
1816
|
+
let D = G[U++].slice(1);
|
|
1817
|
+
if (q.params === null)
|
|
1818
|
+
q.params = T(D);
|
|
1819
|
+
else if (q.params.name !== D)
|
|
1820
|
+
if (Q)
|
|
1821
|
+
return A;
|
|
1822
|
+
else
|
|
1823
|
+
throw new Error(`Cannot create route "${b}" with parameter "${D}" because a route already exists with a different parameter name ("${q.params.name}") in the same location`);
|
|
1824
|
+
let S = q.params;
|
|
1825
|
+
if (S.inert === null) {
|
|
1826
|
+
q = S.inert = Y(B);
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
q = S.inert;
|
|
1830
|
+
}
|
|
1831
|
+
for (let D = 0;; ) {
|
|
1832
|
+
if (D === B.length) {
|
|
1833
|
+
if (D < q.part.length) {
|
|
1834
|
+
let S = k(q, q.part.slice(D));
|
|
1835
|
+
Object.assign(q, Y(B, [S]));
|
|
1836
|
+
}
|
|
1837
|
+
break;
|
|
1838
|
+
}
|
|
1839
|
+
if (D === q.part.length) {
|
|
1840
|
+
if (q.inert === null)
|
|
1841
|
+
q.inert = {};
|
|
1842
|
+
let S = q.inert[B.charCodeAt(D)];
|
|
1843
|
+
if (S) {
|
|
1844
|
+
q = S, B = B.slice(D), D = 0;
|
|
1845
|
+
continue;
|
|
1846
|
+
}
|
|
1847
|
+
let Z = Y(B.slice(D));
|
|
1848
|
+
q.inert[B.charCodeAt(D)] = Z, q = Z;
|
|
1849
|
+
break;
|
|
1850
|
+
}
|
|
1851
|
+
if (B[D] !== q.part[D]) {
|
|
1852
|
+
let S = k(q, q.part.slice(D)), Z = Y(B.slice(D));
|
|
1853
|
+
Object.assign(q, Y(q.part.slice(0, D), [S, Z])), q = Z;
|
|
1854
|
+
break;
|
|
1855
|
+
}
|
|
1856
|
+
++D;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
if (U < G.length) {
|
|
1860
|
+
let B = G[U].slice(1);
|
|
1861
|
+
if (q.params === null)
|
|
1862
|
+
q.params = T(B);
|
|
1863
|
+
else if (q.params.name !== B)
|
|
1864
|
+
if (Q)
|
|
1865
|
+
return A;
|
|
1866
|
+
else
|
|
1867
|
+
throw new Error(`Cannot create route "${b}" with parameter "${B}" because a route already exists with a different parameter name ("${q.params.name}") in the same location`);
|
|
1868
|
+
if (q.params.store === null)
|
|
1869
|
+
q.params.store = A;
|
|
1870
|
+
return q.params.store;
|
|
1871
|
+
}
|
|
1872
|
+
if (X) {
|
|
1873
|
+
if (q.wildcardStore === null)
|
|
1874
|
+
q.wildcardStore = A;
|
|
1875
|
+
return q.wildcardStore;
|
|
1876
|
+
}
|
|
1877
|
+
if (q.store === null)
|
|
1878
|
+
q.store = A;
|
|
1879
|
+
return q.store;
|
|
1880
|
+
}
|
|
1881
|
+
find(v, b) {
|
|
1882
|
+
let A = this.root[v];
|
|
1883
|
+
if (!A)
|
|
1884
|
+
return null;
|
|
1885
|
+
return $(b, b.length, A, 0, this.config.onParam);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
var $ = (v, b, A, Q, O) => {
|
|
1889
|
+
let V = A.part, X = V.length, J = Q + X;
|
|
1890
|
+
if (X > 1) {
|
|
1891
|
+
if (J > b)
|
|
1892
|
+
return null;
|
|
1893
|
+
if (X < 15) {
|
|
1894
|
+
for (let K = 1, G = Q + 1;K < X; ++K, ++G)
|
|
1895
|
+
if (V.charCodeAt(K) !== v.charCodeAt(G))
|
|
1896
|
+
return null;
|
|
1897
|
+
} else if (v.slice(Q, J) !== V)
|
|
1898
|
+
return null;
|
|
1899
|
+
}
|
|
1900
|
+
if (J === b) {
|
|
1901
|
+
if (A.store !== null)
|
|
1902
|
+
return { store: A.store, params: {} };
|
|
1903
|
+
if (A.wildcardStore !== null)
|
|
1904
|
+
return { store: A.wildcardStore, params: { "*": "" } };
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
if (A.inert !== null) {
|
|
1908
|
+
let K = A.inert[v.charCodeAt(J)];
|
|
1909
|
+
if (K !== undefined) {
|
|
1910
|
+
let G = $(v, b, K, J, O);
|
|
1911
|
+
if (G !== null)
|
|
1912
|
+
return G;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
if (A.params !== null) {
|
|
1916
|
+
let { store: K, name: G, inert: q } = A.params, U = v.indexOf("/", J);
|
|
1917
|
+
if (U !== J) {
|
|
1918
|
+
if (U === -1 || U >= b) {
|
|
1919
|
+
if (K !== null) {
|
|
1920
|
+
let F = {};
|
|
1921
|
+
if (F[G] = v.substring(J, b), O)
|
|
1922
|
+
for (let B = 0;B < O.length; B++) {
|
|
1923
|
+
let D = O[B](F[G], G);
|
|
1924
|
+
if (D !== undefined)
|
|
1925
|
+
F[G] = D;
|
|
1926
|
+
}
|
|
1927
|
+
return { store: K, params: F };
|
|
1928
|
+
}
|
|
1929
|
+
} else if (q !== null) {
|
|
1930
|
+
let F = $(v, b, q, U, O);
|
|
1931
|
+
if (F !== null) {
|
|
1932
|
+
if (F.params[G] = v.substring(J, U), O)
|
|
1933
|
+
for (let B = 0;B < O.length; B++) {
|
|
1934
|
+
let D = O[B](F.params[G], G);
|
|
1935
|
+
if (D !== undefined)
|
|
1936
|
+
F.params[G] = D;
|
|
1937
|
+
}
|
|
1938
|
+
return F;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (A.wildcardStore !== null)
|
|
1944
|
+
return { store: A.wildcardStore, params: { "*": v.substring(J, b) } };
|
|
1945
|
+
return null;
|
|
1946
|
+
};
|
|
1947
|
+
var w = _;
|
|
1948
|
+
|
|
1949
|
+
// src/Router/adapters/MemoiristAdapter.ts
|
|
1950
|
+
class MemoiristAdapter {
|
|
1951
|
+
router = new w;
|
|
1952
|
+
pendingMiddlewares = new Map;
|
|
1953
|
+
find(req) {
|
|
1954
|
+
const method = req.method;
|
|
1955
|
+
const pathname = req.urlObject.pathname;
|
|
1956
|
+
const searchParams = req.urlObject.searchParams;
|
|
1957
|
+
const result = this.router.find(method, pathname);
|
|
1958
|
+
if (!result)
|
|
1959
|
+
return null;
|
|
1960
|
+
return {
|
|
1961
|
+
route: result.store.route,
|
|
1962
|
+
model: result.store.model,
|
|
1963
|
+
params: result.params,
|
|
1964
|
+
search: Object.fromEntries(searchParams),
|
|
1965
|
+
middleware: compile(result.store.middlewares ?? [])
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
list() {
|
|
1969
|
+
return this.router.history.map((v) => v[2].route);
|
|
1970
|
+
}
|
|
1971
|
+
addRoute(data) {
|
|
1972
|
+
this.router.add(data.method, data.endpoint, { route: data });
|
|
1973
|
+
const pending = this.pendingMiddlewares.get(data.id);
|
|
1974
|
+
if (pending) {
|
|
1975
|
+
const store = this.router.find(data.method, data.endpoint)?.store;
|
|
1976
|
+
if (store)
|
|
1977
|
+
store.middlewares = pending;
|
|
1978
|
+
this.pendingMiddlewares.delete(data.id);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
addModel(route, model) {
|
|
1982
|
+
const result = this.router.find(route.method, route.endpoint);
|
|
1983
|
+
if (!result)
|
|
1984
|
+
return;
|
|
1985
|
+
result.store.model = ModelRegistry.toRouterModelData(model);
|
|
1986
|
+
}
|
|
1987
|
+
addMiddleware(middleware) {
|
|
1988
|
+
const resolved = MiddlewareRegistry.resolveRouteIds(middleware);
|
|
1989
|
+
if (resolved.isGlobal) {
|
|
1990
|
+
for (const [, , store] of this.router.history) {
|
|
1991
|
+
store.middlewares = [...store.middlewares ?? [], middleware.handler];
|
|
1992
|
+
}
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1995
|
+
for (const routeId of resolved.routeIds) {
|
|
1996
|
+
const [method, endpoint] = routeId.split(" ", 2);
|
|
1997
|
+
const result = this.router.find(method, endpoint);
|
|
1998
|
+
if (result) {
|
|
1999
|
+
result.store.middlewares = [
|
|
2000
|
+
...result.store.middlewares ?? [],
|
|
2001
|
+
middleware.handler
|
|
2002
|
+
];
|
|
2003
|
+
} else {
|
|
2004
|
+
const pending = this.pendingMiddlewares.get(routeId) ?? [];
|
|
2005
|
+
this.pendingMiddlewares.set(routeId, [...pending, middleware.handler]);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
// src/index.ts
|
|
2011
|
+
var _prefixStore = new GlobalPrefixStore;
|
|
2012
|
+
var _routerStore = new GlobalRouterStore;
|
|
2013
|
+
var _corsStore = new GlobalCorsStore;
|
|
2014
|
+
})
|