mnehmos.trace.mcp 1.0.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/LICENSE +21 -0
- package/README.md +1662 -0
- package/dist/adapters/bootstrap.d.ts +29 -0
- package/dist/adapters/bootstrap.d.ts.map +1 -0
- package/dist/adapters/bootstrap.js +46 -0
- package/dist/adapters/bootstrap.js.map +1 -0
- package/dist/adapters/errors.d.ts +94 -0
- package/dist/adapters/errors.d.ts.map +1 -0
- package/dist/adapters/errors.js +107 -0
- package/dist/adapters/errors.js.map +1 -0
- package/dist/adapters/graphql/index.d.ts +9 -0
- package/dist/adapters/graphql/index.d.ts.map +1 -0
- package/dist/adapters/graphql/index.js +9 -0
- package/dist/adapters/graphql/index.js.map +1 -0
- package/dist/adapters/graphql/sdl-parser.d.ts +74 -0
- package/dist/adapters/graphql/sdl-parser.d.ts.map +1 -0
- package/dist/adapters/graphql/sdl-parser.js +559 -0
- package/dist/adapters/graphql/sdl-parser.js.map +1 -0
- package/dist/adapters/grpc/adapter.d.ts +76 -0
- package/dist/adapters/grpc/adapter.d.ts.map +1 -0
- package/dist/adapters/grpc/adapter.js +362 -0
- package/dist/adapters/grpc/adapter.js.map +1 -0
- package/dist/adapters/grpc/index.d.ts +10 -0
- package/dist/adapters/grpc/index.d.ts.map +1 -0
- package/dist/adapters/grpc/index.js +12 -0
- package/dist/adapters/grpc/index.js.map +1 -0
- package/dist/adapters/grpc/proto-parser.d.ts +76 -0
- package/dist/adapters/grpc/proto-parser.d.ts.map +1 -0
- package/dist/adapters/grpc/proto-parser.js +523 -0
- package/dist/adapters/grpc/proto-parser.js.map +1 -0
- package/dist/adapters/grpc/type-converter.d.ts +43 -0
- package/dist/adapters/grpc/type-converter.d.ts.map +1 -0
- package/dist/adapters/grpc/type-converter.js +270 -0
- package/dist/adapters/grpc/type-converter.js.map +1 -0
- package/dist/adapters/grpc/types.d.ts +85 -0
- package/dist/adapters/grpc/types.d.ts.map +1 -0
- package/dist/adapters/grpc/types.js +7 -0
- package/dist/adapters/grpc/types.js.map +1 -0
- package/dist/adapters/index.d.ts +39 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +50 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mcp.d.ts +23 -0
- package/dist/adapters/mcp.d.ts.map +1 -0
- package/dist/adapters/mcp.js +293 -0
- package/dist/adapters/mcp.js.map +1 -0
- package/dist/adapters/openapi/adapter.d.ts +213 -0
- package/dist/adapters/openapi/adapter.d.ts.map +1 -0
- package/dist/adapters/openapi/adapter.js +557 -0
- package/dist/adapters/openapi/adapter.js.map +1 -0
- package/dist/adapters/openapi/convert.d.ts +120 -0
- package/dist/adapters/openapi/convert.d.ts.map +1 -0
- package/dist/adapters/openapi/convert.js +363 -0
- package/dist/adapters/openapi/convert.js.map +1 -0
- package/dist/adapters/openapi/index.d.ts +39 -0
- package/dist/adapters/openapi/index.d.ts.map +1 -0
- package/dist/adapters/openapi/index.js +48 -0
- package/dist/adapters/openapi/index.js.map +1 -0
- package/dist/adapters/openapi/parser.d.ts +95 -0
- package/dist/adapters/openapi/parser.d.ts.map +1 -0
- package/dist/adapters/openapi/parser.js +171 -0
- package/dist/adapters/openapi/parser.js.map +1 -0
- package/dist/adapters/registry.d.ts +116 -0
- package/dist/adapters/registry.d.ts.map +1 -0
- package/dist/adapters/registry.js +246 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/trpc/adapter.d.ts +159 -0
- package/dist/adapters/trpc/adapter.d.ts.map +1 -0
- package/dist/adapters/trpc/adapter.js +223 -0
- package/dist/adapters/trpc/adapter.js.map +1 -0
- package/dist/adapters/trpc/extractor.d.ts +218 -0
- package/dist/adapters/trpc/extractor.d.ts.map +1 -0
- package/dist/adapters/trpc/extractor.js +708 -0
- package/dist/adapters/trpc/extractor.js.map +1 -0
- package/dist/adapters/trpc/index.d.ts +31 -0
- package/dist/adapters/trpc/index.d.ts.map +1 -0
- package/dist/adapters/trpc/index.js +40 -0
- package/dist/adapters/trpc/index.js.map +1 -0
- package/dist/adapters/trpc/parser.d.ts +119 -0
- package/dist/adapters/trpc/parser.d.ts.map +1 -0
- package/dist/adapters/trpc/parser.js +128 -0
- package/dist/adapters/trpc/parser.js.map +1 -0
- package/dist/compare/index.d.ts +33 -0
- package/dist/compare/index.d.ts.map +1 -0
- package/dist/compare/index.js +261 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/core/types.d.ts +188 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/extract/index.d.ts +26 -0
- package/dist/extract/index.d.ts.map +1 -0
- package/dist/extract/index.js +44 -0
- package/dist/extract/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +674 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/base.d.ts +57 -0
- package/dist/languages/base.d.ts.map +1 -0
- package/dist/languages/base.js +6 -0
- package/dist/languages/base.js.map +1 -0
- package/dist/languages/bootstrap.d.ts +10 -0
- package/dist/languages/bootstrap.d.ts.map +1 -0
- package/dist/languages/bootstrap.js +25 -0
- package/dist/languages/bootstrap.js.map +1 -0
- package/dist/languages/go/handlers/chi.d.ts +24 -0
- package/dist/languages/go/handlers/chi.d.ts.map +1 -0
- package/dist/languages/go/handlers/chi.js +205 -0
- package/dist/languages/go/handlers/chi.js.map +1 -0
- package/dist/languages/go/handlers/gin.d.ts +24 -0
- package/dist/languages/go/handlers/gin.d.ts.map +1 -0
- package/dist/languages/go/handlers/gin.js +156 -0
- package/dist/languages/go/handlers/gin.js.map +1 -0
- package/dist/languages/go/handlers/stdlib.d.ts +19 -0
- package/dist/languages/go/handlers/stdlib.d.ts.map +1 -0
- package/dist/languages/go/handlers/stdlib.js +112 -0
- package/dist/languages/go/handlers/stdlib.js.map +1 -0
- package/dist/languages/go/index.d.ts +18 -0
- package/dist/languages/go/index.d.ts.map +1 -0
- package/dist/languages/go/index.js +20 -0
- package/dist/languages/go/index.js.map +1 -0
- package/dist/languages/go/parser.d.ts +33 -0
- package/dist/languages/go/parser.d.ts.map +1 -0
- package/dist/languages/go/parser.js +95 -0
- package/dist/languages/go/parser.js.map +1 -0
- package/dist/languages/go/struct-extractor.d.ts +59 -0
- package/dist/languages/go/struct-extractor.d.ts.map +1 -0
- package/dist/languages/go/struct-extractor.js +483 -0
- package/dist/languages/go/struct-extractor.js.map +1 -0
- package/dist/languages/go/tag-parser.d.ts +62 -0
- package/dist/languages/go/tag-parser.d.ts.map +1 -0
- package/dist/languages/go/tag-parser.js +108 -0
- package/dist/languages/go/tag-parser.js.map +1 -0
- package/dist/languages/go/type-converter.d.ts +32 -0
- package/dist/languages/go/type-converter.d.ts.map +1 -0
- package/dist/languages/go/type-converter.js +226 -0
- package/dist/languages/go/type-converter.js.map +1 -0
- package/dist/languages/go/types.d.ts +153 -0
- package/dist/languages/go/types.d.ts.map +1 -0
- package/dist/languages/go/types.js +6 -0
- package/dist/languages/go/types.js.map +1 -0
- package/dist/languages/import-resolver.d.ts +645 -0
- package/dist/languages/import-resolver.d.ts.map +1 -0
- package/dist/languages/import-resolver.js +1278 -0
- package/dist/languages/import-resolver.js.map +1 -0
- package/dist/languages/index.d.ts +34 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +93 -0
- package/dist/languages/index.js.map +1 -0
- package/dist/languages/json-schema.d.ts +40 -0
- package/dist/languages/json-schema.d.ts.map +1 -0
- package/dist/languages/json-schema.js +188 -0
- package/dist/languages/json-schema.js.map +1 -0
- package/dist/languages/python-ast/index.d.ts +8 -0
- package/dist/languages/python-ast/index.d.ts.map +1 -0
- package/dist/languages/python-ast/index.js +7 -0
- package/dist/languages/python-ast/index.js.map +1 -0
- package/dist/languages/python-ast/parser.d.ts +174 -0
- package/dist/languages/python-ast/parser.d.ts.map +1 -0
- package/dist/languages/python-ast/parser.js +1205 -0
- package/dist/languages/python-ast/parser.js.map +1 -0
- package/dist/languages/python-ast/type-resolver.d.ts +75 -0
- package/dist/languages/python-ast/type-resolver.d.ts.map +1 -0
- package/dist/languages/python-ast/type-resolver.js +421 -0
- package/dist/languages/python-ast/type-resolver.js.map +1 -0
- package/dist/languages/python-ast/types.d.ts +216 -0
- package/dist/languages/python-ast/types.d.ts.map +1 -0
- package/dist/languages/python-ast/types.js +6 -0
- package/dist/languages/python-ast/types.js.map +1 -0
- package/dist/languages/python.d.ts +55 -0
- package/dist/languages/python.d.ts.map +1 -0
- package/dist/languages/python.js +311 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/languages/typescript.d.ts +272 -0
- package/dist/languages/typescript.d.ts.map +1 -0
- package/dist/languages/typescript.js +1381 -0
- package/dist/languages/typescript.js.map +1 -0
- package/dist/patterns/base.d.ts +146 -0
- package/dist/patterns/base.d.ts.map +1 -0
- package/dist/patterns/base.js +89 -0
- package/dist/patterns/base.js.map +1 -0
- package/dist/patterns/errors.d.ts +172 -0
- package/dist/patterns/errors.d.ts.map +1 -0
- package/dist/patterns/errors.js +185 -0
- package/dist/patterns/errors.js.map +1 -0
- package/dist/patterns/extractors.d.ts +170 -0
- package/dist/patterns/extractors.d.ts.map +1 -0
- package/dist/patterns/extractors.js +305 -0
- package/dist/patterns/extractors.js.map +1 -0
- package/dist/patterns/graphql/apollo-client.d.ts +80 -0
- package/dist/patterns/graphql/apollo-client.d.ts.map +1 -0
- package/dist/patterns/graphql/apollo-client.js +800 -0
- package/dist/patterns/graphql/apollo-client.js.map +1 -0
- package/dist/patterns/graphql/apollo-server.d.ts +55 -0
- package/dist/patterns/graphql/apollo-server.d.ts.map +1 -0
- package/dist/patterns/graphql/apollo-server.js +523 -0
- package/dist/patterns/graphql/apollo-server.js.map +1 -0
- package/dist/patterns/graphql/index.d.ts +11 -0
- package/dist/patterns/graphql/index.d.ts.map +1 -0
- package/dist/patterns/graphql/index.js +12 -0
- package/dist/patterns/graphql/index.js.map +1 -0
- package/dist/patterns/graphql/types.d.ts +213 -0
- package/dist/patterns/graphql/types.d.ts.map +1 -0
- package/dist/patterns/graphql/types.js +16 -0
- package/dist/patterns/graphql/types.js.map +1 -0
- package/dist/patterns/http-clients/axios.d.ts +148 -0
- package/dist/patterns/http-clients/axios.d.ts.map +1 -0
- package/dist/patterns/http-clients/axios.js +652 -0
- package/dist/patterns/http-clients/axios.js.map +1 -0
- package/dist/patterns/http-clients/fetch.d.ts +88 -0
- package/dist/patterns/http-clients/fetch.d.ts.map +1 -0
- package/dist/patterns/http-clients/fetch.js +364 -0
- package/dist/patterns/http-clients/fetch.js.map +1 -0
- package/dist/patterns/http-clients/index.d.ts +36 -0
- package/dist/patterns/http-clients/index.d.ts.map +1 -0
- package/dist/patterns/http-clients/index.js +50 -0
- package/dist/patterns/http-clients/index.js.map +1 -0
- package/dist/patterns/http-clients/property-access.d.ts +46 -0
- package/dist/patterns/http-clients/property-access.d.ts.map +1 -0
- package/dist/patterns/http-clients/property-access.js +818 -0
- package/dist/patterns/http-clients/property-access.js.map +1 -0
- package/dist/patterns/http-clients/type-inference.d.ts +48 -0
- package/dist/patterns/http-clients/type-inference.d.ts.map +1 -0
- package/dist/patterns/http-clients/type-inference.js +293 -0
- package/dist/patterns/http-clients/type-inference.js.map +1 -0
- package/dist/patterns/http-clients/types.d.ts +168 -0
- package/dist/patterns/http-clients/types.d.ts.map +1 -0
- package/dist/patterns/http-clients/types.js +10 -0
- package/dist/patterns/http-clients/types.js.map +1 -0
- package/dist/patterns/http-clients/url-extractor.d.ts +53 -0
- package/dist/patterns/http-clients/url-extractor.d.ts.map +1 -0
- package/dist/patterns/http-clients/url-extractor.js +338 -0
- package/dist/patterns/http-clients/url-extractor.js.map +1 -0
- package/dist/patterns/index.d.ts +44 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +49 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/python/aiohttp.d.ts +21 -0
- package/dist/patterns/python/aiohttp.d.ts.map +1 -0
- package/dist/patterns/python/aiohttp.js +188 -0
- package/dist/patterns/python/aiohttp.js.map +1 -0
- package/dist/patterns/python/httpx.d.ts +20 -0
- package/dist/patterns/python/httpx.d.ts.map +1 -0
- package/dist/patterns/python/httpx.js +183 -0
- package/dist/patterns/python/httpx.js.map +1 -0
- package/dist/patterns/python/index.d.ts +32 -0
- package/dist/patterns/python/index.d.ts.map +1 -0
- package/dist/patterns/python/index.js +63 -0
- package/dist/patterns/python/index.js.map +1 -0
- package/dist/patterns/python/property-access.d.ts +27 -0
- package/dist/patterns/python/property-access.d.ts.map +1 -0
- package/dist/patterns/python/property-access.js +132 -0
- package/dist/patterns/python/property-access.js.map +1 -0
- package/dist/patterns/python/requests.d.ts +19 -0
- package/dist/patterns/python/requests.d.ts.map +1 -0
- package/dist/patterns/python/requests.js +239 -0
- package/dist/patterns/python/requests.js.map +1 -0
- package/dist/patterns/python/types.d.ts +95 -0
- package/dist/patterns/python/types.d.ts.map +1 -0
- package/dist/patterns/python/types.js +43 -0
- package/dist/patterns/python/types.js.map +1 -0
- package/dist/patterns/registry.d.ts +181 -0
- package/dist/patterns/registry.d.ts.map +1 -0
- package/dist/patterns/registry.js +304 -0
- package/dist/patterns/registry.js.map +1 -0
- package/dist/patterns/rest/express.d.ts +78 -0
- package/dist/patterns/rest/express.d.ts.map +1 -0
- package/dist/patterns/rest/express.js +289 -0
- package/dist/patterns/rest/express.js.map +1 -0
- package/dist/patterns/rest/fastify.d.ts +93 -0
- package/dist/patterns/rest/fastify.d.ts.map +1 -0
- package/dist/patterns/rest/fastify.js +420 -0
- package/dist/patterns/rest/fastify.js.map +1 -0
- package/dist/patterns/rest/index.d.ts +31 -0
- package/dist/patterns/rest/index.d.ts.map +1 -0
- package/dist/patterns/rest/index.js +45 -0
- package/dist/patterns/rest/index.js.map +1 -0
- package/dist/patterns/rest/middleware.d.ts +25 -0
- package/dist/patterns/rest/middleware.d.ts.map +1 -0
- package/dist/patterns/rest/middleware.js +219 -0
- package/dist/patterns/rest/middleware.js.map +1 -0
- package/dist/patterns/rest/path-parser.d.ts +50 -0
- package/dist/patterns/rest/path-parser.d.ts.map +1 -0
- package/dist/patterns/rest/path-parser.js +137 -0
- package/dist/patterns/rest/path-parser.js.map +1 -0
- package/dist/patterns/rest/response-inference.d.ts +44 -0
- package/dist/patterns/rest/response-inference.d.ts.map +1 -0
- package/dist/patterns/rest/response-inference.js +218 -0
- package/dist/patterns/rest/response-inference.js.map +1 -0
- package/dist/patterns/rest/types.d.ts +102 -0
- package/dist/patterns/rest/types.d.ts.map +1 -0
- package/dist/patterns/rest/types.js +10 -0
- package/dist/patterns/rest/types.js.map +1 -0
- package/dist/patterns/types.d.ts +105 -0
- package/dist/patterns/types.d.ts.map +1 -0
- package/dist/patterns/types.js +11 -0
- package/dist/patterns/types.js.map +1 -0
- package/dist/report/index.d.ts +11 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +55 -0
- package/dist/report/index.js.map +1 -0
- package/dist/tools/contract-comments.d.ts +48 -0
- package/dist/tools/contract-comments.d.ts.map +1 -0
- package/dist/tools/contract-comments.js +130 -0
- package/dist/tools/contract-comments.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/scaffold.d.ts +38 -0
- package/dist/tools/scaffold.d.ts.map +1 -0
- package/dist/tools/scaffold.js +373 -0
- package/dist/tools/scaffold.js.map +1 -0
- package/dist/trace/index.d.ts +28 -0
- package/dist/trace/index.d.ts.map +1 -0
- package/dist/trace/index.js +45 -0
- package/dist/trace/index.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/dist/watch/cache.d.ts +41 -0
- package/dist/watch/cache.d.ts.map +1 -0
- package/dist/watch/cache.js +230 -0
- package/dist/watch/cache.js.map +1 -0
- package/dist/watch/index.d.ts +9 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +7 -0
- package/dist/watch/index.js.map +1 -0
- package/dist/watch/project.d.ts +128 -0
- package/dist/watch/project.d.ts.map +1 -0
- package/dist/watch/project.js +152 -0
- package/dist/watch/project.js.map +1 -0
- package/dist/watch/watcher.d.ts +76 -0
- package/dist/watch/watcher.d.ts.map +1 -0
- package/dist/watch/watcher.js +235 -0
- package/dist/watch/watcher.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔧 tRPC Router/Procedure Extractor
|
|
3
|
+
*
|
|
4
|
+
* AST-based extraction of tRPC router definitions and Zod schemas.
|
|
5
|
+
* This module provides utilities to:
|
|
6
|
+
* - Parse TypeScript files containing tRPC routers
|
|
7
|
+
* - Extract procedure definitions (query, mutation, subscription)
|
|
8
|
+
* - Convert Zod schemas to NormalizedSchema format
|
|
9
|
+
*
|
|
10
|
+
* Uses ts-morph for AST analysis, supporting patterns like:
|
|
11
|
+
* - `t.router({ ... })` - Standard tRPC v10+ pattern
|
|
12
|
+
* - `createTRPCRouter({ ... })` - Next.js/T3 pattern
|
|
13
|
+
* - Nested routers via object composition
|
|
14
|
+
*
|
|
15
|
+
* @module adapters/trpc/extractor
|
|
16
|
+
*/
|
|
17
|
+
import { Project, Node, } from 'ts-morph';
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Project Creation
|
|
20
|
+
// ============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Create a new ts-morph Project instance for AST analysis.
|
|
23
|
+
*
|
|
24
|
+
* Configures the project without tsconfig to allow standalone
|
|
25
|
+
* file analysis without project-wide compilation context.
|
|
26
|
+
*
|
|
27
|
+
* @returns A new ts-morph Project instance
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const project = createProject();
|
|
32
|
+
* const sourceFile = project.addSourceFileAtPath('./router.ts');
|
|
33
|
+
* const procedures = extractProcedures(sourceFile);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function createProject() {
|
|
37
|
+
return new Project({
|
|
38
|
+
tsConfigFilePath: undefined,
|
|
39
|
+
skipAddingFilesFromTsConfig: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Procedure Extraction
|
|
44
|
+
// ============================================================================
|
|
45
|
+
/**
|
|
46
|
+
* Extract all procedures from a tRPC router source file.
|
|
47
|
+
*
|
|
48
|
+
* Scans the file for router definitions and extracts all procedure
|
|
49
|
+
* definitions, including those in nested routers. Each procedure
|
|
50
|
+
* is returned with its full path, type, and schema nodes.
|
|
51
|
+
*
|
|
52
|
+
* Supported patterns:
|
|
53
|
+
* - `t.router({ user: t.procedure.query(...) })`
|
|
54
|
+
* - `createTRPCRouter({ users: userRouter })`
|
|
55
|
+
* - `createRouter({ nested: { deep: procedure } })`
|
|
56
|
+
*
|
|
57
|
+
* @param sourceFile - The ts-morph SourceFile to analyze
|
|
58
|
+
* @returns Array of ProcedureInfo objects for all found procedures
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const project = createProject();
|
|
63
|
+
* const file = project.addSourceFileAtPath('./server/routers/index.ts');
|
|
64
|
+
* const procedures = extractProcedures(file);
|
|
65
|
+
*
|
|
66
|
+
* for (const proc of procedures) {
|
|
67
|
+
* console.log(`${proc.path.join('.')} - ${proc.type}`);
|
|
68
|
+
* // Output: "users.getById - query"
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function extractProcedures(sourceFile) {
|
|
73
|
+
const procedures = [];
|
|
74
|
+
const filePath = sourceFile.getFilePath();
|
|
75
|
+
// Find all router calls and extract procedures
|
|
76
|
+
sourceFile.forEachDescendant((node) => {
|
|
77
|
+
if (Node.isCallExpression(node) && isRouterCall(node)) {
|
|
78
|
+
const procs = parseRouterObject(node, [], filePath);
|
|
79
|
+
procedures.push(...procs);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return procedures;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if a call expression is a tRPC router call.
|
|
86
|
+
*
|
|
87
|
+
* Matches patterns like:
|
|
88
|
+
* - `t.router(...)` - Method call on object
|
|
89
|
+
* - `trpc.router(...)` - Namespaced call
|
|
90
|
+
* - `createRouter(...)` - Function call
|
|
91
|
+
* - `createTRPCRouter(...)` - T3/Next.js pattern
|
|
92
|
+
*
|
|
93
|
+
* @param node - The AST node to check
|
|
94
|
+
* @returns True if the node is a router call expression
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* sourceFile.forEachDescendant((node) => {
|
|
99
|
+
* if (Node.isCallExpression(node) && isRouterCall(node)) {
|
|
100
|
+
* // Process router definition
|
|
101
|
+
* }
|
|
102
|
+
* });
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export function isRouterCall(node) {
|
|
106
|
+
if (!Node.isCallExpression(node))
|
|
107
|
+
return false;
|
|
108
|
+
const expr = node.getExpression();
|
|
109
|
+
// Match patterns like: t.router(), trpc.router(), createRouter(), createTRPCRouter()
|
|
110
|
+
if (Node.isPropertyAccessExpression(expr)) {
|
|
111
|
+
return expr.getName() === 'router';
|
|
112
|
+
}
|
|
113
|
+
// Match direct function calls
|
|
114
|
+
if (Node.isIdentifier(expr)) {
|
|
115
|
+
const name = expr.getText();
|
|
116
|
+
return name === 'createRouter' || name === 'createTRPCRouter';
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if a node is a tRPC procedure chain.
|
|
122
|
+
*
|
|
123
|
+
* Procedure chains are method chains that terminate in `.query()`,
|
|
124
|
+
* `.mutation()`, or `.subscription()`. This function walks up the
|
|
125
|
+
* chain to find these terminal methods.
|
|
126
|
+
*
|
|
127
|
+
* @param node - The AST node to check
|
|
128
|
+
* @returns True if the node is a procedure chain
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* // These would return true:
|
|
133
|
+
* // t.procedure.input(z.string()).query(...)
|
|
134
|
+
* // t.procedure.mutation(...)
|
|
135
|
+
* // publicProcedure.subscription(...)
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export function isProcedureChain(node) {
|
|
139
|
+
if (!node)
|
|
140
|
+
return false;
|
|
141
|
+
// Walk up the call chain to find terminal methods
|
|
142
|
+
let current = node;
|
|
143
|
+
while (Node.isCallExpression(current)) {
|
|
144
|
+
const expr = current.getExpression();
|
|
145
|
+
if (Node.isPropertyAccessExpression(expr)) {
|
|
146
|
+
const methodName = expr.getName();
|
|
147
|
+
if (methodName === 'query' || methodName === 'mutation' || methodName === 'subscription') {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Move to inner expression to keep checking
|
|
152
|
+
if (Node.isPropertyAccessExpression(expr)) {
|
|
153
|
+
const inner = expr.getExpression();
|
|
154
|
+
if (Node.isCallExpression(inner)) {
|
|
155
|
+
current = inner;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Parse a router object literal and extract all procedures.
|
|
165
|
+
*
|
|
166
|
+
* Recursively processes router definitions, handling:
|
|
167
|
+
* - Direct procedure definitions
|
|
168
|
+
* - Nested router references
|
|
169
|
+
* - Inline nested routers
|
|
170
|
+
*
|
|
171
|
+
* @param routerCall - The router call expression node
|
|
172
|
+
* @param currentPath - Current path prefix for nested routers
|
|
173
|
+
* @param filePath - Path to the source file
|
|
174
|
+
* @returns Array of ProcedureInfo for all found procedures
|
|
175
|
+
*/
|
|
176
|
+
export function parseRouterObject(routerCall, currentPath, filePath) {
|
|
177
|
+
const procedures = [];
|
|
178
|
+
const args = routerCall.getArguments();
|
|
179
|
+
if (args.length === 0)
|
|
180
|
+
return procedures;
|
|
181
|
+
const routerObj = args[0];
|
|
182
|
+
if (!Node.isObjectLiteralExpression(routerObj))
|
|
183
|
+
return procedures;
|
|
184
|
+
for (const prop of routerObj.getProperties()) {
|
|
185
|
+
if (!Node.isPropertyAssignment(prop))
|
|
186
|
+
continue;
|
|
187
|
+
const propName = prop.getName();
|
|
188
|
+
const init = prop.getInitializer();
|
|
189
|
+
if (!init)
|
|
190
|
+
continue;
|
|
191
|
+
// Check if it's a nested router
|
|
192
|
+
if (isNestedRouter(init)) {
|
|
193
|
+
// Recursively parse nested router
|
|
194
|
+
const nestedCall = extractRouterCall(init);
|
|
195
|
+
if (nestedCall) {
|
|
196
|
+
const nested = parseRouterObject(nestedCall, [...currentPath, propName], filePath);
|
|
197
|
+
procedures.push(...nested);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (isProcedureChain(init)) {
|
|
201
|
+
// Parse procedure definition
|
|
202
|
+
const procInfo = parseProcedureChain(init, [...currentPath, propName], filePath);
|
|
203
|
+
if (procInfo) {
|
|
204
|
+
procedures.push(procInfo);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return procedures;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if a node represents a nested router.
|
|
212
|
+
*
|
|
213
|
+
* @param node - The AST node to check
|
|
214
|
+
* @returns True if the node is a nested router definition or reference
|
|
215
|
+
*/
|
|
216
|
+
function isNestedRouter(node) {
|
|
217
|
+
// Direct router call
|
|
218
|
+
if (Node.isCallExpression(node) && isRouterCall(node)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
// Identifier referencing a router (e.g., userRouter)
|
|
222
|
+
if (Node.isIdentifier(node)) {
|
|
223
|
+
const name = node.getText();
|
|
224
|
+
return name.endsWith('Router') || name.endsWith('router');
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Extract the router call from a node.
|
|
230
|
+
*
|
|
231
|
+
* Handles both direct router calls and identifier references
|
|
232
|
+
* to router variables.
|
|
233
|
+
*
|
|
234
|
+
* @param node - The AST node to extract from
|
|
235
|
+
* @returns The router CallExpression, or null if not found
|
|
236
|
+
*/
|
|
237
|
+
function extractRouterCall(node) {
|
|
238
|
+
if (Node.isCallExpression(node) && isRouterCall(node)) {
|
|
239
|
+
return node;
|
|
240
|
+
}
|
|
241
|
+
// For identifier references, we need to follow the variable declaration
|
|
242
|
+
if (Node.isIdentifier(node)) {
|
|
243
|
+
const symbol = node.getSymbol();
|
|
244
|
+
if (symbol) {
|
|
245
|
+
const declarations = symbol.getDeclarations();
|
|
246
|
+
for (const decl of declarations) {
|
|
247
|
+
if (Node.isVariableDeclaration(decl)) {
|
|
248
|
+
const init = decl.getInitializer();
|
|
249
|
+
if (init && Node.isCallExpression(init) && isRouterCall(init)) {
|
|
250
|
+
return init;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Parse a procedure method chain to extract input/output schemas.
|
|
260
|
+
*
|
|
261
|
+
* Walks the method chain (`.input().output().query()`) and extracts:
|
|
262
|
+
* - Input schema from `.input(zodSchema)`
|
|
263
|
+
* - Output schema from `.output(zodSchema)`
|
|
264
|
+
* - Procedure type from terminal method (`.query()`, `.mutation()`, `.subscription()`)
|
|
265
|
+
*
|
|
266
|
+
* @param node - The procedure chain call expression
|
|
267
|
+
* @param path - Full path to this procedure
|
|
268
|
+
* @param filePath - Path to the source file
|
|
269
|
+
* @returns ProcedureInfo or null if parsing fails
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```typescript
|
|
273
|
+
* // For: t.procedure.input(z.object({ id: z.string() })).query(...)
|
|
274
|
+
* // Returns: { path: ['getById'], type: 'query', inputSchema: <Node>, ... }
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
export function parseProcedureChain(node, path, filePath) {
|
|
278
|
+
if (!Node.isCallExpression(node))
|
|
279
|
+
return null;
|
|
280
|
+
const location = {
|
|
281
|
+
file: filePath,
|
|
282
|
+
line: node.getStartLineNumber(),
|
|
283
|
+
};
|
|
284
|
+
let inputSchema;
|
|
285
|
+
let outputSchema;
|
|
286
|
+
let procedureType = 'query';
|
|
287
|
+
// Walk up the method chain to collect .input(), .output(), etc.
|
|
288
|
+
let current = node;
|
|
289
|
+
while (current && Node.isCallExpression(current)) {
|
|
290
|
+
const expr = current.getExpression();
|
|
291
|
+
if (Node.isPropertyAccessExpression(expr)) {
|
|
292
|
+
const methodName = expr.getName();
|
|
293
|
+
const args = current.getArguments();
|
|
294
|
+
switch (methodName) {
|
|
295
|
+
case 'input':
|
|
296
|
+
if (args.length > 0)
|
|
297
|
+
inputSchema = args[0];
|
|
298
|
+
break;
|
|
299
|
+
case 'output':
|
|
300
|
+
if (args.length > 0)
|
|
301
|
+
outputSchema = args[0];
|
|
302
|
+
break;
|
|
303
|
+
case 'query':
|
|
304
|
+
procedureType = 'query';
|
|
305
|
+
break;
|
|
306
|
+
case 'mutation':
|
|
307
|
+
procedureType = 'mutation';
|
|
308
|
+
break;
|
|
309
|
+
case 'subscription':
|
|
310
|
+
procedureType = 'subscription';
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
// Move up the chain
|
|
314
|
+
const inner = expr.getExpression();
|
|
315
|
+
if (Node.isCallExpression(inner)) {
|
|
316
|
+
current = inner;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
path,
|
|
328
|
+
type: procedureType,
|
|
329
|
+
inputSchema,
|
|
330
|
+
outputSchema,
|
|
331
|
+
location,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Zod Schema Conversion
|
|
336
|
+
// ============================================================================
|
|
337
|
+
/**
|
|
338
|
+
* Convert a Zod schema AST node to NormalizedSchema.
|
|
339
|
+
*
|
|
340
|
+
* Parses Zod schema definitions and converts them to the framework's
|
|
341
|
+
* NormalizedSchema format. Supports common Zod types:
|
|
342
|
+
* - Primitives: `z.string()`, `z.number()`, `z.boolean()`
|
|
343
|
+
* - Objects: `z.object({ ... })`
|
|
344
|
+
* - Arrays: `z.array(...)`
|
|
345
|
+
* - Unions: `z.union([...])`, `z.enum([...])`
|
|
346
|
+
* - Modifiers: `.optional()`, `.nullable()`, `.default()`
|
|
347
|
+
* - Constraints: `.min()`, `.max()`, `.email()`, etc.
|
|
348
|
+
*
|
|
349
|
+
* @param node - The AST node containing the Zod schema
|
|
350
|
+
* @param ref - The SchemaRef for source tracking
|
|
351
|
+
* @param location - Source location for error reporting
|
|
352
|
+
* @param name - Optional schema name
|
|
353
|
+
* @returns The converted NormalizedSchema
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```typescript
|
|
357
|
+
* const schema = zodToNormalizedSchema(
|
|
358
|
+
* inputSchemaNode,
|
|
359
|
+
* { source: 'trpc', id: 'trpc:users.create@./router.ts' },
|
|
360
|
+
* { file: './router.ts', line: 42 },
|
|
361
|
+
* 'users.create.input'
|
|
362
|
+
* );
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
export function zodToNormalizedSchema(node, ref, location, name) {
|
|
366
|
+
const properties = {};
|
|
367
|
+
const required = [];
|
|
368
|
+
// Parse z.object({...})
|
|
369
|
+
if (Node.isCallExpression(node)) {
|
|
370
|
+
const expr = node.getExpression();
|
|
371
|
+
const exprText = expr.getText();
|
|
372
|
+
if (exprText === 'z.object' || exprText.endsWith('.object')) {
|
|
373
|
+
const args = node.getArguments();
|
|
374
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
375
|
+
parseZodObjectProperties(args[0], properties, required);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Handle identifier references to named schemas
|
|
380
|
+
if (Node.isIdentifier(node)) {
|
|
381
|
+
const schema = resolveZodSchemaReference(node);
|
|
382
|
+
if (schema) {
|
|
383
|
+
return zodToNormalizedSchema(schema, ref, location, name);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
name: name || ref.id.split('@')[0].split(':')[1],
|
|
388
|
+
properties,
|
|
389
|
+
required,
|
|
390
|
+
source: ref,
|
|
391
|
+
location,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Resolve a Zod schema reference (identifier) to its definition.
|
|
396
|
+
*
|
|
397
|
+
* Follows variable declarations to find the actual schema definition.
|
|
398
|
+
* Used for patterns like `const UserSchema = z.object({ ... })`.
|
|
399
|
+
*
|
|
400
|
+
* @param node - The identifier node to resolve
|
|
401
|
+
* @returns The resolved schema AST node, or null if not found
|
|
402
|
+
*/
|
|
403
|
+
function resolveZodSchemaReference(node) {
|
|
404
|
+
if (!Node.isIdentifier(node))
|
|
405
|
+
return null;
|
|
406
|
+
const symbol = node.getSymbol();
|
|
407
|
+
if (!symbol)
|
|
408
|
+
return null;
|
|
409
|
+
const declarations = symbol.getDeclarations();
|
|
410
|
+
for (const decl of declarations) {
|
|
411
|
+
if (Node.isVariableDeclaration(decl)) {
|
|
412
|
+
const init = decl.getInitializer();
|
|
413
|
+
if (init)
|
|
414
|
+
return init;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Parse properties from a `z.object({ ... })` call.
|
|
421
|
+
*
|
|
422
|
+
* Extracts each property definition and determines which are required.
|
|
423
|
+
* Properties with `.optional()`, `.nullish()`, or `.default()` are
|
|
424
|
+
* considered optional.
|
|
425
|
+
*
|
|
426
|
+
* @param obj - The object literal expression node
|
|
427
|
+
* @param properties - Map to populate with property definitions
|
|
428
|
+
* @param required - Array to populate with required property names
|
|
429
|
+
*/
|
|
430
|
+
function parseZodObjectProperties(obj, properties, required) {
|
|
431
|
+
for (const prop of obj.getProperties()) {
|
|
432
|
+
if (!Node.isPropertyAssignment(prop))
|
|
433
|
+
continue;
|
|
434
|
+
const propName = prop.getName();
|
|
435
|
+
const init = prop.getInitializer();
|
|
436
|
+
if (!init)
|
|
437
|
+
continue;
|
|
438
|
+
const propDef = parseZodTypeToPropDef(init);
|
|
439
|
+
properties[propName] = propDef;
|
|
440
|
+
const initText = init.getText();
|
|
441
|
+
// Check if not optional and not with default
|
|
442
|
+
if (!initText.includes('.optional()') &&
|
|
443
|
+
!initText.includes('.nullish()') &&
|
|
444
|
+
!initText.includes('.default(')) {
|
|
445
|
+
required.push(propName);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Parse a Zod type expression to PropertyDef.
|
|
451
|
+
*
|
|
452
|
+
* Converts a single property's Zod schema to PropertyDef format,
|
|
453
|
+
* extracting type, optionality, nullability, and constraints.
|
|
454
|
+
*
|
|
455
|
+
* @param node - The Zod type expression node
|
|
456
|
+
* @returns The converted PropertyDef
|
|
457
|
+
*/
|
|
458
|
+
function parseZodTypeToPropDef(node) {
|
|
459
|
+
const text = node.getText();
|
|
460
|
+
const type = parseZodTypeToNormalized(node);
|
|
461
|
+
const constraints = extractZodConstraints(text);
|
|
462
|
+
const isOptional = text.includes('.optional()') || text.includes('.nullish()') || text.includes('.default(');
|
|
463
|
+
const isNullable = text.includes('.nullable()') || text.includes('.nullish()');
|
|
464
|
+
return {
|
|
465
|
+
type,
|
|
466
|
+
optional: isOptional,
|
|
467
|
+
nullable: isNullable,
|
|
468
|
+
readonly: false,
|
|
469
|
+
deprecated: false,
|
|
470
|
+
constraints: Object.keys(constraints).length > 0 ? constraints : undefined,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Parse a Zod type to NormalizedType.
|
|
475
|
+
*
|
|
476
|
+
* Recursively converts Zod types to the framework's NormalizedType format.
|
|
477
|
+
* Handles nested types (arrays, objects, unions) and method chains.
|
|
478
|
+
*
|
|
479
|
+
* Supported Zod types:
|
|
480
|
+
* - `z.string()` → `{ kind: 'primitive', value: 'string' }`
|
|
481
|
+
* - `z.number()` → `{ kind: 'primitive', value: 'number' }`
|
|
482
|
+
* - `z.boolean()` → `{ kind: 'primitive', value: 'boolean' }`
|
|
483
|
+
* - `z.array(T)` → `{ kind: 'array', element: T }`
|
|
484
|
+
* - `z.object({})` → `{ kind: 'object', schema: {...} }`
|
|
485
|
+
* - `z.enum([])` → `{ kind: 'union', variants: [...] }`
|
|
486
|
+
* - `z.literal(v)` → `{ kind: 'literal', value: v }`
|
|
487
|
+
*
|
|
488
|
+
* @param node - The Zod type AST node
|
|
489
|
+
* @returns The converted NormalizedType
|
|
490
|
+
*/
|
|
491
|
+
function parseZodTypeToNormalized(node) {
|
|
492
|
+
const text = node.getText();
|
|
493
|
+
const trimmedText = text.trim();
|
|
494
|
+
// First, resolve identifier references
|
|
495
|
+
if (Node.isIdentifier(node)) {
|
|
496
|
+
const resolved = resolveZodSchemaReference(node);
|
|
497
|
+
if (resolved) {
|
|
498
|
+
return parseZodTypeToNormalized(resolved);
|
|
499
|
+
}
|
|
500
|
+
return { kind: 'ref', name: node.getText() };
|
|
501
|
+
}
|
|
502
|
+
// Handle chained methods (e.g., z.string().optional())
|
|
503
|
+
// Must check BEFORE primitive types to unwrap the chain
|
|
504
|
+
if (Node.isCallExpression(node)) {
|
|
505
|
+
const expr = node.getExpression();
|
|
506
|
+
if (Node.isPropertyAccessExpression(expr)) {
|
|
507
|
+
const methodName = expr.getName();
|
|
508
|
+
// Skip over .optional(), .nullable(), .default(), etc. and parse inner
|
|
509
|
+
if (['optional', 'nullable', 'nullish', 'default', 'describe',
|
|
510
|
+
'min', 'max', 'int', 'positive', 'email', 'uuid', 'url'].includes(methodName)) {
|
|
511
|
+
const inner = expr.getExpression();
|
|
512
|
+
return parseZodTypeToNormalized(inner);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
// z.string()
|
|
517
|
+
if (trimmedText.startsWith('z.string()') || trimmedText.startsWith('z.string(')) {
|
|
518
|
+
return { kind: 'primitive', value: 'string' };
|
|
519
|
+
}
|
|
520
|
+
// z.number()
|
|
521
|
+
if (trimmedText.startsWith('z.number()') || trimmedText.startsWith('z.number(')) {
|
|
522
|
+
return { kind: 'primitive', value: 'number' };
|
|
523
|
+
}
|
|
524
|
+
// z.boolean()
|
|
525
|
+
if (trimmedText.startsWith('z.boolean()') || trimmedText.startsWith('z.boolean(')) {
|
|
526
|
+
return { kind: 'primitive', value: 'boolean' };
|
|
527
|
+
}
|
|
528
|
+
// z.date()
|
|
529
|
+
if (trimmedText.startsWith('z.date()') || trimmedText.startsWith('z.date(')) {
|
|
530
|
+
return { kind: 'primitive', value: 'string' }; // Date serialized as string
|
|
531
|
+
}
|
|
532
|
+
// z.enum([...])
|
|
533
|
+
if (trimmedText.startsWith('z.enum(')) {
|
|
534
|
+
const enumMatch = text.match(/z\.enum\(\[(.*?)\]\)/s);
|
|
535
|
+
if (enumMatch) {
|
|
536
|
+
const values = enumMatch[1]
|
|
537
|
+
.split(',')
|
|
538
|
+
.map(v => v.trim().replace(/['"]/g, ''))
|
|
539
|
+
.filter(v => v.length > 0);
|
|
540
|
+
const variants = values.map(v => ({
|
|
541
|
+
kind: 'literal',
|
|
542
|
+
value: v,
|
|
543
|
+
}));
|
|
544
|
+
return { kind: 'union', variants };
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
// z.array(...)
|
|
548
|
+
if (trimmedText.startsWith('z.array(')) {
|
|
549
|
+
if (Node.isCallExpression(node)) {
|
|
550
|
+
const args = node.getArguments();
|
|
551
|
+
if (args.length > 0) {
|
|
552
|
+
const elementType = parseZodTypeToNormalized(args[0]);
|
|
553
|
+
return { kind: 'array', element: elementType };
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return { kind: 'array', element: { kind: 'unknown' } };
|
|
557
|
+
}
|
|
558
|
+
// z.object(...) - check multiple patterns
|
|
559
|
+
if (trimmedText.startsWith('z.object(') || trimmedText.startsWith('z.object({')) {
|
|
560
|
+
if (Node.isCallExpression(node)) {
|
|
561
|
+
const args = node.getArguments();
|
|
562
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
563
|
+
const properties = {};
|
|
564
|
+
const required = [];
|
|
565
|
+
parseZodObjectProperties(args[0], properties, required);
|
|
566
|
+
return {
|
|
567
|
+
kind: 'object',
|
|
568
|
+
schema: {
|
|
569
|
+
properties,
|
|
570
|
+
required,
|
|
571
|
+
source: { source: 'trpc', id: 'inline' },
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return { kind: 'object', schema: { properties: {}, required: [], source: { source: 'trpc', id: 'inline' } } };
|
|
577
|
+
}
|
|
578
|
+
// z.literal(...)
|
|
579
|
+
if (trimmedText.startsWith('z.literal(')) {
|
|
580
|
+
const match = text.match(/z\.literal\(['"](.+?)['"]\)/);
|
|
581
|
+
if (match) {
|
|
582
|
+
return { kind: 'literal', value: match[1] };
|
|
583
|
+
}
|
|
584
|
+
const numMatch = text.match(/z\.literal\((\d+)\)/);
|
|
585
|
+
if (numMatch) {
|
|
586
|
+
return { kind: 'literal', value: parseInt(numMatch[1]) };
|
|
587
|
+
}
|
|
588
|
+
const boolMatch = text.match(/z\.literal\((true|false)\)/);
|
|
589
|
+
if (boolMatch) {
|
|
590
|
+
return { kind: 'literal', value: boolMatch[1] === 'true' };
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// z.union([...])
|
|
594
|
+
if (trimmedText.startsWith('z.union(')) {
|
|
595
|
+
if (Node.isCallExpression(node)) {
|
|
596
|
+
const args = node.getArguments();
|
|
597
|
+
if (args.length > 0 && Node.isArrayLiteralExpression(args[0])) {
|
|
598
|
+
const variants = args[0].getElements().map(el => parseZodTypeToNormalized(el));
|
|
599
|
+
return { kind: 'union', variants };
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// Fallback: Check if this is a CallExpression that looks like z.X pattern
|
|
604
|
+
if (Node.isCallExpression(node)) {
|
|
605
|
+
const expr = node.getExpression();
|
|
606
|
+
if (Node.isPropertyAccessExpression(expr)) {
|
|
607
|
+
const objExpr = expr.getExpression();
|
|
608
|
+
if (Node.isIdentifier(objExpr) && objExpr.getText() === 'z') {
|
|
609
|
+
const methodName = expr.getName();
|
|
610
|
+
if (methodName === 'object') {
|
|
611
|
+
const args = node.getArguments();
|
|
612
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
613
|
+
const properties = {};
|
|
614
|
+
const required = [];
|
|
615
|
+
parseZodObjectProperties(args[0], properties, required);
|
|
616
|
+
return {
|
|
617
|
+
kind: 'object',
|
|
618
|
+
schema: {
|
|
619
|
+
properties,
|
|
620
|
+
required,
|
|
621
|
+
source: { source: 'trpc', id: 'inline' },
|
|
622
|
+
},
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return { kind: 'unknown' };
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Extract validation constraints from a Zod type chain.
|
|
633
|
+
*
|
|
634
|
+
* Parses the Zod method chain text to extract constraints like:
|
|
635
|
+
* - String: `.min(n)`, `.max(n)`, `.email()`, `.uuid()`, `.url()`
|
|
636
|
+
* - Number: `.min(n)`, `.max(n)`, `.int()`, `.positive()`
|
|
637
|
+
*
|
|
638
|
+
* @param text - The Zod type chain as text
|
|
639
|
+
* @returns Extracted constraints object
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```typescript
|
|
643
|
+
* extractZodConstraints('z.string().min(1).max(100).email()')
|
|
644
|
+
* // { minLength: 1, maxLength: 100, format: 'email' }
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
function extractZodConstraints(text) {
|
|
648
|
+
const constraints = {};
|
|
649
|
+
// String constraints
|
|
650
|
+
const minLengthMatch = text.match(/\.min\((\d+)\)/);
|
|
651
|
+
const maxLengthMatch = text.match(/\.max\((\d+)\)/);
|
|
652
|
+
// For strings
|
|
653
|
+
if (text.includes('z.string()')) {
|
|
654
|
+
if (minLengthMatch)
|
|
655
|
+
constraints.minLength = parseInt(minLengthMatch[1]);
|
|
656
|
+
if (maxLengthMatch)
|
|
657
|
+
constraints.maxLength = parseInt(maxLengthMatch[1]);
|
|
658
|
+
// Format constraints
|
|
659
|
+
if (text.includes('.email()'))
|
|
660
|
+
constraints.format = 'email';
|
|
661
|
+
if (text.includes('.uuid()'))
|
|
662
|
+
constraints.format = 'uuid';
|
|
663
|
+
if (text.includes('.url()'))
|
|
664
|
+
constraints.format = 'url';
|
|
665
|
+
}
|
|
666
|
+
// Number constraints
|
|
667
|
+
if (text.includes('z.number()')) {
|
|
668
|
+
if (minLengthMatch)
|
|
669
|
+
constraints.minimum = parseInt(minLengthMatch[1]);
|
|
670
|
+
if (maxLengthMatch)
|
|
671
|
+
constraints.maximum = parseInt(maxLengthMatch[1]);
|
|
672
|
+
}
|
|
673
|
+
return constraints;
|
|
674
|
+
}
|
|
675
|
+
// ============================================================================
|
|
676
|
+
// Utility Functions
|
|
677
|
+
// ============================================================================
|
|
678
|
+
/**
|
|
679
|
+
* Create an empty NormalizedSchema.
|
|
680
|
+
*
|
|
681
|
+
* Used for procedures without explicit input or output schemas.
|
|
682
|
+
* Returns a valid schema with empty properties.
|
|
683
|
+
*
|
|
684
|
+
* @param ref - The SchemaRef for source tracking
|
|
685
|
+
* @param location - Source location for error reporting
|
|
686
|
+
* @param name - Optional schema name
|
|
687
|
+
* @returns An empty NormalizedSchema
|
|
688
|
+
*
|
|
689
|
+
* @example
|
|
690
|
+
* ```typescript
|
|
691
|
+
* // For a procedure with no .input() call
|
|
692
|
+
* const emptyInput = createEmptySchema(
|
|
693
|
+
* ref,
|
|
694
|
+
* { file: './router.ts', line: 10 },
|
|
695
|
+
* 'users.list.input'
|
|
696
|
+
* );
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
export function createEmptySchema(ref, location, name) {
|
|
700
|
+
return {
|
|
701
|
+
name: name || ref.id.split('@')[0].split(':')[1],
|
|
702
|
+
properties: {},
|
|
703
|
+
required: [],
|
|
704
|
+
source: ref,
|
|
705
|
+
location,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
//# sourceMappingURL=extractor.js.map
|