elysia 0.2.0 → 0.2.2
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/index.d.ts +2 -3
- package/dist/index.js +83 -67
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +11 -38
- package/package.json +5 -4
- package/dist/router.d.ts +0 -26
- package/dist/router.js +0 -201
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/// <reference types="bun-types" />
|
|
2
2
|
import type { Serve, Server } from 'bun';
|
|
3
|
-
import { Router } from './router';
|
|
4
3
|
import { SCHEMA, DEFS } from './utils';
|
|
5
4
|
import type { Context } from './context';
|
|
6
5
|
import type { Handler, BeforeRequestHandler, TypedRoute, ElysiaInstance, ElysiaConfig, HTTPMethod, InternalRoute, BodyParser, ErrorHandler, TypedSchema, LocalHook, LocalHandler, LifeCycle, LifeCycleEvent, LifeCycleStore, VoidLifeCycle, AfterRequestHandler, MergeIfNotNull, IsAny, OverwritableTypeRoute, MergeSchema, ListenCallback, NoReturnHandler, ElysiaRoute, MaybePromise, IsNever } from './types';
|
|
@@ -107,8 +106,8 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
|
|
|
107
106
|
schema: Instance['schema'];
|
|
108
107
|
}>;
|
|
109
108
|
}
|
|
110
|
-
export { Elysia
|
|
109
|
+
export { Elysia };
|
|
111
110
|
export { Type as t } from '@sinclair/typebox';
|
|
112
|
-
export { SCHEMA, DEFS,
|
|
111
|
+
export { SCHEMA, DEFS, createValidationError, getSchemaValidator, mergeDeep, mergeHook, mergeObjectArray, mapPathnameAndQueryRegEx, mapQuery } from './utils';
|
|
113
112
|
export type { Context, PreContext } from './context';
|
|
114
113
|
export type { Handler, RegisteredHook, BeforeRequestHandler, TypedRoute, OverwritableTypeRoute, ElysiaInstance, ElysiaConfig, HTTPMethod, ComposedHandler, InternalRoute, BodyParser, ErrorHandler, ErrorCode, TypedSchema, LocalHook, LocalHandler, LifeCycle, LifeCycleEvent, AfterRequestHandler, HookHandler, TypedSchemaToRoute, UnwrapSchema, LifeCycleStore, VoidLifeCycle, SchemaValidator, ElysiaRoute, ExtractPath, IsPathParameter, IsAny, IsNever, UnknownFallback, WithArray, ObjectValues, PickInOrder, MaybePromise, MergeIfNotNull } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Raikiri } from 'raikiri';
|
|
2
2
|
import { mapResponse, mapEarlyResponse } from './handler';
|
|
3
|
-
import { mapQuery, clone, mergeHook, mergeDeep, createValidationError, getSchemaValidator, SCHEMA, DEFS, getResponseSchemaValidator } from './utils';
|
|
3
|
+
import { mapQuery, clone, mergeHook, mergeDeep, createValidationError, getSchemaValidator, SCHEMA, DEFS, getResponseSchemaValidator, mapPathnameAndQueryRegEx } from './utils';
|
|
4
4
|
import { registerSchemaPath } from './schema';
|
|
5
5
|
import { mapErrorCode, mapErrorStatus } from './error';
|
|
6
|
+
const ASYNC_FN = 'AsyncFunction';
|
|
6
7
|
export default class Elysia {
|
|
7
8
|
constructor(config = {}) {
|
|
8
9
|
this.store = {
|
|
@@ -22,7 +23,7 @@ export default class Elysia {
|
|
|
22
23
|
};
|
|
23
24
|
this.server = null;
|
|
24
25
|
this.$schema = null;
|
|
25
|
-
this.router = new
|
|
26
|
+
this.router = new Raikiri();
|
|
26
27
|
this.fallbackRoute = {};
|
|
27
28
|
this.routes = [];
|
|
28
29
|
this.lazyLoadModules = [];
|
|
@@ -74,17 +75,17 @@ export default class Elysia {
|
|
|
74
75
|
hooks: mergeHook(clone(this.event), hook),
|
|
75
76
|
validator
|
|
76
77
|
};
|
|
77
|
-
|
|
78
|
+
const mainHandler = {
|
|
78
79
|
handle: handler,
|
|
79
80
|
hooks: mergeHook(clone(this.event), hook),
|
|
80
81
|
validator
|
|
81
82
|
};
|
|
83
|
+
this.router.add(method, path, mainHandler);
|
|
82
84
|
if (!this.config.strictPath && path !== '/')
|
|
83
85
|
if (path.endsWith('/'))
|
|
84
|
-
this.router.
|
|
86
|
+
this.router.add(method, path.substring(0, path.length - 1), mainHandler);
|
|
85
87
|
else
|
|
86
|
-
this.router.
|
|
87
|
-
this.router.register(path)[method];
|
|
88
|
+
this.router.add(method, `${path}/`, mainHandler);
|
|
88
89
|
}
|
|
89
90
|
onStart(handler) {
|
|
90
91
|
this.event.start.push(handler);
|
|
@@ -280,34 +281,38 @@ export default class Elysia {
|
|
|
280
281
|
context.request = request;
|
|
281
282
|
context.set = set;
|
|
282
283
|
context.store = this.store;
|
|
284
|
+
context.query = {};
|
|
283
285
|
}
|
|
284
286
|
else {
|
|
285
287
|
context = {
|
|
286
288
|
set,
|
|
287
289
|
store: this.store,
|
|
288
|
-
request
|
|
290
|
+
request,
|
|
291
|
+
query: {}
|
|
289
292
|
};
|
|
290
293
|
}
|
|
291
294
|
try {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
let
|
|
295
|
-
|
|
296
|
-
response =
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
295
|
+
const event = this.event;
|
|
296
|
+
if (event.request.length)
|
|
297
|
+
for (let i = 0; i < event.request.length; i++) {
|
|
298
|
+
const onRequest = event.request[i];
|
|
299
|
+
let response = onRequest(context);
|
|
300
|
+
if (onRequest.constructor.name === ASYNC_FN)
|
|
301
|
+
response = await response;
|
|
302
|
+
response = mapEarlyResponse(response, set);
|
|
303
|
+
if (response)
|
|
304
|
+
return response;
|
|
305
|
+
}
|
|
306
|
+
const fracture = request.url.match(mapPathnameAndQueryRegEx);
|
|
307
|
+
if (!fracture)
|
|
308
|
+
throw new Error('NOT_FOUND');
|
|
309
|
+
const route = this.router.match(request.method, fracture[1]) ||
|
|
310
|
+
this.router.match('ALL', fracture[1]);
|
|
303
311
|
if (!route)
|
|
304
312
|
throw new Error('NOT_FOUND');
|
|
305
|
-
const handler = route.store[request.method]
|
|
306
|
-
route.store.ALL ||
|
|
307
|
-
this.fallbackRoute[request.method];
|
|
313
|
+
const handler = route.store || this.fallbackRoute[request.method];
|
|
308
314
|
if (!handler)
|
|
309
315
|
throw new Error('NOT_FOUND');
|
|
310
|
-
const hooks = handler.hooks;
|
|
311
316
|
let body;
|
|
312
317
|
if (request.method !== 'GET') {
|
|
313
318
|
let contentType = request.headers.get('content-type');
|
|
@@ -315,15 +320,17 @@ export default class Elysia {
|
|
|
315
320
|
const index = contentType.indexOf(';');
|
|
316
321
|
if (index !== -1)
|
|
317
322
|
contentType = contentType.slice(0, index);
|
|
318
|
-
|
|
319
|
-
let
|
|
320
|
-
|
|
321
|
-
temp =
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
if (event.parse.length)
|
|
324
|
+
for (let i = 0; i < event.parse.length; i++) {
|
|
325
|
+
const fn = event.parse[i];
|
|
326
|
+
let temp = fn(context, contentType);
|
|
327
|
+
if (fn.constructor.name === ASYNC_FN)
|
|
328
|
+
temp = await temp;
|
|
329
|
+
if (temp) {
|
|
330
|
+
body = temp;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
325
333
|
}
|
|
326
|
-
}
|
|
327
334
|
if (body === undefined) {
|
|
328
335
|
switch (contentType) {
|
|
329
336
|
case 'application/json':
|
|
@@ -333,7 +340,7 @@ export default class Elysia {
|
|
|
333
340
|
body = await request.text();
|
|
334
341
|
break;
|
|
335
342
|
case 'application/x-www-form-urlencoded':
|
|
336
|
-
body = mapQuery(await request.text()
|
|
343
|
+
body = mapQuery(await request.text());
|
|
337
344
|
break;
|
|
338
345
|
}
|
|
339
346
|
}
|
|
@@ -341,12 +348,16 @@ export default class Elysia {
|
|
|
341
348
|
}
|
|
342
349
|
context.body = body;
|
|
343
350
|
context.params = route?.params || {};
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
351
|
+
if (fracture[2])
|
|
352
|
+
context.query = mapQuery(fracture[2]);
|
|
353
|
+
const hooks = handler.hooks;
|
|
354
|
+
if (hooks.transform.length)
|
|
355
|
+
for (let i = 0; i < hooks.transform.length; i++) {
|
|
356
|
+
const fn = hooks.transform[i];
|
|
357
|
+
const operation = fn(context);
|
|
358
|
+
if (fn.constructor.name === ASYNC_FN)
|
|
359
|
+
await operation;
|
|
360
|
+
}
|
|
350
361
|
if (handler.validator) {
|
|
351
362
|
const validator = handler.validator;
|
|
352
363
|
if (validator.headers) {
|
|
@@ -363,36 +374,41 @@ export default class Elysia {
|
|
|
363
374
|
if (validator.body?.Check(body) === false)
|
|
364
375
|
throw createValidationError('body', validator.body, body);
|
|
365
376
|
}
|
|
366
|
-
|
|
367
|
-
let
|
|
368
|
-
|
|
369
|
-
response =
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
+
if (hooks.beforeHandle.length)
|
|
378
|
+
for (let i = 0; i < hooks.beforeHandle.length; i++) {
|
|
379
|
+
const fn = hooks.beforeHandle[i];
|
|
380
|
+
let response = fn(context);
|
|
381
|
+
if (fn.constructor.name === ASYNC_FN)
|
|
382
|
+
response = await response;
|
|
383
|
+
if (response !== null && response !== undefined) {
|
|
384
|
+
for (let i = 0; i < hooks.afterHandle.length; i++) {
|
|
385
|
+
const fn = hooks.afterHandle[i];
|
|
386
|
+
let newResponse = fn(context, response);
|
|
387
|
+
if (fn.constructor.name === ASYNC_FN)
|
|
388
|
+
newResponse = await newResponse;
|
|
389
|
+
if (newResponse)
|
|
390
|
+
response = newResponse;
|
|
391
|
+
}
|
|
392
|
+
const result = mapEarlyResponse(response, context.set);
|
|
393
|
+
if (result)
|
|
394
|
+
return result;
|
|
377
395
|
}
|
|
378
|
-
const result = mapEarlyResponse(response, context.set);
|
|
379
|
-
if (result)
|
|
380
|
-
return result;
|
|
381
396
|
}
|
|
382
|
-
}
|
|
383
397
|
let response = handler.handle(context);
|
|
384
398
|
if (response instanceof Promise)
|
|
385
399
|
response = await response;
|
|
386
400
|
if (handler.validator?.response?.Check(response) === false)
|
|
387
401
|
throw createValidationError('response', handler.validator.response, response);
|
|
388
|
-
|
|
389
|
-
let
|
|
390
|
-
|
|
391
|
-
newResponse =
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
402
|
+
if (hooks.afterHandle.length)
|
|
403
|
+
for (let i = 0; i < hooks.afterHandle.length; i++) {
|
|
404
|
+
const afterHandle = hooks.afterHandle[i];
|
|
405
|
+
let newResponse = afterHandle(context, response);
|
|
406
|
+
if (afterHandle.constructor.name === ASYNC_FN)
|
|
407
|
+
newResponse = await newResponse;
|
|
408
|
+
const result = mapEarlyResponse(newResponse, context.set);
|
|
409
|
+
if (result)
|
|
410
|
+
return result;
|
|
411
|
+
}
|
|
396
412
|
return mapResponse(response, context.set);
|
|
397
413
|
}
|
|
398
414
|
catch (error) {
|
|
@@ -400,8 +416,7 @@ export default class Elysia {
|
|
|
400
416
|
}
|
|
401
417
|
}
|
|
402
418
|
async handleError(error, set = {
|
|
403
|
-
headers: {}
|
|
404
|
-
status: undefined
|
|
419
|
+
headers: {}
|
|
405
420
|
}) {
|
|
406
421
|
for (let i = 0; i < this.event.error.length; i++) {
|
|
407
422
|
let response = this.event.error[i]({
|
|
@@ -422,12 +437,12 @@ export default class Elysia {
|
|
|
422
437
|
listen(options, callback) {
|
|
423
438
|
if (!Bun)
|
|
424
439
|
throw new Error('Bun to run');
|
|
425
|
-
const fetch = this.handle.bind(this);
|
|
426
440
|
if (typeof options === 'string') {
|
|
427
441
|
options = +options;
|
|
428
442
|
if (Number.isNaN(options))
|
|
429
443
|
throw new Error('Port must be a numeric value');
|
|
430
444
|
}
|
|
445
|
+
const fetch = this.handle.bind(this);
|
|
431
446
|
const serve = typeof options === 'object'
|
|
432
447
|
? {
|
|
433
448
|
...this.config.serve,
|
|
@@ -452,7 +467,8 @@ export default class Elysia {
|
|
|
452
467
|
if (callback)
|
|
453
468
|
callback(this.server);
|
|
454
469
|
Promise.all(this.lazyLoadModules).then(() => {
|
|
455
|
-
|
|
470
|
+
if (!this.server.pendingRequests)
|
|
471
|
+
Bun.gc(true);
|
|
456
472
|
});
|
|
457
473
|
return this;
|
|
458
474
|
}
|
|
@@ -467,6 +483,6 @@ export default class Elysia {
|
|
|
467
483
|
return this;
|
|
468
484
|
}
|
|
469
485
|
}
|
|
470
|
-
export { Elysia
|
|
486
|
+
export { Elysia };
|
|
471
487
|
export { Type as t } from '@sinclair/typebox';
|
|
472
|
-
export { SCHEMA, DEFS,
|
|
488
|
+
export { SCHEMA, DEFS, createValidationError, getSchemaValidator, mergeDeep, mergeHook, mergeObjectArray, mapPathnameAndQueryRegEx, mapQuery } from './utils';
|
package/dist/utils.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export declare const DEFS: unique symbol;
|
|
|
6
6
|
export declare const mergeObjectArray: <T>(a: T | T[], b: T | T[]) => T[];
|
|
7
7
|
export declare const mergeHook: (a: LocalHook<any> | LifeCycleStore<any>, b: LocalHook<any>) => RegisteredHook<any>;
|
|
8
8
|
export declare const clone: <T extends Object | any[] = Object | any[]>(value: T) => T;
|
|
9
|
-
export declare const
|
|
10
|
-
export declare const mapQuery: (url: string
|
|
9
|
+
export declare const mapPathnameAndQueryRegEx: RegExp;
|
|
10
|
+
export declare const mapQuery: (url: string) => Record<string, string>;
|
|
11
11
|
export declare const mergeDeep: <A extends Object = Object, B extends Object = Object>(target: A, source: B) => DeepMergeTwoTypes<A, B>;
|
|
12
12
|
export declare const createValidationError: (type: string, validator: TypeCheck<any>, value: any) => Error;
|
|
13
13
|
export declare const getSchemaValidator: (s: TSchema | string | undefined, models: Record<string, TSchema>, additionalProperties?: boolean) => TypeCheck<TSchema> | undefined;
|
package/dist/utils.js
CHANGED
|
@@ -26,46 +26,19 @@ export const mergeHook = (a, b) => {
|
|
|
26
26
|
};
|
|
27
27
|
};
|
|
28
28
|
export const clone = (value) => [value][0];
|
|
29
|
-
export const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return url.substring(url.indexOf('/', 9), queryIndex);
|
|
38
|
-
};
|
|
39
|
-
export const mapQuery = (url, queryIndex = url.indexOf('?')) => {
|
|
40
|
-
if (queryIndex === -1)
|
|
41
|
-
return {};
|
|
42
|
-
const query = {};
|
|
43
|
-
if (queryIndex)
|
|
44
|
-
url = url.slice(queryIndex);
|
|
45
|
-
else
|
|
46
|
-
url = ';' + url;
|
|
47
|
-
while (true) {
|
|
48
|
-
const sep = url.indexOf('&', 4);
|
|
49
|
-
if (sep === -1) {
|
|
50
|
-
const equal = url.indexOf('=');
|
|
51
|
-
let value = url.slice(equal + 1);
|
|
52
|
-
const hashIndex = value.indexOf('#');
|
|
53
|
-
if (hashIndex !== -1)
|
|
54
|
-
value = value.slice(0, hashIndex);
|
|
55
|
-
if (value.includes('%'))
|
|
56
|
-
value = decodeURI(value);
|
|
57
|
-
query[url.slice(1, equal)] = value;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
const path = url.slice(0, sep);
|
|
61
|
-
const equal = path.indexOf('=');
|
|
62
|
-
let value = path.slice(equal + 1);
|
|
29
|
+
export const mapPathnameAndQueryRegEx = /:\/\/[^/]+([^#?]+)(?:\?([^#]+))?/;
|
|
30
|
+
export const mapQuery = (url) => {
|
|
31
|
+
const mapped = {};
|
|
32
|
+
const paths = url.split('&');
|
|
33
|
+
for (let i = 0; i < paths.length; i++) {
|
|
34
|
+
const part = paths[i];
|
|
35
|
+
const index = part.indexOf('=');
|
|
36
|
+
let value = part.slice(index + 1);
|
|
63
37
|
if (value.includes('%'))
|
|
64
|
-
value =
|
|
65
|
-
|
|
66
|
-
url = url.slice(sep);
|
|
38
|
+
value = decodeURIComponent(value);
|
|
39
|
+
mapped[part.slice(0, index)] = value;
|
|
67
40
|
}
|
|
68
|
-
return
|
|
41
|
+
return mapped;
|
|
69
42
|
};
|
|
70
43
|
const isObject = (item) => item && typeof item === 'object' && !Array.isArray(item);
|
|
71
44
|
export const mergeDeep = (target, source) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elysia",
|
|
3
3
|
"description": "Fast, and friendly Bun web framework",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "saltyAom",
|
|
7
7
|
"url": "https://github.com/SaltyAom",
|
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
"release": "npm run build && npm run test && npm publish"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@sinclair/typebox": "0.25.21",
|
|
39
|
-
"openapi-types": "^12.1.0"
|
|
38
|
+
"@sinclair/typebox": "^0.25.21",
|
|
39
|
+
"openapi-types": "^12.1.0",
|
|
40
|
+
"raikiri": "0.0.0-beta.3"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/node": "^18.11.18",
|
|
@@ -47,4 +48,4 @@
|
|
|
47
48
|
"rimraf": "^3.0.2",
|
|
48
49
|
"typescript": "^4.9.4"
|
|
49
50
|
}
|
|
50
|
-
}
|
|
51
|
+
}
|
package/dist/router.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ComposedHandler } from '../src';
|
|
2
|
-
export interface FindResult {
|
|
3
|
-
store: Partial<{
|
|
4
|
-
[k in string]: ComposedHandler;
|
|
5
|
-
}> & Partial<{
|
|
6
|
-
wildcardStore?: Map<number, any> | null;
|
|
7
|
-
}>;
|
|
8
|
-
params: Record<string, any>;
|
|
9
|
-
}
|
|
10
|
-
export interface ParametricNode {
|
|
11
|
-
paramName: string;
|
|
12
|
-
store: Record<string, any> | null;
|
|
13
|
-
staticChild: Node | null;
|
|
14
|
-
}
|
|
15
|
-
export interface Node {
|
|
16
|
-
pathPart: string;
|
|
17
|
-
store: Record<string, any> | null;
|
|
18
|
-
staticChildren: Map<any, any> | null;
|
|
19
|
-
parametricChild: ParametricNode | null;
|
|
20
|
-
wildcardStore: Map<number, any> | null;
|
|
21
|
-
}
|
|
22
|
-
export declare class Router {
|
|
23
|
-
private _root;
|
|
24
|
-
register(path: string): FindResult['store'];
|
|
25
|
-
find(url: string, queryIndex: number): FindResult | null;
|
|
26
|
-
}
|
package/dist/router.js
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { getPath } from './utils';
|
|
2
|
-
function createNode(pathPart, staticChildren) {
|
|
3
|
-
return {
|
|
4
|
-
pathPart,
|
|
5
|
-
store: null,
|
|
6
|
-
staticChildren: staticChildren !== undefined
|
|
7
|
-
? new Map(staticChildren.map((child) => [
|
|
8
|
-
child.pathPart.charCodeAt(0),
|
|
9
|
-
child
|
|
10
|
-
]))
|
|
11
|
-
: null,
|
|
12
|
-
parametricChild: null,
|
|
13
|
-
wildcardStore: null
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
function cloneNode(node, newPathPart) {
|
|
17
|
-
return {
|
|
18
|
-
pathPart: newPathPart,
|
|
19
|
-
store: node.store,
|
|
20
|
-
staticChildren: node.staticChildren,
|
|
21
|
-
parametricChild: node.parametricChild,
|
|
22
|
-
wildcardStore: node.wildcardStore
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function createParametricNode(paramName) {
|
|
26
|
-
return {
|
|
27
|
-
paramName,
|
|
28
|
-
store: null,
|
|
29
|
-
staticChild: null
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
export class Router {
|
|
33
|
-
constructor() {
|
|
34
|
-
this._root = createNode('/');
|
|
35
|
-
}
|
|
36
|
-
register(path) {
|
|
37
|
-
if (typeof path !== 'string')
|
|
38
|
-
throw new TypeError('Route path must be a string');
|
|
39
|
-
if (path === '' || path[0] !== '/')
|
|
40
|
-
throw new Error(`Invalid route: ${path}\nRoute path must begin with a "/"`);
|
|
41
|
-
const endsWithWildcard = path.endsWith('*');
|
|
42
|
-
if (endsWithWildcard)
|
|
43
|
-
path = path.slice(0, -1);
|
|
44
|
-
const staticParts = path.split(/:.+?(?=\/|$)/);
|
|
45
|
-
const paramParts = path.match(/:.+?(?=\/|$)/g) || [];
|
|
46
|
-
if (staticParts[staticParts.length - 1] === '')
|
|
47
|
-
staticParts.pop();
|
|
48
|
-
let node = this._root;
|
|
49
|
-
let paramPartsIndex = 0;
|
|
50
|
-
for (let i = 0; i < staticParts.length; ++i) {
|
|
51
|
-
let pathPart = staticParts[i];
|
|
52
|
-
if (i > 0) {
|
|
53
|
-
const paramName = paramParts[paramPartsIndex++].slice(1);
|
|
54
|
-
if (node.parametricChild === null)
|
|
55
|
-
node.parametricChild = createParametricNode(paramName);
|
|
56
|
-
else if (node.parametricChild.paramName !== paramName)
|
|
57
|
-
throw new Error(`Cannot create route "${path}" with parameter "${paramName}" ` +
|
|
58
|
-
'because a route already exists with a different parameter name ' +
|
|
59
|
-
`("${node.parametricChild.paramName}") in the same location`);
|
|
60
|
-
const { parametricChild } = node;
|
|
61
|
-
if (parametricChild.staticChild === null) {
|
|
62
|
-
node = parametricChild.staticChild = createNode(pathPart);
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
node = parametricChild.staticChild;
|
|
66
|
-
}
|
|
67
|
-
for (let j = 0;;) {
|
|
68
|
-
if (j === pathPart.length) {
|
|
69
|
-
if (j < node.pathPart.length) {
|
|
70
|
-
const childNode = cloneNode(node, node.pathPart.slice(j));
|
|
71
|
-
Object.assign(node, createNode(pathPart, [childNode]));
|
|
72
|
-
}
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
if (j === node.pathPart.length) {
|
|
76
|
-
if (node.staticChildren === null)
|
|
77
|
-
node.staticChildren = new Map();
|
|
78
|
-
else if (node.staticChildren.has(pathPart.charCodeAt(j))) {
|
|
79
|
-
node = node.staticChildren.get(pathPart.charCodeAt(j));
|
|
80
|
-
pathPart = pathPart.slice(j);
|
|
81
|
-
j = 0;
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
const childNode = createNode(pathPart.slice(j));
|
|
85
|
-
node.staticChildren.set(pathPart.charCodeAt(j), childNode);
|
|
86
|
-
node = childNode;
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
if (pathPart[j] !== node.pathPart[j]) {
|
|
90
|
-
const existingChild = cloneNode(node, node.pathPart.slice(j));
|
|
91
|
-
const newChild = createNode(pathPart.slice(j));
|
|
92
|
-
Object.assign(node, createNode(node.pathPart.slice(0, j), [
|
|
93
|
-
existingChild,
|
|
94
|
-
newChild
|
|
95
|
-
]));
|
|
96
|
-
node = newChild;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
++j;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (paramPartsIndex < paramParts.length) {
|
|
103
|
-
const param = paramParts[paramPartsIndex];
|
|
104
|
-
const paramName = param.slice(1);
|
|
105
|
-
if (node.parametricChild === null)
|
|
106
|
-
node.parametricChild = createParametricNode(paramName);
|
|
107
|
-
else if (node.parametricChild.paramName !== paramName)
|
|
108
|
-
throw new Error(`Cannot create route "${path}" with parameter "${paramName}" ` +
|
|
109
|
-
'because a route already exists with a different parameter name ' +
|
|
110
|
-
`("${node.parametricChild.paramName}") in the same location`);
|
|
111
|
-
if (node.parametricChild.store === null)
|
|
112
|
-
node.parametricChild.store = Object.create(null);
|
|
113
|
-
return node.parametricChild.store;
|
|
114
|
-
}
|
|
115
|
-
if (endsWithWildcard) {
|
|
116
|
-
if (node.wildcardStore === null)
|
|
117
|
-
node.wildcardStore = Object.create(null);
|
|
118
|
-
return node.wildcardStore;
|
|
119
|
-
}
|
|
120
|
-
if (node.store === null)
|
|
121
|
-
node.store = Object.create(null);
|
|
122
|
-
return node.store;
|
|
123
|
-
}
|
|
124
|
-
find(url, queryIndex) {
|
|
125
|
-
url = getPath(url, queryIndex);
|
|
126
|
-
return matchRoute(url, url.length, this._root, 0);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function matchRoute(url, urlLength, node, startIndex) {
|
|
130
|
-
const pathPart = node.pathPart;
|
|
131
|
-
const pathPartEndIndex = startIndex + pathPart.length;
|
|
132
|
-
if (pathPart.length > 1) {
|
|
133
|
-
if (pathPartEndIndex > urlLength)
|
|
134
|
-
return null;
|
|
135
|
-
if (pathPart.length < 15)
|
|
136
|
-
for (let i = 1, j = startIndex + 1; i < pathPart.length; ++i, ++j) {
|
|
137
|
-
if (pathPart[i] !== url[j]) {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
else if (url.slice(startIndex, pathPartEndIndex) !== pathPart)
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
if (pathPartEndIndex === urlLength) {
|
|
145
|
-
if (node.store)
|
|
146
|
-
return {
|
|
147
|
-
store: node.store,
|
|
148
|
-
params: {}
|
|
149
|
-
};
|
|
150
|
-
if (node.wildcardStore)
|
|
151
|
-
return {
|
|
152
|
-
store: node.wildcardStore,
|
|
153
|
-
params: { '*': '' }
|
|
154
|
-
};
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
if (node.staticChildren) {
|
|
158
|
-
const staticChild = node.staticChildren.get(url.charCodeAt(pathPartEndIndex));
|
|
159
|
-
if (staticChild !== undefined) {
|
|
160
|
-
const route = matchRoute(url, urlLength, staticChild, pathPartEndIndex);
|
|
161
|
-
if (route)
|
|
162
|
-
return route;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (node.parametricChild) {
|
|
166
|
-
const slashIndex = url.indexOf('/', pathPartEndIndex);
|
|
167
|
-
if (slashIndex !== pathPartEndIndex) {
|
|
168
|
-
if (slashIndex === -1 || slashIndex >= urlLength) {
|
|
169
|
-
if (node.parametricChild.store) {
|
|
170
|
-
const params = {};
|
|
171
|
-
let paramData = url.slice(pathPartEndIndex, urlLength);
|
|
172
|
-
if (paramData.includes('%'))
|
|
173
|
-
paramData = decodeURI(paramData);
|
|
174
|
-
params[node.parametricChild.paramName] = paramData;
|
|
175
|
-
return {
|
|
176
|
-
store: node.parametricChild.store,
|
|
177
|
-
params
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
else if (node.parametricChild.staticChild) {
|
|
182
|
-
const route = matchRoute(url, urlLength, node.parametricChild.staticChild, slashIndex);
|
|
183
|
-
if (route) {
|
|
184
|
-
let paramData = url.slice(pathPartEndIndex, slashIndex);
|
|
185
|
-
if (paramData.includes('%'))
|
|
186
|
-
paramData = decodeURI(paramData);
|
|
187
|
-
route.params[node.parametricChild.paramName] = paramData;
|
|
188
|
-
return route;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (node.wildcardStore)
|
|
194
|
-
return {
|
|
195
|
-
store: node.wildcardStore,
|
|
196
|
-
params: {
|
|
197
|
-
'*': url.slice(pathPartEndIndex, urlLength)
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
return null;
|
|
201
|
-
}
|