@lovrozagar/honey 0.4.2 → 0.5.1
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.d.ts +1 -1
- package/dist/codegen.d.ts.map +1 -1
- package/dist/tree.d.ts +34 -0
- package/dist/tree.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/codegen.ts +155 -173
- package/src/tree.ts +393 -0
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` + `assembleRouteTree` |
|
|
1543
1543
|
| `honey/cli` | `honey` binary |
|
|
1544
1544
|
| `honey/errors` | `defineErrors` (also on the root export) |
|
|
1545
1545
|
|
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 type
|
|
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":"AAOA,OAAO,EACN,KAAK,qBAAqB,EAG1B,MAAM,uBAAuB,CAAA;AAY9B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,
|
|
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,6 +76,40 @@ 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;
|
|
79
113
|
export declare function insertRoute(root: TreeNode, method: HttpMethod | "ALL", path: string, handler: RouteHandler): void;
|
|
80
114
|
export declare function matchRoute(root: TreeNode, method: HttpMethod, path: string): MatchResult;
|
|
81
115
|
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":"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"}
|
package/package.json
CHANGED
package/src/codegen.ts
CHANGED
|
@@ -22,7 +22,14 @@ import {
|
|
|
22
22
|
import { ERROR_META } from "./errors.ts"
|
|
23
23
|
import type { ErrorMetaEntry } from "./errors.ts"
|
|
24
24
|
import type { Honey } from "./index.ts"
|
|
25
|
-
import
|
|
25
|
+
import {
|
|
26
|
+
type PackedColumn,
|
|
27
|
+
type PackedRouteTable,
|
|
28
|
+
packInputSchemas,
|
|
29
|
+
packOutputSchemas,
|
|
30
|
+
type RouteHandler,
|
|
31
|
+
type TreeNode,
|
|
32
|
+
} from "./tree.ts"
|
|
26
33
|
import { irToTs } from "./ts-type-emitter.ts"
|
|
27
34
|
import { createTypeEmitState, emitSchemaType } from "./type-emitter.ts"
|
|
28
35
|
import type { TypeEmitState } from "./type-emitter.ts"
|
|
@@ -1991,217 +1998,192 @@ function collectRoutesForTree(node: TreeNode, currentPath: string, routes: Route
|
|
|
1991
1998
|
}
|
|
1992
1999
|
}
|
|
1993
2000
|
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
dynamic: { child: TreeBuild; name: string } | null
|
|
1997
|
-
handlers: Array<{ handler: string; method: string }>
|
|
1998
|
-
wildcard: {
|
|
1999
|
-
handlers: Array<{ handler: string; method: string }>
|
|
2000
|
-
name: string
|
|
2001
|
-
} | null
|
|
2001
|
+
function selectorOf(route: RouteConfig): string {
|
|
2002
|
+
return `${route.method} ${route.path}`
|
|
2002
2003
|
}
|
|
2003
2004
|
|
|
2004
|
-
function
|
|
2005
|
-
|
|
2005
|
+
function encodePackedColumn<T>(values: T[]): PackedColumn<T> {
|
|
2006
|
+
const table: T[] = []
|
|
2007
|
+
const index: number[] = []
|
|
2008
|
+
const seen = new Map<string, number>()
|
|
2009
|
+
for (const value of values) {
|
|
2010
|
+
const key = JSON.stringify(value) ?? "null"
|
|
2011
|
+
let id = seen.get(key)
|
|
2012
|
+
if (id === undefined) {
|
|
2013
|
+
id = table.length
|
|
2014
|
+
seen.set(key, id)
|
|
2015
|
+
table.push(value)
|
|
2016
|
+
}
|
|
2017
|
+
index.push(id)
|
|
2018
|
+
}
|
|
2019
|
+
const dict: PackedColumn<T> = { t: table, x: index }
|
|
2020
|
+
return JSON.stringify(dict).length < JSON.stringify(values).length ? dict : values
|
|
2006
2021
|
}
|
|
2007
2022
|
|
|
2008
|
-
function
|
|
2009
|
-
|
|
2010
|
-
const sExpr =
|
|
2011
|
-
staticEntries.length > 0
|
|
2012
|
-
? `Object.assign(Object.create(null), { ${staticEntries.map(([k, v]) => `${JSON.stringify(k)}: ${serializeNode(v)}`).join(", ")} })`
|
|
2013
|
-
: "E"
|
|
2014
|
-
|
|
2015
|
-
const dExpr = node.dynamic
|
|
2016
|
-
? `{ n: ${JSON.stringify(node.dynamic.name)}, c: ${serializeNode(node.dynamic.child)} }`
|
|
2017
|
-
: "null"
|
|
2018
|
-
|
|
2019
|
-
const wExpr = node.wildcard
|
|
2020
|
-
? `{ n: ${JSON.stringify(node.wildcard.name)}, m: { ${node.wildcard.handlers.map((h) => `${h.method}: ${h.handler}`).join(", ")} } }`
|
|
2021
|
-
: "null"
|
|
2022
|
-
|
|
2023
|
-
const mExpr =
|
|
2024
|
-
node.handlers.length > 0 ? `{ ${node.handlers.map((h) => `${h.method}: ${h.handler}`).join(", ")} }` : "null"
|
|
2025
|
-
|
|
2026
|
-
return `{ s: ${sExpr}, d: ${dExpr}, w: ${wExpr}, m: ${mExpr}, ws: null }`
|
|
2023
|
+
function pickSmallerJson<T extends object>(candidate: T, fallback: T): T {
|
|
2024
|
+
return JSON.stringify(candidate).length < JSON.stringify(fallback).length ? candidate : fallback
|
|
2027
2025
|
}
|
|
2028
2026
|
|
|
2029
|
-
function
|
|
2030
|
-
if (
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
if (Array.isArray(v)) return "unknown[]"
|
|
2034
|
-
if (typeof v === "string") return JSON.stringify(v)
|
|
2035
|
-
if (typeof v === "number") return String(v)
|
|
2036
|
-
if (typeof v === "object") return "Record<string, unknown>"
|
|
2037
|
-
return typeof v
|
|
2027
|
+
function sharedErrorKeys(rows: string[][]): string[] {
|
|
2028
|
+
if (rows.length === 0) return []
|
|
2029
|
+
const sets = rows.map((row) => new Set(row))
|
|
2030
|
+
return rows[0].filter((key) => sets.every((set) => set.has(key)))
|
|
2038
2031
|
}
|
|
2039
2032
|
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
/* emit handler configs with pre-built ek and mt */
|
|
2054
|
-
for (const { index, route } of handlerEntries) {
|
|
2055
|
-
const mwList = "[]"
|
|
2056
|
-
const bekExpr = route.boundaryErrorKey !== null ? JSON.stringify(route.boundaryErrorKey) : "null"
|
|
2057
|
-
const ekSet = route.errorKeys.length > 0 ? `new Set(${JSON.stringify(route.errorKeys)})` : "new Set()"
|
|
2058
|
-
const mtExpr = route.meta !== null ? JSON.stringify(route.meta) : "null"
|
|
2059
|
-
const ivExpr =
|
|
2060
|
-
route.inputSchemas !== null ? `${JSON.stringify(route.inputSchemas)} as unknown as RouteHandler["iv"]` : "null"
|
|
2061
|
-
const osExpr =
|
|
2062
|
-
route.outputSchemas !== null ? `${JSON.stringify(route.outputSchemas)} as unknown as RouteHandler["os"]` : "null"
|
|
2063
|
-
lines.push(
|
|
2064
|
-
`const H${index}: RouteHandler = { bek: ${bekExpr}, ef: null, fn: null as unknown as RouteHandler["fn"], mw: ${mwList}, ek: ${ekSet}, iv: ${ivExpr}, os: ${osExpr}, mt: ${mtExpr}, ov: null, rp: "" }`,
|
|
2065
|
-
)
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
|
-
lines.push("")
|
|
2069
|
-
|
|
2070
|
-
/* build tree structure */
|
|
2071
|
-
const rootBuild = createTreeBuild()
|
|
2072
|
-
|
|
2073
|
-
for (const { index, route } of handlerEntries) {
|
|
2074
|
-
const hName = `H${index}`
|
|
2075
|
-
const segments = route.path.split("/").filter((s) => s.length > 0)
|
|
2076
|
-
let node = rootBuild
|
|
2077
|
-
|
|
2078
|
-
for (let i = 0; i < segments.length; i++) {
|
|
2079
|
-
const seg = segments[i]
|
|
2080
|
-
if (seg.startsWith("*")) {
|
|
2081
|
-
const name = seg.length > 1 ? seg.slice(1) : "*"
|
|
2082
|
-
if (node.wildcard === null) {
|
|
2083
|
-
node.wildcard = { handlers: [], name }
|
|
2084
|
-
}
|
|
2085
|
-
node.wildcard.handlers.push({ handler: hName, method: route.method })
|
|
2086
|
-
break
|
|
2087
|
-
}
|
|
2088
|
-
if (seg.startsWith(":")) {
|
|
2089
|
-
const name = seg.endsWith("?") ? seg.slice(1, -1) : seg.slice(1)
|
|
2090
|
-
if (node.dynamic === null) {
|
|
2091
|
-
node.dynamic = { child: createTreeBuild(), name }
|
|
2092
|
-
}
|
|
2093
|
-
if (i === segments.length - 1) {
|
|
2094
|
-
node.dynamic.child.handlers.push({
|
|
2095
|
-
handler: hName,
|
|
2096
|
-
method: route.method,
|
|
2097
|
-
})
|
|
2098
|
-
} else {
|
|
2099
|
-
node = node.dynamic.child
|
|
2100
|
-
}
|
|
2101
|
-
continue
|
|
2102
|
-
}
|
|
2103
|
-
if (node.children[seg] === undefined) {
|
|
2104
|
-
node.children[seg] = createTreeBuild()
|
|
2105
|
-
}
|
|
2106
|
-
if (i === segments.length - 1) {
|
|
2107
|
-
node.children[seg].handlers.push({
|
|
2108
|
-
handler: hName,
|
|
2109
|
-
method: route.method,
|
|
2110
|
-
})
|
|
2111
|
-
} else {
|
|
2112
|
-
node = node.children[seg]
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2033
|
+
function encodeErrorKeys(rows: string[][]): Pick<PackedRouteTable, "e" | "k"> {
|
|
2034
|
+
if (!rows.some((row) => row.length > 0)) return {}
|
|
2035
|
+
const full: Pick<PackedRouteTable, "e" | "k"> = { e: encodePackedColumn(rows) }
|
|
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)
|
|
2043
|
+
}
|
|
2115
2044
|
|
|
2116
|
-
|
|
2117
|
-
|
|
2045
|
+
function encodeMetaColumn(rows: Array<Record<string, unknown> | null>): Pick<PackedRouteTable, "m" | "u"> {
|
|
2046
|
+
if (!rows.some((row) => row !== null)) return {}
|
|
2047
|
+
const names: string[] = []
|
|
2048
|
+
const seen = new Set<string>()
|
|
2049
|
+
for (const row of rows) {
|
|
2050
|
+
if (row === null) continue
|
|
2051
|
+
for (const key of Object.keys(row)) {
|
|
2052
|
+
if (seen.has(key)) continue
|
|
2053
|
+
if (/^\d+$/.test(key)) return { m: encodePackedColumn(rows) }
|
|
2054
|
+
seen.add(key)
|
|
2055
|
+
names.push(key)
|
|
2118
2056
|
}
|
|
2119
2057
|
}
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
)
|
|
2128
|
-
lines.push(`export const handlers: Record<string, RouteHandler> = {\n${handlerMapEntries.join(",\n")}\n}`)
|
|
2129
|
-
lines.push("")
|
|
2130
|
-
|
|
2131
|
-
return lines.join("\n")
|
|
2058
|
+
if (names.length === 0) return { m: encodePackedColumn(rows) }
|
|
2059
|
+
const packed = rows.map((row) => {
|
|
2060
|
+
if (row === null) return null
|
|
2061
|
+
const out: Record<string, unknown> = Object.create(null)
|
|
2062
|
+
for (const [key, value] of Object.entries(row)) out[String(names.indexOf(key))] = value
|
|
2063
|
+
return out
|
|
2064
|
+
})
|
|
2065
|
+
return pickSmallerJson({ m: encodePackedColumn(packed), u: names }, { m: encodePackedColumn(rows) })
|
|
2132
2066
|
}
|
|
2133
2067
|
|
|
2134
|
-
function
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
const
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2068
|
+
function packRouteTable(routes: RouteConfig[]): PackedRouteTable {
|
|
2069
|
+
const table: PackedRouteTable = { s: routes.map(selectorOf) }
|
|
2070
|
+
const b = routes.map((r) => r.boundaryErrorKey)
|
|
2071
|
+
const e = routes.map((r) => r.errorKeys)
|
|
2072
|
+
const i = routes.map((r) => packInputSchemas(r.inputSchemas))
|
|
2073
|
+
const o = routes.map((r) => packOutputSchemas(r.outputSchemas))
|
|
2074
|
+
const m = routes.map((r) => r.meta)
|
|
2075
|
+
if (b.some((x) => x !== null)) {
|
|
2076
|
+
const first = b[0]
|
|
2077
|
+
if (first !== null && b.every((x) => x === first)) table.b = first
|
|
2078
|
+
else table.b = b
|
|
2079
|
+
}
|
|
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
|
+
}
|
|
2142
2086
|
|
|
2143
|
-
|
|
2144
|
-
const
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
metaEntries.push(`\t${JSON.stringify(`${method} ${path}`)}: ${JSON.stringify(handler.mt)}`)
|
|
2087
|
+
function emitPackedLiteral(value: unknown): string {
|
|
2088
|
+
const json = JSON.stringify(value)
|
|
2089
|
+
if (json.includes('"__proto__"')) {
|
|
2090
|
+
return `JSON.parse(${JSON.stringify(json)})`
|
|
2148
2091
|
}
|
|
2092
|
+
return json
|
|
2093
|
+
}
|
|
2149
2094
|
|
|
2150
|
-
|
|
2095
|
+
function collectMetaShape(collected: CollectedRoute[]): {
|
|
2096
|
+
allKeys: Map<string, Set<string>>
|
|
2097
|
+
keyCount: Map<string, number>
|
|
2098
|
+
metaHandlerCount: number
|
|
2099
|
+
} {
|
|
2151
2100
|
const allKeys = new Map<string, Set<string>>()
|
|
2101
|
+
const keyCount = new Map<string, number>()
|
|
2152
2102
|
let metaHandlerCount = 0
|
|
2153
2103
|
for (const { handler } of collected) {
|
|
2154
2104
|
if (handler.mt === null) continue
|
|
2155
2105
|
metaHandlerCount++
|
|
2156
2106
|
for (const [k, v] of Object.entries(handler.mt)) {
|
|
2157
2107
|
if (!allKeys.has(k)) allKeys.set(k, new Set())
|
|
2158
|
-
allKeys.get(k)?.add(
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
/* count per-key occurrences to determine optionality */
|
|
2163
|
-
const keyCount = new Map<string, number>()
|
|
2164
|
-
for (const { handler } of collected) {
|
|
2165
|
-
if (handler.mt === null) continue
|
|
2166
|
-
for (const k of Object.keys(handler.mt)) {
|
|
2108
|
+
allKeys.get(k)?.add(emitLiteral(v))
|
|
2167
2109
|
keyCount.set(k, (keyCount.get(k) ?? 0) + 1)
|
|
2168
2110
|
}
|
|
2169
2111
|
}
|
|
2112
|
+
return { allKeys, keyCount, metaHandlerCount }
|
|
2113
|
+
}
|
|
2170
2114
|
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
})
|
|
2179
|
-
|
|
2115
|
+
function emitMetaShapeType(shape: ReturnType<typeof collectMetaShape>): string {
|
|
2116
|
+
if (shape.allKeys.size === 0) return ""
|
|
2117
|
+
const props = [...shape.allKeys.entries()]
|
|
2118
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
2119
|
+
.map(([key, types]) => {
|
|
2120
|
+
const optional = (shape.keyCount.get(key) ?? 0) < shape.metaHandlerCount ? "?" : ""
|
|
2121
|
+
const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`
|
|
2122
|
+
return `\t${safeKey}${optional}: ${[...types].join(" | ")}`
|
|
2123
|
+
})
|
|
2124
|
+
return `export type MetaShape = {\n${props.join("\n")}\n} & Record<string, unknown>\n`
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
function emitRouteTree(routes: RouteConfig[], collected: CollectedRoute[] | null): string {
|
|
2128
|
+
const metaShape = collected !== null ? collectMetaShape(collected) : null
|
|
2129
|
+
const hasMetaShape = metaShape !== null && metaShape.allKeys.size > 0
|
|
2130
|
+
const selectors = routes.map(selectorOf)
|
|
2131
|
+
const table = packRouteTable(routes)
|
|
2132
|
+
const lines: string[] = []
|
|
2133
|
+
lines.push(
|
|
2134
|
+
'import { assembleRouteTree, type PackedRouteTable, type RouteHandler, type RouteTree, type TreeNode } from "@lovrozagar/honey/tree"',
|
|
2135
|
+
)
|
|
2136
|
+
lines.push("")
|
|
2137
|
+
lines.push(`declare const P: readonly ${JSON.stringify(selectors)}`)
|
|
2138
|
+
lines.push(`const D: PackedRouteTable = ${emitPackedLiteral(table)}`)
|
|
2139
|
+
lines.push("export const routeTree: RouteTree = assembleRouteTree(D)")
|
|
2140
|
+
lines.push("export const tree: TreeNode = routeTree.root")
|
|
2141
|
+
lines.push("export const handlers: Record<string, RouteHandler> = routeTree.handlers ?? {}")
|
|
2142
|
+
let code = `${lines.join("\n")}\n`
|
|
2143
|
+
if (collected === null) return code
|
|
2144
|
+
|
|
2145
|
+
if (hasMetaShape && metaShape !== null) {
|
|
2146
|
+
code += `\n${emitMetaShapeType(metaShape)}\n`
|
|
2180
2147
|
}
|
|
2181
2148
|
|
|
2182
|
-
|
|
2183
|
-
const selectors = new Set<string>()
|
|
2149
|
+
const publicSelectors: string[] = []
|
|
2184
2150
|
for (const { handler, method, path } of collected) {
|
|
2185
2151
|
if (handler._skip || isMetaInternal(handler)) continue
|
|
2186
|
-
|
|
2152
|
+
publicSelectors.push(`${method} ${path}`)
|
|
2187
2153
|
}
|
|
2188
|
-
if (
|
|
2189
|
-
|
|
2190
|
-
|
|
2154
|
+
if (publicSelectors.length > 0) {
|
|
2155
|
+
if (publicSelectors.length === selectors.length) {
|
|
2156
|
+
code += "export type RouteSelector = typeof P[number]\n"
|
|
2157
|
+
} else {
|
|
2158
|
+
const parts = publicSelectors.map((s) => {
|
|
2159
|
+
const idx = selectors.indexOf(s)
|
|
2160
|
+
return idx >= 0 ? `typeof P[${idx}]` : JSON.stringify(s)
|
|
2161
|
+
})
|
|
2162
|
+
code += `export type RouteSelector = ${parts.join(" | ")}\n`
|
|
2163
|
+
}
|
|
2191
2164
|
}
|
|
2192
2165
|
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2166
|
+
const hasMeta = routes.some((r) => r.meta !== null && Object.keys(r.meta).length > 0)
|
|
2167
|
+
if (hasMeta) {
|
|
2168
|
+
const metaType = hasMetaShape ? "Record<string, MetaShape>" : "Record<string, Record<string, unknown>>"
|
|
2169
|
+
code += `export const meta: ${metaType} = routeTree.meta as ${metaType}\n`
|
|
2196
2170
|
}
|
|
2197
2171
|
|
|
2198
|
-
/* emit routeTree convenience export for .routeTree() */
|
|
2199
|
-
const hasMetaExport = metaEntries.length > 0
|
|
2200
|
-
code += `export const routeTree: RouteTree = { root: tree, ${hasMetaExport ? "meta, " : "meta: {}, "}handlers }\n`
|
|
2201
|
-
|
|
2202
2172
|
return code
|
|
2203
2173
|
}
|
|
2204
2174
|
|
|
2175
|
+
export function generateRouteTree(routes: RouteConfig[]): string {
|
|
2176
|
+
return emitRouteTree(routes, null)
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
function generateFromTreeRoot(root: TreeNode): string {
|
|
2180
|
+
const routes: RouteConfig[] = []
|
|
2181
|
+
collectRoutesForTree(root, "", routes)
|
|
2182
|
+
const collected: CollectedRoute[] = []
|
|
2183
|
+
walkTree(root, "", collected)
|
|
2184
|
+
return emitRouteTree(routes, collected)
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2205
2187
|
export function generateRouteTreeFromApp<TEnv, TCtx>(
|
|
2206
2188
|
app: Honey<TEnv, TCtx, unknown, unknown, unknown, string, string>,
|
|
2207
2189
|
): string {
|
package/src/tree.ts
CHANGED
|
@@ -108,6 +108,399 @@ 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
|
+
|
|
111
504
|
function splitSegments(path: string): string[] {
|
|
112
505
|
return path.split("/").filter((s) => s.length > 0)
|
|
113
506
|
}
|