@depup/h3 2.0.1-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/bin/h3.mjs +36 -0
  4. package/changes.json +5 -0
  5. package/dist/THIRD-PARTY-LICENSES.md +70 -0
  6. package/dist/_entries/bun.d.mts +6 -0
  7. package/dist/_entries/bun.mjs +16 -0
  8. package/dist/_entries/cloudflare.d.mts +6 -0
  9. package/dist/_entries/cloudflare.mjs +16 -0
  10. package/dist/_entries/deno.d.mts +6 -0
  11. package/dist/_entries/deno.mjs +16 -0
  12. package/dist/_entries/generic.d.mts +6 -0
  13. package/dist/_entries/generic.mjs +16 -0
  14. package/dist/_entries/node.d.mts +10 -0
  15. package/dist/_entries/node.mjs +19 -0
  16. package/dist/_entries/service-worker.d.mts +6 -0
  17. package/dist/_entries/service-worker.mjs +16 -0
  18. package/dist/_utils.mjs +240 -0
  19. package/dist/cache.mjs +599 -0
  20. package/dist/cache2.mjs +50 -0
  21. package/dist/cors.mjs +292 -0
  22. package/dist/docs/0.guide/0.index/index.md +117 -0
  23. package/dist/docs/0.guide/1.basics/0.lifecycle.md +68 -0
  24. package/dist/docs/0.guide/1.basics/1.routing.md +167 -0
  25. package/dist/docs/0.guide/1.basics/2.middleware.md +97 -0
  26. package/dist/docs/0.guide/1.basics/3.handler.md +165 -0
  27. package/dist/docs/0.guide/1.basics/4.response.md +171 -0
  28. package/dist/docs/0.guide/1.basics/5.error.md +117 -0
  29. package/dist/docs/0.guide/1.basics/6.nested-apps.md +57 -0
  30. package/dist/docs/0.guide/2.rules.md +698 -0
  31. package/dist/docs/0.guide/3.api/0.h3.md +144 -0
  32. package/dist/docs/0.guide/3.api/1.h3event.md +160 -0
  33. package/dist/docs/0.guide/4.advanced/0.plugins.md +50 -0
  34. package/dist/docs/0.guide/4.advanced/1.websocket.md +176 -0
  35. package/dist/docs/0.guide/4.advanced/2.nightly.md +13 -0
  36. package/dist/docs/1.utils/0.index/index.md +46 -0
  37. package/dist/docs/1.utils/1.request.md +447 -0
  38. package/dist/docs/1.utils/2.response.md +172 -0
  39. package/dist/docs/1.utils/3.cookie.md +33 -0
  40. package/dist/docs/1.utils/4.security.md +175 -0
  41. package/dist/docs/1.utils/5.proxy.md +57 -0
  42. package/dist/docs/1.utils/6.mcp.md +75 -0
  43. package/dist/docs/1.utils/7.more.md +117 -0
  44. package/dist/docs/1.utils/8.community.md +48 -0
  45. package/dist/docs/2.examples/0.index/index.md +17 -0
  46. package/dist/docs/2.examples/1.handle-cookie.md +67 -0
  47. package/dist/docs/2.examples/2.handle-query.md +76 -0
  48. package/dist/docs/2.examples/3.handle-session.md +210 -0
  49. package/dist/docs/2.examples/4.serve-static-assets.md +66 -0
  50. package/dist/docs/2.examples/5.stream-response.md +76 -0
  51. package/dist/docs/2.examples/6.validate-data.md +193 -0
  52. package/dist/docs/3.migration/0.index/index.md +204 -0
  53. package/dist/docs/README.md +37 -0
  54. package/dist/h3.d.mts +1669 -0
  55. package/dist/h3.mjs +1809 -0
  56. package/dist/index.d.mts +1634 -0
  57. package/dist/match.d.mts +123 -0
  58. package/dist/middleware.mjs +123 -0
  59. package/dist/normalize.mjs +645 -0
  60. package/dist/path.mjs +42 -0
  61. package/dist/proxy.mjs +254 -0
  62. package/dist/response.mjs +465 -0
  63. package/dist/rules/cache.d.mts +29 -0
  64. package/dist/rules/cache.mjs +163 -0
  65. package/dist/rules/compiler.d.mts +94 -0
  66. package/dist/rules/compiler.mjs +173 -0
  67. package/dist/rules/index.d.mts +77 -0
  68. package/dist/rules/index.mjs +34 -0
  69. package/dist/rules/proxy.d.mts +3 -0
  70. package/dist/rules/proxy.mjs +14 -0
  71. package/dist/tracing.d.mts +33 -0
  72. package/dist/tracing.mjs +89 -0
  73. package/package.json +148 -0
@@ -0,0 +1,123 @@
1
+ import { MatchResult, MatchedRouteRule, MatchedRouteRules, NormalizedRouteRules, RuleHandlers } from "./h3.mjs";
2
+
3
+ interface PreMergedRouteRules {
4
+ /** The pattern this layer is registered at (params lookup key). */
5
+ route: string;
6
+ /**
7
+ * Containment depth of {@link route}: how many patterns of the rule set
8
+ * strictly subsume it. Any set of simultaneously matched patterns is a
9
+ * containment chain (partial overlaps are rejected below), and depth
10
+ * strictly increases along a chain — if `a` subsumes `b` then every subsumer
11
+ * of `a` also subsumes `b`, plus `a` itself — so the most specific matched
12
+ * layer is the one with the highest rank.
13
+ *
14
+ * Layer *position* cannot be used for this — see {@link RouteRuleEntry.rank}
15
+ * for rou3's documented optional-syntax carve-out and what taking the last
16
+ * layer costs. The chain-cleanliness check below cannot catch that case
17
+ * either: those pairs are `subset`, not `partial`, so nothing throws and the
18
+ * compiler's fail-safe fallback never fires.
19
+ */
20
+ rank: number;
21
+ rules: PreMergedRouteRuleEntry[];
22
+ /**
23
+ * Rule names this chain reset with `false`, when any. The reset itself is
24
+ * applied here at build time, so the resolved `rules` cannot show it — but the
25
+ * cross-reading union has to tell "reset by this path" apart from "never
26
+ * matched" to stop a broader alternate reading resurrecting a permission
27
+ * (`mergeMatchedRouteRules`). Plain mode reads the same information off the
28
+ * `false` entries it merges per request; recording it keeps both modes — and
29
+ * the compiled table — resolving alike. The pattern that reset a rule is not
30
+ * kept: the union records the reset at this layer's {@link route}, which is
31
+ * inside every pattern of its chain, so only a pattern at least that specific
32
+ * may reinstate the rule — never more than plain mode allows.
33
+ */
34
+ resets?: string[];
35
+ }
36
+ interface PreMergedRouteRuleEntry extends RouteRuleEntry {
37
+ /**
38
+ * Patterns whose layers contributed to this rule (chain order), when different
39
+ * from `[route]` — used to merge exact per-rule `params` from only the layers
40
+ * that carried the rule.
41
+ */
42
+ paramRoutes?: string[];
43
+ }
44
+ /** Decide whether an alternate path reading may override a matched rule. */
45
+ type RouteOverridePredicate = (currentRoute: string, incomingRoute: string) => boolean;
46
+ /** One normalized rule registered for a route pattern. */
47
+ export interface RouteRuleEntry {
48
+ name: string;
49
+ route: string;
50
+ options: unknown;
51
+ handler?: MatchedRouteRule["handler"];
52
+ /**
53
+ * Pattern-containment depth. Required because rou3 result order is not
54
+ * containment order for optional/modifier parameters.
55
+ */
56
+ rank?: number;
57
+ }
58
+ /** A matched route layer containing rule data and route parameters. */
59
+ export interface RouteRuleLayer {
60
+ data: RouteRuleEntry[] | PreMergedRouteRules;
61
+ params?: Record<string, string>;
62
+ }
63
+ /**
64
+ * Merge served-path and alternate-reading layers. Alternate readings may add
65
+ * rules but override existing ones only when `canOverride` permits it.
66
+ */
67
+ export declare function mergeMatchedRouteRules(rawLayers: RouteRuleLayer[] | undefined, altLayers?: readonly (RouteRuleLayer[] | undefined)[], canOverride?: RouteOverridePredicate): MatchedRouteRules;
68
+ export interface RouteRulesMatcherOptions {
69
+ /**
70
+ * Base URL prefix for all rule patterns (trailing slash trimmed).
71
+ */
72
+ baseURL?: string;
73
+ /**
74
+ * Add or override rule handler constructors by name.
75
+ * Registry defaults are `headers`, `redirect`, `cors`; `cache` and
76
+ * `proxy` are opt-in (register them from `h3/rules/cache` / `h3/rules/proxy`).
77
+ * Setting a name to `undefined` makes that rule data-only.
78
+ */
79
+ handlers?: RuleHandlers;
80
+ /**
81
+ * Pre-merge compatible pattern chains at startup. Throws for partial overlaps
82
+ * or patterns that cannot be analyzed, such as regex parameters.
83
+ */
84
+ preMerge?: boolean;
85
+ }
86
+ export interface MatcherMemoizeOptions {
87
+ /**
88
+ * Maximum number of memoized `method + pathname` entries. On overflow the
89
+ * oldest entry not hit since the eviction hand last passed it is evicted
90
+ * (SIEVE). `0` (or negative) disables memoization.
91
+ * @default 1024
92
+ */
93
+ max?: number;
94
+ }
95
+ export type RouteRulesMatcher = (method: string, pathname: string) => MatchResult;
96
+ /** A `findAllRoutes`-compatible lookup, as produced by `rou3/compiler` codegen. */
97
+ export type FindRouteRules = (method: string, pathname: string) => RouteRuleLayer[];
98
+ /**
99
+ * Create a route-rules matcher from a **normalized** rule set (see {@link normalizeRouteRules}).
100
+ * Returns `(method, pathname) => { routeRules, matchedRules, routeRuleMiddleware }`.
101
+ */
102
+ export declare function createRouteRulesMatcher(rules: Record<string, NormalizedRouteRules>, opts?: RouteRulesMatcherOptions): RouteRulesMatcher;
103
+ /**
104
+ * Create a matcher from a `findAllRoutes`-compatible lookup, typically generated
105
+ * by `h3/rules/compiler`.
106
+ *
107
+ * Results are not memoized. The default override guard fails closed when route
108
+ * specificity is ambiguous.
109
+ */
110
+ export declare function createMatcherFromFind(findRouteRules: FindRouteRules, canOverride?: RouteOverridePredicate): RouteRulesMatcher;
111
+ /**
112
+ * Memoize matches by method and pathname with a 1024-entry cap by default.
113
+ * Returned objects are shared and must be treated as immutable.
114
+ *
115
+ * Eviction is SIEVE: insertion-ordered like a FIFO, but an entry hit since the
116
+ * hand last passed it survives that pass. Plain FIFO evicts a hot static path
117
+ * on the same schedule as the one-shot dynamic paths that displaced it, so a
118
+ * mixed workload (a small hot set plus high-cardinality `/:id` traffic) loses
119
+ * cached entries it is about to ask for again. Unlike LRU this costs no map
120
+ * mutation on a hit — only a boolean store — which keeps the hot path the
121
+ * single lookup the memo exists to provide.
122
+ */
123
+ export declare function memoizeRouteRulesMatcher(matcher: RouteRulesMatcher, opts?: MatcherMemoizeOptions): RouteRulesMatcher;
@@ -0,0 +1,123 @@
1
+ import { kNotFound, normalizeRoute } from "./response.mjs";
2
+ import { addRoute, createRouter, findRoute } from "rou3";
3
+ const LITERAL_ROUTE_RE = /^(?:\/[^/:*(){}\\?^\0- "#<>`\x7F-\uFFFF]+)*\/?$/;
4
+ const LITERAL_PREFIX_ROUTE_RE = /^((?:\/[^/:*(){}\\?^\0- "#<>`\x7F-\uFFFF]+)*)\/\*\*\/?$/;
5
+ function createRouteMatcher(route) {
6
+ if (route.charCodeAt(0) !== 47) route = `/${route}`;
7
+ const prefixMatch = LITERAL_PREFIX_ROUTE_RE.exec(route);
8
+ if (prefixMatch) {
9
+ const base = prefixMatch[1];
10
+ const prefix = `${base}/`;
11
+ return (pathname) => {
12
+ if (pathname === base || pathname === prefix) return;
13
+ if (!pathname.startsWith(prefix)) return false;
14
+ const rest = trimTrailingSlash(pathname.slice(prefix.length));
15
+ return {
16
+ 0: rest,
17
+ _: rest
18
+ };
19
+ };
20
+ }
21
+ if (LITERAL_ROUTE_RE.test(route)) {
22
+ const base = route.endsWith("/") ? route.slice(0, -1) : route;
23
+ return (pathname) => pathname === base || pathname === `${base}/` ? void 0 : false;
24
+ }
25
+ const router = createRouter();
26
+ addRoute(router, "", route, true);
27
+ return (pathname) => {
28
+ const match = findRoute(router, "", pathname);
29
+ return match ? match.params : false;
30
+ };
31
+ }
32
+ function trimTrailingSlash(rest) {
33
+ return rest.endsWith("/") ? rest.slice(0, -1) : rest;
34
+ }
35
+ function defineMiddleware(input) {
36
+ return input;
37
+ }
38
+ function normalizeMiddleware(input, opts = {}) {
39
+ const matcher = createMatcher(opts);
40
+ if (!matcher && (input.length > 1 || input.constructor?.name === "AsyncFunction")) return input;
41
+ return (event, next) => {
42
+ if (matcher && !matcher(event)) return next();
43
+ const res = input(event, next);
44
+ return res === void 0 || res === kNotFound ? next() : res;
45
+ };
46
+ }
47
+ function createMatcher(opts) {
48
+ if (!opts.route && !opts.method && !opts.match) return;
49
+ const routeMatcher = opts.route ? createRouteMatcher(normalizeRoute(opts.route)) : void 0;
50
+ const method = opts.method?.toUpperCase();
51
+ return function _middlewareMatcher(event) {
52
+ if (method) {
53
+ const reqMethod = event.req.method.toUpperCase();
54
+ if (reqMethod !== method && !(method === "GET" && reqMethod === "HEAD")) return false;
55
+ }
56
+ if (opts.match && !opts.match(event)) return false;
57
+ if (!routeMatcher) return true;
58
+ const params = routeMatcher(event.url.pathname);
59
+ if (params === false) return false;
60
+ if (params) event.context.middlewareParams = {
61
+ ...event.context.middlewareParams,
62
+ ...params
63
+ };
64
+ return true;
65
+ };
66
+ }
67
+ function composeMiddleware(middleware) {
68
+ let chain = (event, handler) => handler(event);
69
+ for (let i = middleware.length - 1; i >= 0; i--) {
70
+ const fn = middleware[i];
71
+ const inner = chain;
72
+ chain = (event, handler) => callLayer(fn, event, handler, inner);
73
+ }
74
+ return chain;
75
+ }
76
+ function composeHandler(middleware, handler) {
77
+ const chain = composeMiddleware(middleware);
78
+ return function _composedHandler(event) {
79
+ return chain(event, handler);
80
+ };
81
+ }
82
+ function callMiddleware(event, middleware, handler, index = 0) {
83
+ return index === middleware.length ? handler(event) : callLayer(middleware[index], event, handler, (_event, _handler) => callMiddleware(_event, middleware, _handler, index + 1));
84
+ }
85
+ function callLayer(fn, event, handler, inner) {
86
+ let nextCalled;
87
+ let nextResult;
88
+ const next = () => {
89
+ if (nextCalled) return nextResult;
90
+ nextCalled = true;
91
+ nextResult = inner(event, handler);
92
+ return nextResult;
93
+ };
94
+ const ret = fn(event, next);
95
+ return isUnhandledResponse(ret) ? next() : typeof ret?.then === "function" ? ret.then((resolved) => isUnhandledResponse(resolved) ? next() : resolved) : ret;
96
+ }
97
+ function isUnhandledResponse(val) {
98
+ return val === void 0 || val === kNotFound;
99
+ }
100
+ function toMiddleware(input) {
101
+ let h = input.handler || input;
102
+ let isFunction = typeof h === "function";
103
+ if (!isFunction && typeof input?.fetch === "function") {
104
+ isFunction = true;
105
+ h = function _fetchHandler(event) {
106
+ return input.fetch(event.req);
107
+ };
108
+ }
109
+ if (!isFunction) return function noopMiddleware(event, next) {
110
+ return next();
111
+ };
112
+ if (h.length === 2) return h;
113
+ return function _middlewareHandler(event, next) {
114
+ const res = h(event);
115
+ return typeof res?.then === "function" ? res.then((r) => {
116
+ return is404(r) ? next() : r;
117
+ }) : is404(res) ? next() : res;
118
+ };
119
+ }
120
+ function is404(val) {
121
+ return isUnhandledResponse(val) || val?.status === 404 && val instanceof Response;
122
+ }
123
+ export { callMiddleware, composeHandler, composeMiddleware, defineMiddleware, normalizeMiddleware, toMiddleware };