@lovrozagar/honey 0.1.0 → 0.2.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.
- package/README.md +1596 -6
- package/dist/accepts.d.ts +6 -0
- package/dist/accepts.d.ts.map +1 -0
- package/dist/body-limit.d.ts +22 -0
- package/dist/body-limit.d.ts.map +1 -0
- package/dist/build/index.d.ts +42 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/client/error.d.ts +14 -0
- package/dist/client/error.d.ts.map +1 -0
- package/dist/client/http.d.ts +88 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/index.d.ts +22 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/sdk.d.ts +34 -0
- package/dist/client/sdk.d.ts.map +1 -0
- package/dist/client/sse.d.ts +10 -0
- package/dist/client/sse.d.ts.map +1 -0
- package/dist/client/types.d.ts +124 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/ws.d.ts +15 -0
- package/dist/client/ws.d.ts.map +1 -0
- package/dist/client-mcp/server.d.ts +10 -0
- package/dist/client-mcp/server.d.ts.map +1 -0
- package/dist/client-mcp/types.d.ts +16 -0
- package/dist/client-mcp/types.d.ts.map +1 -0
- package/dist/cloudflare/realtime.d.ts +30 -0
- package/dist/cloudflare/realtime.d.ts.map +1 -0
- package/dist/codegen-go-cli.d.ts +22 -0
- package/dist/codegen-go-cli.d.ts.map +1 -0
- package/dist/codegen-go.d.ts +29 -0
- package/dist/codegen-go.d.ts.map +1 -0
- package/dist/codegen-hash.d.ts +3 -0
- package/dist/codegen-hash.d.ts.map +1 -0
- package/dist/codegen-ir.d.ts +123 -0
- package/dist/codegen-ir.d.ts.map +1 -0
- package/dist/codegen-loaders.d.ts +12 -0
- package/dist/codegen-loaders.d.ts.map +1 -0
- package/dist/codegen-mcp.d.ts +13 -0
- package/dist/codegen-mcp.d.ts.map +1 -0
- package/dist/codegen-python.d.ts +21 -0
- package/dist/codegen-python.d.ts.map +1 -0
- package/dist/codegen-rust.d.ts +18 -0
- package/dist/codegen-rust.d.ts.map +1 -0
- package/dist/codegen-sanitize.d.ts +3 -0
- package/dist/codegen-sanitize.d.ts.map +1 -0
- package/dist/codegen-schema-naming.d.ts +22 -0
- package/dist/codegen-schema-naming.d.ts.map +1 -0
- package/dist/codegen.d.ts +178 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/context.d.ts +56 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/cookie-sign.d.ts +3 -0
- package/dist/cookie-sign.d.ts.map +1 -0
- package/dist/cookie.d.ts +12 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cors.d.ts +13 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/crypto.d.ts +2 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/csrf.d.ts +9 -0
- package/dist/csrf.d.ts.map +1 -0
- package/dist/curl-logger.d.ts +33 -0
- package/dist/curl-logger.d.ts.map +1 -0
- package/dist/detect-runtime.d.ts +16 -0
- package/dist/detect-runtime.d.ts.map +1 -0
- package/dist/error.d.ts +21 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/etag.d.ts +9 -0
- package/dist/etag.d.ts.map +1 -0
- package/dist/feature-load.d.ts +3 -0
- package/dist/feature-load.d.ts.map +1 -0
- package/dist/go-type-emitter.d.ts +33 -0
- package/dist/go-type-emitter.d.ts.map +1 -0
- package/dist/honey-response.d.ts +82 -0
- package/dist/honey-response.d.ts.map +1 -0
- package/dist/i18n-slot.d.ts +7 -0
- package/dist/i18n-slot.d.ts.map +1 -0
- package/dist/i18n.d.ts +26 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/index.d.ts +470 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/init.d.ts +7 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/input.d.ts +6 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/ip-restrict.d.ts +12 -0
- package/dist/ip-restrict.d.ts.map +1 -0
- package/dist/logger.d.ts +45 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/meta-spec.d.ts +81 -0
- package/dist/meta-spec.d.ts.map +1 -0
- package/dist/middleware.d.ts +60 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/node-request.d.ts +59 -0
- package/dist/node-request.d.ts.map +1 -0
- package/dist/node.d.ts +13 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/openapi/register.d.ts +3 -0
- package/dist/openapi/register.d.ts.map +1 -0
- package/dist/openapi/scalar.d.ts +12 -0
- package/dist/openapi/scalar.d.ts.map +1 -0
- package/dist/openapi/spec-factory.d.ts +31 -0
- package/dist/openapi/spec-factory.d.ts.map +1 -0
- package/dist/openapi/spec.d.ts +19 -0
- package/dist/openapi/spec.d.ts.map +1 -0
- package/dist/openapi/swagger.d.ts +12 -0
- package/dist/openapi/swagger.d.ts.map +1 -0
- package/dist/plugin.d.ts +167 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/powered-by.d.ts +7 -0
- package/dist/powered-by.d.ts.map +1 -0
- package/dist/pretty-json.d.ts +10 -0
- package/dist/pretty-json.d.ts.map +1 -0
- package/dist/proxy.d.ts +51 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/python-type-emitter.d.ts +11 -0
- package/dist/python-type-emitter.d.ts.map +1 -0
- package/dist/realtime/buffer.d.ts +18 -0
- package/dist/realtime/buffer.d.ts.map +1 -0
- package/dist/realtime/bus.d.ts +11 -0
- package/dist/realtime/bus.d.ts.map +1 -0
- package/dist/realtime/longpoll.d.ts +23 -0
- package/dist/realtime/longpoll.d.ts.map +1 -0
- package/dist/realtime/protocol.d.ts +50 -0
- package/dist/realtime/protocol.d.ts.map +1 -0
- package/dist/realtime/route.d.ts +42 -0
- package/dist/realtime/route.d.ts.map +1 -0
- package/dist/realtime/runtime.d.ts +74 -0
- package/dist/realtime/runtime.d.ts.map +1 -0
- package/dist/request-id.d.ts +12 -0
- package/dist/request-id.d.ts.map +1 -0
- package/dist/request-to-curl.d.ts +9 -0
- package/dist/request-to-curl.d.ts.map +1 -0
- package/dist/response.d.ts +57 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/rust-type-emitter.d.ts +32 -0
- package/dist/rust-type-emitter.d.ts.map +1 -0
- package/dist/secure-headers.d.ts +16 -0
- package/dist/secure-headers.d.ts.map +1 -0
- package/dist/serve-register.d.ts +2 -0
- package/dist/serve-register.d.ts.map +1 -0
- package/dist/serve-slot.d.ts +6 -0
- package/dist/serve-slot.d.ts.map +1 -0
- package/dist/serve.d.ts +21 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/server-timing.d.ts +9 -0
- package/dist/server-timing.d.ts.map +1 -0
- package/dist/static.d.ts +12 -0
- package/dist/static.d.ts.map +1 -0
- package/dist/telemetry/otel.d.ts +59 -0
- package/dist/telemetry/otel.d.ts.map +1 -0
- package/dist/testing.d.ts +24 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/timeout.d.ts +7 -0
- package/dist/timeout.d.ts.map +1 -0
- package/dist/tree.d.ts +90 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/ts-type-emitter.d.ts +4 -0
- package/dist/ts-type-emitter.d.ts.map +1 -0
- package/dist/type-emitter.d.ts +8 -0
- package/dist/type-emitter.d.ts.map +1 -0
- package/dist/type-extractor.d.ts +39 -0
- package/dist/type-extractor.d.ts.map +1 -0
- package/dist/types.d.ts +451 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validation.d.ts +18 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/ws/bun.d.ts +28 -0
- package/dist/ws/bun.d.ts.map +1 -0
- package/dist/ws/cloudflare.d.ts +55 -0
- package/dist/ws/cloudflare.d.ts.map +1 -0
- package/dist/ws/deno.d.ts +13 -0
- package/dist/ws/deno.d.ts.map +1 -0
- package/dist/ws/node.d.ts +10 -0
- package/dist/ws/node.d.ts.map +1 -0
- package/dist/yaml.d.ts +4 -0
- package/dist/yaml.d.ts.map +1 -0
- package/docs/api-ref/go.md +82 -82
- package/docs/api-ref/python.md +82 -82
- package/docs/api-ref/rust.md +82 -82
- package/docs/api-ref/typescript.md +82 -82
- package/docs/invalidate-spec.md +13 -13
- package/docs/meta-spec.md +513 -0
- package/docs/packaging.md +85 -0
- package/docs/sdk.md +87 -73
- package/package.json +195 -51
- package/src/body-limit.ts +2 -1
- package/src/build/index.ts +2 -9
- package/src/cli.ts +2 -3
- package/src/client/http.ts +204 -223
- package/src/client/index.ts +6 -14
- package/src/client/sdk.ts +24 -17
- package/src/client/types.ts +4 -9
- package/src/client/ws.ts +3 -5
- package/src/client-mcp/server.ts +1 -4
- package/src/cloudflare/realtime.ts +8 -10
- package/src/codegen-go-cli.ts +74 -67
- package/src/codegen-go.ts +71 -51
- package/src/codegen-ir.ts +16 -27
- package/src/codegen-loaders.ts +5 -8
- package/src/codegen-mcp.ts +9 -27
- package/src/codegen-python.ts +47 -27
- package/src/codegen-rust.ts +110 -66
- package/src/codegen-sanitize.ts +1 -7
- package/src/codegen-schema-naming.ts +8 -2
- package/src/codegen.ts +299 -196
- package/src/context.ts +91 -95
- package/src/cookie-sign.ts +5 -13
- package/src/cookie.ts +1 -2
- package/src/cors.ts +4 -6
- package/src/crypto.ts +1 -4
- package/src/csrf.ts +3 -3
- package/src/curl-logger.ts +14 -32
- package/src/detect-runtime.ts +1 -3
- package/src/errors.ts +1 -3
- package/src/etag.ts +3 -4
- package/src/go-type-emitter.ts +45 -40
- package/src/honey-response.ts +1 -3
- package/src/i18n.ts +3 -12
- package/src/index.ts +1168 -1412
- package/src/ip-restrict.ts +2 -1
- package/src/logger.ts +13 -13
- package/src/meta-spec.ts +0 -0
- package/src/middleware.ts +111 -79
- package/src/node-request.ts +2 -1
- package/src/node.ts +9 -10
- package/src/openapi/register.ts +1 -2
- package/src/openapi/scalar.ts +9 -9
- package/src/openapi/spec-factory.ts +2 -2
- package/src/openapi/spec.ts +26 -19
- package/src/openapi/swagger.ts +9 -9
- package/src/plugin.ts +226 -256
- package/src/powered-by.ts +2 -1
- package/src/pretty-json.ts +2 -1
- package/src/proxy.ts +1 -3
- package/src/python-type-emitter.ts +37 -12
- package/src/realtime/route.ts +1 -1
- package/src/realtime/runtime.ts +9 -12
- package/src/request-id.ts +3 -4
- package/src/request-to-curl.ts +1 -4
- package/src/response.ts +150 -199
- package/src/rust-type-emitter.ts +66 -25
- package/src/secure-headers.ts +2 -1
- package/src/serve.ts +3 -12
- package/src/server-timing.ts +2 -1
- package/src/static.ts +3 -4
- package/src/telemetry/otel.ts +1 -6
- package/src/testing.ts +2 -11
- package/src/timeout.ts +2 -1
- package/src/tree.ts +12 -27
- package/src/ts-type-emitter.ts +71 -15
- package/src/type-emitter.ts +226 -237
- package/src/type-extractor.ts +260 -334
- package/src/types.ts +168 -68
- package/src/validation.ts +7 -30
- package/src/ws/bun.ts +2 -9
- package/src/ws/cloudflare.ts +13 -16
- package/src/ws/deno.ts +31 -40
- package/src/ws/node.ts +4 -19
package/src/type-extractor.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { existsSync } from "node:fs"
|
|
2
|
-
import { dirname, relative, resolve } from "node:path"
|
|
3
|
-
import type { ts } from "ts-morph"
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { dirname, relative, resolve } from "node:path"
|
|
3
|
+
import type { ts } from "ts-morph"
|
|
4
4
|
|
|
5
5
|
export type ExtractedBaseCtx = {
|
|
6
|
-
envType: string
|
|
7
|
-
middlewareType: string | null
|
|
8
|
-
tapsType: string | null
|
|
9
|
-
}
|
|
6
|
+
envType: string
|
|
7
|
+
middlewareType: string | null
|
|
8
|
+
tapsType: string | null
|
|
9
|
+
}
|
|
10
10
|
|
|
11
11
|
function findTsConfig(fromPath: string): string | undefined {
|
|
12
|
-
let dir = dirname(resolve(fromPath))
|
|
13
|
-
let prev = ""
|
|
12
|
+
let dir = dirname(resolve(fromPath))
|
|
13
|
+
let prev = ""
|
|
14
14
|
while (dir !== prev) {
|
|
15
|
-
const p = resolve(dir, "tsconfig.json")
|
|
16
|
-
if (existsSync(p)) return p
|
|
17
|
-
prev = dir
|
|
18
|
-
dir = dirname(dir)
|
|
15
|
+
const p = resolve(dir, "tsconfig.json")
|
|
16
|
+
if (existsSync(p)) return p
|
|
17
|
+
prev = dir
|
|
18
|
+
dir = dirname(dir)
|
|
19
19
|
}
|
|
20
|
-
return undefined
|
|
20
|
+
return undefined
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -25,85 +25,76 @@ function findTsConfig(fromPath: string): string | undefined {
|
|
|
25
25
|
* Returns inline type strings so the gen file has zero imports from user code.
|
|
26
26
|
*/
|
|
27
27
|
export async function extractBaseCtx(options: {
|
|
28
|
-
entryPath: string
|
|
29
|
-
exportName: string
|
|
30
|
-
outputDir?: string
|
|
31
|
-
tsconfigPath?: string
|
|
28
|
+
entryPath: string
|
|
29
|
+
exportName: string
|
|
30
|
+
outputDir?: string
|
|
31
|
+
tsconfigPath?: string
|
|
32
32
|
}): Promise<ExtractedBaseCtx> {
|
|
33
|
-
const { Project, ts } = await import("ts-morph")
|
|
33
|
+
const { Project, ts } = await import("ts-morph")
|
|
34
34
|
|
|
35
|
-
const tsConfigFilePath =
|
|
36
|
-
options.tsconfigPath ?? findTsConfig(options.entryPath);
|
|
35
|
+
const tsConfigFilePath = options.tsconfigPath ?? findTsConfig(options.entryPath)
|
|
37
36
|
|
|
38
37
|
const project = new Project({
|
|
39
38
|
skipAddingFilesFromTsConfig: true,
|
|
40
39
|
tsConfigFilePath,
|
|
41
|
-
})
|
|
40
|
+
})
|
|
42
41
|
|
|
43
|
-
project.addSourceFilesAtPaths(options.entryPath)
|
|
44
|
-
project.resolveSourceFileDependencies()
|
|
42
|
+
project.addSourceFilesAtPaths(options.entryPath)
|
|
43
|
+
project.resolveSourceFileDependencies()
|
|
45
44
|
|
|
46
|
-
const sourceFile = project.getSourceFileOrThrow(options.entryPath)
|
|
47
|
-
const varDecl = sourceFile.getVariableDeclaration(options.exportName)
|
|
45
|
+
const sourceFile = project.getSourceFileOrThrow(options.entryPath)
|
|
46
|
+
const varDecl = sourceFile.getVariableDeclaration(options.exportName)
|
|
48
47
|
if (!varDecl) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
`Export "${options.exportName}" not found in ${options.entryPath}`,
|
|
51
|
-
);
|
|
48
|
+
throw new Error(`Export "${options.exportName}" not found in ${options.entryPath}`)
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
const type = varDecl.getType()
|
|
51
|
+
const type = varDecl.getType()
|
|
55
52
|
/* ts-morph bundles different TS version — structurally identical at runtime */
|
|
56
53
|
function bridge<T>(value: unknown): T {
|
|
57
|
-
return value as T
|
|
54
|
+
return value as T
|
|
58
55
|
}
|
|
59
|
-
const checker: ts.TypeChecker = bridge(
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
sym: ReturnType<typeof type.getProperty>,
|
|
67
|
-
): ts.Type {
|
|
68
|
-
const s: ts.Symbol = bridge(sym?.compilerSymbol);
|
|
69
|
-
return checker.getTypeOfSymbolAtLocation(s, node);
|
|
56
|
+
const checker: ts.TypeChecker = bridge(project.getTypeChecker().compilerObject)
|
|
57
|
+
const node: ts.Node = bridge(varDecl.compilerNode)
|
|
58
|
+
const tsLib: typeof ts = bridge(ts)
|
|
59
|
+
|
|
60
|
+
function getType(sym: ReturnType<typeof type.getProperty>): ts.Type {
|
|
61
|
+
const s: ts.Symbol = bridge(sym?.compilerSymbol)
|
|
62
|
+
return checker.getTypeOfSymbolAtLocation(s, node)
|
|
70
63
|
}
|
|
71
64
|
|
|
72
65
|
/* extract $env */
|
|
73
|
-
let envType = "Record<string, unknown>"
|
|
74
|
-
const envProp = type.getProperty("$env")
|
|
66
|
+
let envType = "Record<string, unknown>"
|
|
67
|
+
const envProp = type.getProperty("$env")
|
|
75
68
|
if (envProp) {
|
|
76
|
-
envType = serialize(getType(envProp), checker, node, tsLib)
|
|
69
|
+
envType = serialize(getType(envProp), checker, node, tsLib)
|
|
77
70
|
}
|
|
78
71
|
|
|
79
72
|
/* extract $ctx and isolate middleware additions */
|
|
80
|
-
const ctxProp = type.getProperty("$ctx")
|
|
73
|
+
const ctxProp = type.getProperty("$ctx")
|
|
81
74
|
if (!ctxProp) {
|
|
82
|
-
throw new Error(
|
|
83
|
-
`Export "${options.exportName}" has no $ctx — not a Honey instance`,
|
|
84
|
-
);
|
|
75
|
+
throw new Error(`Export "${options.exportName}" has no $ctx — not a Honey instance`)
|
|
85
76
|
}
|
|
86
77
|
|
|
87
|
-
const ctxType = getType(ctxProp)
|
|
88
|
-
const middlewareType = extractMiddleware(ctxType, checker, node, tsLib)
|
|
78
|
+
const ctxType = getType(ctxProp)
|
|
79
|
+
const middlewareType = extractMiddleware(ctxType, checker, node, tsLib)
|
|
89
80
|
|
|
90
81
|
/* extract $taps — typed tap payloads declared via .taps<T>() */
|
|
91
|
-
let tapsType: string | null = null
|
|
92
|
-
const tapsProp = type.getProperty("$taps")
|
|
82
|
+
let tapsType: string | null = null
|
|
83
|
+
const tapsProp = type.getProperty("$taps")
|
|
93
84
|
if (tapsProp) {
|
|
94
|
-
const tapsT = getType(tapsProp)
|
|
95
|
-
const tapsStr = serialize(tapsT, checker, node, tsLib)
|
|
85
|
+
const tapsT = getType(tapsProp)
|
|
86
|
+
const tapsStr = serialize(tapsT, checker, node, tsLib)
|
|
96
87
|
if (tapsStr !== "{}" && tapsStr !== "Record<string, unknown>") {
|
|
97
|
-
tapsType = tapsStr
|
|
88
|
+
tapsType = tapsStr
|
|
98
89
|
}
|
|
99
90
|
}
|
|
100
91
|
|
|
101
|
-
const sanitize = (s: string) => sanitizeImportPaths(s, options.outputDir)
|
|
92
|
+
const sanitize = (s: string) => sanitizeImportPaths(s, options.outputDir)
|
|
102
93
|
return {
|
|
103
94
|
envType: sanitize(envType),
|
|
104
95
|
middlewareType: middlewareType ? sanitize(middlewareType) : null,
|
|
105
96
|
tapsType: tapsType ? sanitize(tapsType) : null,
|
|
106
|
-
}
|
|
97
|
+
}
|
|
107
98
|
}
|
|
108
99
|
|
|
109
100
|
/* properties that live on HoneyContext — everything else is middleware-added */
|
|
@@ -123,7 +114,7 @@ const HONEY_CTX_PROPS = new Set([
|
|
|
123
114
|
"search",
|
|
124
115
|
"searchAll",
|
|
125
116
|
"tap",
|
|
126
|
-
])
|
|
117
|
+
])
|
|
127
118
|
|
|
128
119
|
/** Resolve an import() reference from a declaration's type annotation */
|
|
129
120
|
function resolveTypeRefFromDecl(
|
|
@@ -137,7 +128,7 @@ function resolveTypeRefFromDecl(
|
|
|
137
128
|
const typeName = (typeRef as ts.TypeReferenceNode).typeName
|
|
138
129
|
|
|
139
130
|
let refSym = checker.getSymbolAtLocation(typeName)
|
|
140
|
-
while (refSym &&
|
|
131
|
+
while (refSym && refSym.flags & compiler.SymbolFlags.Alias) {
|
|
141
132
|
refSym = checker.getAliasedSymbol(refSym)
|
|
142
133
|
}
|
|
143
134
|
if (!refSym) return undefined
|
|
@@ -159,46 +150,46 @@ function extractMiddlewareProps(
|
|
|
159
150
|
node: ts.Node,
|
|
160
151
|
compiler: typeof ts,
|
|
161
152
|
): MwPropEntry[] {
|
|
162
|
-
const allProps = ctxType.getProperties()
|
|
153
|
+
const allProps = ctxType.getProperties()
|
|
163
154
|
const mwProps = allProps.filter((p) => {
|
|
164
|
-
const name = p.getName()
|
|
165
|
-
if (HONEY_CTX_PROPS.has(name)) return false
|
|
155
|
+
const name = p.getName()
|
|
156
|
+
if (HONEY_CTX_PROPS.has(name)) return false
|
|
166
157
|
/* skip internal/private properties (underscore-prefixed) */
|
|
167
|
-
if (name.startsWith("_")) return false
|
|
168
|
-
return true
|
|
169
|
-
})
|
|
158
|
+
if (name.startsWith("_")) return false
|
|
159
|
+
return true
|
|
160
|
+
})
|
|
170
161
|
|
|
171
162
|
return mwProps.map((p) => {
|
|
172
|
-
const t = checker.getTypeOfSymbolAtLocation(p, p.valueDeclaration ?? node)
|
|
173
|
-
const opt = (p.flags & compiler.SymbolFlags.Optional) !== 0
|
|
163
|
+
const t = checker.getTypeOfSymbolAtLocation(p, p.valueDeclaration ?? node)
|
|
164
|
+
const opt = (p.flags & compiler.SymbolFlags.Optional) !== 0
|
|
174
165
|
|
|
175
166
|
/* 1. try declaration-site TypeReference (explicit annotation on property or variable) */
|
|
176
|
-
let typeStr: string | undefined
|
|
177
|
-
const mwDecl = p.valueDeclaration ?? p.declarations?.[0]
|
|
178
|
-
typeStr = resolveTypeRefFromDecl(mwDecl, checker, compiler)
|
|
167
|
+
let typeStr: string | undefined
|
|
168
|
+
const mwDecl = p.valueDeclaration ?? p.declarations?.[0]
|
|
169
|
+
typeStr = resolveTypeRefFromDecl(mwDecl, checker, compiler)
|
|
179
170
|
|
|
180
171
|
/* 1b. shorthand property — trace to the underlying variable's type annotation */
|
|
181
172
|
if (!typeStr && mwDecl && compiler.isShorthandPropertyAssignment(mwDecl)) {
|
|
182
|
-
const varSym = checker.getShorthandAssignmentValueSymbol(mwDecl)
|
|
173
|
+
const varSym = checker.getShorthandAssignmentValueSymbol(mwDecl)
|
|
183
174
|
if (varSym) {
|
|
184
|
-
const varDecl = varSym.valueDeclaration ?? varSym.declarations?.[0]
|
|
185
|
-
typeStr = resolveTypeRefFromDecl(varDecl, checker, compiler)
|
|
175
|
+
const varDecl = varSym.valueDeclaration ?? varSym.declarations?.[0]
|
|
176
|
+
typeStr = resolveTypeRefFromDecl(varDecl, checker, compiler)
|
|
186
177
|
|
|
187
178
|
/* 1c. destructured binding (e.g. const { shardDb } = openShardSession(...))
|
|
188
179
|
* — trace through the function return type to find the original type annotation */
|
|
189
180
|
if (!typeStr && varDecl && compiler.isBindingElement(varDecl)) {
|
|
190
|
-
let parent: ts.Node = varDecl.parent
|
|
191
|
-
while (parent && !compiler.isVariableDeclaration(parent)) parent = parent.parent
|
|
181
|
+
let parent: ts.Node = varDecl.parent
|
|
182
|
+
while (parent && !compiler.isVariableDeclaration(parent)) parent = parent.parent
|
|
192
183
|
if (parent && compiler.isVariableDeclaration(parent)) {
|
|
193
184
|
const initializer = (parent as ts.VariableDeclaration).initializer
|
|
194
185
|
if (initializer) {
|
|
195
|
-
const initType = checker.getTypeAtLocation(initializer)
|
|
196
|
-
const retProp = initType.getProperty(p.getName())
|
|
186
|
+
const initType = checker.getTypeAtLocation(initializer)
|
|
187
|
+
const retProp = initType.getProperty(p.getName())
|
|
197
188
|
if (retProp?.valueDeclaration && compiler.isShorthandPropertyAssignment(retProp.valueDeclaration)) {
|
|
198
|
-
const innerSym = checker.getShorthandAssignmentValueSymbol(retProp.valueDeclaration)
|
|
189
|
+
const innerSym = checker.getShorthandAssignmentValueSymbol(retProp.valueDeclaration)
|
|
199
190
|
if (innerSym) {
|
|
200
|
-
const innerDecl = innerSym.valueDeclaration ?? innerSym.declarations?.[0]
|
|
201
|
-
typeStr = resolveTypeRefFromDecl(innerDecl, checker, compiler)
|
|
191
|
+
const innerDecl = innerSym.valueDeclaration ?? innerSym.declarations?.[0]
|
|
192
|
+
typeStr = resolveTypeRefFromDecl(innerDecl, checker, compiler)
|
|
202
193
|
}
|
|
203
194
|
}
|
|
204
195
|
}
|
|
@@ -209,16 +200,16 @@ function extractMiddlewareProps(
|
|
|
209
200
|
|
|
210
201
|
/* 2. try resolved type as named import reference */
|
|
211
202
|
if (!typeStr) {
|
|
212
|
-
typeStr = serializeAsRef(t, checker, node, compiler) ?? undefined
|
|
203
|
+
typeStr = serializeAsRef(t, checker, node, compiler) ?? undefined
|
|
213
204
|
}
|
|
214
205
|
|
|
215
206
|
/* 3. structural fallback */
|
|
216
207
|
if (!typeStr) {
|
|
217
|
-
typeStr = serialize(t, checker, node, compiler)
|
|
208
|
+
typeStr = serialize(t, checker, node, compiler)
|
|
218
209
|
}
|
|
219
210
|
|
|
220
|
-
return { name: p.getName(), opt, type: typeStr }
|
|
221
|
-
})
|
|
211
|
+
return { name: p.getName(), opt, type: typeStr }
|
|
212
|
+
})
|
|
222
213
|
}
|
|
223
214
|
|
|
224
215
|
function extractMiddleware(
|
|
@@ -227,24 +218,20 @@ function extractMiddleware(
|
|
|
227
218
|
node: ts.Node,
|
|
228
219
|
compiler: typeof ts,
|
|
229
220
|
): string | null {
|
|
230
|
-
const props = extractMiddlewareProps(ctxType, checker, node, compiler)
|
|
231
|
-
if (props.length === 0) return null
|
|
232
|
-
const entries = props.map((p) => `${p.name}${p.opt ? "?" : ""}: ${p.type}`)
|
|
233
|
-
return `{ ${entries.join("; ")} }
|
|
221
|
+
const props = extractMiddlewareProps(ctxType, checker, node, compiler)
|
|
222
|
+
if (props.length === 0) return null
|
|
223
|
+
const entries = props.map((p) => `${p.name}${p.opt ? "?" : ""}: ${p.type}`)
|
|
224
|
+
return `{ ${entries.join("; ")} }`
|
|
234
225
|
}
|
|
235
226
|
|
|
236
227
|
/** Check if a symbol is exported from its source file */
|
|
237
|
-
function isExportedFromFile(
|
|
238
|
-
sym: ts.Symbol,
|
|
239
|
-
sf: ts.SourceFile,
|
|
240
|
-
checker: ts.TypeChecker,
|
|
241
|
-
): boolean {
|
|
228
|
+
function isExportedFromFile(sym: ts.Symbol, sf: ts.SourceFile, checker: ts.TypeChecker): boolean {
|
|
242
229
|
/* .d.ts module types are always accessible */
|
|
243
|
-
if (sf.isDeclarationFile) return true
|
|
244
|
-
const moduleSym = checker.getSymbolAtLocation(sf)
|
|
245
|
-
if (!moduleSym) return false
|
|
246
|
-
const exports = checker.getExportsOfModule(moduleSym)
|
|
247
|
-
return exports.some((e) => e.getName() === sym.getName())
|
|
230
|
+
if (sf.isDeclarationFile) return true
|
|
231
|
+
const moduleSym = checker.getSymbolAtLocation(sf)
|
|
232
|
+
if (!moduleSym) return false
|
|
233
|
+
const exports = checker.getExportsOfModule(moduleSym)
|
|
234
|
+
return exports.some((e) => e.getName() === sym.getName())
|
|
248
235
|
}
|
|
249
236
|
|
|
250
237
|
/**
|
|
@@ -263,153 +250,134 @@ function serializeAsRef(
|
|
|
263
250
|
compiler: typeof ts,
|
|
264
251
|
depth = 0,
|
|
265
252
|
): string | null {
|
|
266
|
-
if (depth > 6) return null
|
|
253
|
+
if (depth > 6) return null
|
|
267
254
|
|
|
268
255
|
/* union with never members (from intersection narrowing) — simplify first */
|
|
269
256
|
if (type.isUnion()) {
|
|
270
|
-
const nonNever = (type as ts.UnionType).types.filter(
|
|
271
|
-
(t) => !(t.flags & compiler.TypeFlags.Never),
|
|
272
|
-
);
|
|
257
|
+
const nonNever = (type as ts.UnionType).types.filter((t) => !(t.flags & compiler.TypeFlags.Never))
|
|
273
258
|
if (nonNever.length === 1) {
|
|
274
|
-
return serializeAsRef(nonNever[0], checker, node, compiler, depth)
|
|
259
|
+
return serializeAsRef(nonNever[0], checker, node, compiler, depth)
|
|
275
260
|
}
|
|
276
261
|
}
|
|
277
262
|
|
|
278
|
-
const sym = type.getSymbol() ?? type.aliasSymbol
|
|
279
|
-
if (!sym) return null
|
|
263
|
+
const sym = type.getSymbol() ?? type.aliasSymbol
|
|
264
|
+
if (!sym) return null
|
|
280
265
|
|
|
281
|
-
const name = sym.getName()
|
|
282
|
-
if (!name || name.startsWith("__") || name === "Object") return null
|
|
266
|
+
const name = sym.getName()
|
|
267
|
+
if (!name || name.startsWith("__") || name === "Object") return null
|
|
283
268
|
|
|
284
|
-
const decls = sym.declarations
|
|
285
|
-
if (!decls?.length) return null
|
|
269
|
+
const decls = sym.declarations
|
|
270
|
+
if (!decls?.length) return null
|
|
286
271
|
|
|
287
|
-
const sf = decls[0].getSourceFile()
|
|
288
|
-
const fileName = sf.fileName
|
|
272
|
+
const sf = decls[0].getSourceFile()
|
|
273
|
+
const fileName = sf.fileName
|
|
289
274
|
|
|
290
275
|
/* skip global/lib types — serialize() handles these with its globals set */
|
|
291
|
-
if (fileName.includes("/lib.") || fileName.includes("/lib/")) return null
|
|
276
|
+
if (fileName.includes("/lib.") || fileName.includes("/lib/")) return null
|
|
292
277
|
|
|
293
|
-
const isDts = sf.isDeclarationFile
|
|
278
|
+
const isDts = sf.isDeclarationFile
|
|
294
279
|
const isTypeAlias =
|
|
295
|
-
(sym.flags & compiler.SymbolFlags.TypeAlias) !== 0 ||
|
|
296
|
-
decls.some((d) => compiler.isTypeAliasDeclaration(d));
|
|
280
|
+
(sym.flags & compiler.SymbolFlags.TypeAlias) !== 0 || decls.some((d) => compiler.isTypeAliasDeclaration(d))
|
|
297
281
|
|
|
298
282
|
/* only reference .d.ts symbols or explicit type aliases from .ts files */
|
|
299
|
-
if (!isDts && !isTypeAlias) return null
|
|
283
|
+
if (!isDts && !isTypeAlias) return null
|
|
300
284
|
|
|
301
285
|
/* skip ambient .d.ts scripts (no exports) — import() can't resolve them */
|
|
302
286
|
if (isDts) {
|
|
303
|
-
const tsSf = sf as unknown as { externalModuleIndicator?: unknown }
|
|
304
|
-
if (!tsSf.externalModuleIndicator) return null
|
|
287
|
+
const tsSf = sf as unknown as { externalModuleIndicator?: unknown }
|
|
288
|
+
if (!tsSf.externalModuleIndicator) return null
|
|
305
289
|
}
|
|
306
290
|
|
|
307
291
|
/* skip non-exported symbols — import() on them resolves to `any` */
|
|
308
|
-
if (!isExportedFromFile(sym, sf, checker)) return null
|
|
292
|
+
if (!isExportedFromFile(sym, sf, checker)) return null
|
|
309
293
|
|
|
310
294
|
/* build type arguments if present */
|
|
311
|
-
const ref = type as ts.TypeReference
|
|
312
|
-
const typeArgs = ref.typeArguments ?? type.aliasTypeArguments
|
|
295
|
+
const ref = type as ts.TypeReference
|
|
296
|
+
const typeArgs = ref.typeArguments ?? type.aliasTypeArguments
|
|
313
297
|
if (typeArgs?.length) {
|
|
314
298
|
const args = typeArgs.map((arg) => {
|
|
315
|
-
const argRef = serializeAsRef(arg, checker, node, compiler, depth + 1)
|
|
316
|
-
return argRef ?? serialize(arg, checker, node, compiler)
|
|
317
|
-
})
|
|
318
|
-
return `import("${fileName}").${name}<${args.join(", ")}
|
|
299
|
+
const argRef = serializeAsRef(arg, checker, node, compiler, depth + 1)
|
|
300
|
+
return argRef ?? serialize(arg, checker, node, compiler)
|
|
301
|
+
})
|
|
302
|
+
return `import("${fileName}").${name}<${args.join(", ")}>`
|
|
319
303
|
}
|
|
320
304
|
|
|
321
|
-
return `import("${fileName}").${name}
|
|
305
|
+
return `import("${fileName}").${name}`
|
|
322
306
|
}
|
|
323
307
|
|
|
324
|
-
function serialize(
|
|
325
|
-
|
|
326
|
-
checker: ts.TypeChecker,
|
|
327
|
-
node: ts.Node,
|
|
328
|
-
compiler: typeof ts,
|
|
329
|
-
depth = 0,
|
|
330
|
-
): string {
|
|
331
|
-
if (depth > 8) return "unknown";
|
|
308
|
+
function serialize(type: ts.Type, checker: ts.TypeChecker, node: ts.Node, compiler: typeof ts, depth = 0): string {
|
|
309
|
+
if (depth > 8) return "unknown"
|
|
332
310
|
|
|
333
|
-
const next = (t: ts.Type) =>
|
|
334
|
-
serialize(t, checker, node, compiler, depth + 1);
|
|
311
|
+
const next = (t: ts.Type) => serialize(t, checker, node, compiler, depth + 1)
|
|
335
312
|
|
|
336
313
|
/* primitives */
|
|
337
|
-
if (type.flags & compiler.TypeFlags.String) return "string"
|
|
338
|
-
if (type.flags & compiler.TypeFlags.Number) return "number"
|
|
339
|
-
if (type.flags & compiler.TypeFlags.Boolean) return "boolean"
|
|
340
|
-
if (type.flags & compiler.TypeFlags.BigInt) return "bigint"
|
|
341
|
-
if (type.flags & compiler.TypeFlags.ESSymbol) return "symbol"
|
|
342
|
-
if (type.flags & compiler.TypeFlags.Void) return "void"
|
|
343
|
-
if (type.flags & compiler.TypeFlags.Undefined) return "undefined"
|
|
344
|
-
if (type.flags & compiler.TypeFlags.Null) return "null"
|
|
345
|
-
if (type.flags & compiler.TypeFlags.Never) return "never"
|
|
346
|
-
if (type.flags & compiler.TypeFlags.Unknown) return "unknown"
|
|
347
|
-
if (type.flags & compiler.TypeFlags.Any) return "unknown"
|
|
314
|
+
if (type.flags & compiler.TypeFlags.String) return "string"
|
|
315
|
+
if (type.flags & compiler.TypeFlags.Number) return "number"
|
|
316
|
+
if (type.flags & compiler.TypeFlags.Boolean) return "boolean"
|
|
317
|
+
if (type.flags & compiler.TypeFlags.BigInt) return "bigint"
|
|
318
|
+
if (type.flags & compiler.TypeFlags.ESSymbol) return "symbol"
|
|
319
|
+
if (type.flags & compiler.TypeFlags.Void) return "void"
|
|
320
|
+
if (type.flags & compiler.TypeFlags.Undefined) return "undefined"
|
|
321
|
+
if (type.flags & compiler.TypeFlags.Null) return "null"
|
|
322
|
+
if (type.flags & compiler.TypeFlags.Never) return "never"
|
|
323
|
+
if (type.flags & compiler.TypeFlags.Unknown) return "unknown"
|
|
324
|
+
if (type.flags & compiler.TypeFlags.Any) return "unknown"
|
|
348
325
|
|
|
349
326
|
/* literals */
|
|
350
|
-
if (type.isStringLiteral())
|
|
351
|
-
|
|
352
|
-
if (type.isNumberLiteral())
|
|
353
|
-
return `${(type as ts.NumberLiteralType).value}`;
|
|
327
|
+
if (type.isStringLiteral()) return `"${(type as ts.StringLiteralType).value}"`
|
|
328
|
+
if (type.isNumberLiteral()) return `${(type as ts.NumberLiteralType).value}`
|
|
354
329
|
|
|
355
330
|
/* union */
|
|
356
331
|
if (type.isUnion()) {
|
|
357
332
|
return type.types
|
|
358
333
|
.map((t) => {
|
|
359
|
-
const s = next(t)
|
|
334
|
+
const s = next(t)
|
|
360
335
|
/* parenthesize function types inside unions — TS syntax requires it */
|
|
361
|
-
return s.includes("=>") ? `(${s})` : s
|
|
336
|
+
return s.includes("=>") ? `(${s})` : s
|
|
362
337
|
})
|
|
363
|
-
.join(" | ")
|
|
338
|
+
.join(" | ")
|
|
364
339
|
}
|
|
365
340
|
|
|
366
341
|
/* intersection */
|
|
367
|
-
if (type.isIntersection()) return type.types.map(next).join(" & ")
|
|
342
|
+
if (type.isIntersection()) return type.types.map(next).join(" & ")
|
|
368
343
|
|
|
369
344
|
/* array */
|
|
370
345
|
if (checker.isArrayType(type)) {
|
|
371
|
-
const args = checker.getTypeArguments(
|
|
372
|
-
type as ts.TypeReference,
|
|
373
|
-
);
|
|
346
|
+
const args = checker.getTypeArguments(type as ts.TypeReference)
|
|
374
347
|
if (args.length > 0) {
|
|
375
|
-
const el = next(args[0])
|
|
376
|
-
return el.includes("|") || el.includes("&") ? `(${el})[]` : `${el}[]
|
|
348
|
+
const el = next(args[0])
|
|
349
|
+
return el.includes("|") || el.includes("&") ? `(${el})[]` : `${el}[]`
|
|
377
350
|
}
|
|
378
|
-
return "unknown[]"
|
|
351
|
+
return "unknown[]"
|
|
379
352
|
}
|
|
380
353
|
|
|
381
354
|
/* tuple */
|
|
382
355
|
if (checker.isTupleType(type)) {
|
|
383
|
-
const args = checker.getTypeArguments(
|
|
384
|
-
|
|
385
|
-
);
|
|
386
|
-
return `[${args.map(next).join(", ")}]`;
|
|
356
|
+
const args = checker.getTypeArguments(type as ts.TypeReference)
|
|
357
|
+
return `[${args.map(next).join(", ")}]`
|
|
387
358
|
}
|
|
388
359
|
|
|
389
360
|
/* function (pure callable, no data properties) */
|
|
390
|
-
const sigs = type.getCallSignatures()
|
|
361
|
+
const sigs = type.getCallSignatures()
|
|
391
362
|
if (sigs.length > 0 && type.getProperties().length === 0) {
|
|
392
|
-
const sig = sigs[0]
|
|
363
|
+
const sig = sigs[0]
|
|
393
364
|
const params = sig.getParameters().map((p) => {
|
|
394
|
-
const pt = checker.getTypeOfSymbolAtLocation(
|
|
395
|
-
|
|
396
|
-
p.valueDeclaration ?? node,
|
|
397
|
-
);
|
|
398
|
-
const decl = p.valueDeclaration;
|
|
365
|
+
const pt = checker.getTypeOfSymbolAtLocation(p, p.valueDeclaration ?? node)
|
|
366
|
+
const decl = p.valueDeclaration
|
|
399
367
|
const isRest =
|
|
400
368
|
decl !== undefined &&
|
|
401
369
|
compiler.isParameter(decl) &&
|
|
402
|
-
(decl as ts.ParameterDeclaration).dotDotDotToken !== undefined
|
|
403
|
-
return `${isRest ? "..." : ""}${p.getName()}: ${next(pt)}
|
|
404
|
-
})
|
|
405
|
-
const ret = checker.getReturnTypeOfSignature(sig)
|
|
406
|
-
return `(${params.join(", ")}) => ${next(ret)}
|
|
370
|
+
(decl as ts.ParameterDeclaration).dotDotDotToken !== undefined
|
|
371
|
+
return `${isRest ? "..." : ""}${p.getName()}: ${next(pt)}`
|
|
372
|
+
})
|
|
373
|
+
const ret = checker.getReturnTypeOfSignature(sig)
|
|
374
|
+
return `(${params.join(", ")}) => ${next(ret)}`
|
|
407
375
|
}
|
|
408
376
|
|
|
409
377
|
/* well-known globals — keep name, serialize type args */
|
|
410
|
-
const sym = type.getSymbol()
|
|
378
|
+
const sym = type.getSymbol()
|
|
411
379
|
if (sym) {
|
|
412
|
-
const name = sym.getName()
|
|
380
|
+
const name = sym.getName()
|
|
413
381
|
const globals = new Set([
|
|
414
382
|
"AbortController",
|
|
415
383
|
"AbortSignal",
|
|
@@ -435,72 +403,65 @@ function serialize(
|
|
|
435
403
|
"WeakMap",
|
|
436
404
|
"WeakSet",
|
|
437
405
|
"WritableStream",
|
|
438
|
-
])
|
|
406
|
+
])
|
|
439
407
|
if (globals.has(name)) {
|
|
440
|
-
const ref = type as ts.TypeReference
|
|
408
|
+
const ref = type as ts.TypeReference
|
|
441
409
|
if (ref.typeArguments?.length) {
|
|
442
|
-
return `${name}<${ref.typeArguments.map(next).join(", ")}
|
|
410
|
+
return `${name}<${ref.typeArguments.map(next).join(", ")}>`
|
|
443
411
|
}
|
|
444
|
-
return name
|
|
412
|
+
return name
|
|
445
413
|
}
|
|
446
414
|
|
|
447
415
|
/* named types — prefer import() reference over structural expansion */
|
|
448
416
|
if (name && !name.startsWith("__") && name !== "Object") {
|
|
449
|
-
const ref = serializeAsRef(type, checker, node, compiler, 0)
|
|
450
|
-
if (ref) return ref
|
|
417
|
+
const ref = serializeAsRef(type, checker, node, compiler, 0)
|
|
418
|
+
if (ref) return ref
|
|
451
419
|
|
|
452
420
|
/* fallback: typeToString for .d.ts types without clean ref */
|
|
453
|
-
const decls = sym.declarations
|
|
421
|
+
const decls = sym.declarations
|
|
454
422
|
if (decls?.length) {
|
|
455
|
-
const sf = decls[0].getSourceFile()
|
|
423
|
+
const sf = decls[0].getSourceFile()
|
|
456
424
|
if (sf.isDeclarationFile) {
|
|
457
425
|
return checker.typeToString(
|
|
458
426
|
type,
|
|
459
427
|
node,
|
|
460
|
-
compiler.TypeFormatFlags.UseFullyQualifiedType |
|
|
461
|
-
|
|
462
|
-
);
|
|
428
|
+
compiler.TypeFormatFlags.UseFullyQualifiedType | compiler.TypeFormatFlags.NoTruncation,
|
|
429
|
+
)
|
|
463
430
|
}
|
|
464
431
|
}
|
|
465
432
|
}
|
|
466
433
|
}
|
|
467
434
|
|
|
468
435
|
/* Record<string, V> — index signature only */
|
|
469
|
-
const stringIdx = type.getStringIndexType()
|
|
436
|
+
const stringIdx = type.getStringIndexType()
|
|
470
437
|
if (stringIdx && type.getProperties().length === 0) {
|
|
471
|
-
return `Record<string, ${next(stringIdx)}
|
|
438
|
+
return `Record<string, ${next(stringIdx)}>`
|
|
472
439
|
}
|
|
473
440
|
|
|
474
441
|
/* object — expand to structural form */
|
|
475
|
-
const props = type.getProperties()
|
|
442
|
+
const props = type.getProperties()
|
|
476
443
|
if (props.length > 50) {
|
|
477
|
-
return checker.typeToString(type, node, compiler.TypeFormatFlags.NoTruncation)
|
|
444
|
+
return checker.typeToString(type, node, compiler.TypeFormatFlags.NoTruncation)
|
|
478
445
|
}
|
|
479
446
|
if (props.length > 0) {
|
|
480
|
-
const entries: string[] = []
|
|
447
|
+
const entries: string[] = []
|
|
481
448
|
for (const p of props) {
|
|
482
|
-
const decls = p.getDeclarations()
|
|
449
|
+
const decls = p.getDeclarations()
|
|
483
450
|
if (decls?.length) {
|
|
484
|
-
const flags = compiler.getCombinedModifierFlags(decls[0])
|
|
485
|
-
if (flags & (compiler.ModifierFlags.Private | compiler.ModifierFlags.Protected))
|
|
486
|
-
continue;
|
|
451
|
+
const flags = compiler.getCombinedModifierFlags(decls[0])
|
|
452
|
+
if (flags & (compiler.ModifierFlags.Private | compiler.ModifierFlags.Protected)) continue
|
|
487
453
|
}
|
|
488
|
-
const pt = checker.getTypeOfSymbolAtLocation(p, decls?.[0] ?? node)
|
|
489
|
-
const opt = (p.flags & compiler.SymbolFlags.Optional) !== 0
|
|
454
|
+
const pt = checker.getTypeOfSymbolAtLocation(p, decls?.[0] ?? node)
|
|
455
|
+
const opt = (p.flags & compiler.SymbolFlags.Optional) !== 0
|
|
490
456
|
const ro =
|
|
491
|
-
decls?.some(
|
|
492
|
-
|
|
493
|
-
(compiler.getCombinedModifierFlags(d) & compiler.ModifierFlags.Readonly) !== 0,
|
|
494
|
-
) ?? false;
|
|
495
|
-
entries.push(
|
|
496
|
-
`${ro ? "readonly " : ""}${p.getName()}${opt ? "?" : ""}: ${next(pt)}`,
|
|
497
|
-
);
|
|
457
|
+
decls?.some((d) => (compiler.getCombinedModifierFlags(d) & compiler.ModifierFlags.Readonly) !== 0) ?? false
|
|
458
|
+
entries.push(`${ro ? "readonly " : ""}${p.getName()}${opt ? "?" : ""}: ${next(pt)}`)
|
|
498
459
|
}
|
|
499
|
-
if (entries.length > 0) return `{ ${entries.join("; ")} }
|
|
460
|
+
if (entries.length > 0) return `{ ${entries.join("; ")} }`
|
|
500
461
|
}
|
|
501
462
|
|
|
502
463
|
/* fallback */
|
|
503
|
-
return checker.typeToString(type, undefined, compiler.TypeFormatFlags.NoTruncation)
|
|
464
|
+
return checker.typeToString(type, undefined, compiler.TypeFormatFlags.NoTruncation)
|
|
504
465
|
}
|
|
505
466
|
|
|
506
467
|
/**
|
|
@@ -511,35 +472,26 @@ function serialize(
|
|
|
511
472
|
*/
|
|
512
473
|
function sanitizeImportPaths(str: string, outputDir?: string): string {
|
|
513
474
|
return str.replace(/import\("([^"]+)"\)/g, (_match, filePath: string) => {
|
|
514
|
-
const nmIdx = filePath.lastIndexOf("/node_modules/")
|
|
475
|
+
const nmIdx = filePath.lastIndexOf("/node_modules/")
|
|
515
476
|
if (nmIdx !== -1) {
|
|
516
|
-
return `import("${filePath.slice(nmIdx + "/node_modules/".length)}")
|
|
477
|
+
return `import("${filePath.slice(nmIdx + "/node_modules/".length)}")`
|
|
517
478
|
}
|
|
518
|
-
if (!outputDir) return `import("${filePath}")
|
|
519
|
-
let rel = relative(outputDir, filePath)
|
|
520
|
-
if (!rel.startsWith(".")) rel = `./${rel}
|
|
521
|
-
return `import("${rel}")
|
|
522
|
-
})
|
|
479
|
+
if (!outputDir) return `import("${filePath}")`
|
|
480
|
+
let rel = relative(outputDir, filePath)
|
|
481
|
+
if (!rel.startsWith(".")) rel = `./${rel}`
|
|
482
|
+
return `import("${rel}")`
|
|
483
|
+
})
|
|
523
484
|
}
|
|
524
485
|
|
|
525
486
|
export type ExtractedChainTypes = {
|
|
526
|
-
base: ExtractedBaseCtx
|
|
487
|
+
base: ExtractedBaseCtx
|
|
527
488
|
/** per-route middleware additions keyed by "method /path" */
|
|
528
|
-
routeMiddleware: Record<string, string
|
|
489
|
+
routeMiddleware: Record<string, string>
|
|
529
490
|
/** structured per-property middleware data for sub-type dedup */
|
|
530
|
-
routeMiddlewareProps: Record<string, Array<{ name: string; opt: boolean; type: string }
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
const HTTP_METHODS = new Set([
|
|
534
|
-
"all",
|
|
535
|
-
"delete",
|
|
536
|
-
"get",
|
|
537
|
-
"head",
|
|
538
|
-
"options",
|
|
539
|
-
"patch",
|
|
540
|
-
"post",
|
|
541
|
-
"put",
|
|
542
|
-
]);
|
|
491
|
+
routeMiddlewareProps: Record<string, Array<{ name: string; opt: boolean; type: string }>>
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const HTTP_METHODS = new Set(["all", "delete", "get", "head", "options", "patch", "post", "put"])
|
|
543
495
|
|
|
544
496
|
/**
|
|
545
497
|
* Extracts base ctx and per-route middleware additions from sub-chains.
|
|
@@ -548,134 +500,110 @@ const HTTP_METHODS = new Set([
|
|
|
548
500
|
* and returns per-route additions keyed by "method /path".
|
|
549
501
|
*/
|
|
550
502
|
export async function extractChainTypes(options: {
|
|
551
|
-
entryPath: string
|
|
552
|
-
exportName: string
|
|
553
|
-
outputDir?: string
|
|
554
|
-
tsconfigPath?: string
|
|
503
|
+
entryPath: string
|
|
504
|
+
exportName: string
|
|
505
|
+
outputDir?: string
|
|
506
|
+
tsconfigPath?: string
|
|
555
507
|
}): Promise<ExtractedChainTypes> {
|
|
556
|
-
const { Project, ts } = await import("ts-morph")
|
|
508
|
+
const { Project, ts } = await import("ts-morph")
|
|
557
509
|
|
|
558
|
-
const tsConfigFilePath =
|
|
559
|
-
options.tsconfigPath ?? findTsConfig(options.entryPath);
|
|
510
|
+
const tsConfigFilePath = options.tsconfigPath ?? findTsConfig(options.entryPath)
|
|
560
511
|
|
|
561
512
|
const project = new Project({
|
|
562
513
|
skipAddingFilesFromTsConfig: true,
|
|
563
514
|
tsConfigFilePath,
|
|
564
|
-
})
|
|
515
|
+
})
|
|
565
516
|
|
|
566
|
-
project.addSourceFilesAtPaths(options.entryPath)
|
|
567
|
-
project.resolveSourceFileDependencies()
|
|
517
|
+
project.addSourceFilesAtPaths(options.entryPath)
|
|
518
|
+
project.resolveSourceFileDependencies()
|
|
568
519
|
|
|
569
|
-
const sourceFile = project.getSourceFileOrThrow(options.entryPath)
|
|
570
|
-
const baseVarDecl = sourceFile.getVariableDeclaration(options.exportName)
|
|
520
|
+
const sourceFile = project.getSourceFileOrThrow(options.entryPath)
|
|
521
|
+
const baseVarDecl = sourceFile.getVariableDeclaration(options.exportName)
|
|
571
522
|
if (!baseVarDecl) {
|
|
572
|
-
throw new Error(
|
|
573
|
-
`Export "${options.exportName}" not found in ${options.entryPath}`,
|
|
574
|
-
);
|
|
523
|
+
throw new Error(`Export "${options.exportName}" not found in ${options.entryPath}`)
|
|
575
524
|
}
|
|
576
525
|
|
|
577
526
|
function bridge<T>(value: unknown): T {
|
|
578
|
-
return value as T
|
|
527
|
+
return value as T
|
|
579
528
|
}
|
|
580
|
-
const checker: ts.TypeChecker = bridge(
|
|
581
|
-
|
|
582
|
-
)
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
function getCtxType(
|
|
589
|
-
tsType: ts.Type,
|
|
590
|
-
locationNode?: ts.Node,
|
|
591
|
-
): ts.Type | null {
|
|
592
|
-
const sym = tsType.getProperty("$ctx");
|
|
593
|
-
if (!sym) return null;
|
|
594
|
-
return checker.getTypeOfSymbolAtLocation(sym, locationNode ?? sym.valueDeclaration ?? baseNode);
|
|
529
|
+
const checker: ts.TypeChecker = bridge(project.getTypeChecker().compilerObject)
|
|
530
|
+
const baseNode: ts.Node = bridge(baseVarDecl.compilerNode)
|
|
531
|
+
const tsLib: typeof ts = bridge(ts)
|
|
532
|
+
|
|
533
|
+
function getCtxType(tsType: ts.Type, locationNode?: ts.Node): ts.Type | null {
|
|
534
|
+
const sym = tsType.getProperty("$ctx")
|
|
535
|
+
if (!sym) return null
|
|
536
|
+
return checker.getTypeOfSymbolAtLocation(sym, locationNode ?? sym.valueDeclaration ?? baseNode)
|
|
595
537
|
}
|
|
596
538
|
|
|
597
539
|
/* extract base ctx */
|
|
598
|
-
const baseType = bridge<ts.Type>(
|
|
599
|
-
|
|
600
|
-
)
|
|
601
|
-
let envType = "Record<string, unknown>";
|
|
602
|
-
const envSym = baseType.getProperty("$env");
|
|
540
|
+
const baseType = bridge<ts.Type>(baseVarDecl.getType().compilerType)
|
|
541
|
+
let envType = "Record<string, unknown>"
|
|
542
|
+
const envSym = baseType.getProperty("$env")
|
|
603
543
|
if (envSym) {
|
|
604
|
-
envType = serialize(
|
|
605
|
-
checker.getTypeOfSymbolAtLocation(envSym, baseNode),
|
|
606
|
-
checker,
|
|
607
|
-
baseNode,
|
|
608
|
-
tsLib,
|
|
609
|
-
);
|
|
544
|
+
envType = serialize(checker.getTypeOfSymbolAtLocation(envSym, baseNode), checker, baseNode, tsLib)
|
|
610
545
|
}
|
|
611
546
|
|
|
612
|
-
const baseCtxType = getCtxType(baseType)
|
|
547
|
+
const baseCtxType = getCtxType(baseType)
|
|
613
548
|
if (!baseCtxType) {
|
|
614
|
-
throw new Error(
|
|
615
|
-
`Export "${options.exportName}" has no $ctx — not a Honey instance`,
|
|
616
|
-
);
|
|
549
|
+
throw new Error(`Export "${options.exportName}" has no $ctx — not a Honey instance`)
|
|
617
550
|
}
|
|
618
|
-
const baseMwProps = extractMiddlewareProps(baseCtxType, checker, baseNode, tsLib)
|
|
619
|
-
const baseMiddlewareType =
|
|
620
|
-
|
|
551
|
+
const baseMwProps = extractMiddlewareProps(baseCtxType, checker, baseNode, tsLib)
|
|
552
|
+
const baseMiddlewareType =
|
|
553
|
+
baseMwProps.length === 0
|
|
554
|
+
? null
|
|
555
|
+
: `{ ${baseMwProps.map((p) => `${p.name}${p.opt ? "?" : ""}: ${p.type}`).join("; ")} }`
|
|
621
556
|
|
|
622
557
|
/* build name→type map for reusing base type references in route middleware */
|
|
623
|
-
const basePropTypeByName = new Map<string, string>()
|
|
558
|
+
const basePropTypeByName = new Map<string, string>()
|
|
624
559
|
for (const p of baseMwProps) {
|
|
625
|
-
basePropTypeByName.set(p.name, p.type)
|
|
560
|
+
basePropTypeByName.set(p.name, p.type)
|
|
626
561
|
}
|
|
627
562
|
|
|
628
563
|
/* collect base middleware property names for diffing */
|
|
629
|
-
const baseMwPropNames = new Set<string>()
|
|
564
|
+
const baseMwPropNames = new Set<string>()
|
|
630
565
|
for (const p of baseCtxType.getProperties()) {
|
|
631
|
-
const name = p.getName()
|
|
566
|
+
const name = p.getName()
|
|
632
567
|
if (!HONEY_CTX_PROPS.has(name) && !name.startsWith("_")) {
|
|
633
|
-
baseMwPropNames.add(name)
|
|
568
|
+
baseMwPropNames.add(name)
|
|
634
569
|
}
|
|
635
570
|
}
|
|
636
571
|
|
|
637
|
-
const sanitize = (s: string) => sanitizeImportPaths(s, options.outputDir)
|
|
572
|
+
const sanitize = (s: string) => sanitizeImportPaths(s, options.outputDir)
|
|
638
573
|
|
|
639
574
|
/* scan for route-defining calls: <expr>.<httpMethod>(<pathLiteral>) */
|
|
640
|
-
const routeMiddleware: Record<string, string> = {}
|
|
641
|
-
const routeMiddlewareProps: Record<string, Array<{ name: string; opt: boolean; type: string }>> = {}
|
|
642
|
-
const compilerSourceFile: ts.SourceFile = bridge(
|
|
643
|
-
sourceFile.compilerNode,
|
|
644
|
-
);
|
|
575
|
+
const routeMiddleware: Record<string, string> = {}
|
|
576
|
+
const routeMiddlewareProps: Record<string, Array<{ name: string; opt: boolean; type: string }>> = {}
|
|
577
|
+
const compilerSourceFile: ts.SourceFile = bridge(sourceFile.compilerNode)
|
|
645
578
|
|
|
646
579
|
function visitNode(node: ts.Node): void {
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
tsLib.isPropertyAccessExpression(node.expression)
|
|
650
|
-
) {
|
|
651
|
-
const methodName = node.expression.name.text;
|
|
580
|
+
if (tsLib.isCallExpression(node) && tsLib.isPropertyAccessExpression(node.expression)) {
|
|
581
|
+
const methodName = node.expression.name.text
|
|
652
582
|
if (HTTP_METHODS.has(methodName) && node.arguments.length > 0) {
|
|
653
|
-
const pathArg = node.arguments[0]
|
|
583
|
+
const pathArg = node.arguments[0]
|
|
654
584
|
if (tsLib.isStringLiteral(pathArg)) {
|
|
655
|
-
const path = pathArg.text
|
|
585
|
+
const path = pathArg.text
|
|
656
586
|
/* get the type of the receiver (the expression before .get/.post/etc.) */
|
|
657
|
-
const receiverExpr = node.expression.expression
|
|
658
|
-
const receiverType = checker.getTypeAtLocation(receiverExpr)
|
|
659
|
-
const ctxType = getCtxType(receiverType, receiverExpr)
|
|
587
|
+
const receiverExpr = node.expression.expression
|
|
588
|
+
const receiverType = checker.getTypeAtLocation(receiverExpr)
|
|
589
|
+
const ctxType = getCtxType(receiverType, receiverExpr)
|
|
660
590
|
|
|
661
591
|
if (ctxType) {
|
|
662
592
|
/* extract extra middleware props by iterating intersection MEMBERS individually.
|
|
663
593
|
* This preserves source-level declarations (e.g. ShorthandPropertyAssignment)
|
|
664
594
|
* that get lost when extracting from the flattened intersection. */
|
|
665
|
-
const extraEntries: MwPropEntry[] = []
|
|
666
|
-
const dupeNames = new Set<string>()
|
|
667
|
-
const members = ctxType.isIntersection()
|
|
668
|
-
? (ctxType as ts.IntersectionType).types
|
|
669
|
-
: [ctxType];
|
|
595
|
+
const extraEntries: MwPropEntry[] = []
|
|
596
|
+
const dupeNames = new Set<string>()
|
|
597
|
+
const members = ctxType.isIntersection() ? (ctxType as ts.IntersectionType).types : [ctxType]
|
|
670
598
|
for (const member of members) {
|
|
671
|
-
const memberProps = extractMiddlewareProps(member, checker, baseNode, tsLib)
|
|
599
|
+
const memberProps = extractMiddlewareProps(member, checker, baseNode, tsLib)
|
|
672
600
|
for (const rp of memberProps) {
|
|
673
|
-
if (baseMwPropNames.has(rp.name)) continue
|
|
601
|
+
if (baseMwPropNames.has(rp.name)) continue
|
|
674
602
|
if (extraEntries.some((e) => e.name === rp.name)) {
|
|
675
|
-
dupeNames.add(rp.name)
|
|
676
|
-
continue
|
|
603
|
+
dupeNames.add(rp.name)
|
|
604
|
+
continue
|
|
677
605
|
}
|
|
678
|
-
extraEntries.push(rp)
|
|
606
|
+
extraEntries.push(rp)
|
|
679
607
|
}
|
|
680
608
|
}
|
|
681
609
|
/* for properties that appeared in multiple intersection members
|
|
@@ -683,13 +611,13 @@ export async function extractChainTypes(options: {
|
|
|
683
611
|
* flattened intersection so TypeScript computes the correct
|
|
684
612
|
* intersected type instead of using the first member's type */
|
|
685
613
|
for (const dupeName of dupeNames) {
|
|
686
|
-
const sym = ctxType.getProperty(dupeName)
|
|
687
|
-
if (!sym) continue
|
|
688
|
-
const t = checker.getTypeOfSymbolAtLocation(sym, sym.valueDeclaration ?? baseNode)
|
|
689
|
-
const typeStr = serializeAsRef(t, checker, baseNode, tsLib) ?? serialize(t, checker, baseNode, tsLib)
|
|
690
|
-
const idx = extraEntries.findIndex((e) => e.name === dupeName)
|
|
614
|
+
const sym = ctxType.getProperty(dupeName)
|
|
615
|
+
if (!sym) continue
|
|
616
|
+
const t = checker.getTypeOfSymbolAtLocation(sym, sym.valueDeclaration ?? baseNode)
|
|
617
|
+
const typeStr = serializeAsRef(t, checker, baseNode, tsLib) ?? serialize(t, checker, baseNode, tsLib)
|
|
618
|
+
const idx = extraEntries.findIndex((e) => e.name === dupeName)
|
|
691
619
|
if (idx !== -1) {
|
|
692
|
-
extraEntries[idx] = { ...extraEntries[idx], type: typeStr }
|
|
620
|
+
extraEntries[idx] = { ...extraEntries[idx], type: typeStr }
|
|
693
621
|
}
|
|
694
622
|
}
|
|
695
623
|
|
|
@@ -702,48 +630,46 @@ export async function extractChainTypes(options: {
|
|
|
702
630
|
return { ...e, type: baseRef }
|
|
703
631
|
}
|
|
704
632
|
return e
|
|
705
|
-
})
|
|
633
|
+
})
|
|
706
634
|
|
|
707
|
-
const key = `${methodName} ${path}
|
|
635
|
+
const key = `${methodName} ${path}`
|
|
708
636
|
routeMiddleware[key] = sanitize(
|
|
709
637
|
`{ ${entries.map((e) => `${e.name}${e.opt ? "?" : ""}: ${e.type}`).join("; ")} }`,
|
|
710
|
-
)
|
|
638
|
+
)
|
|
711
639
|
routeMiddlewareProps[key] = entries.map((e) => ({
|
|
712
640
|
name: e.name,
|
|
713
641
|
opt: e.opt,
|
|
714
642
|
type: sanitize(e.type),
|
|
715
|
-
}))
|
|
643
|
+
}))
|
|
716
644
|
}
|
|
717
645
|
}
|
|
718
646
|
}
|
|
719
647
|
}
|
|
720
648
|
}
|
|
721
649
|
|
|
722
|
-
tsLib.forEachChild(node, visitNode)
|
|
650
|
+
tsLib.forEachChild(node, visitNode)
|
|
723
651
|
}
|
|
724
652
|
|
|
725
|
-
visitNode(compilerSourceFile)
|
|
653
|
+
visitNode(compilerSourceFile)
|
|
726
654
|
|
|
727
655
|
/* extract $taps */
|
|
728
|
-
let tapsType: string | null = null
|
|
729
|
-
const tapsSym = baseType.getProperty("$taps")
|
|
656
|
+
let tapsType: string | null = null
|
|
657
|
+
const tapsSym = baseType.getProperty("$taps")
|
|
730
658
|
if (tapsSym) {
|
|
731
|
-
const tapsT = checker.getTypeOfSymbolAtLocation(tapsSym, baseNode)
|
|
732
|
-
const tapsStr = serialize(tapsT, checker, baseNode, tsLib)
|
|
659
|
+
const tapsT = checker.getTypeOfSymbolAtLocation(tapsSym, baseNode)
|
|
660
|
+
const tapsStr = serialize(tapsT, checker, baseNode, tsLib)
|
|
733
661
|
if (tapsStr !== "{}" && tapsStr !== "Record<string, unknown>") {
|
|
734
|
-
tapsType = tapsStr
|
|
662
|
+
tapsType = tapsStr
|
|
735
663
|
}
|
|
736
664
|
}
|
|
737
665
|
|
|
738
666
|
return {
|
|
739
667
|
base: {
|
|
740
668
|
envType: sanitize(envType),
|
|
741
|
-
middlewareType: baseMiddlewareType
|
|
742
|
-
? sanitize(baseMiddlewareType)
|
|
743
|
-
: null,
|
|
669
|
+
middlewareType: baseMiddlewareType ? sanitize(baseMiddlewareType) : null,
|
|
744
670
|
tapsType: tapsType ? sanitize(tapsType) : null,
|
|
745
671
|
},
|
|
746
672
|
routeMiddleware,
|
|
747
673
|
routeMiddlewareProps,
|
|
748
|
-
}
|
|
674
|
+
}
|
|
749
675
|
}
|