routup 6.0.0-beta.0 → 6.0.0-beta.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/bun.d.mts +2 -2
- package/dist/bun.mjs +1 -1
- package/dist/cloudflare.d.mts +2 -2
- package/dist/cloudflare.mjs +1 -1
- package/dist/deno.d.mts +2 -2
- package/dist/deno.mjs +1 -1
- package/dist/generic.d.mts +2 -2
- package/dist/generic.mjs +1 -1
- package/dist/{index-kxLRw2Wc.d.mts → index-6qjxyFZh.d.mts} +170 -365
- package/dist/node.d.mts +2 -2
- package/dist/node.mjs +1 -1
- package/dist/service-worker.d.mts +2 -2
- package/dist/service-worker.mjs +1 -1
- package/dist/{src-gmPicCWT.mjs → src-BfsqxIfL.mjs} +174 -551
- package/dist/src-BfsqxIfL.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/src-gmPicCWT.mjs.map +0 -1
|
@@ -20,11 +20,9 @@ const DEFAULT_MAX_SIZE = 1024;
|
|
|
20
20
|
* `ICache` (e.g. wrapping `lru-cache`) and pass it via the router's
|
|
21
21
|
* `BaseRouterOptions.cache` slot.
|
|
22
22
|
*/
|
|
23
|
-
var LruCache = class
|
|
24
|
-
options;
|
|
23
|
+
var LruCache = class {
|
|
25
24
|
inner;
|
|
26
25
|
constructor(options = {}) {
|
|
27
|
-
this.options = options;
|
|
28
26
|
this.inner = new QuickLRU({ maxSize: options.maxSize ?? DEFAULT_MAX_SIZE });
|
|
29
27
|
}
|
|
30
28
|
get(key) {
|
|
@@ -39,9 +37,6 @@ var LruCache = class LruCache {
|
|
|
39
37
|
clear() {
|
|
40
38
|
this.inner.clear();
|
|
41
39
|
}
|
|
42
|
-
clone() {
|
|
43
|
-
return new LruCache({ ...this.options });
|
|
44
|
-
}
|
|
45
40
|
};
|
|
46
41
|
//#endregion
|
|
47
42
|
//#region src/constants.ts
|
|
@@ -815,16 +810,17 @@ var DispatcherEvent = class {
|
|
|
815
810
|
error;
|
|
816
811
|
/**
|
|
817
812
|
* Options of the App currently dispatching this event. Set on
|
|
818
|
-
* entry to `App.dispatch` and restored on exit
|
|
819
|
-
*
|
|
820
|
-
* reading before any dispatch get a valid
|
|
813
|
+
* entry to `App.dispatch` and restored on exit so re-entrant
|
|
814
|
+
* dispatch calls leave the caller's view intact. Initialized to
|
|
815
|
+
* `{}` so consumers reading before any dispatch get a valid
|
|
816
|
+
* (empty) shape.
|
|
821
817
|
*/
|
|
822
818
|
appOptions;
|
|
823
819
|
/**
|
|
824
|
-
* `true` while
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
820
|
+
* `true` while an `App.dispatch` call is on the stack for this
|
|
821
|
+
* event. `App.dispatch` reads this on entry to derive `isRoot`
|
|
822
|
+
* and writes it on entry/exit so re-entrant calls behave
|
|
823
|
+
* correctly.
|
|
828
824
|
*/
|
|
829
825
|
isDispatching;
|
|
830
826
|
_dispatched;
|
|
@@ -950,148 +946,12 @@ const HandlerType = {
|
|
|
950
946
|
};
|
|
951
947
|
const HandlerSymbol = Symbol.for("Handler");
|
|
952
948
|
//#endregion
|
|
953
|
-
//#region src/hook/constants.ts
|
|
954
|
-
const HookName = {
|
|
955
|
-
/**
|
|
956
|
-
* Fired at the start of `App.dispatch`, before the pipeline walk.
|
|
957
|
-
* Once per router per request.
|
|
958
|
-
*/
|
|
959
|
-
START: "start",
|
|
960
|
-
/**
|
|
961
|
-
* Fired at the end of `App.dispatch`, after the pipeline walk
|
|
962
|
-
* (and OPTIONS auto-Allow synthesis) completes. Once per router per
|
|
963
|
-
* request.
|
|
964
|
-
*/
|
|
965
|
-
END: "end",
|
|
966
|
-
ERROR: "error",
|
|
967
|
-
CHILD_MATCH: "childMatch",
|
|
968
|
-
CHILD_DISPATCH_BEFORE: "childDispatchBefore",
|
|
969
|
-
CHILD_DISPATCH_AFTER: "childDispatchAfter"
|
|
970
|
-
};
|
|
971
|
-
//#endregion
|
|
972
|
-
//#region src/hook/module.ts
|
|
973
|
-
var Hooks = class Hooks {
|
|
974
|
-
items;
|
|
975
|
-
/**
|
|
976
|
-
* Derived bit: `true` iff at least one entry exists across all
|
|
977
|
-
* hook names. Maintained on every `addListener` / `removeListener`
|
|
978
|
-
* so the dispatch hot path can short-circuit on a single boolean
|
|
979
|
-
* read rather than per-name lookup. Apps that never register a
|
|
980
|
-
* hook (the common case) pay one boolean check per request
|
|
981
|
-
* instead of a property access per pipeline step.
|
|
982
|
-
*/
|
|
983
|
-
_hasAny;
|
|
984
|
-
constructor() {
|
|
985
|
-
this.items = {};
|
|
986
|
-
this._hasAny = false;
|
|
987
|
-
}
|
|
988
|
-
hasAny() {
|
|
989
|
-
return this._hasAny;
|
|
990
|
-
}
|
|
991
|
-
hasListeners(name) {
|
|
992
|
-
if (!this._hasAny) return false;
|
|
993
|
-
return this.items[name] !== void 0;
|
|
994
|
-
}
|
|
995
|
-
addListener(name, fn, priority = 0) {
|
|
996
|
-
this.items[name] = this.items[name] || [];
|
|
997
|
-
const entry = {
|
|
998
|
-
fn,
|
|
999
|
-
priority
|
|
1000
|
-
};
|
|
1001
|
-
let i = 0;
|
|
1002
|
-
while (i < this.items[name].length && this.items[name][i].priority >= priority) i++;
|
|
1003
|
-
this.items[name].splice(i, 0, entry);
|
|
1004
|
-
this._hasAny = true;
|
|
1005
|
-
return () => {
|
|
1006
|
-
this.removeListener(name, fn);
|
|
1007
|
-
};
|
|
1008
|
-
}
|
|
1009
|
-
removeListener(name, fn) {
|
|
1010
|
-
if (!this.items[name]) return;
|
|
1011
|
-
if (typeof fn === "undefined") {
|
|
1012
|
-
delete this.items[name];
|
|
1013
|
-
this.recomputeHasAny();
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
if (typeof fn === "function") {
|
|
1017
|
-
const index = this.items[name].findIndex((entry) => entry.fn === fn);
|
|
1018
|
-
if (index !== -1) this.items[name].splice(index, 1);
|
|
1019
|
-
}
|
|
1020
|
-
if (this.items[name].length === 0) delete this.items[name];
|
|
1021
|
-
this.recomputeHasAny();
|
|
1022
|
-
}
|
|
1023
|
-
/**
|
|
1024
|
-
* Recompute `_hasAny` from the current `items` map. O(k) where k
|
|
1025
|
-
* is the number of distinct hook names ever registered (≤ ~6) —
|
|
1026
|
-
* effectively O(1). Called from `removeListener` so the fast-path
|
|
1027
|
-
* flag stays in sync with registration state.
|
|
1028
|
-
*/
|
|
1029
|
-
recomputeHasAny() {
|
|
1030
|
-
for (const name in this.items) if (this.items[name] && this.items[name].length > 0) {
|
|
1031
|
-
this._hasAny = true;
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
this._hasAny = false;
|
|
1035
|
-
}
|
|
1036
|
-
/**
|
|
1037
|
-
* Create a new `Hooks` instance seeded with the same listeners as this
|
|
1038
|
-
* one.
|
|
1039
|
-
*
|
|
1040
|
-
* Listener functions are shared by reference; priority and ordering are
|
|
1041
|
-
* preserved. Future mutations on the returned instance do not affect this
|
|
1042
|
-
* one (and vice versa).
|
|
1043
|
-
*/
|
|
1044
|
-
clone() {
|
|
1045
|
-
const next = new Hooks();
|
|
1046
|
-
const names = Object.keys(this.items);
|
|
1047
|
-
for (const name of names) {
|
|
1048
|
-
const entries = this.items[name];
|
|
1049
|
-
for (const entry of entries) next.addListener(name, entry.fn, entry.priority);
|
|
1050
|
-
}
|
|
1051
|
-
return next;
|
|
1052
|
-
}
|
|
1053
|
-
async trigger(name, event) {
|
|
1054
|
-
if (!this.items[name] || this.items[name].length === 0) return;
|
|
1055
|
-
try {
|
|
1056
|
-
for (let i = 0; i < this.items[name].length; i++) {
|
|
1057
|
-
const { fn } = this.items[name][i];
|
|
1058
|
-
await this.triggerListener(name, event, fn);
|
|
1059
|
-
if (event.dispatched) {
|
|
1060
|
-
if (event.error) event.error = void 0;
|
|
1061
|
-
return;
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
} catch (e) {
|
|
1065
|
-
if (!event.error) event.error = createError(e);
|
|
1066
|
-
if (!this.isErrorListenerHook(name)) {
|
|
1067
|
-
await this.trigger(HookName.ERROR, event);
|
|
1068
|
-
if (event.dispatched) {
|
|
1069
|
-
if (event.error) event.error = void 0;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
triggerListener(name, event, listener) {
|
|
1075
|
-
if (this.isErrorListenerHook(name)) {
|
|
1076
|
-
if (event.error) return listener(event);
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
return listener(event);
|
|
1080
|
-
}
|
|
1081
|
-
isErrorListenerHook(input) {
|
|
1082
|
-
return input === HookName.ERROR;
|
|
1083
|
-
}
|
|
1084
|
-
};
|
|
1085
|
-
//#endregion
|
|
1086
949
|
//#region src/handler/module.ts
|
|
1087
950
|
var Handler = class {
|
|
1088
951
|
config;
|
|
1089
|
-
hooks;
|
|
1090
952
|
method;
|
|
1091
953
|
constructor(handler) {
|
|
1092
954
|
this.config = handler;
|
|
1093
|
-
this.hooks = new Hooks();
|
|
1094
|
-
this.mountHooks();
|
|
1095
955
|
if (typeof handler.path === "string") this.config.path = withLeadingSlash(handler.path);
|
|
1096
956
|
this.method = this.config.method ? toMethodName(this.config.method) : void 0;
|
|
1097
957
|
markInstanceof(this, HandlerSymbol);
|
|
@@ -1103,15 +963,12 @@ var Handler = class {
|
|
|
1103
963
|
return this.config.path;
|
|
1104
964
|
}
|
|
1105
965
|
async dispatch(event) {
|
|
1106
|
-
if (this.hooks.hasListeners(HookName.CHILD_DISPATCH_BEFORE)) {
|
|
1107
|
-
await this.hooks.trigger(HookName.CHILD_DISPATCH_BEFORE, event);
|
|
1108
|
-
if (event.dispatched) return;
|
|
1109
|
-
}
|
|
1110
966
|
let response;
|
|
967
|
+
let handlerEvent;
|
|
968
|
+
let cleanupParentListener;
|
|
1111
969
|
try {
|
|
1112
970
|
const effectiveTimeout = this.resolveTimeout(event.appOptions);
|
|
1113
971
|
let childController;
|
|
1114
|
-
let cleanupParentListener;
|
|
1115
972
|
if (effectiveTimeout) {
|
|
1116
973
|
const parentSignal = event.signal;
|
|
1117
974
|
childController = new AbortController();
|
|
@@ -1122,41 +979,42 @@ var Handler = class {
|
|
|
1122
979
|
cleanupParentListener = () => parentSignal.removeEventListener("abort", onAbort);
|
|
1123
980
|
}
|
|
1124
981
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
const { fn } = this.config;
|
|
1136
|
-
invocation = fn(handlerEvent);
|
|
1137
|
-
}
|
|
1138
|
-
if (skipFn) {} else if (effectiveTimeout) result = await this.executeWithTimeout(() => this.resolveHandlerResult(invocation, handlerEvent), effectiveTimeout, childController);
|
|
1139
|
-
else if (isPromise(invocation)) {
|
|
1140
|
-
const awaited = await invocation;
|
|
1141
|
-
result = typeof awaited === "undefined" ? await this.resolveHandlerResult(void 0, handlerEvent) : awaited;
|
|
1142
|
-
} else if (typeof invocation === "undefined") result = await this.resolveHandlerResult(void 0, handlerEvent);
|
|
1143
|
-
else result = invocation;
|
|
1144
|
-
} finally {
|
|
1145
|
-
if (cleanupParentListener) cleanupParentListener();
|
|
982
|
+
handlerEvent = childController ? event.build(childController.signal) : event.build();
|
|
983
|
+
const skipFn = this.config.type === HandlerType.ERROR && !event.error;
|
|
984
|
+
if (!skipFn && this.config.onBefore) await this.config.onBefore(handlerEvent);
|
|
985
|
+
let invocation;
|
|
986
|
+
if (skipFn) {} else if (this.config.type === HandlerType.ERROR) {
|
|
987
|
+
const { fn } = this.config;
|
|
988
|
+
invocation = fn(event.error, handlerEvent);
|
|
989
|
+
} else {
|
|
990
|
+
const { fn } = this.config;
|
|
991
|
+
invocation = fn(handlerEvent);
|
|
1146
992
|
}
|
|
993
|
+
let result;
|
|
994
|
+
if (skipFn) {} else if (effectiveTimeout) result = await this.executeWithTimeout(() => this.resolveHandlerResult(invocation, handlerEvent), effectiveTimeout, childController);
|
|
995
|
+
else if (isPromise(invocation)) {
|
|
996
|
+
const awaited = await invocation;
|
|
997
|
+
result = typeof awaited === "undefined" ? await this.resolveHandlerResult(void 0, handlerEvent) : awaited;
|
|
998
|
+
} else if (typeof invocation === "undefined") result = await this.resolveHandlerResult(void 0, handlerEvent);
|
|
999
|
+
else result = invocation;
|
|
1147
1000
|
const toResp = toResponse(result, handlerEvent);
|
|
1148
1001
|
response = isPromise(toResp) ? await toResp : toResp;
|
|
1149
1002
|
if (response) {
|
|
1150
1003
|
event.dispatched = true;
|
|
1151
1004
|
if (this.config.type === HandlerType.ERROR && event.error) event.error = void 0;
|
|
1152
1005
|
}
|
|
1006
|
+
if (!skipFn && this.config.onAfter) await this.config.onAfter(handlerEvent, response);
|
|
1153
1007
|
} catch (e) {
|
|
1154
1008
|
event.error = isError(e) ? e : createError(e);
|
|
1155
|
-
if (this.
|
|
1156
|
-
|
|
1157
|
-
|
|
1009
|
+
if (this.config.onError) try {
|
|
1010
|
+
await this.config.onError(event.error, handlerEvent ?? event.build());
|
|
1011
|
+
} catch (innerErr) {
|
|
1012
|
+
event.error = isError(innerErr) ? innerErr : createError(innerErr);
|
|
1013
|
+
}
|
|
1014
|
+
throw event.error;
|
|
1015
|
+
} finally {
|
|
1016
|
+
if (cleanupParentListener) cleanupParentListener();
|
|
1158
1017
|
}
|
|
1159
|
-
if (this.hooks.hasListeners(HookName.CHILD_DISPATCH_AFTER)) await this.hooks.trigger(HookName.CHILD_DISPATCH_AFTER, event);
|
|
1160
1018
|
return response;
|
|
1161
1019
|
}
|
|
1162
1020
|
/**
|
|
@@ -1220,11 +1078,6 @@ var Handler = class {
|
|
|
1220
1078
|
if (appOptions.handlerTimeoutOverridable) return handlerOverride;
|
|
1221
1079
|
return Math.min(routerDefault, handlerOverride);
|
|
1222
1080
|
}
|
|
1223
|
-
mountHooks() {
|
|
1224
|
-
if (this.config.onBefore) this.hooks.addListener(HookName.CHILD_DISPATCH_BEFORE, this.config.onBefore);
|
|
1225
|
-
if (this.config.onAfter) this.hooks.addListener(HookName.CHILD_DISPATCH_AFTER, this.config.onAfter);
|
|
1226
|
-
if (this.config.onError) this.hooks.addListener(HookName.ERROR, this.config.onError);
|
|
1227
|
-
}
|
|
1228
1081
|
};
|
|
1229
1082
|
//#endregion
|
|
1230
1083
|
//#region src/handler/core/define.ts
|
|
@@ -1681,7 +1534,7 @@ function buildRoutePathMatcher(route) {
|
|
|
1681
1534
|
* `BaseRouterOptions.cache` to skip the linear walk on repeated
|
|
1682
1535
|
* requests for the same path. Default is no caching.
|
|
1683
1536
|
*/
|
|
1684
|
-
var LinearRouter = class
|
|
1537
|
+
var LinearRouter = class {
|
|
1685
1538
|
_routes;
|
|
1686
1539
|
_matchers;
|
|
1687
1540
|
cache;
|
|
@@ -1722,9 +1575,6 @@ var LinearRouter = class LinearRouter {
|
|
|
1722
1575
|
this.cache?.set(path, matches);
|
|
1723
1576
|
return matches;
|
|
1724
1577
|
}
|
|
1725
|
-
clone() {
|
|
1726
|
-
return new LinearRouter({ cache: this.cache?.clone() });
|
|
1727
|
-
}
|
|
1728
1578
|
};
|
|
1729
1579
|
//#endregion
|
|
1730
1580
|
//#region src/router/trie/parser.ts
|
|
@@ -2131,7 +1981,7 @@ function pushIntoBucket(buckets, methodKey, route) {
|
|
|
2131
1981
|
* method's buckets) is the full answer — no need to walk the param
|
|
2132
1982
|
* branch or collect prefix candidates at intermediate nodes.
|
|
2133
1983
|
*/
|
|
2134
|
-
var TrieRouter = class
|
|
1984
|
+
var TrieRouter = class {
|
|
2135
1985
|
/**
|
|
2136
1986
|
* Monotonic counter assigned as the registration `index` on each
|
|
2137
1987
|
* route — the dispatch loop uses it to preserve registration
|
|
@@ -2231,9 +2081,6 @@ var TrieRouter = class TrieRouter {
|
|
|
2231
2081
|
this.cache?.set(cacheKey, matches);
|
|
2232
2082
|
return matches;
|
|
2233
2083
|
}
|
|
2234
|
-
clone() {
|
|
2235
|
-
return new TrieRouter({ cache: this.cache?.clone() });
|
|
2236
|
-
}
|
|
2237
2084
|
/**
|
|
2238
2085
|
* T1: returns the pre-computed candidate list when the request's
|
|
2239
2086
|
* static spine has no param sibling, no prefix routes, and no
|
|
@@ -2345,7 +2192,7 @@ const DEFAULT_THRESHOLD = 30;
|
|
|
2345
2192
|
* buffer; on the first `lookup()` call, picks `LinearRouter` or
|
|
2346
2193
|
* `TrieRouter` based on the registered route count and replays the
|
|
2347
2194
|
* pending list onto the chosen inner router. Every subsequent call
|
|
2348
|
-
* — `add`, `lookup
|
|
2195
|
+
* — `add`, `lookup` — forwards to the inner.
|
|
2349
2196
|
*
|
|
2350
2197
|
* Use this when you don't want to commit to a router family up-front
|
|
2351
2198
|
* (e.g. a library that registers a variable number of routes
|
|
@@ -2357,7 +2204,7 @@ const DEFAULT_THRESHOLD = 30;
|
|
|
2357
2204
|
* that matters in routup today: linear-vs-trie at the registration-
|
|
2358
2205
|
* size crossover.
|
|
2359
2206
|
*/
|
|
2360
|
-
var SmartRouter = class
|
|
2207
|
+
var SmartRouter = class {
|
|
2361
2208
|
inner;
|
|
2362
2209
|
pending = [];
|
|
2363
2210
|
threshold;
|
|
@@ -2385,12 +2232,6 @@ var SmartRouter = class SmartRouter {
|
|
|
2385
2232
|
}
|
|
2386
2233
|
return this.inner.lookup(path, method);
|
|
2387
2234
|
}
|
|
2388
|
-
clone() {
|
|
2389
|
-
return new SmartRouter({
|
|
2390
|
-
threshold: this.threshold,
|
|
2391
|
-
cache: this.cache?.clone()
|
|
2392
|
-
});
|
|
2393
|
-
}
|
|
2394
2235
|
/**
|
|
2395
2236
|
* Pick the inner router based on the registered route count.
|
|
2396
2237
|
* `LinearRouter` for tiny tables, `TrieRouter` past the
|
|
@@ -2426,18 +2267,6 @@ function normalizeAppOptions(input) {
|
|
|
2426
2267
|
//#endregion
|
|
2427
2268
|
//#region src/app/constants.ts
|
|
2428
2269
|
const AppSymbol = Symbol.for("App");
|
|
2429
|
-
const AppPipelineStep = {
|
|
2430
|
-
START: 0,
|
|
2431
|
-
LOOKUP: 1,
|
|
2432
|
-
CHILD_BEFORE: 2,
|
|
2433
|
-
CHILD_DISPATCH: 3,
|
|
2434
|
-
CHILD_AFTER: 4,
|
|
2435
|
-
FINISH: 5
|
|
2436
|
-
};
|
|
2437
|
-
const RouteEntryType = {
|
|
2438
|
-
APP: "app",
|
|
2439
|
-
HANDLER: "handler"
|
|
2440
|
-
};
|
|
2441
2270
|
//#endregion
|
|
2442
2271
|
//#region src/app/check.ts
|
|
2443
2272
|
function isAppInstance(input) {
|
|
@@ -2463,26 +2292,9 @@ function mergeMatchParams(event, matchParams) {
|
|
|
2463
2292
|
...matchParams
|
|
2464
2293
|
};
|
|
2465
2294
|
}
|
|
2466
|
-
/**
|
|
2467
|
-
* Copy `source[key]` into `target[key]` when the target's value is
|
|
2468
|
-
* undefined; return whether a write happened.
|
|
2469
|
-
*
|
|
2470
|
-
* Bound to a single key `K` per call, so TypeScript can prove the
|
|
2471
|
-
* read and write hit the same property's value type — no `as` cast
|
|
2472
|
-
* needed. This is the standard escape hatch for the variance trap
|
|
2473
|
-
* you'd otherwise hit by writing `target[key] = source[key]` inside
|
|
2474
|
-
* a loop where `key: keyof AppOptions` is a *union* (read returns
|
|
2475
|
-
* the union of value types, write requires the intersection, which
|
|
2476
|
-
* collapses to `never`).
|
|
2477
|
-
*/
|
|
2478
|
-
function copyOptionIfUnset(target, source, key) {
|
|
2479
|
-
if (typeof target[key] !== "undefined") return false;
|
|
2480
|
-
target[key] = source[key];
|
|
2481
|
-
return true;
|
|
2482
|
-
}
|
|
2483
2295
|
var App = class App {
|
|
2484
2296
|
/**
|
|
2485
|
-
* A label for the
|
|
2297
|
+
* A label for the App instance.
|
|
2486
2298
|
*/
|
|
2487
2299
|
name;
|
|
2488
2300
|
/**
|
|
@@ -2502,54 +2314,60 @@ var App = class App {
|
|
|
2502
2314
|
*/
|
|
2503
2315
|
router;
|
|
2504
2316
|
/**
|
|
2505
|
-
* Lifecycle hook registry.
|
|
2506
|
-
*
|
|
2507
|
-
* @protected
|
|
2508
|
-
*/
|
|
2509
|
-
hooks;
|
|
2510
|
-
/**
|
|
2511
2317
|
* Normalized options for this App instance.
|
|
2512
2318
|
*
|
|
2513
|
-
* Frozen on construction
|
|
2514
|
-
*
|
|
2515
|
-
*
|
|
2516
|
-
* router-global state. `extendOptions` therefore uses a
|
|
2517
|
-
* functional update (build a new object, freeze it, replace
|
|
2518
|
-
* the slot) rather than mutating in place.
|
|
2319
|
+
* Frozen on construction — once published to `event.appOptions`
|
|
2320
|
+
* it is shared across all requests, and a handler must not be
|
|
2321
|
+
* able to mutate router-global state.
|
|
2519
2322
|
*/
|
|
2520
2323
|
_options;
|
|
2521
2324
|
/**
|
|
2522
|
-
* Registry of installed plugins (name → version) on this
|
|
2325
|
+
* Registry of installed plugins (name → version) on this App.
|
|
2326
|
+
*
|
|
2327
|
+
* Read by `use(otherApp)` (via the public `plugins` getter) so
|
|
2328
|
+
* plugin registries merge into the parent at flatten time —
|
|
2329
|
+
* `parent.hasPlugin('foo')` then reflects plugins installed on
|
|
2330
|
+
* apps mounted into it.
|
|
2523
2331
|
*
|
|
2524
2332
|
* @protected
|
|
2525
2333
|
*/
|
|
2526
|
-
|
|
2334
|
+
_plugins;
|
|
2527
2335
|
/**
|
|
2528
2336
|
* Every route registered on this App, in registration order.
|
|
2529
2337
|
*
|
|
2530
|
-
*
|
|
2531
|
-
* `
|
|
2532
|
-
* read from here instead of asking the router. Routes are
|
|
2533
|
-
* pushed alongside every `this.router.add()` via the `register`
|
|
2534
|
-
* helper.
|
|
2535
|
-
*
|
|
2536
|
-
* @protected
|
|
2338
|
+
* Read by `use(otherApp)` to snapshot routes at flatten time.
|
|
2339
|
+
* Late mutations to `_routes` after a flatten do not propagate.
|
|
2537
2340
|
*/
|
|
2538
2341
|
_routes = [];
|
|
2539
2342
|
constructor(input = {}) {
|
|
2540
2343
|
this.name = input.name;
|
|
2541
2344
|
this._path = input.path;
|
|
2542
|
-
this.
|
|
2543
|
-
this.plugins = new Map(input.plugins);
|
|
2345
|
+
this._plugins = /* @__PURE__ */ new Map();
|
|
2544
2346
|
this.router = input.router ?? new LinearRouter();
|
|
2545
2347
|
this._options = Object.freeze(normalizeAppOptions(input.options ?? {}));
|
|
2546
2348
|
markInstanceof(this, AppSymbol);
|
|
2547
2349
|
}
|
|
2548
2350
|
/**
|
|
2351
|
+
* Public read of the canonical route list. Used by `use(child)`
|
|
2352
|
+
* to snapshot the child's routes at flatten time. Returned
|
|
2353
|
+
* as `readonly` — callers must not mutate.
|
|
2354
|
+
*/
|
|
2355
|
+
get routes() {
|
|
2356
|
+
return this._routes;
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Public read of the installed-plugin registry. Used by
|
|
2360
|
+
* `use(child)` to merge child plugins into the parent at
|
|
2361
|
+
* flatten time. Returned as `ReadonlyMap` — callers must not
|
|
2362
|
+
* mutate; go through `use(plugin)` to install.
|
|
2363
|
+
*/
|
|
2364
|
+
get plugins() {
|
|
2365
|
+
return this._plugins;
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2549
2368
|
* Register a route with the active router and record it on the
|
|
2550
|
-
* App so
|
|
2551
|
-
*
|
|
2552
|
-
* independent of the router instance.
|
|
2369
|
+
* App so `setRouter` / `use(child)` can read the canonical list
|
|
2370
|
+
* back.
|
|
2553
2371
|
*
|
|
2554
2372
|
* @protected
|
|
2555
2373
|
*/
|
|
@@ -2622,199 +2440,6 @@ var App = class App {
|
|
|
2622
2440
|
headers
|
|
2623
2441
|
});
|
|
2624
2442
|
}
|
|
2625
|
-
/**
|
|
2626
|
-
* Mount-time option inheritance — fill in any of this App's
|
|
2627
|
-
* unset option keys from the supplied parent options. Called by
|
|
2628
|
-
* `App.use(child)` after narrowing to `App` via `isAppInstance`.
|
|
2629
|
-
*
|
|
2630
|
-
* Public so callers don't need to reach into another App's
|
|
2631
|
-
* protected fields: the parent passes its options by value and
|
|
2632
|
-
* the child decides what to do with them.
|
|
2633
|
-
*
|
|
2634
|
-
* Shallow per-key merge. App-local concerns — `name`, `path`,
|
|
2635
|
-
* `hooks`, `plugins`, `router`, and the router's cache — are
|
|
2636
|
-
* deliberately not propagated; they sit on `AppContext`, not
|
|
2637
|
-
* inside `AppOptions`.
|
|
2638
|
-
*
|
|
2639
|
-
* Cascades to any Apps already mounted on this one — a deeper
|
|
2640
|
-
* grandchild gets the new keys too. Without this, mounting
|
|
2641
|
-
* grandchild → child → parent in that order would leave the
|
|
2642
|
-
* grandchild without parent's options (it adopted from child
|
|
2643
|
-
* before child had them).
|
|
2644
|
-
*
|
|
2645
|
-
* Late mutation of the parent's options after this call does NOT
|
|
2646
|
-
* propagate. `AppOptions` is configured at construction-and-mount;
|
|
2647
|
-
* later changes are not a supported workflow.
|
|
2648
|
-
*/
|
|
2649
|
-
extendOptions(incoming) {
|
|
2650
|
-
let next;
|
|
2651
|
-
const keys = Object.keys(incoming);
|
|
2652
|
-
for (const key of keys) {
|
|
2653
|
-
if (typeof this._options[key] !== "undefined") continue;
|
|
2654
|
-
if (typeof incoming[key] === "undefined") continue;
|
|
2655
|
-
next ??= { ...this._options };
|
|
2656
|
-
copyOptionIfUnset(next, incoming, key);
|
|
2657
|
-
}
|
|
2658
|
-
if (!next) return;
|
|
2659
|
-
this._options = Object.freeze(next);
|
|
2660
|
-
for (const route of this._routes) if (route.data.type === RouteEntryType.APP && isAppInstance(route.data.data)) route.data.data.extendOptions(this._options);
|
|
2661
|
-
}
|
|
2662
|
-
async executePipelineStep(context) {
|
|
2663
|
-
while (context.step !== AppPipelineStep.FINISH) switch (context.step) {
|
|
2664
|
-
case AppPipelineStep.START:
|
|
2665
|
-
await this.executePipelineStepStart(context);
|
|
2666
|
-
break;
|
|
2667
|
-
case AppPipelineStep.LOOKUP:
|
|
2668
|
-
await this.executePipelineStepLookup(context);
|
|
2669
|
-
break;
|
|
2670
|
-
case AppPipelineStep.CHILD_BEFORE:
|
|
2671
|
-
await this.executePipelineStepChildBefore(context);
|
|
2672
|
-
break;
|
|
2673
|
-
case AppPipelineStep.CHILD_DISPATCH:
|
|
2674
|
-
await this.executePipelineStepChildDispatch(context);
|
|
2675
|
-
break;
|
|
2676
|
-
case AppPipelineStep.CHILD_AFTER:
|
|
2677
|
-
await this.executePipelineStepChildAfter(context);
|
|
2678
|
-
break;
|
|
2679
|
-
default:
|
|
2680
|
-
context.step = AppPipelineStep.FINISH;
|
|
2681
|
-
break;
|
|
2682
|
-
}
|
|
2683
|
-
await this.executePipelineStepFinish(context);
|
|
2684
|
-
}
|
|
2685
|
-
async executePipelineStepStart(context) {
|
|
2686
|
-
if (this.hooks.hasListeners(HookName.START)) await this.hooks.trigger(HookName.START, context.event);
|
|
2687
|
-
if (context.event.dispatched) context.step = AppPipelineStep.FINISH;
|
|
2688
|
-
else context.step = AppPipelineStep.LOOKUP;
|
|
2689
|
-
}
|
|
2690
|
-
async executePipelineStepLookup(context) {
|
|
2691
|
-
if (typeof context.matches === "undefined" || context.matchesPath !== context.event.path) {
|
|
2692
|
-
context.matches = this.router.lookup(context.event.path, context.event.method);
|
|
2693
|
-
context.matchesPath = context.event.path;
|
|
2694
|
-
}
|
|
2695
|
-
const { matches } = context;
|
|
2696
|
-
while (!context.event.dispatched && context.matchIndex < matches.length) {
|
|
2697
|
-
const { route } = matches[context.matchIndex];
|
|
2698
|
-
if (route.data.type === RouteEntryType.HANDLER) {
|
|
2699
|
-
const handler = route.data.data;
|
|
2700
|
-
if (context.event.error && handler.type === HandlerType.CORE || !context.event.error && handler.type === HandlerType.ERROR) {
|
|
2701
|
-
context.matchIndex++;
|
|
2702
|
-
continue;
|
|
2703
|
-
}
|
|
2704
|
-
const { method } = route;
|
|
2705
|
-
if (method) context.event.methodsAllowed.add(method);
|
|
2706
|
-
if (!matchHandlerMethod(method, context.event.method)) {
|
|
2707
|
-
context.matchIndex++;
|
|
2708
|
-
continue;
|
|
2709
|
-
}
|
|
2710
|
-
}
|
|
2711
|
-
if (this.hooks.hasListeners(HookName.CHILD_MATCH)) await this.hooks.trigger(HookName.CHILD_MATCH, context.event);
|
|
2712
|
-
if (context.event.dispatched) {
|
|
2713
|
-
context.step = AppPipelineStep.FINISH;
|
|
2714
|
-
return;
|
|
2715
|
-
}
|
|
2716
|
-
if (context.event.path !== context.matchesPath) {
|
|
2717
|
-
context.matches = void 0;
|
|
2718
|
-
context.matchIndex = 0;
|
|
2719
|
-
context.step = AppPipelineStep.LOOKUP;
|
|
2720
|
-
return;
|
|
2721
|
-
}
|
|
2722
|
-
context.step = AppPipelineStep.CHILD_BEFORE;
|
|
2723
|
-
return;
|
|
2724
|
-
}
|
|
2725
|
-
context.step = AppPipelineStep.FINISH;
|
|
2726
|
-
}
|
|
2727
|
-
async executePipelineStepChildBefore(context) {
|
|
2728
|
-
if (this.hooks.hasListeners(HookName.CHILD_DISPATCH_BEFORE)) await this.hooks.trigger(HookName.CHILD_DISPATCH_BEFORE, context.event);
|
|
2729
|
-
if (context.event.dispatched) {
|
|
2730
|
-
context.step = AppPipelineStep.FINISH;
|
|
2731
|
-
return;
|
|
2732
|
-
}
|
|
2733
|
-
if (context.event.path !== context.matchesPath) {
|
|
2734
|
-
context.matches = void 0;
|
|
2735
|
-
context.matchIndex = 0;
|
|
2736
|
-
context.step = AppPipelineStep.LOOKUP;
|
|
2737
|
-
return;
|
|
2738
|
-
}
|
|
2739
|
-
context.step = AppPipelineStep.CHILD_DISPATCH;
|
|
2740
|
-
}
|
|
2741
|
-
async executePipelineStepChildAfter(context) {
|
|
2742
|
-
if (this.hooks.hasListeners(HookName.CHILD_DISPATCH_AFTER)) await this.hooks.trigger(HookName.CHILD_DISPATCH_AFTER, context.event);
|
|
2743
|
-
if (context.event.dispatched) context.step = AppPipelineStep.FINISH;
|
|
2744
|
-
else context.step = AppPipelineStep.LOOKUP;
|
|
2745
|
-
}
|
|
2746
|
-
async executePipelineStepChildDispatch(context) {
|
|
2747
|
-
const match = context.matches?.[context.matchIndex];
|
|
2748
|
-
if (context.event.dispatched || typeof match === "undefined") {
|
|
2749
|
-
context.step = AppPipelineStep.FINISH;
|
|
2750
|
-
return;
|
|
2751
|
-
}
|
|
2752
|
-
const { route } = match;
|
|
2753
|
-
const { event } = context;
|
|
2754
|
-
const savedPath = event.path;
|
|
2755
|
-
const savedMountPath = event.mountPath;
|
|
2756
|
-
const savedParams = event.params;
|
|
2757
|
-
if (route.data.type === RouteEntryType.APP && typeof match.path === "string") {
|
|
2758
|
-
event.mountPath = cleanDoubleSlashes(`${event.mountPath}/${match.path}`);
|
|
2759
|
-
if (event.path === match.path) event.path = "/";
|
|
2760
|
-
else event.path = withLeadingSlash(event.path.substring(match.path.length));
|
|
2761
|
-
mergeMatchParams(event, match.params);
|
|
2762
|
-
} else if (route.data.type === RouteEntryType.HANDLER && typeof match.path === "string") mergeMatchParams(event, match.params);
|
|
2763
|
-
try {
|
|
2764
|
-
const parentMatches = context.matches;
|
|
2765
|
-
const parentMatchesPath = context.matchesPath;
|
|
2766
|
-
const nextMatchIndex = context.matchIndex + 1;
|
|
2767
|
-
event.setNext(async (error) => {
|
|
2768
|
-
if (error) event.error = createError(error);
|
|
2769
|
-
const pathChanged = event.path !== parentMatchesPath;
|
|
2770
|
-
const savedStep = context.step;
|
|
2771
|
-
const savedMatchIndex = context.matchIndex;
|
|
2772
|
-
const savedMatches = context.matches;
|
|
2773
|
-
const savedMatchesPath = context.matchesPath;
|
|
2774
|
-
context.step = AppPipelineStep.LOOKUP;
|
|
2775
|
-
context.matchIndex = pathChanged ? 0 : nextMatchIndex;
|
|
2776
|
-
context.matches = pathChanged ? void 0 : parentMatches;
|
|
2777
|
-
context.matchesPath = pathChanged ? void 0 : parentMatchesPath;
|
|
2778
|
-
try {
|
|
2779
|
-
await this.executePipelineStep(context);
|
|
2780
|
-
} finally {
|
|
2781
|
-
context.step = savedStep;
|
|
2782
|
-
context.matchIndex = savedMatchIndex;
|
|
2783
|
-
context.matches = savedMatches;
|
|
2784
|
-
context.matchesPath = savedMatchesPath;
|
|
2785
|
-
}
|
|
2786
|
-
return context.response;
|
|
2787
|
-
});
|
|
2788
|
-
const response = await route.data.data.dispatch(event);
|
|
2789
|
-
if (response) {
|
|
2790
|
-
context.response = response;
|
|
2791
|
-
event.dispatched = true;
|
|
2792
|
-
}
|
|
2793
|
-
} catch (e) {
|
|
2794
|
-
event.error = createError(e);
|
|
2795
|
-
if (this.hooks.hasListeners(HookName.ERROR)) await this.hooks.trigger(HookName.ERROR, event);
|
|
2796
|
-
}
|
|
2797
|
-
if (!event.dispatched) {
|
|
2798
|
-
event.path = savedPath;
|
|
2799
|
-
event.mountPath = savedMountPath;
|
|
2800
|
-
event.params = savedParams;
|
|
2801
|
-
}
|
|
2802
|
-
context.matchIndex++;
|
|
2803
|
-
context.step = AppPipelineStep.CHILD_AFTER;
|
|
2804
|
-
}
|
|
2805
|
-
async executePipelineStepFinish(context) {
|
|
2806
|
-
if (!context.event.error && !context.event.dispatched && context.isRoot && context.event.method === MethodName.OPTIONS) {
|
|
2807
|
-
if (context.event.methodsAllowed.has(MethodName.GET)) context.event.methodsAllowed.add(MethodName.HEAD);
|
|
2808
|
-
const options = [...context.event.methodsAllowed].map((key) => key.toUpperCase()).join(",");
|
|
2809
|
-
const optionsHeaders = new Headers(context.event.response.headers);
|
|
2810
|
-
optionsHeaders.set(HeaderName.ALLOW, options);
|
|
2811
|
-
context.response = new Response(options, {
|
|
2812
|
-
status: context.event.response.status || 200,
|
|
2813
|
-
headers: optionsHeaders
|
|
2814
|
-
});
|
|
2815
|
-
context.event.dispatched = true;
|
|
2816
|
-
}
|
|
2817
|
-
}
|
|
2818
2443
|
async dispatch(event) {
|
|
2819
2444
|
const savedPath = event.path;
|
|
2820
2445
|
const savedMountPath = event.mountPath;
|
|
@@ -2822,17 +2447,23 @@ var App = class App {
|
|
|
2822
2447
|
const savedAppOptions = event.appOptions;
|
|
2823
2448
|
const wasDispatching = event.isDispatching;
|
|
2824
2449
|
const isRoot = !wasDispatching;
|
|
2825
|
-
const context = {
|
|
2826
|
-
step: AppPipelineStep.START,
|
|
2827
|
-
event,
|
|
2828
|
-
isRoot,
|
|
2829
|
-
matchIndex: 0
|
|
2830
|
-
};
|
|
2831
2450
|
event.appOptions = this._options;
|
|
2832
2451
|
event.isDispatching = true;
|
|
2452
|
+
let response;
|
|
2833
2453
|
try {
|
|
2834
|
-
|
|
2835
|
-
|
|
2454
|
+
const matches = this.router.lookup(event.path, event.method);
|
|
2455
|
+
response = await this.runMatches(event, matches, event.path, 0);
|
|
2456
|
+
if (!event.error && !event.dispatched && isRoot && event.method === MethodName.OPTIONS) {
|
|
2457
|
+
if (event.methodsAllowed.has(MethodName.GET)) event.methodsAllowed.add(MethodName.HEAD);
|
|
2458
|
+
const options = [...event.methodsAllowed].map((key) => key.toUpperCase()).join(",");
|
|
2459
|
+
const optionsHeaders = new Headers(event.response.headers);
|
|
2460
|
+
optionsHeaders.set(HeaderName.ALLOW, options);
|
|
2461
|
+
response = new Response(options, {
|
|
2462
|
+
status: event.response.status || 200,
|
|
2463
|
+
headers: optionsHeaders
|
|
2464
|
+
});
|
|
2465
|
+
event.dispatched = true;
|
|
2466
|
+
}
|
|
2836
2467
|
} finally {
|
|
2837
2468
|
event.appOptions = savedAppOptions;
|
|
2838
2469
|
event.isDispatching = wasDispatching;
|
|
@@ -2842,7 +2473,57 @@ var App = class App {
|
|
|
2842
2473
|
event.params = savedParams;
|
|
2843
2474
|
}
|
|
2844
2475
|
}
|
|
2845
|
-
return
|
|
2476
|
+
return response;
|
|
2477
|
+
}
|
|
2478
|
+
/**
|
|
2479
|
+
* Walk the matched routes for the current event, dispatching each
|
|
2480
|
+
* handler in order. Re-entered (recursively) from the `setNext`
|
|
2481
|
+
* continuation so `event.next()` resumes from the next match.
|
|
2482
|
+
*/
|
|
2483
|
+
async runMatches(event, matches, matchesPath, startIndex) {
|
|
2484
|
+
let i = startIndex;
|
|
2485
|
+
let response;
|
|
2486
|
+
while (!event.dispatched && i < matches.length) {
|
|
2487
|
+
const match = matches[i];
|
|
2488
|
+
const handler = match.route.data;
|
|
2489
|
+
if (event.error && handler.type === HandlerType.CORE || !event.error && handler.type === HandlerType.ERROR) {
|
|
2490
|
+
i++;
|
|
2491
|
+
continue;
|
|
2492
|
+
}
|
|
2493
|
+
const { method } = match.route;
|
|
2494
|
+
if (method) event.methodsAllowed.add(method);
|
|
2495
|
+
if (!matchHandlerMethod(method, event.method)) {
|
|
2496
|
+
i++;
|
|
2497
|
+
continue;
|
|
2498
|
+
}
|
|
2499
|
+
mergeMatchParams(event, match.params);
|
|
2500
|
+
const savedMountPath = event.mountPath;
|
|
2501
|
+
if (typeof match.path === "string") event.mountPath = match.path;
|
|
2502
|
+
const capturedMatches = matches;
|
|
2503
|
+
const capturedMatchesPath = matchesPath;
|
|
2504
|
+
const nextIndex = i + 1;
|
|
2505
|
+
event.setNext(async (error) => {
|
|
2506
|
+
if (error) event.error = createError(error);
|
|
2507
|
+
const pathChanged = event.path !== capturedMatchesPath;
|
|
2508
|
+
const nextMatches = pathChanged ? this.router.lookup(event.path, event.method) : capturedMatches;
|
|
2509
|
+
const nextMatchesPath = pathChanged ? event.path : capturedMatchesPath;
|
|
2510
|
+
const nextStart = pathChanged ? 0 : nextIndex;
|
|
2511
|
+
return this.runMatches(event, nextMatches, nextMatchesPath, nextStart);
|
|
2512
|
+
});
|
|
2513
|
+
try {
|
|
2514
|
+
const dispatchResponse = await handler.dispatch(event);
|
|
2515
|
+
if (dispatchResponse) {
|
|
2516
|
+
response = dispatchResponse;
|
|
2517
|
+
event.dispatched = true;
|
|
2518
|
+
}
|
|
2519
|
+
} catch (e) {
|
|
2520
|
+
event.error = createError(e);
|
|
2521
|
+
} finally {
|
|
2522
|
+
event.mountPath = savedMountPath;
|
|
2523
|
+
}
|
|
2524
|
+
i++;
|
|
2525
|
+
}
|
|
2526
|
+
return response;
|
|
2846
2527
|
}
|
|
2847
2528
|
delete(...input) {
|
|
2848
2529
|
this.useForMethod(MethodName.DELETE, ...input);
|
|
@@ -2889,10 +2570,7 @@ var App = class App {
|
|
|
2889
2570
|
this.register({
|
|
2890
2571
|
path: joinPaths(this._path, path, handler.path),
|
|
2891
2572
|
method,
|
|
2892
|
-
data:
|
|
2893
|
-
type: RouteEntryType.HANDLER,
|
|
2894
|
-
data: handler
|
|
2895
|
-
}
|
|
2573
|
+
data: handler
|
|
2896
2574
|
});
|
|
2897
2575
|
}
|
|
2898
2576
|
}
|
|
@@ -2904,24 +2582,14 @@ var App = class App {
|
|
|
2904
2582
|
continue;
|
|
2905
2583
|
}
|
|
2906
2584
|
if (isAppInstance(item)) {
|
|
2907
|
-
|
|
2908
|
-
this.register({
|
|
2909
|
-
path: joinPaths(this._path, path),
|
|
2910
|
-
data: {
|
|
2911
|
-
type: RouteEntryType.APP,
|
|
2912
|
-
data: item
|
|
2913
|
-
}
|
|
2914
|
-
});
|
|
2585
|
+
this.flatten(item, path);
|
|
2915
2586
|
continue;
|
|
2916
2587
|
}
|
|
2917
2588
|
if (isHandler(item)) {
|
|
2918
2589
|
this.register({
|
|
2919
2590
|
path: joinPaths(this._path, path, item.path),
|
|
2920
2591
|
method: item.method,
|
|
2921
|
-
data:
|
|
2922
|
-
type: RouteEntryType.HANDLER,
|
|
2923
|
-
data: item
|
|
2924
|
-
}
|
|
2592
|
+
data: item
|
|
2925
2593
|
});
|
|
2926
2594
|
continue;
|
|
2927
2595
|
}
|
|
@@ -2930,10 +2598,7 @@ var App = class App {
|
|
|
2930
2598
|
this.register({
|
|
2931
2599
|
path: joinPaths(this._path, path, handler.path),
|
|
2932
2600
|
method: handler.method,
|
|
2933
|
-
data:
|
|
2934
|
-
type: RouteEntryType.HANDLER,
|
|
2935
|
-
data: handler
|
|
2936
|
-
}
|
|
2601
|
+
data: handler
|
|
2937
2602
|
});
|
|
2938
2603
|
continue;
|
|
2939
2604
|
}
|
|
@@ -2943,90 +2608,48 @@ var App = class App {
|
|
|
2943
2608
|
return this;
|
|
2944
2609
|
}
|
|
2945
2610
|
/**
|
|
2946
|
-
*
|
|
2611
|
+
* Snapshot a child App's routes and plugin registry into this
|
|
2612
|
+
* one. Each route's path is prefixed with `this._path`, the
|
|
2613
|
+
* supplied mount `path`, and the route's own path (in that
|
|
2614
|
+
* order); the resulting entry is registered on this App's
|
|
2615
|
+
* router. The child app is not retained — late mutations on it
|
|
2616
|
+
* after this call do not propagate.
|
|
2617
|
+
*
|
|
2618
|
+
* @protected
|
|
2947
2619
|
*/
|
|
2948
|
-
|
|
2949
|
-
|
|
2620
|
+
flatten(child, path) {
|
|
2621
|
+
for (const name of child.plugins.keys()) if (this._plugins.has(name)) throw new PluginAlreadyInstalledError(name);
|
|
2622
|
+
for (const [name, version] of child.plugins) this._plugins.set(name, version);
|
|
2623
|
+
for (const route of child.routes) this.register({
|
|
2624
|
+
path: joinPaths(this._path, path, route.path),
|
|
2625
|
+
method: route.method,
|
|
2626
|
+
data: route.data
|
|
2627
|
+
});
|
|
2950
2628
|
}
|
|
2951
2629
|
/**
|
|
2952
|
-
*
|
|
2953
|
-
* or `undefined` if the plugin is not installed here.
|
|
2630
|
+
* Check if a plugin with the given name is installed on this App.
|
|
2954
2631
|
*/
|
|
2955
|
-
|
|
2956
|
-
return this.
|
|
2957
|
-
}
|
|
2958
|
-
install(plugin, context = {}) {
|
|
2959
|
-
if (this.plugins.has(plugin.name)) throw new PluginAlreadyInstalledError(plugin.name);
|
|
2960
|
-
const router = new App({
|
|
2961
|
-
name: plugin.name,
|
|
2962
|
-
router: this.router.clone()
|
|
2963
|
-
});
|
|
2964
|
-
plugin.install(router);
|
|
2965
|
-
if (context.path) this.use(context.path, router);
|
|
2966
|
-
else this.use(router);
|
|
2967
|
-
this.plugins.set(plugin.name, plugin.version);
|
|
2968
|
-
return this;
|
|
2632
|
+
hasPlugin(name) {
|
|
2633
|
+
return this._plugins.has(name);
|
|
2969
2634
|
}
|
|
2970
2635
|
/**
|
|
2971
|
-
*
|
|
2972
|
-
*
|
|
2973
|
-
*
|
|
2974
|
-
* The new router has:
|
|
2975
|
-
* - a fresh `stack` array of shallow-copied entries (handlers and child
|
|
2976
|
-
* routers are shared by reference; only the wrapping entries are new)
|
|
2977
|
-
* - the same `pathMatcher` reference (it is stateless)
|
|
2978
|
-
* - a fresh `Hooks` instance seeded with the current listeners
|
|
2979
|
-
* - a shallow copy of `_options`
|
|
2980
|
-
* - a fresh `plugins` map with the same entries
|
|
2981
|
-
*
|
|
2982
|
-
* Use this when the same logical router needs to be mounted under
|
|
2983
|
-
* multiple paths — each mount can receive its own clone so subsequent
|
|
2984
|
-
* mutations on one mount do not bleed into the others.
|
|
2636
|
+
* Get the version of an installed plugin by name, or `undefined`
|
|
2637
|
+
* if the plugin is not installed.
|
|
2985
2638
|
*/
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
name: this.name,
|
|
2989
|
-
path: this._path,
|
|
2990
|
-
options: { ...this._options },
|
|
2991
|
-
hooks: this.hooks.clone(),
|
|
2992
|
-
plugins: this.plugins,
|
|
2993
|
-
router: this.router.clone()
|
|
2994
|
-
});
|
|
2995
|
-
for (const route of this._routes) {
|
|
2996
|
-
if (route.data.type === RouteEntryType.APP) {
|
|
2997
|
-
next.register({
|
|
2998
|
-
path: route.path,
|
|
2999
|
-
data: {
|
|
3000
|
-
type: RouteEntryType.APP,
|
|
3001
|
-
data: route.data.data.clone()
|
|
3002
|
-
}
|
|
3003
|
-
});
|
|
3004
|
-
continue;
|
|
3005
|
-
}
|
|
3006
|
-
next.register({
|
|
3007
|
-
path: route.path,
|
|
3008
|
-
method: route.method,
|
|
3009
|
-
data: {
|
|
3010
|
-
type: RouteEntryType.HANDLER,
|
|
3011
|
-
data: route.data.data
|
|
3012
|
-
}
|
|
3013
|
-
});
|
|
3014
|
-
}
|
|
3015
|
-
return next;
|
|
3016
|
-
}
|
|
3017
|
-
on(name, fn, priority) {
|
|
3018
|
-
return this.hooks.addListener(name, fn, priority);
|
|
2639
|
+
getPluginVersion(name) {
|
|
2640
|
+
return this._plugins.get(name);
|
|
3019
2641
|
}
|
|
3020
|
-
|
|
3021
|
-
if (
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
this.
|
|
2642
|
+
install(plugin, context = {}) {
|
|
2643
|
+
if (this._plugins.has(plugin.name)) throw new PluginAlreadyInstalledError(plugin.name);
|
|
2644
|
+
const scratch = new App({ name: plugin.name });
|
|
2645
|
+
plugin.install(scratch);
|
|
2646
|
+
if (context.path) this.use(context.path, scratch);
|
|
2647
|
+
else this.use(scratch);
|
|
2648
|
+
this._plugins.set(plugin.name, plugin.version);
|
|
3026
2649
|
return this;
|
|
3027
2650
|
}
|
|
3028
2651
|
};
|
|
3029
2652
|
//#endregion
|
|
3030
2653
|
export { isError as $, fromWebHandler as A, DispatcherEvent as B, getRequestAcceptableEncodings as C, matchHandlerMethod as D, isRequestCacheable as E, defineErrorHandler as F, getRequestAcceptableContentTypes as G, sendRedirect as H, defineCoreHandler as I, sendFile as J, useRequestNegotiator as K, Handler as L, isWebHandlerProvider as M, fromNodeHandler as N, isHandler as O, fromNodeMiddleware as P, createError as Q, HandlerSymbol as R, getRequestAcceptableEncoding as S, getRequestAcceptableCharsets as T, sendFormat as U, sendStream as V, getRequestAcceptableContentType as W, sendAccepted as X, sendCreated as Y, toResponse as Z, getRequestIP as _, LinearRouter as a, appendResponseHeaderDirective as at, getRequestAcceptableLanguage as b, PluginNotInstalledError as c, AppError as ct, PluginError as d, AppEvent as dt, setResponseHeaderContentType as et, isPluginError as f, HeaderName as ft, getRequestProtocol as g, PathMatcher as h, TrieRouter as i, appendResponseHeader as it, isWebHandler as j, isHandlerOptions as k, PluginInstallError as l, ErrorSymbol as lt, isPath as m, LruCache as mt, normalizeAppOptions as n, setResponseHeaderInline as nt, buildRoutePathMatcher as o, createEventStream as ot, PluginErrorCode as p, MethodName as pt, getRequestHeader as q, SmartRouter as r, setResponseContentTypeByFileName as rt, isPlugin as s, serializeEventStreamMessage as st, App as t, setResponseHeaderAttachment as tt, PluginAlreadyInstalledError as u, setResponseCacheHeaders as ut, getRequestHostName as v, getRequestAcceptableCharset as w, getRequestAcceptableLanguages as x, matchRequestContentType as y, HandlerType as z };
|
|
3031
2654
|
|
|
3032
|
-
//# sourceMappingURL=src-
|
|
2655
|
+
//# sourceMappingURL=src-BfsqxIfL.mjs.map
|