@lovrozagar/honey 0.5.1 → 0.5.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/README.md +1 -1
- package/dist/codegen-route-tree-intern.d.ts +29 -0
- package/dist/codegen-route-tree-intern.d.ts.map +1 -0
- package/dist/codegen.d.ts +1 -1
- package/dist/codegen.d.ts.map +1 -1
- package/dist/tree.d.ts +0 -34
- package/dist/tree.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/codegen-route-tree-intern.ts +171 -0
- package/src/codegen.ts +184 -106
- package/src/tree.ts +0 -393
package/README.md
CHANGED
|
@@ -1539,7 +1539,7 @@ Import features from their path.
|
|
|
1539
1539
|
| `honey/codegen-go-cli` | `generateGoCLI` |
|
|
1540
1540
|
| `honey/codegen/extract` | ts-morph chain extractor |
|
|
1541
1541
|
| `honey/build` | `createBuildPlugin` |
|
|
1542
|
-
| `honey/tree` | Tree types + `mergeTree`
|
|
1542
|
+
| `honey/tree` | Tree types + `mergeTree` |
|
|
1543
1543
|
| `honey/cli` | `honey` binary |
|
|
1544
1544
|
| `honey/errors` | `defineErrors` (also on the root export) |
|
|
1545
1545
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural intern for generated route-tree modules.
|
|
3
|
+
*
|
|
4
|
+
* Handler objects stay unique (`.routeTree()` patches `fn`/`mw`/`iv`/`os` onto each
|
|
5
|
+
* `H*`). Values they point at — JSON Schema subtrees, meta objects, selector strings,
|
|
6
|
+
* error-key arrays — are shared by identity when their JSON is identical.
|
|
7
|
+
*/
|
|
8
|
+
export type ForcedPrefix = "I" | "M" | "O" | "P";
|
|
9
|
+
export declare class InternPool {
|
|
10
|
+
private readonly counts;
|
|
11
|
+
private readonly firstSeen;
|
|
12
|
+
private readonly forced;
|
|
13
|
+
private readonly forcedPrefix;
|
|
14
|
+
private readonly ids;
|
|
15
|
+
private readonly prefixCounters;
|
|
16
|
+
private readonly values;
|
|
17
|
+
private sealed;
|
|
18
|
+
count(v: unknown): void;
|
|
19
|
+
force(v: unknown, prefix: ForcedPrefix): void;
|
|
20
|
+
seal(): void;
|
|
21
|
+
id(v: unknown): string | undefined;
|
|
22
|
+
expr(v: unknown): string;
|
|
23
|
+
emitConstLines(): string[];
|
|
24
|
+
private shouldIntern;
|
|
25
|
+
private prefixFor;
|
|
26
|
+
private bump;
|
|
27
|
+
private printExpanded;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=codegen-route-tree-intern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codegen-route-tree-intern.d.ts","sourceRoot":"","sources":["../src/codegen-route-tree-intern.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,YAAY,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AA4BhD,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,MAAM,CAAQ;IAEtB,KAAK,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAsBtB;IAED,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAU5C;IAED,IAAI,IAAI,IAAI,CAWX;IAED,EAAE,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGjC;IAED,IAAI,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAKvB;IAED,cAAc,IAAI,MAAM,EAAE,CAczB;IAED,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,aAAa;CAerB"}
|
package/dist/codegen.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type InvalidateCheckConfig } from "./invalidate-check.ts";
|
|
2
2
|
import type { Honey } from "./index.ts";
|
|
3
|
-
import {
|
|
3
|
+
import type { TreeNode } from "./tree.ts";
|
|
4
4
|
export { prepareCodegen } from "./codegen-loaders.ts";
|
|
5
5
|
export type { InvalidateCheckConfig, InvalidateCheckLevel } from "./invalidate-check.ts";
|
|
6
6
|
export { toYaml, yamlSiblingPath } from "./yaml.ts";
|
package/dist/codegen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"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"}
|
package/dist/tree.d.ts
CHANGED
|
@@ -76,40 +76,6 @@ export type RouteTree = {
|
|
|
76
76
|
root: TreeNode;
|
|
77
77
|
};
|
|
78
78
|
export declare function createNode(): TreeNode;
|
|
79
|
-
/** Dense array, or unique-value table + per-row indices when that JSON is smaller. */
|
|
80
|
-
export type PackedColumn<T> = readonly T[] | {
|
|
81
|
-
readonly t: readonly T[];
|
|
82
|
-
readonly x: readonly number[];
|
|
83
|
-
};
|
|
84
|
-
/** Columnar packed route table consumed by `assembleRouteTree`. */
|
|
85
|
-
export type PackedRouteTable = {
|
|
86
|
-
/** Boundary error key. A string applies to every route. */
|
|
87
|
-
b?: Array<string | null> | string;
|
|
88
|
-
/** Per-route error-key lists. When `k` is set, each row is the extras beyond `k`. */
|
|
89
|
-
e?: PackedColumn<readonly string[] | null>;
|
|
90
|
-
/** Per-route input schemas. */
|
|
91
|
-
i?: PackedColumn<unknown>;
|
|
92
|
-
/** Error keys shared by every route; prepended onto each `e` row at inflate. */
|
|
93
|
-
k?: readonly string[];
|
|
94
|
-
/** Per-route meta objects. Keys are `u` indices when `u` is set. */
|
|
95
|
-
m?: PackedColumn<Record<string, unknown> | null>;
|
|
96
|
-
/** Per-route output schemas. */
|
|
97
|
-
o?: PackedColumn<unknown>;
|
|
98
|
-
/** Route selectors (`METHOD /path`), same order as the other columns. */
|
|
99
|
-
s: readonly string[];
|
|
100
|
-
/** Meta key dictionary; packed meta objects use decimal index keys. */
|
|
101
|
-
u?: readonly string[];
|
|
102
|
-
};
|
|
103
|
-
export declare function packInputSchemas(iv: unknown): unknown;
|
|
104
|
-
export declare function unpackInputSchemas(iv: unknown): unknown;
|
|
105
|
-
export declare function packOutputSchemas(os: unknown): unknown;
|
|
106
|
-
export declare function unpackOutputSchemas(os: unknown): unknown;
|
|
107
|
-
/**
|
|
108
|
-
* Inflate a packed route table into a live route tree.
|
|
109
|
-
* Each handler and each `ek` Set is a unique object so `.routeTree()` patches
|
|
110
|
-
* cannot cross-wire routes.
|
|
111
|
-
*/
|
|
112
|
-
export declare function assembleRouteTree(table: PackedRouteTable): RouteTree;
|
|
113
79
|
export declare function insertRoute(root: TreeNode, method: HttpMethod | "ALL", path: string, handler: RouteHandler): void;
|
|
114
80
|
export declare function matchRoute(root: TreeNode, method: HttpMethod, path: string): MatchResult;
|
|
115
81
|
export declare function insertWsRoute(root: TreeNode, path: string, handler: WSRouteHandler): void;
|
package/dist/tree.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAEnD,YAAY,EAAE,UAAU,EAAE,CAAA;AAE1B,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAEjF,MAAM,MAAM,YAAY,GAAG;IAC1B,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACzD,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,4GAA4G;IAC5G,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,oEAAoE;IACpE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,GAAG,IAAI,CAAA;IACxD,wEAAwE;IACxE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACf,uBAAuB;IACvB,EAAE,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAClD,4DAA4D;IAC5D,EAAE,EAAE,eAAe,GAAG,IAAI,CAAA;IAC1B,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAClC,kFAAkF;IAClF,EAAE,EAAE,KAAK,CACR,CACC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,KAC5D,OAAO,CAAC,QAAQ,CAAC,CACtB,CAAA;IACD,mEAAmE;IACnE,EAAE,EAAE,eAAe,GAAG,IAAI,CAAA;IAC1B,yEAAyE;IACzE,EAAE,EAAE,eAAe,GAAG,IAAI,CAAA;IAC1B,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAA;CACV,CAAA;AAED,YAAY,EAAE,eAAe,EAAE,CAAA;AAE/B,MAAM,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACf,EAAE,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IACtB,EAAE,EAAE,eAAe,GAAG,IAAI,CAAA;IAC1B,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAClC,EAAE,EAAE,KAAK,CACR,CACC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,KAC5D,OAAO,CAAC,QAAQ,CAAC,CACtB,CAAA;IACD,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAA;CACV,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACtB,gEAAgE;IAChE,CAAC,EAAE;QAAE,CAAC,EAAE,QAAQ,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpC,qFAAqF;IACrF,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,mEAAmE;IACnE,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,KAAK,EAAE,YAAY,CAAC,GAAG,IAAI,CAAA;IAClD,mEAAmE;IACnE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC3B,qEAAqE;IACrE,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,KAAK,EAAE,YAAY,CAAC,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpE,sCAAsC;IACtC,EAAE,EAAE,cAAc,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAIrD;AAED,MAAM,MAAM,WAAW,GACpB;IAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,OAAO,EAAE,KAAK,CAAA;CAAE,GACzC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACxE,IAAI,CAAA;AAEP,MAAM,WAAW,SAAS;CAAG;AAE7B,MAAM,MAAM,SAAS,GAAG;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAC/B,IAAI,EAAE,QAAQ,CAAA;CACd,CAAA;AAWD,wBAAgB,UAAU,IAAI,QAAQ,CAQrC;AAMD,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAmEjH;AAqBD,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAsDxF;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAoCzF;AAED,wBAAgB,YAAY,CAC3B,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,GACV;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,CAqCpE;AAyED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAGlE;AAED,KAAK,SAAS,GAAG,SAAS,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAmBjE,wBAAgB,SAAS,CAAC,GAAG,KAAK,EAAE,SAAS,EAAE,GAAG,SAAS,CAc1D"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural intern for generated route-tree modules.
|
|
3
|
+
*
|
|
4
|
+
* Handler objects stay unique (`.routeTree()` patches `fn`/`mw`/`iv`/`os` onto each
|
|
5
|
+
* `H*`). Values they point at — JSON Schema subtrees, meta objects, selector strings,
|
|
6
|
+
* error-key arrays — are shared by identity when their JSON is identical.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type ForcedPrefix = "I" | "M" | "O" | "P"
|
|
10
|
+
|
|
11
|
+
const STRING_MIN = 8
|
|
12
|
+
const NUMBER_MIN = 8
|
|
13
|
+
|
|
14
|
+
function canonical(v: unknown): string {
|
|
15
|
+
if (v === undefined) return "__undefined__"
|
|
16
|
+
return JSON.stringify(v) as string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function valueDepth(v: unknown): number {
|
|
20
|
+
if (v === null || typeof v !== "object") return 0
|
|
21
|
+
let max = 0
|
|
22
|
+
if (Array.isArray(v)) {
|
|
23
|
+
for (const x of v) {
|
|
24
|
+
const d = valueDepth(x)
|
|
25
|
+
if (d > max) max = d
|
|
26
|
+
}
|
|
27
|
+
return max + 1
|
|
28
|
+
}
|
|
29
|
+
for (const x of Object.values(v as Record<string, unknown>)) {
|
|
30
|
+
if (x === undefined) continue
|
|
31
|
+
const d = valueDepth(x)
|
|
32
|
+
if (d > max) max = d
|
|
33
|
+
}
|
|
34
|
+
return max + 1
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class InternPool {
|
|
38
|
+
private readonly counts = new Map<string, number>()
|
|
39
|
+
private readonly firstSeen: string[] = []
|
|
40
|
+
private readonly forced = new Set<string>()
|
|
41
|
+
private readonly forcedPrefix = new Map<string, ForcedPrefix>()
|
|
42
|
+
private readonly ids = new Map<string, string>()
|
|
43
|
+
private readonly prefixCounters = new Map<string, number>()
|
|
44
|
+
private readonly values = new Map<string, unknown>()
|
|
45
|
+
private sealed = false
|
|
46
|
+
|
|
47
|
+
count(v: unknown): void {
|
|
48
|
+
if (v === null || v === undefined) return
|
|
49
|
+
const t = typeof v
|
|
50
|
+
if (t === "boolean") return
|
|
51
|
+
if (t === "number") {
|
|
52
|
+
if (Number.isFinite(v) && String(v).length >= NUMBER_MIN) this.bump(v)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
if (t === "string") {
|
|
56
|
+
if ((v as string).length >= STRING_MIN) this.bump(v)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
if (t === "object") {
|
|
60
|
+
this.bump(v)
|
|
61
|
+
if (Array.isArray(v)) {
|
|
62
|
+
for (const x of v) this.count(x)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
for (const x of Object.values(v as Record<string, unknown>)) {
|
|
66
|
+
if (x !== undefined) this.count(x)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
force(v: unknown, prefix: ForcedPrefix): void {
|
|
72
|
+
if (v === null || v === undefined) return
|
|
73
|
+
const c = canonical(v)
|
|
74
|
+
if (!this.values.has(c)) {
|
|
75
|
+
this.values.set(c, v)
|
|
76
|
+
this.firstSeen.push(c)
|
|
77
|
+
this.counts.set(c, this.counts.get(c) ?? 1)
|
|
78
|
+
}
|
|
79
|
+
this.forced.add(c)
|
|
80
|
+
if (!this.forcedPrefix.has(c)) this.forcedPrefix.set(c, prefix)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
seal(): void {
|
|
84
|
+
if (this.sealed) return
|
|
85
|
+
this.sealed = true
|
|
86
|
+
for (const c of this.firstSeen) {
|
|
87
|
+
const v = this.values.get(c)
|
|
88
|
+
if (!this.shouldIntern(v)) continue
|
|
89
|
+
const prefix = this.prefixFor(c, v)
|
|
90
|
+
const n = this.prefixCounters.get(prefix) ?? 0
|
|
91
|
+
this.prefixCounters.set(prefix, n + 1)
|
|
92
|
+
this.ids.set(c, `${prefix}${n}`)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
id(v: unknown): string | undefined {
|
|
97
|
+
if (v === undefined) return undefined
|
|
98
|
+
return this.ids.get(canonical(v))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
expr(v: unknown): string {
|
|
102
|
+
if (v === undefined) return "undefined"
|
|
103
|
+
const ref = this.ids.get(canonical(v))
|
|
104
|
+
if (ref !== undefined) return ref
|
|
105
|
+
return this.printExpanded(v)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
emitConstLines(): string[] {
|
|
109
|
+
const items: Array<{ c: string; depth: number; id: string; seen: number; v: unknown }> = []
|
|
110
|
+
for (const [c, id] of this.ids) {
|
|
111
|
+
const v = this.values.get(c)
|
|
112
|
+
items.push({ c, depth: valueDepth(v), id, seen: this.firstSeen.indexOf(c), v })
|
|
113
|
+
}
|
|
114
|
+
items.sort((a, b) => a.depth - b.depth || a.seen - b.seen || a.id.localeCompare(b.id))
|
|
115
|
+
return items.map((it) => {
|
|
116
|
+
const kind = this.forcedPrefix.get(it.c)
|
|
117
|
+
let rhs = this.printExpanded(it.v)
|
|
118
|
+
if (kind === "I") rhs += ' as unknown as RouteHandler["iv"]'
|
|
119
|
+
else if (kind === "O") rhs += ' as unknown as RouteHandler["os"]'
|
|
120
|
+
return `const ${it.id} = ${rhs}`
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private shouldIntern(v: unknown): boolean {
|
|
125
|
+
if (v === null || v === undefined) return false
|
|
126
|
+
const c = canonical(v)
|
|
127
|
+
if (this.forced.has(c)) return true
|
|
128
|
+
const n = this.counts.get(c) ?? 0
|
|
129
|
+
if (n < 2) return false
|
|
130
|
+
const t = typeof v
|
|
131
|
+
if (t === "string") return (v as string).length >= STRING_MIN
|
|
132
|
+
if (t === "number") return Number.isFinite(v) && String(v).length >= NUMBER_MIN
|
|
133
|
+
if (Array.isArray(v) && v.length === 0) return false
|
|
134
|
+
return t === "object"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private prefixFor(c: string, v: unknown): string {
|
|
138
|
+
const forced = this.forcedPrefix.get(c)
|
|
139
|
+
if (forced !== undefined) return forced
|
|
140
|
+
if (typeof v === "string") return "T"
|
|
141
|
+
if (typeof v === "number") return "U"
|
|
142
|
+
if (Array.isArray(v)) return "A"
|
|
143
|
+
return "J"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private bump(v: unknown): void {
|
|
147
|
+
const c = canonical(v)
|
|
148
|
+
const n = (this.counts.get(c) ?? 0) + 1
|
|
149
|
+
this.counts.set(c, n)
|
|
150
|
+
if (n === 1) {
|
|
151
|
+
this.values.set(c, v)
|
|
152
|
+
this.firstSeen.push(c)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private printExpanded(v: unknown): string {
|
|
157
|
+
if (v === null) return "null"
|
|
158
|
+
if (v === true) return "true"
|
|
159
|
+
if (v === false) return "false"
|
|
160
|
+
const t = typeof v
|
|
161
|
+
if (t === "string") return JSON.stringify(v)
|
|
162
|
+
if (t === "number") return String(v)
|
|
163
|
+
if (Array.isArray(v)) return `[${v.map((x) => this.expr(x)).join(",")}]`
|
|
164
|
+
if (t === "object") {
|
|
165
|
+
const obj = v as Record<string, unknown>
|
|
166
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== undefined)
|
|
167
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${this.expr(obj[k])}`).join(",")}}`
|
|
168
|
+
}
|
|
169
|
+
return JSON.stringify(v) ?? "undefined"
|
|
170
|
+
}
|
|
171
|
+
}
|
package/src/codegen.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { InternPool } from "./codegen-route-tree-intern.ts"
|
|
1
2
|
import { deriveErrorEnvelopeName, deriveSchemaName, isErrorEnvelope, shortHash } from "./codegen-schema-naming.ts"
|
|
2
3
|
import type { SchemaNameContext } from "./codegen-schema-naming.ts"
|
|
3
4
|
import { effectJsonSchemaFn, loadEffectJsonSchema, loadToJSONSchema, toJSONSchemaFn } from "./codegen-loaders.ts"
|
|
@@ -22,14 +23,7 @@ import {
|
|
|
22
23
|
import { ERROR_META } from "./errors.ts"
|
|
23
24
|
import type { ErrorMetaEntry } from "./errors.ts"
|
|
24
25
|
import type { Honey } from "./index.ts"
|
|
25
|
-
import {
|
|
26
|
-
type PackedColumn,
|
|
27
|
-
type PackedRouteTable,
|
|
28
|
-
packInputSchemas,
|
|
29
|
-
packOutputSchemas,
|
|
30
|
-
type RouteHandler,
|
|
31
|
-
type TreeNode,
|
|
32
|
-
} from "./tree.ts"
|
|
26
|
+
import type { RouteHandler, TreeNode } from "./tree.ts"
|
|
33
27
|
import { irToTs } from "./ts-type-emitter.ts"
|
|
34
28
|
import { createTypeEmitState, emitSchemaType } from "./type-emitter.ts"
|
|
35
29
|
import type { TypeEmitState } from "./type-emitter.ts"
|
|
@@ -1998,98 +1992,129 @@ function collectRoutesForTree(node: TreeNode, currentPath: string, routes: Route
|
|
|
1998
1992
|
}
|
|
1999
1993
|
}
|
|
2000
1994
|
|
|
2001
|
-
|
|
2002
|
-
|
|
1995
|
+
type TreeBuild = {
|
|
1996
|
+
children: Record<string, TreeBuild>
|
|
1997
|
+
dynamic: { child: TreeBuild; name: string } | null
|
|
1998
|
+
handlers: Array<{ handler: string; method: string }>
|
|
1999
|
+
wildcard: {
|
|
2000
|
+
handlers: Array<{ handler: string; method: string }>
|
|
2001
|
+
name: string
|
|
2002
|
+
} | null
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
function createTreeBuild(): TreeBuild {
|
|
2006
|
+
return { children: {}, dynamic: null, handlers: [], wildcard: null }
|
|
2003
2007
|
}
|
|
2004
2008
|
|
|
2005
|
-
function
|
|
2006
|
-
const
|
|
2007
|
-
const
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2009
|
+
function serializeNode(node: TreeBuild): string {
|
|
2010
|
+
const staticEntries = Object.entries(node.children)
|
|
2011
|
+
const sExpr =
|
|
2012
|
+
staticEntries.length > 0
|
|
2013
|
+
? `S({ ${staticEntries.map(([k, v]) => `${JSON.stringify(k)}: ${serializeNode(v)}`).join(", ")} })`
|
|
2014
|
+
: "E"
|
|
2015
|
+
|
|
2016
|
+
const dExpr = node.dynamic
|
|
2017
|
+
? `{ n: ${JSON.stringify(node.dynamic.name)}, c: ${serializeNode(node.dynamic.child)} }`
|
|
2018
|
+
: "null"
|
|
2019
|
+
|
|
2020
|
+
const wExpr = node.wildcard
|
|
2021
|
+
? `{ n: ${JSON.stringify(node.wildcard.name)}, m: { ${node.wildcard.handlers.map((h) => `${h.method}: ${h.handler}`).join(", ")} } }`
|
|
2022
|
+
: "null"
|
|
2023
|
+
|
|
2024
|
+
const mExpr =
|
|
2025
|
+
node.handlers.length > 0 ? `{ ${node.handlers.map((h) => `${h.method}: ${h.handler}`).join(", ")} }` : "null"
|
|
2026
|
+
|
|
2027
|
+
const args = [sExpr]
|
|
2028
|
+
if (mExpr !== "null" || dExpr !== "null" || wExpr !== "null") {
|
|
2029
|
+
args.push(mExpr)
|
|
2030
|
+
if (dExpr !== "null" || wExpr !== "null") {
|
|
2031
|
+
args.push(dExpr)
|
|
2032
|
+
if (wExpr !== "null") args.push(wExpr)
|
|
2016
2033
|
}
|
|
2017
|
-
index.push(id)
|
|
2018
2034
|
}
|
|
2019
|
-
|
|
2020
|
-
return JSON.stringify(dict).length < JSON.stringify(values).length ? dict : values
|
|
2035
|
+
return `N(${args.join(", ")})`
|
|
2021
2036
|
}
|
|
2022
2037
|
|
|
2023
|
-
function
|
|
2024
|
-
|
|
2038
|
+
function nodeHasStatic(node: TreeBuild): boolean {
|
|
2039
|
+
if (Object.keys(node.children).length > 0) return true
|
|
2040
|
+
if (node.dynamic !== null) return nodeHasStatic(node.dynamic.child)
|
|
2041
|
+
return false
|
|
2025
2042
|
}
|
|
2026
2043
|
|
|
2027
|
-
function
|
|
2028
|
-
|
|
2029
|
-
const sets = rows.map((row) => new Set(row))
|
|
2030
|
-
return rows[0].filter((key) => sets.every((set) => set.has(key)))
|
|
2044
|
+
function selectorOf(route: RouteConfig): string {
|
|
2045
|
+
return `${route.method} ${route.path}`
|
|
2031
2046
|
}
|
|
2032
2047
|
|
|
2033
|
-
function
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
const shared = sharedErrorKeys(rows)
|
|
2037
|
-
if (shared.length === 0) return full
|
|
2038
|
-
const sharedSet = new Set(shared)
|
|
2039
|
-
const extras = rows.map((row) => row.filter((key) => !sharedSet.has(key)))
|
|
2040
|
-
const withBase: Pick<PackedRouteTable, "e" | "k"> = { k: shared }
|
|
2041
|
-
if (extras.some((row) => row.length > 0)) withBase.e = encodePackedColumn(extras)
|
|
2042
|
-
return pickSmallerJson(withBase, full)
|
|
2048
|
+
function mapKeyExpr(intern: InternPool, selector: string): string {
|
|
2049
|
+
const id = intern.id(selector)
|
|
2050
|
+
return id !== undefined ? `[${id}]` : JSON.stringify(selector)
|
|
2043
2051
|
}
|
|
2044
2052
|
|
|
2045
|
-
function
|
|
2046
|
-
|
|
2047
|
-
const
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
seen.add(key)
|
|
2055
|
-
names.push(key)
|
|
2056
|
-
}
|
|
2053
|
+
function buildInternPool(routes: RouteConfig[]): InternPool {
|
|
2054
|
+
const intern = new InternPool()
|
|
2055
|
+
for (const route of routes) {
|
|
2056
|
+
intern.count(route.boundaryErrorKey)
|
|
2057
|
+
intern.count(route.errorKeys)
|
|
2058
|
+
intern.count(route.inputSchemas)
|
|
2059
|
+
intern.count(route.outputSchemas)
|
|
2060
|
+
intern.count(route.meta)
|
|
2061
|
+
intern.count(selectorOf(route))
|
|
2057
2062
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
return
|
|
2063
|
+
for (const route of routes) {
|
|
2064
|
+
intern.force(route.inputSchemas, "I")
|
|
2065
|
+
intern.force(route.outputSchemas, "O")
|
|
2066
|
+
intern.force(route.meta, "M")
|
|
2067
|
+
intern.force(selectorOf(route), "P")
|
|
2068
|
+
}
|
|
2069
|
+
intern.seal()
|
|
2070
|
+
return intern
|
|
2066
2071
|
}
|
|
2067
2072
|
|
|
2068
|
-
function
|
|
2069
|
-
const
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2073
|
+
function insertHandler(rootBuild: TreeBuild, route: RouteConfig, hName: string): void {
|
|
2074
|
+
const segments = route.path.split("/").filter((s) => s.length > 0)
|
|
2075
|
+
let node = rootBuild
|
|
2076
|
+
|
|
2077
|
+
for (let i = 0; i < segments.length; i++) {
|
|
2078
|
+
const seg = segments[i]
|
|
2079
|
+
if (seg.startsWith("*")) {
|
|
2080
|
+
const name = seg.length > 1 ? seg.slice(1) : "*"
|
|
2081
|
+
if (node.wildcard === null) {
|
|
2082
|
+
node.wildcard = { handlers: [], name }
|
|
2083
|
+
}
|
|
2084
|
+
node.wildcard.handlers.push({ handler: hName, method: route.method })
|
|
2085
|
+
return
|
|
2086
|
+
}
|
|
2087
|
+
if (seg.startsWith(":")) {
|
|
2088
|
+
const name = seg.endsWith("?") ? seg.slice(1, -1) : seg.slice(1)
|
|
2089
|
+
if (node.dynamic === null) {
|
|
2090
|
+
node.dynamic = { child: createTreeBuild(), name }
|
|
2091
|
+
}
|
|
2092
|
+
if (i === segments.length - 1) {
|
|
2093
|
+
node.dynamic.child.handlers.push({
|
|
2094
|
+
handler: hName,
|
|
2095
|
+
method: route.method,
|
|
2096
|
+
})
|
|
2097
|
+
} else {
|
|
2098
|
+
node = node.dynamic.child
|
|
2099
|
+
}
|
|
2100
|
+
continue
|
|
2101
|
+
}
|
|
2102
|
+
if (node.children[seg] === undefined) {
|
|
2103
|
+
node.children[seg] = createTreeBuild()
|
|
2104
|
+
}
|
|
2105
|
+
if (i === segments.length - 1) {
|
|
2106
|
+
node.children[seg].handlers.push({
|
|
2107
|
+
handler: hName,
|
|
2108
|
+
method: route.method,
|
|
2109
|
+
})
|
|
2110
|
+
} else {
|
|
2111
|
+
node = node.children[seg]
|
|
2112
|
+
}
|
|
2079
2113
|
}
|
|
2080
|
-
Object.assign(table, encodeErrorKeys(e))
|
|
2081
|
-
if (i.some((x) => x !== null)) table.i = encodePackedColumn(i)
|
|
2082
|
-
if (o.some((x) => x !== null)) table.o = encodePackedColumn(o)
|
|
2083
|
-
Object.assign(table, encodeMetaColumn(m))
|
|
2084
|
-
return table
|
|
2085
|
-
}
|
|
2086
2114
|
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
if (json.includes('"__proto__"')) {
|
|
2090
|
-
return `JSON.parse(${JSON.stringify(json)})`
|
|
2115
|
+
if (segments.length === 0) {
|
|
2116
|
+
node.handlers.push({ handler: hName, method: route.method })
|
|
2091
2117
|
}
|
|
2092
|
-
return json
|
|
2093
2118
|
}
|
|
2094
2119
|
|
|
2095
2120
|
function collectMetaShape(collected: CollectedRoute[]): {
|
|
@@ -2125,50 +2150,103 @@ function emitMetaShapeType(shape: ReturnType<typeof collectMetaShape>): string {
|
|
|
2125
2150
|
}
|
|
2126
2151
|
|
|
2127
2152
|
function emitRouteTree(routes: RouteConfig[], collected: CollectedRoute[] | null): string {
|
|
2153
|
+
const intern = buildInternPool(routes)
|
|
2128
2154
|
const metaShape = collected !== null ? collectMetaShape(collected) : null
|
|
2129
2155
|
const hasMetaShape = metaShape !== null && metaShape.allKeys.size > 0
|
|
2130
|
-
const selectors = routes.map(selectorOf)
|
|
2131
|
-
const table = packRouteTable(routes)
|
|
2132
2156
|
const lines: string[] = []
|
|
2157
|
+
lines.push('import type { TreeNode, RouteHandler, RouteTree } from "@lovrozagar/honey/tree"')
|
|
2158
|
+
lines.push("")
|
|
2159
|
+
lines.push("const E = Object.create(null) as Record<string, TreeNode>")
|
|
2133
2160
|
lines.push(
|
|
2134
|
-
'
|
|
2161
|
+
'function N(s: Record<string, TreeNode>, m: Record<string, RouteHandler> | null = null, d: TreeNode["d"] = null, w: { n: string; m: Record<string, RouteHandler> } | null = null): TreeNode {',
|
|
2135
2162
|
)
|
|
2163
|
+
lines.push('\treturn { s, d, w: w as TreeNode["w"], m: m as TreeNode["m"], ws: null }')
|
|
2164
|
+
lines.push("}")
|
|
2165
|
+
|
|
2166
|
+
const rootBuild = createTreeBuild()
|
|
2167
|
+
for (let i = 0; i < routes.length; i++) {
|
|
2168
|
+
insertHandler(rootBuild, routes[i], `H${i}`)
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
if (nodeHasStatic(rootBuild)) {
|
|
2172
|
+
lines.push("function S(o: Record<string, TreeNode>): Record<string, TreeNode> {")
|
|
2173
|
+
lines.push("\treturn Object.assign(Object.create(null), o) as Record<string, TreeNode>")
|
|
2174
|
+
lines.push("}")
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
if (routes.length > 0) {
|
|
2178
|
+
lines.push('const FN = null as unknown as RouteHandler["fn"]')
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
const consts = intern.emitConstLines()
|
|
2182
|
+
if (consts.length > 0) {
|
|
2183
|
+
lines.push("")
|
|
2184
|
+
for (const line of consts) {
|
|
2185
|
+
if (hasMetaShape && /^const M\d+ = /.test(line)) lines.push(`${line} as unknown as MetaShape`)
|
|
2186
|
+
else lines.push(line)
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
if (routes.length > 0) {
|
|
2191
|
+
lines.push("")
|
|
2192
|
+
/* unique handler per route — sharing H objects would cross-wire .routeTree() patches */
|
|
2193
|
+
for (let i = 0; i < routes.length; i++) {
|
|
2194
|
+
const route = routes[i]
|
|
2195
|
+
const bekExpr = route.boundaryErrorKey !== null ? intern.expr(route.boundaryErrorKey) : "null"
|
|
2196
|
+
const ekSet = route.errorKeys.length > 0 ? `new Set(${intern.expr(route.errorKeys)})` : "new Set()"
|
|
2197
|
+
const ivExpr = route.inputSchemas !== null ? intern.expr(route.inputSchemas) : "null"
|
|
2198
|
+
const osExpr = route.outputSchemas !== null ? intern.expr(route.outputSchemas) : "null"
|
|
2199
|
+
const mtExpr = route.meta !== null ? intern.expr(route.meta) : "null"
|
|
2200
|
+
lines.push(
|
|
2201
|
+
`const H${i}: RouteHandler = { bek: ${bekExpr}, ef: null, fn: FN, mw: [], ek: ${ekSet}, iv: ${ivExpr}, os: ${osExpr}, mt: ${mtExpr}, ov: null, rp: "" }`,
|
|
2202
|
+
)
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2136
2206
|
lines.push("")
|
|
2137
|
-
lines.push(`
|
|
2138
|
-
lines.push(
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
lines.push(
|
|
2142
|
-
|
|
2207
|
+
lines.push(`export const tree: TreeNode = ${serializeNode(rootBuild)}`)
|
|
2208
|
+
lines.push("")
|
|
2209
|
+
|
|
2210
|
+
const handlerMapEntries = routes.map((route, index) => `\t${mapKeyExpr(intern, selectorOf(route))}: H${index}`)
|
|
2211
|
+
lines.push(`export const handlers: Record<string, RouteHandler> = {\n${handlerMapEntries.join(",\n")}\n}`)
|
|
2212
|
+
lines.push("")
|
|
2213
|
+
|
|
2214
|
+
let code = lines.join("\n")
|
|
2143
2215
|
if (collected === null) return code
|
|
2144
2216
|
|
|
2217
|
+
const metaEntries: string[] = []
|
|
2218
|
+
for (const { handler, method, path } of collected) {
|
|
2219
|
+
if (handler.mt === null || Object.keys(handler.mt).length === 0) continue
|
|
2220
|
+
const selector = `${method} ${path}`
|
|
2221
|
+
metaEntries.push(`\t${mapKeyExpr(intern, selector)}: ${intern.expr(handler.mt)}`)
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2145
2224
|
if (hasMetaShape && metaShape !== null) {
|
|
2146
2225
|
code += `\n${emitMetaShapeType(metaShape)}\n`
|
|
2147
2226
|
}
|
|
2148
2227
|
|
|
2149
|
-
const
|
|
2228
|
+
const selectors = new Set<string>()
|
|
2150
2229
|
for (const { handler, method, path } of collected) {
|
|
2151
2230
|
if (handler._skip || isMetaInternal(handler)) continue
|
|
2152
|
-
|
|
2231
|
+
selectors.add(`${method} ${path}`)
|
|
2153
2232
|
}
|
|
2154
|
-
if (
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
})
|
|
2162
|
-
code += `export type RouteSelector = ${parts.join(" | ")}\n`
|
|
2163
|
-
}
|
|
2233
|
+
if (selectors.size > 0) {
|
|
2234
|
+
const sorted = [...selectors].sort()
|
|
2235
|
+
const parts = sorted.map((s) => {
|
|
2236
|
+
const id = intern.id(s)
|
|
2237
|
+
return id !== undefined ? `typeof ${id}` : JSON.stringify(s)
|
|
2238
|
+
})
|
|
2239
|
+
code += `\nexport type RouteSelector = ${parts.join(" | ")}\n`
|
|
2164
2240
|
}
|
|
2165
2241
|
|
|
2166
|
-
|
|
2167
|
-
if (hasMeta) {
|
|
2242
|
+
if (metaEntries.length > 0) {
|
|
2168
2243
|
const metaType = hasMetaShape ? "Record<string, MetaShape>" : "Record<string, Record<string, unknown>>"
|
|
2169
|
-
code += `export const meta: ${metaType} =
|
|
2244
|
+
code += `export const meta: ${metaType} = {\n${metaEntries.join(",\n")}\n}\n`
|
|
2170
2245
|
}
|
|
2171
2246
|
|
|
2247
|
+
const hasMetaExport = metaEntries.length > 0
|
|
2248
|
+
code += `export const routeTree: RouteTree = { root: tree, ${hasMetaExport ? "meta, " : "meta: {}, "}handlers }\n`
|
|
2249
|
+
|
|
2172
2250
|
return code
|
|
2173
2251
|
}
|
|
2174
2252
|
|
package/src/tree.ts
CHANGED
|
@@ -108,399 +108,6 @@ export function createNode(): TreeNode {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const UNSET_FN = null as unknown as RouteHandler["fn"]
|
|
112
|
-
|
|
113
|
-
/** Dense array, or unique-value table + per-row indices when that JSON is smaller. */
|
|
114
|
-
export type PackedColumn<T> = readonly T[] | { readonly t: readonly T[]; readonly x: readonly number[] }
|
|
115
|
-
|
|
116
|
-
/** Columnar packed route table consumed by `assembleRouteTree`. */
|
|
117
|
-
export type PackedRouteTable = {
|
|
118
|
-
/** Boundary error key. A string applies to every route. */
|
|
119
|
-
b?: Array<string | null> | string
|
|
120
|
-
/** Per-route error-key lists. When `k` is set, each row is the extras beyond `k`. */
|
|
121
|
-
e?: PackedColumn<readonly string[] | null>
|
|
122
|
-
/** Per-route input schemas. */
|
|
123
|
-
i?: PackedColumn<unknown>
|
|
124
|
-
/** Error keys shared by every route; prepended onto each `e` row at inflate. */
|
|
125
|
-
k?: readonly string[]
|
|
126
|
-
/** Per-route meta objects. Keys are `u` indices when `u` is set. */
|
|
127
|
-
m?: PackedColumn<Record<string, unknown> | null>
|
|
128
|
-
/** Per-route output schemas. */
|
|
129
|
-
o?: PackedColumn<unknown>
|
|
130
|
-
/** Route selectors (`METHOD /path`), same order as the other columns. */
|
|
131
|
-
s: readonly string[]
|
|
132
|
-
/** Meta key dictionary; packed meta objects use decimal index keys. */
|
|
133
|
-
u?: readonly string[]
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const SCHEMA_KEY_PACK = new Map<string, string>([
|
|
137
|
-
["$ref", "$"],
|
|
138
|
-
["additionalItems", "ai"],
|
|
139
|
-
["additionalProperties", "ap"],
|
|
140
|
-
["allOf", "ao"],
|
|
141
|
-
["anyOf", "yo"],
|
|
142
|
-
["const", "c"],
|
|
143
|
-
["contains", "cs"],
|
|
144
|
-
["contentEncoding", "ce"],
|
|
145
|
-
["contentMediaType", "cm"],
|
|
146
|
-
["contentSchema", "cc"],
|
|
147
|
-
["default", "d"],
|
|
148
|
-
["definitions", "df"],
|
|
149
|
-
["dependentRequired", "dr"],
|
|
150
|
-
["dependentSchemas", "dh"],
|
|
151
|
-
["description", "ds"],
|
|
152
|
-
["else", "el"],
|
|
153
|
-
["enum", "en"],
|
|
154
|
-
["examples", "ex"],
|
|
155
|
-
["exclusiveMaximum", "xm"],
|
|
156
|
-
["exclusiveMinimum", "xn"],
|
|
157
|
-
["format", "f"],
|
|
158
|
-
["if", "if"],
|
|
159
|
-
["items", "it"],
|
|
160
|
-
["maxItems", "xi"],
|
|
161
|
-
["maxLength", "xl"],
|
|
162
|
-
["maxProperties", "xp"],
|
|
163
|
-
["maximum", "xa"],
|
|
164
|
-
["minItems", "ni"],
|
|
165
|
-
["minLength", "nl"],
|
|
166
|
-
["minProperties", "np"],
|
|
167
|
-
["minimum", "na"],
|
|
168
|
-
["multipleOf", "mo"],
|
|
169
|
-
["not", "nt"],
|
|
170
|
-
["nullable", "nu"],
|
|
171
|
-
["oneOf", "oo"],
|
|
172
|
-
["pattern", "pt"],
|
|
173
|
-
["patternProperties", "pp"],
|
|
174
|
-
["prefixItems", "pi"],
|
|
175
|
-
["properties", "p"],
|
|
176
|
-
["propertyNames", "pn"],
|
|
177
|
-
["required", "r"],
|
|
178
|
-
["then", "th"],
|
|
179
|
-
["title", "ti"],
|
|
180
|
-
["type", "t"],
|
|
181
|
-
["unevaluatedItems", "ue"],
|
|
182
|
-
["unevaluatedProperties", "up"],
|
|
183
|
-
["uniqueItems", "ui"],
|
|
184
|
-
])
|
|
185
|
-
|
|
186
|
-
function invertStringMap(src: Map<string, string>): Map<string, string> {
|
|
187
|
-
const out = new Map<string, string>()
|
|
188
|
-
for (const [long, short] of src) out.set(short, long)
|
|
189
|
-
return out
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const SCHEMA_KEY_UNPACK = invertStringMap(SCHEMA_KEY_PACK)
|
|
193
|
-
|
|
194
|
-
const SCHEMA_TYPE_PACK = new Map<string, string>([
|
|
195
|
-
["array", "a"],
|
|
196
|
-
["boolean", "b"],
|
|
197
|
-
["integer", "i"],
|
|
198
|
-
["null", "l"],
|
|
199
|
-
["number", "n"],
|
|
200
|
-
["object", "o"],
|
|
201
|
-
["string", "s"],
|
|
202
|
-
])
|
|
203
|
-
const SCHEMA_TYPE_UNPACK = invertStringMap(SCHEMA_TYPE_PACK)
|
|
204
|
-
|
|
205
|
-
const SCHEMA_FORMAT_PACK = new Map<string, string>([
|
|
206
|
-
["binary", "bin"],
|
|
207
|
-
["date", "d"],
|
|
208
|
-
["date-time", "dt"],
|
|
209
|
-
["email", "e"],
|
|
210
|
-
["hostname", "hn"],
|
|
211
|
-
["int32", "i3"],
|
|
212
|
-
["int64", "i6"],
|
|
213
|
-
["ipv4", "v4"],
|
|
214
|
-
["ipv6", "v6"],
|
|
215
|
-
["time", "tm"],
|
|
216
|
-
["uri", "u"],
|
|
217
|
-
["uri-reference", "ur"],
|
|
218
|
-
["uuid", "id"],
|
|
219
|
-
])
|
|
220
|
-
const SCHEMA_FORMAT_UNPACK = invertStringMap(SCHEMA_FORMAT_PACK)
|
|
221
|
-
|
|
222
|
-
const INPUT_KEY_PACK = new Map<string, string>([
|
|
223
|
-
["cookies", "c"],
|
|
224
|
-
["form", "f"],
|
|
225
|
-
["headers", "h"],
|
|
226
|
-
["json", "j"],
|
|
227
|
-
["params", "p"],
|
|
228
|
-
["search", "q"],
|
|
229
|
-
])
|
|
230
|
-
const INPUT_KEY_UNPACK = invertStringMap(INPUT_KEY_PACK)
|
|
231
|
-
|
|
232
|
-
const OUTPUT_CT_PACK = new Map<string, string>([
|
|
233
|
-
["application/cbor", "cb"],
|
|
234
|
-
["application/json", "j"],
|
|
235
|
-
["application/msgpack", "mp"],
|
|
236
|
-
["application/octet-stream", "os"],
|
|
237
|
-
["application/pdf", "pf"],
|
|
238
|
-
["application/xml", "xm"],
|
|
239
|
-
["redirect", "rd"],
|
|
240
|
-
["text/csv", "csv"],
|
|
241
|
-
["text/event-stream", "sse"],
|
|
242
|
-
["text/html", "ht"],
|
|
243
|
-
["text/plain", "tp"],
|
|
244
|
-
])
|
|
245
|
-
const OUTPUT_CT_UNPACK = invertStringMap(OUTPUT_CT_PACK)
|
|
246
|
-
|
|
247
|
-
const SCHEMA_NAME_CHILDREN = new Set(["$defs", "definitions", "dependentSchemas", "patternProperties", "properties"])
|
|
248
|
-
const SCHEMA_NODE_CHILDREN = new Set([
|
|
249
|
-
"$defs",
|
|
250
|
-
"additionalItems",
|
|
251
|
-
"additionalProperties",
|
|
252
|
-
"allOf",
|
|
253
|
-
"anyOf",
|
|
254
|
-
"contains",
|
|
255
|
-
"contentSchema",
|
|
256
|
-
"definitions",
|
|
257
|
-
"dependentSchemas",
|
|
258
|
-
"else",
|
|
259
|
-
"if",
|
|
260
|
-
"items",
|
|
261
|
-
"not",
|
|
262
|
-
"oneOf",
|
|
263
|
-
"patternProperties",
|
|
264
|
-
"prefixItems",
|
|
265
|
-
"properties",
|
|
266
|
-
"propertyNames",
|
|
267
|
-
"then",
|
|
268
|
-
"unevaluatedItems",
|
|
269
|
-
"unevaluatedProperties",
|
|
270
|
-
])
|
|
271
|
-
|
|
272
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
273
|
-
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function remapLeaf(value: unknown, map: Map<string, string>): unknown {
|
|
277
|
-
if (typeof value === "string") return map.get(value) ?? value
|
|
278
|
-
if (Array.isArray(value)) {
|
|
279
|
-
return value.map((item) => (typeof item === "string" ? (map.get(item) ?? item) : item))
|
|
280
|
-
}
|
|
281
|
-
return value
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function isNullTypeSchema(value: unknown): boolean {
|
|
285
|
-
return isPlainObject(value) && Object.keys(value).length === 1 && value.type === "null"
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function packJsonSchema(node: unknown): unknown {
|
|
289
|
-
if (Array.isArray(node)) return node.map(packJsonSchema)
|
|
290
|
-
if (!isPlainObject(node)) return node
|
|
291
|
-
|
|
292
|
-
const oneOf = node.oneOf
|
|
293
|
-
if (Array.isArray(oneOf) && oneOf.length === 2 && Object.keys(node).length === 1) {
|
|
294
|
-
if (isPlainObject(oneOf[0]) && isNullTypeSchema(oneOf[1])) {
|
|
295
|
-
const packed = packJsonSchema(oneOf[0])
|
|
296
|
-
if (isPlainObject(packed)) {
|
|
297
|
-
packed.n1 = 1
|
|
298
|
-
return packed
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
if (isNullTypeSchema(oneOf[0]) && isPlainObject(oneOf[1])) {
|
|
302
|
-
const packed = packJsonSchema(oneOf[1])
|
|
303
|
-
if (isPlainObject(packed)) {
|
|
304
|
-
packed.n1 = 0
|
|
305
|
-
return packed
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const out: Record<string, unknown> = Object.create(null)
|
|
311
|
-
for (const [key, value] of Object.entries(node)) {
|
|
312
|
-
const nextKey = SCHEMA_KEY_PACK.get(key) ?? key
|
|
313
|
-
if (key === "type") {
|
|
314
|
-
out[nextKey] = remapLeaf(value, SCHEMA_TYPE_PACK)
|
|
315
|
-
continue
|
|
316
|
-
}
|
|
317
|
-
if (key === "format") {
|
|
318
|
-
out[nextKey] = remapLeaf(value, SCHEMA_FORMAT_PACK)
|
|
319
|
-
continue
|
|
320
|
-
}
|
|
321
|
-
if (SCHEMA_NAME_CHILDREN.has(key) && isPlainObject(value)) {
|
|
322
|
-
const inner: Record<string, unknown> = Object.create(null)
|
|
323
|
-
for (const [name, child] of Object.entries(value)) inner[name] = packJsonSchema(child)
|
|
324
|
-
out[nextKey] = inner
|
|
325
|
-
continue
|
|
326
|
-
}
|
|
327
|
-
if (SCHEMA_NODE_CHILDREN.has(key)) {
|
|
328
|
-
out[nextKey] = packJsonSchema(value)
|
|
329
|
-
continue
|
|
330
|
-
}
|
|
331
|
-
out[nextKey] = value
|
|
332
|
-
}
|
|
333
|
-
return out
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function unpackJsonSchema(node: unknown): unknown {
|
|
337
|
-
if (Array.isArray(node)) return node.map(unpackJsonSchema)
|
|
338
|
-
if (!isPlainObject(node)) return node
|
|
339
|
-
|
|
340
|
-
if (Object.hasOwn(node, "n1")) {
|
|
341
|
-
const copy: Record<string, unknown> = Object.create(null)
|
|
342
|
-
for (const [key, value] of Object.entries(node)) {
|
|
343
|
-
if (key !== "n1") copy[key] = value
|
|
344
|
-
}
|
|
345
|
-
const rest = unpackJsonSchema(copy)
|
|
346
|
-
const nullBranch = { type: "null" }
|
|
347
|
-
return node.n1 === 0 ? { oneOf: [nullBranch, rest] } : { oneOf: [rest, nullBranch] }
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const out: Record<string, unknown> = Object.create(null)
|
|
351
|
-
for (const [key, value] of Object.entries(node)) {
|
|
352
|
-
const nextKey = SCHEMA_KEY_UNPACK.get(key) ?? key
|
|
353
|
-
if (nextKey === "type") {
|
|
354
|
-
out[nextKey] = remapLeaf(value, SCHEMA_TYPE_UNPACK)
|
|
355
|
-
continue
|
|
356
|
-
}
|
|
357
|
-
if (nextKey === "format") {
|
|
358
|
-
out[nextKey] = remapLeaf(value, SCHEMA_FORMAT_UNPACK)
|
|
359
|
-
continue
|
|
360
|
-
}
|
|
361
|
-
if (SCHEMA_NAME_CHILDREN.has(nextKey) && isPlainObject(value)) {
|
|
362
|
-
const inner: Record<string, unknown> = Object.create(null)
|
|
363
|
-
for (const [name, child] of Object.entries(value)) inner[name] = unpackJsonSchema(child)
|
|
364
|
-
out[nextKey] = inner
|
|
365
|
-
continue
|
|
366
|
-
}
|
|
367
|
-
if (SCHEMA_NODE_CHILDREN.has(nextKey)) {
|
|
368
|
-
out[nextKey] = unpackJsonSchema(value)
|
|
369
|
-
continue
|
|
370
|
-
}
|
|
371
|
-
out[nextKey] = value
|
|
372
|
-
}
|
|
373
|
-
return out
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
export function packInputSchemas(iv: unknown): unknown {
|
|
377
|
-
if (iv === null || typeof iv !== "object") return iv
|
|
378
|
-
const out: Record<string, unknown> = Object.create(null)
|
|
379
|
-
for (const [source, schema] of Object.entries(iv as Record<string, unknown>)) {
|
|
380
|
-
out[INPUT_KEY_PACK.get(source) ?? source] = packJsonSchema(schema)
|
|
381
|
-
}
|
|
382
|
-
return out
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
export function unpackInputSchemas(iv: unknown): unknown {
|
|
386
|
-
if (iv === null || typeof iv !== "object") return iv
|
|
387
|
-
const out: Record<string, unknown> = Object.create(null)
|
|
388
|
-
for (const [source, schema] of Object.entries(iv as Record<string, unknown>)) {
|
|
389
|
-
out[INPUT_KEY_UNPACK.get(source) ?? source] = unpackJsonSchema(schema)
|
|
390
|
-
}
|
|
391
|
-
return out
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
export function packOutputSchemas(os: unknown): unknown {
|
|
395
|
-
if (os === null || typeof os !== "object") return os
|
|
396
|
-
const out: Record<string, unknown> = Object.create(null)
|
|
397
|
-
for (const [contentType, statuses] of Object.entries(os as Record<string, unknown>)) {
|
|
398
|
-
const packedCt = OUTPUT_CT_PACK.get(contentType) ?? contentType
|
|
399
|
-
if (contentType === "redirect" || statuses === null || typeof statuses !== "object") {
|
|
400
|
-
out[packedCt] = statuses
|
|
401
|
-
continue
|
|
402
|
-
}
|
|
403
|
-
const inner: Record<string, unknown> = Object.create(null)
|
|
404
|
-
for (const [statusKey, schema] of Object.entries(statuses as Record<string, unknown>)) {
|
|
405
|
-
inner[statusKey] = schema === true ? true : packJsonSchema(schema)
|
|
406
|
-
}
|
|
407
|
-
out[packedCt] = inner
|
|
408
|
-
}
|
|
409
|
-
return out
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
export function unpackOutputSchemas(os: unknown): unknown {
|
|
413
|
-
if (os === null || typeof os !== "object") return os
|
|
414
|
-
const out: Record<string, unknown> = Object.create(null)
|
|
415
|
-
for (const [contentType, statuses] of Object.entries(os as Record<string, unknown>)) {
|
|
416
|
-
const longCt = OUTPUT_CT_UNPACK.get(contentType) ?? contentType
|
|
417
|
-
if (longCt === "redirect" || statuses === null || typeof statuses !== "object") {
|
|
418
|
-
out[longCt] = statuses
|
|
419
|
-
continue
|
|
420
|
-
}
|
|
421
|
-
const inner: Record<string, unknown> = Object.create(null)
|
|
422
|
-
for (const [statusKey, schema] of Object.entries(statuses as Record<string, unknown>)) {
|
|
423
|
-
inner[statusKey] = schema === true ? true : unpackJsonSchema(schema)
|
|
424
|
-
}
|
|
425
|
-
out[longCt] = inner
|
|
426
|
-
}
|
|
427
|
-
return out
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function columnAt<T>(col: PackedColumn<T> | undefined, index: number, fallback: T): T {
|
|
431
|
-
if (col === undefined) return fallback
|
|
432
|
-
if (!Array.isArray(col) && "x" in col) {
|
|
433
|
-
const value = col.t[col.x[index]]
|
|
434
|
-
return (value === undefined ? fallback : value) as T
|
|
435
|
-
}
|
|
436
|
-
const value = col[index]
|
|
437
|
-
return (value === undefined ? fallback : value) as T
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function mapColumn<T, U>(col: PackedColumn<T> | undefined, fn: (value: T) => U): PackedColumn<U> | undefined {
|
|
441
|
-
if (col === undefined) return undefined
|
|
442
|
-
if (!Array.isArray(col) && "x" in col) {
|
|
443
|
-
return { t: col.t.map(fn), x: col.x }
|
|
444
|
-
}
|
|
445
|
-
return (col as readonly T[]).map(fn)
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function unpackMeta(
|
|
449
|
-
mt: Record<string, unknown> | null,
|
|
450
|
-
names: readonly string[] | undefined,
|
|
451
|
-
): Record<string, unknown> | null {
|
|
452
|
-
if (mt === null || names === undefined) return mt
|
|
453
|
-
const out: Record<string, unknown> = Object.create(null)
|
|
454
|
-
for (const [key, value] of Object.entries(mt)) {
|
|
455
|
-
const idx = Number(key)
|
|
456
|
-
const name = Number.isInteger(idx) ? names[idx] : undefined
|
|
457
|
-
out[name ?? key] = value
|
|
458
|
-
}
|
|
459
|
-
return out
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Inflate a packed route table into a live route tree.
|
|
464
|
-
* Each handler and each `ek` Set is a unique object so `.routeTree()` patches
|
|
465
|
-
* cannot cross-wire routes.
|
|
466
|
-
*/
|
|
467
|
-
export function assembleRouteTree(table: PackedRouteTable): RouteTree {
|
|
468
|
-
const root = createNode()
|
|
469
|
-
const handlers: Record<string, RouteHandler> = Object.create(null)
|
|
470
|
-
const meta: Record<string, Record<string, unknown>> = Object.create(null)
|
|
471
|
-
const selectors = table.s
|
|
472
|
-
const sharedEk = table.k ?? []
|
|
473
|
-
const inputCol = mapColumn(table.i, unpackInputSchemas)
|
|
474
|
-
const outputCol = mapColumn(table.o, unpackOutputSchemas)
|
|
475
|
-
const metaCol = mapColumn(table.m, (mt) => unpackMeta(mt, table.u))
|
|
476
|
-
for (let i = 0; i < selectors.length; i++) {
|
|
477
|
-
const sel = selectors[i]
|
|
478
|
-
const bek = typeof table.b === "string" ? table.b : (table.b?.[i] ?? null)
|
|
479
|
-
const extra = columnAt(table.e, i, [] as readonly string[])
|
|
480
|
-
const mt = columnAt(metaCol, i, null)
|
|
481
|
-
const handler: RouteHandler = {
|
|
482
|
-
bek,
|
|
483
|
-
ef: null,
|
|
484
|
-
ek: new Set(sharedEk.length === 0 ? (extra ?? []) : [...sharedEk, ...(extra ?? [])]),
|
|
485
|
-
fn: UNSET_FN,
|
|
486
|
-
iv: columnAt(inputCol, i, null) as RouteHandler["iv"],
|
|
487
|
-
mt,
|
|
488
|
-
mw: [],
|
|
489
|
-
os: columnAt(outputCol, i, null) as RouteHandler["os"],
|
|
490
|
-
ov: null,
|
|
491
|
-
rp: "",
|
|
492
|
-
}
|
|
493
|
-
handlers[sel] = handler
|
|
494
|
-
if (mt !== null) meta[sel] = mt
|
|
495
|
-
const sp = sel.indexOf(" ")
|
|
496
|
-
if (sp === -1) {
|
|
497
|
-
throw new Error(`Invalid route selector: ${sel}`)
|
|
498
|
-
}
|
|
499
|
-
insertRoute(root, sel.slice(0, sp) as HttpMethod, sel.slice(sp + 1), handler)
|
|
500
|
-
}
|
|
501
|
-
return { handlers, meta, root }
|
|
502
|
-
}
|
|
503
|
-
|
|
504
111
|
function splitSegments(path: string): string[] {
|
|
505
112
|
return path.split("/").filter((s) => s.length > 0)
|
|
506
113
|
}
|