@dudousxd/nestjs-codegen 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/CHANGELOG.md +77 -0
- package/LICENSE +21 -0
- package/bin/nestjs-codegen.js +8 -0
- package/dist/cli/main.cjs +5044 -0
- package/dist/cli/main.cjs.map +1 -0
- package/dist/cli/main.d.cts +10 -0
- package/dist/cli/main.d.ts +10 -0
- package/dist/cli/main.js +5024 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/extension/index.cjs +35 -0
- package/dist/extension/index.cjs.map +1 -0
- package/dist/extension/index.d.cts +2 -0
- package/dist/extension/index.d.ts +2 -0
- package/dist/extension/index.js +8 -0
- package/dist/extension/index.js.map +1 -0
- package/dist/index-BwIRjOQA.d.cts +509 -0
- package/dist/index-BwIRjOQA.d.ts +509 -0
- package/dist/index.cjs +3975 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +128 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.js +3938 -0
- package/dist/index.js.map +1 -0
- package/dist/nest/index.cjs +3941 -0
- package/dist/nest/index.cjs.map +1 -0
- package/dist/nest/index.d.cts +67 -0
- package/dist/nest/index.d.ts +67 -0
- package/dist/nest/index.js +3919 -0
- package/dist/nest/index.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/extension/index.ts
|
|
21
|
+
var extension_exports = {};
|
|
22
|
+
__export(extension_exports, {
|
|
23
|
+
defineExtension: () => defineExtension
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(extension_exports);
|
|
26
|
+
|
|
27
|
+
// src/extension/types.ts
|
|
28
|
+
function defineExtension(ext) {
|
|
29
|
+
return ext;
|
|
30
|
+
}
|
|
31
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
32
|
+
0 && (module.exports = {
|
|
33
|
+
defineExtension
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/extension/index.ts","../../src/extension/types.ts"],"sourcesContent":["export { defineExtension } from './types.js';\nexport type {\n ApiClientLayer,\n ApiHeaderContribution,\n ApiModuleDeps,\n ApiTransport,\n CodegenExtension,\n EmittedFile,\n ExtensionContext,\n LeafModel,\n RequestModel,\n} from './types.js';\n","import type { Project } from 'ts-morph';\nimport type { ResolvedConfig } from '../config/types.js';\nimport type { RouteDescriptor } from '../discovery/types.js';\n\n/**\n * The published, versioned extension contract for `@dudousxd/nestjs-codegen`.\n *\n * Extensions are **build-time** objects (usually returned by a factory so they can take\n * options) registered explicitly via `forRoot({ extensions: [...] })`. The host runs them\n * around the core discovery → IR → emit pipeline.\n *\n * Hooks split into **multi** (every extension runs; results accumulate or chain) and\n * **single-slot** (at most one extension may claim it — two claimers is a hard error).\n *\n * @remarks Semver 0.x — the shape may change until 1.0. Out-of-repo extensions should pin\n * a compatible `@dudousxd/nestjs-codegen` peer range.\n */\nexport interface CodegenExtension {\n /** Unique id. Used in conflict/collision errors and for deterministic ordering. */\n name: string;\n\n // ── multi hooks (every extension runs) ────────────────────────────────────\n\n /**\n * Mutate/augment the route IR before emit. Runs in registration order, chained\n * (each extension sees the previous one's output). Return the new array, or mutate\n * in place and return void. Example: the filter extension attaches `filterFields` to\n * matching routes here.\n */\n transformRoutes?(\n routes: RouteDescriptor[],\n ctx: ExtensionContext,\n ): RouteDescriptor[] | undefined | Promise<RouteDescriptor[] | undefined>;\n\n /**\n * Contribute extra output files (additive). Paths are relative to `outDir`; a path\n * claimed by two extensions is a hard error. Example: the Inertia extension does its\n * own page discovery via `ctx.project()` and emits `pages.d.ts` + `components.json`.\n */\n emitFiles?(ctx: ExtensionContext): EmittedFile[] | Promise<EmittedFile[]>;\n\n /**\n * Contribute top-level code to `api.ts` (imports + statements). Runs in registration\n * order; imports are deduped by the host. Example: the Inertia extension adds\n * `import { router } from '@inertiajs/react'` and the `navigate()` helper.\n */\n apiHeader?(ctx: ExtensionContext): ApiHeaderContribution | undefined;\n\n /**\n * Add named members to a **handle** leaf. Only runs when a client layer is active\n * (i.e. the leaf is a handle, not a bare callable). Member-name collisions across\n * extensions are a hard error. Example: the filter extension adds `filterQuery` to\n * leaves whose route carries `filterFields`.\n */\n apiMembers?(leaf: LeafModel, ctx: ExtensionContext): Record<string, string> | undefined;\n\n // ── single-slot hooks (at most one extension) ─────────────────────────────\n\n /**\n * Claims **how** a single endpoint issues its request. When unset by every extension,\n * the host falls back to the neutral fetcher transport. Example: the Inertia extension\n * routes mutations through the Inertia router while GETs stay fetcher-typed.\n */\n apiTransport?: ApiTransport;\n\n /**\n * Claims **what** a leaf returns. When unset, a leaf is a bare callable returning a\n * `Promise`. Example: the TanStack extension wraps each leaf into a handle exposing\n * `{ fetch, queryKey, queryOptions | mutationOptions }`.\n */\n apiClientLayer?: ApiClientLayer;\n}\n\n/** Shared, read-only context handed to every extension hook. */\nexport interface ExtensionContext {\n cwd: string;\n outDir: string;\n routes: readonly RouteDescriptor[];\n config: ResolvedConfig;\n /** Lazily-created shared ts-morph Project for AST work (pages, custom decorators). */\n project(): Project;\n}\n\n/** A file contributed by an extension's `emitFiles` hook. */\nexport interface EmittedFile {\n /** Path relative to `outDir`. A collision across extensions throws. */\n path: string;\n contents: string;\n}\n\n/** Top-level `api.ts` contributions from an extension's `apiHeader` hook. */\nexport interface ApiHeaderContribution {\n /** Raw import lines (e.g. `import { router } from '@inertiajs/react';`), deduped by the host. */\n imports?: string[];\n /** Top-level statements appended after the api factory (e.g. the `navigate()` helper). */\n statements?: string[];\n}\n\n/**\n * The neutral, per-endpoint request model the host builds for each leaf before any\n * transport/layer runs. Extensions read this to render their output.\n */\nexport interface RequestModel {\n /** Dot-path route name, e.g. `users.show`. */\n routeName: string;\n method: 'get' | 'post' | 'put' | 'patch' | 'delete';\n isGet: boolean;\n /** True for reads: a GET, or a filter-search route (has `filterFields`) even when POST.\n * Client layers use this (not `isGet`) to decide query vs mutation helpers. */\n isQuery: boolean;\n hasParams: boolean;\n hasBody: boolean;\n /** Type of the leaf's `input` arg, e.g. `{ params: ...; query?: ... }` or `Record<string, never>`. */\n inputType: string;\n /** URL expression, e.g. `route('users.show', input?.params) || '/api/users/:id'`. */\n urlExpr: string;\n /** Request-options expression, e.g. `{ query: ... }` or `{ body: input?.body }`. */\n optsExpr: string;\n /** Response type access, e.g. `ApiRouter['users']['show']['response']`. */\n responseType: string;\n /** Body type access, e.g. `ApiRouter['users']['create']['body']` (for mutation layers). */\n bodyType: string;\n /** Stable query-key expression, e.g. `[\"users.show\", input] as const`. */\n queryKeyExpr: string;\n}\n\n/**\n * Per-leaf model passed through the api.ts pipeline: transport → layer → member\n * contributors → render. `requestExpr` is set by the transport; `members`, when present,\n * flips the leaf from a bare callable to a handle.\n */\nexport interface LeafModel {\n route: RouteDescriptor;\n request: RequestModel;\n /** The expression that issues the request (set by the transport, default = fetcher). */\n requestExpr: string;\n /** When present, the leaf renders as a handle exposing these members (ordered). */\n members?: Record<string, string>;\n}\n\n/**\n * Top-level `api.ts` imports + helpers a transport or layer depends on. Functions of the\n * context so they can be route-aware (e.g. only import `mutationOptions` when a mutation\n * exists). Imports are deduped by the host across all extensions.\n */\nexport interface ApiModuleDeps {\n /** Raw import lines (e.g. `import { queryOptions as _q } from '@tanstack/react-query';`). */\n imports?(ctx: ExtensionContext): string[];\n /** Module-level helper declarations the rendered expressions depend on. */\n helpers?(ctx: ExtensionContext): string[];\n}\n\n/** Single-slot: decides how an endpoint issues its request. */\nexport interface ApiTransport extends ApiModuleDeps {\n name: string;\n /** Render the expression that issues this endpoint's request (e.g. `fetcher.get<Res>(url, opts)`). */\n renderRequest(leaf: LeafModel, ctx: ExtensionContext): string;\n}\n\n/** Single-slot: decides what a leaf returns (the handle members). */\nexport interface ApiClientLayer extends ApiModuleDeps {\n name: string;\n /**\n * Given the request expression (from the transport) and the leaf, return the handle's\n * members as an ordered `name → value` map (value is the expression after `name: `).\n * Returning members flips the leaf from a bare callable to a handle.\n */\n buildMembers(requestExpr: string, leaf: LeafModel, ctx: ExtensionContext): Record<string, string>;\n}\n\n/** Identity helper for authoring extensions with full type inference. */\nexport function defineExtension(ext: CodegenExtension): CodegenExtension {\n return ext;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2KO,SAAS,gBAAgB,KAAyC;AACvE,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/extension/types.ts"],"sourcesContent":["import type { Project } from 'ts-morph';\nimport type { ResolvedConfig } from '../config/types.js';\nimport type { RouteDescriptor } from '../discovery/types.js';\n\n/**\n * The published, versioned extension contract for `@dudousxd/nestjs-codegen`.\n *\n * Extensions are **build-time** objects (usually returned by a factory so they can take\n * options) registered explicitly via `forRoot({ extensions: [...] })`. The host runs them\n * around the core discovery → IR → emit pipeline.\n *\n * Hooks split into **multi** (every extension runs; results accumulate or chain) and\n * **single-slot** (at most one extension may claim it — two claimers is a hard error).\n *\n * @remarks Semver 0.x — the shape may change until 1.0. Out-of-repo extensions should pin\n * a compatible `@dudousxd/nestjs-codegen` peer range.\n */\nexport interface CodegenExtension {\n /** Unique id. Used in conflict/collision errors and for deterministic ordering. */\n name: string;\n\n // ── multi hooks (every extension runs) ────────────────────────────────────\n\n /**\n * Mutate/augment the route IR before emit. Runs in registration order, chained\n * (each extension sees the previous one's output). Return the new array, or mutate\n * in place and return void. Example: the filter extension attaches `filterFields` to\n * matching routes here.\n */\n transformRoutes?(\n routes: RouteDescriptor[],\n ctx: ExtensionContext,\n ): RouteDescriptor[] | undefined | Promise<RouteDescriptor[] | undefined>;\n\n /**\n * Contribute extra output files (additive). Paths are relative to `outDir`; a path\n * claimed by two extensions is a hard error. Example: the Inertia extension does its\n * own page discovery via `ctx.project()` and emits `pages.d.ts` + `components.json`.\n */\n emitFiles?(ctx: ExtensionContext): EmittedFile[] | Promise<EmittedFile[]>;\n\n /**\n * Contribute top-level code to `api.ts` (imports + statements). Runs in registration\n * order; imports are deduped by the host. Example: the Inertia extension adds\n * `import { router } from '@inertiajs/react'` and the `navigate()` helper.\n */\n apiHeader?(ctx: ExtensionContext): ApiHeaderContribution | undefined;\n\n /**\n * Add named members to a **handle** leaf. Only runs when a client layer is active\n * (i.e. the leaf is a handle, not a bare callable). Member-name collisions across\n * extensions are a hard error. Example: the filter extension adds `filterQuery` to\n * leaves whose route carries `filterFields`.\n */\n apiMembers?(leaf: LeafModel, ctx: ExtensionContext): Record<string, string> | undefined;\n\n // ── single-slot hooks (at most one extension) ─────────────────────────────\n\n /**\n * Claims **how** a single endpoint issues its request. When unset by every extension,\n * the host falls back to the neutral fetcher transport. Example: the Inertia extension\n * routes mutations through the Inertia router while GETs stay fetcher-typed.\n */\n apiTransport?: ApiTransport;\n\n /**\n * Claims **what** a leaf returns. When unset, a leaf is a bare callable returning a\n * `Promise`. Example: the TanStack extension wraps each leaf into a handle exposing\n * `{ fetch, queryKey, queryOptions | mutationOptions }`.\n */\n apiClientLayer?: ApiClientLayer;\n}\n\n/** Shared, read-only context handed to every extension hook. */\nexport interface ExtensionContext {\n cwd: string;\n outDir: string;\n routes: readonly RouteDescriptor[];\n config: ResolvedConfig;\n /** Lazily-created shared ts-morph Project for AST work (pages, custom decorators). */\n project(): Project;\n}\n\n/** A file contributed by an extension's `emitFiles` hook. */\nexport interface EmittedFile {\n /** Path relative to `outDir`. A collision across extensions throws. */\n path: string;\n contents: string;\n}\n\n/** Top-level `api.ts` contributions from an extension's `apiHeader` hook. */\nexport interface ApiHeaderContribution {\n /** Raw import lines (e.g. `import { router } from '@inertiajs/react';`), deduped by the host. */\n imports?: string[];\n /** Top-level statements appended after the api factory (e.g. the `navigate()` helper). */\n statements?: string[];\n}\n\n/**\n * The neutral, per-endpoint request model the host builds for each leaf before any\n * transport/layer runs. Extensions read this to render their output.\n */\nexport interface RequestModel {\n /** Dot-path route name, e.g. `users.show`. */\n routeName: string;\n method: 'get' | 'post' | 'put' | 'patch' | 'delete';\n isGet: boolean;\n /** True for reads: a GET, or a filter-search route (has `filterFields`) even when POST.\n * Client layers use this (not `isGet`) to decide query vs mutation helpers. */\n isQuery: boolean;\n hasParams: boolean;\n hasBody: boolean;\n /** Type of the leaf's `input` arg, e.g. `{ params: ...; query?: ... }` or `Record<string, never>`. */\n inputType: string;\n /** URL expression, e.g. `route('users.show', input?.params) || '/api/users/:id'`. */\n urlExpr: string;\n /** Request-options expression, e.g. `{ query: ... }` or `{ body: input?.body }`. */\n optsExpr: string;\n /** Response type access, e.g. `ApiRouter['users']['show']['response']`. */\n responseType: string;\n /** Body type access, e.g. `ApiRouter['users']['create']['body']` (for mutation layers). */\n bodyType: string;\n /** Stable query-key expression, e.g. `[\"users.show\", input] as const`. */\n queryKeyExpr: string;\n}\n\n/**\n * Per-leaf model passed through the api.ts pipeline: transport → layer → member\n * contributors → render. `requestExpr` is set by the transport; `members`, when present,\n * flips the leaf from a bare callable to a handle.\n */\nexport interface LeafModel {\n route: RouteDescriptor;\n request: RequestModel;\n /** The expression that issues the request (set by the transport, default = fetcher). */\n requestExpr: string;\n /** When present, the leaf renders as a handle exposing these members (ordered). */\n members?: Record<string, string>;\n}\n\n/**\n * Top-level `api.ts` imports + helpers a transport or layer depends on. Functions of the\n * context so they can be route-aware (e.g. only import `mutationOptions` when a mutation\n * exists). Imports are deduped by the host across all extensions.\n */\nexport interface ApiModuleDeps {\n /** Raw import lines (e.g. `import { queryOptions as _q } from '@tanstack/react-query';`). */\n imports?(ctx: ExtensionContext): string[];\n /** Module-level helper declarations the rendered expressions depend on. */\n helpers?(ctx: ExtensionContext): string[];\n}\n\n/** Single-slot: decides how an endpoint issues its request. */\nexport interface ApiTransport extends ApiModuleDeps {\n name: string;\n /** Render the expression that issues this endpoint's request (e.g. `fetcher.get<Res>(url, opts)`). */\n renderRequest(leaf: LeafModel, ctx: ExtensionContext): string;\n}\n\n/** Single-slot: decides what a leaf returns (the handle members). */\nexport interface ApiClientLayer extends ApiModuleDeps {\n name: string;\n /**\n * Given the request expression (from the transport) and the leaf, return the handle's\n * members as an ordered `name → value` map (value is the expression after `name: `).\n * Returning members flips the leaf from a bare callable to a handle.\n */\n buildMembers(requestExpr: string, leaf: LeafModel, ctx: ExtensionContext): Record<string, string>;\n}\n\n/** Identity helper for authoring extensions with full type inference. */\nexport function defineExtension(ext: CodegenExtension): CodegenExtension {\n return ext;\n}\n"],"mappings":";AA2KO,SAAS,gBAAgB,KAAyC;AACvE,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import { Project } from 'ts-morph';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Neutral validation IR. Produced by `dto-to-ir` (class-validator AST → IR) and,
|
|
5
|
+
* later, by parsing `defineContract` zod schemas back into the same shape. A
|
|
6
|
+
* `ValidationAdapter` renders a `SchemaNode` to source text in a concrete lib
|
|
7
|
+
* (zod today; valibot/arktype later).
|
|
8
|
+
*
|
|
9
|
+
* Design note: a few nodes (`raw`, `annotated`, message-carrying checks) preserve
|
|
10
|
+
* source-level detail so the zod adapter can reproduce the previous emitter's
|
|
11
|
+
* output byte-for-byte. Adapters for other libs read the semantic fields and may
|
|
12
|
+
* normalize incidental formatting (quote style, comment wording).
|
|
13
|
+
*/
|
|
14
|
+
/** A `{ message: '...' }` options arg, stored as the verbatim source text (e.g. `'Bad email'`). */
|
|
15
|
+
type MessageRaw = string;
|
|
16
|
+
type StringCheck = {
|
|
17
|
+
check: 'email';
|
|
18
|
+
messageRaw?: MessageRaw;
|
|
19
|
+
} | {
|
|
20
|
+
check: 'url';
|
|
21
|
+
messageRaw?: MessageRaw;
|
|
22
|
+
} | {
|
|
23
|
+
check: 'uuid';
|
|
24
|
+
messageRaw?: MessageRaw;
|
|
25
|
+
} | {
|
|
26
|
+
check: 'regex';
|
|
27
|
+
pattern: string;
|
|
28
|
+
} | {
|
|
29
|
+
check: 'min';
|
|
30
|
+
value: string;
|
|
31
|
+
} | {
|
|
32
|
+
check: 'max';
|
|
33
|
+
value: string;
|
|
34
|
+
};
|
|
35
|
+
type NumberCheck = {
|
|
36
|
+
check: 'int';
|
|
37
|
+
} | {
|
|
38
|
+
check: 'min';
|
|
39
|
+
value: string;
|
|
40
|
+
} | {
|
|
41
|
+
check: 'max';
|
|
42
|
+
value: string;
|
|
43
|
+
} | {
|
|
44
|
+
check: 'positive';
|
|
45
|
+
} | {
|
|
46
|
+
check: 'negative';
|
|
47
|
+
};
|
|
48
|
+
type SchemaNode = {
|
|
49
|
+
kind: 'string';
|
|
50
|
+
checks: StringCheck[];
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'number';
|
|
53
|
+
checks: NumberCheck[];
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'boolean';
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'date';
|
|
58
|
+
}
|
|
59
|
+
/** Unknown/opaque value. `note`, when present, becomes a trailing comment
|
|
60
|
+
* (e.g. recursion degradation or unresolvable-enum fallback reasons). Kept
|
|
61
|
+
* neutral so each adapter renders its own lib's `unknown` + comment. */
|
|
62
|
+
| {
|
|
63
|
+
kind: 'unknown';
|
|
64
|
+
note?: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'instanceof';
|
|
67
|
+
ctor: string;
|
|
68
|
+
}
|
|
69
|
+
/** Members are verbatim literal source texts (quote style preserved). */
|
|
70
|
+
| {
|
|
71
|
+
kind: 'enum';
|
|
72
|
+
literals: string[];
|
|
73
|
+
} | {
|
|
74
|
+
kind: 'literal';
|
|
75
|
+
raw: string;
|
|
76
|
+
} | {
|
|
77
|
+
kind: 'union';
|
|
78
|
+
options: SchemaNode[];
|
|
79
|
+
} | {
|
|
80
|
+
kind: 'object';
|
|
81
|
+
fields: Array<{
|
|
82
|
+
key: string;
|
|
83
|
+
value: SchemaNode;
|
|
84
|
+
}>;
|
|
85
|
+
passthrough: boolean;
|
|
86
|
+
} | {
|
|
87
|
+
kind: 'array';
|
|
88
|
+
element: SchemaNode;
|
|
89
|
+
} | {
|
|
90
|
+
kind: 'optional';
|
|
91
|
+
inner: SchemaNode;
|
|
92
|
+
}
|
|
93
|
+
/** Reference to a hoisted named schema (emitted in `SchemaModule.named`). */
|
|
94
|
+
| {
|
|
95
|
+
kind: 'ref';
|
|
96
|
+
name: string;
|
|
97
|
+
}
|
|
98
|
+
/** Lazy reference to a hoisted named schema (recursion site). */
|
|
99
|
+
| {
|
|
100
|
+
kind: 'lazyRef';
|
|
101
|
+
name: string;
|
|
102
|
+
}
|
|
103
|
+
/** Wraps a node with trailing comments for unmappable decorators (by name). */
|
|
104
|
+
| {
|
|
105
|
+
kind: 'annotated';
|
|
106
|
+
inner: SchemaNode;
|
|
107
|
+
unmappable: string[];
|
|
108
|
+
};
|
|
109
|
+
/** A root schema plus hoisted named (nested/recursive) schemas. */
|
|
110
|
+
interface SchemaModule {
|
|
111
|
+
root: SchemaNode;
|
|
112
|
+
named: Map<string, SchemaNode>;
|
|
113
|
+
warnings: string[];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Signals to an adapter what it must import to render the current output. */
|
|
117
|
+
interface AdapterUsage {
|
|
118
|
+
/** Whether any schema is rendered at all (drives import emission). */
|
|
119
|
+
used: boolean;
|
|
120
|
+
}
|
|
121
|
+
interface RenderContext {
|
|
122
|
+
/** Hoisted named schemas being emitted alongside the root. */
|
|
123
|
+
named: Map<string, SchemaNode>;
|
|
124
|
+
}
|
|
125
|
+
interface RenderedModule {
|
|
126
|
+
/** Root schema source text, e.g. `"z.object({ email: z.string().email() })"`. */
|
|
127
|
+
schemaText: string;
|
|
128
|
+
/** name → schema source text, hoisted above the parent. */
|
|
129
|
+
namedNestedSchemas: Map<string, string>;
|
|
130
|
+
warnings: string[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Renders the neutral {@link SchemaNode} IR into a concrete validation lib's
|
|
134
|
+
* source text. Designed around the Standard Schema spec: a new lib is added by
|
|
135
|
+
* implementing `render`/`renderModule` + `importStatements`.
|
|
136
|
+
*/
|
|
137
|
+
interface ValidationAdapter {
|
|
138
|
+
/** 'zod' | 'valibot' | 'arktype'. */
|
|
139
|
+
name: string;
|
|
140
|
+
/** Import lines required for any rendered text (e.g. `import { z } from 'zod'`). */
|
|
141
|
+
importStatements(usage: AdapterUsage): string[];
|
|
142
|
+
/** Render a single node to this lib's source text. */
|
|
143
|
+
render(node: SchemaNode, ctx: RenderContext): string;
|
|
144
|
+
/** Render a full module (root + hoisted named) to text. */
|
|
145
|
+
renderModule(mod: SchemaModule): RenderedModule;
|
|
146
|
+
/** TS expression for the inferred type of a schema const, e.g. `z.infer<typeof X>`. */
|
|
147
|
+
inferType(schemaConst: string): string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** A built-in adapter name or a custom adapter instance. */
|
|
151
|
+
type ValidationOption = 'zod' | 'valibot' | 'arktype' | ValidationAdapter;
|
|
152
|
+
/**
|
|
153
|
+
* Resolve a `validation` config value to a {@link ValidationAdapter}. `'zod'` is
|
|
154
|
+
* bundled in core; the valibot/arktype adapters ship as their own packages — import
|
|
155
|
+
* the adapter instance and pass it directly (it passes through here). A custom
|
|
156
|
+
* adapter object also passes through.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* import { valibotAdapter } from '@dudousxd/nestjs-codegen-valibot';
|
|
160
|
+
* defineConfig({ validation: valibotAdapter });
|
|
161
|
+
*/
|
|
162
|
+
declare function resolveAdapter(option: ValidationOption): ValidationAdapter;
|
|
163
|
+
|
|
164
|
+
interface UserConfig {
|
|
165
|
+
/**
|
|
166
|
+
* Codegen extensions, applied in order. Each may augment the route IR
|
|
167
|
+
* (`transformRoutes`), contribute extra output files (`emitFiles`), and — once a
|
|
168
|
+
* client layer is active — shape `api.ts`. Registered explicitly, e.g.
|
|
169
|
+
* `extensions: [nestjsInertiaCodegen(), tanstackQuery()]`.
|
|
170
|
+
*/
|
|
171
|
+
extensions?: CodegenExtension[];
|
|
172
|
+
/**
|
|
173
|
+
* Validation library for emitted `forms.ts` schemas. `'zod'` (bundled, default)
|
|
174
|
+
* or an imported adapter instance (`valibotAdapter`/`arktypeAdapter`).
|
|
175
|
+
* @default 'zod'
|
|
176
|
+
*/
|
|
177
|
+
validation?: ValidationOption;
|
|
178
|
+
/** Inertia page discovery. Omit when you don't use Inertia. */
|
|
179
|
+
pages?: {
|
|
180
|
+
glob: string;
|
|
181
|
+
propsExport?: string;
|
|
182
|
+
componentNameStrategy?: 'relative-no-ext' | 'kebab' | ((path: string) => string);
|
|
183
|
+
};
|
|
184
|
+
contracts?: {
|
|
185
|
+
/** Glob pattern (relative to cwd) for controller files. Default: `'src/**\/\*.controller.ts'` */
|
|
186
|
+
glob?: string;
|
|
187
|
+
/** Debounce delay in ms before re-running route discovery. Default: `500` */
|
|
188
|
+
debounceMs?: number;
|
|
189
|
+
};
|
|
190
|
+
scopes?: Record<string, ScopeConfig>;
|
|
191
|
+
codegen?: {
|
|
192
|
+
outDir?: string;
|
|
193
|
+
cwd?: string;
|
|
194
|
+
};
|
|
195
|
+
app?: {
|
|
196
|
+
moduleEntry: string;
|
|
197
|
+
tsconfig?: string;
|
|
198
|
+
} | null;
|
|
199
|
+
/**
|
|
200
|
+
* Custom fetcher configuration. When `importPath` is set, the codegen
|
|
201
|
+
* imports `fetcher` from that path instead of generating `createFetcher()`.
|
|
202
|
+
* This lets users configure baseUrl, headers, plugins (e.g. superjson).
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* // nestjs-inertia.config.ts
|
|
206
|
+
* fetcher: { importPath: '~/lib/api' }
|
|
207
|
+
*
|
|
208
|
+
* // inertia/lib/api.ts
|
|
209
|
+
* import { createFetcher } from '@dudousxd/nestjs-inertia-client';
|
|
210
|
+
* export const fetcher = createFetcher({ baseUrl: '/api' });
|
|
211
|
+
*/
|
|
212
|
+
fetcher?: {
|
|
213
|
+
importPath: string;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Typed-form schema emit (`forms.ts`). Re-exports / translates contract and
|
|
217
|
+
* class-validator-decorated DTO schemas into zod schemas for client-side
|
|
218
|
+
* validation.
|
|
219
|
+
*/
|
|
220
|
+
forms?: {
|
|
221
|
+
/** Emit `forms.ts`. Default: `true` (when ≥1 validatable body exists). */
|
|
222
|
+
enabled?: boolean;
|
|
223
|
+
/** DTO glob to watch for form-schema regen. Default: `'src/**\/\*.dto.ts'`. */
|
|
224
|
+
watch?: string;
|
|
225
|
+
/** Module specifier for the `z` import. Default: `'zod'`. */
|
|
226
|
+
zodImport?: string;
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
interface ScopeConfig {
|
|
230
|
+
glob: string;
|
|
231
|
+
prefix?: string;
|
|
232
|
+
}
|
|
233
|
+
interface ResolvedPagesConfig {
|
|
234
|
+
glob: string;
|
|
235
|
+
propsExport: string;
|
|
236
|
+
componentNameStrategy: 'relative-no-ext' | 'kebab' | ((path: string) => string);
|
|
237
|
+
}
|
|
238
|
+
interface ResolvedContractsConfig {
|
|
239
|
+
/** Glob pattern relative to `codegen.cwd` for watching controller files. */
|
|
240
|
+
glob: string;
|
|
241
|
+
/** Debounce delay in ms before re-running route discovery. */
|
|
242
|
+
debounceMs: number;
|
|
243
|
+
}
|
|
244
|
+
interface ResolvedCodegenConfig {
|
|
245
|
+
outDir: string;
|
|
246
|
+
cwd: string;
|
|
247
|
+
}
|
|
248
|
+
interface ResolvedAppConfig {
|
|
249
|
+
moduleEntry: string;
|
|
250
|
+
tsconfig: string | null;
|
|
251
|
+
}
|
|
252
|
+
interface ResolvedFormsConfig {
|
|
253
|
+
enabled: boolean;
|
|
254
|
+
watch: string;
|
|
255
|
+
zodImport: string;
|
|
256
|
+
}
|
|
257
|
+
interface ResolvedConfig {
|
|
258
|
+
extensions: CodegenExtension[];
|
|
259
|
+
validation: ValidationAdapter;
|
|
260
|
+
pages: ResolvedPagesConfig | null;
|
|
261
|
+
contracts: ResolvedContractsConfig;
|
|
262
|
+
scopes: Record<string, ScopeConfig>;
|
|
263
|
+
codegen: ResolvedCodegenConfig;
|
|
264
|
+
app: ResolvedAppConfig | null;
|
|
265
|
+
fetcher: {
|
|
266
|
+
importPath: string;
|
|
267
|
+
} | null;
|
|
268
|
+
forms: ResolvedFormsConfig;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
interface TypeRef {
|
|
272
|
+
name: string;
|
|
273
|
+
filePath: string;
|
|
274
|
+
isArray?: boolean;
|
|
275
|
+
}
|
|
276
|
+
type FieldTypeKind = 'string' | 'number' | 'boolean' | 'date' | 'json' | 'unknown';
|
|
277
|
+
/**
|
|
278
|
+
* A classified filter field.
|
|
279
|
+
*
|
|
280
|
+
* INVARIANT — `typeRef` precedence: when `typeRef` is set it is the SOLE source
|
|
281
|
+
* of truth for the emitted type; `kind`/`enumValues`/`numericEnum` are then only
|
|
282
|
+
* a best-effort fallback recorded for non-emit consumers (tests/introspection),
|
|
283
|
+
* NOT something the emitter reads. A discriminated union would make "carries both
|
|
284
|
+
* a ref AND a literal kind" unrepresentable, but the producers and tests read
|
|
285
|
+
* `kind` and `typeRef` off the same object freely, so the union ripples too
|
|
286
|
+
* widely and fights the existing nullable handling. Instead this invariant is
|
|
287
|
+
* concentrated in the single normalizing constructor {@link toFilterFieldType}
|
|
288
|
+
* (the only place a `FilterFieldType` is built from a classification) and honored
|
|
289
|
+
* by the single emit-side reader (`emitFieldTypesLiteral`). Do not branch on
|
|
290
|
+
* `typeRef` vs `kind` anywhere else.
|
|
291
|
+
*/
|
|
292
|
+
interface FilterFieldType {
|
|
293
|
+
/** Field name, e.g. 'age' or 'tasks.id' (dot-notation for relations). */
|
|
294
|
+
name: string;
|
|
295
|
+
kind: FieldTypeKind;
|
|
296
|
+
/** String/number-literal union members (enums), if any. */
|
|
297
|
+
enumValues?: string[];
|
|
298
|
+
/** Whether the field's TS type includes null/undefined. */
|
|
299
|
+
nullable?: boolean;
|
|
300
|
+
/** True when enumValues are numeric literals (emit unquoted). */
|
|
301
|
+
numericEnum?: boolean;
|
|
302
|
+
/**
|
|
303
|
+
* When the field's type is a named enum / type alias / interface inferred from
|
|
304
|
+
* a `@FilterFor` method parameter, the importable reference to that symbol.
|
|
305
|
+
* The emitter references `typeRef.name` in the type map M and emits a real
|
|
306
|
+
* `import type { <name> } from '<path>'` at the top of the generated file.
|
|
307
|
+
* Takes precedence over `kind`/`enumValues` when present (see invariant above).
|
|
308
|
+
*/
|
|
309
|
+
typeRef?: TypeRef;
|
|
310
|
+
}
|
|
311
|
+
interface ContractSource {
|
|
312
|
+
query: string | null;
|
|
313
|
+
body: string | null;
|
|
314
|
+
response: string;
|
|
315
|
+
queryRef?: TypeRef | null;
|
|
316
|
+
bodyRef?: TypeRef | null;
|
|
317
|
+
responseRef?: TypeRef | null;
|
|
318
|
+
filterFields?: string[] | null;
|
|
319
|
+
filterFieldTypes?: FilterFieldType[] | null;
|
|
320
|
+
filterSource?: 'body' | 'query' | null;
|
|
321
|
+
/** Raw zod source for the body schema (Path A inline, or Path B synthesized). */
|
|
322
|
+
bodyZodText?: string | null;
|
|
323
|
+
/** Importable named schema to re-export for the body (Path A). */
|
|
324
|
+
bodyZodRef?: TypeRef | null;
|
|
325
|
+
/** Raw zod source for the query schema (Path A inline, or Path B synthesized). */
|
|
326
|
+
queryZodText?: string | null;
|
|
327
|
+
/** Importable named schema to re-export for the query (Path A). */
|
|
328
|
+
queryZodRef?: TypeRef | null;
|
|
329
|
+
/** Hoisted nested schemas (name → zod text) referenced by body/query (Path B). */
|
|
330
|
+
formNestedSchemas?: Record<string, string> | null;
|
|
331
|
+
/** Unmappable-decorator warnings surfaced to console + a header comment. */
|
|
332
|
+
formWarnings?: string[];
|
|
333
|
+
/**
|
|
334
|
+
* Neutral validation IR for the body, when synthesized from a class-validator
|
|
335
|
+
* DTO. Lets `emit-forms` render via the configured adapter (valibot/arktype),
|
|
336
|
+
* not only zod. `null`/absent for `defineContract` (hand-written zod) bodies.
|
|
337
|
+
*/
|
|
338
|
+
bodySchema?: SchemaModule | null;
|
|
339
|
+
/** Neutral validation IR for the query (class-validator DTO only). */
|
|
340
|
+
querySchema?: SchemaModule | null;
|
|
341
|
+
}
|
|
342
|
+
interface ContractDescriptor {
|
|
343
|
+
contractSource: ContractSource;
|
|
344
|
+
}
|
|
345
|
+
interface ControllerRef {
|
|
346
|
+
className: string;
|
|
347
|
+
methodName: string;
|
|
348
|
+
filePath: string;
|
|
349
|
+
}
|
|
350
|
+
interface RouteDescriptor {
|
|
351
|
+
method: string;
|
|
352
|
+
path: string;
|
|
353
|
+
name: string;
|
|
354
|
+
params: Array<{
|
|
355
|
+
name: string;
|
|
356
|
+
source: 'path' | 'query' | 'body' | 'header';
|
|
357
|
+
}>;
|
|
358
|
+
contract?: ContractDescriptor;
|
|
359
|
+
controllerRef?: ControllerRef;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* The published, versioned extension contract for `@dudousxd/nestjs-codegen`.
|
|
364
|
+
*
|
|
365
|
+
* Extensions are **build-time** objects (usually returned by a factory so they can take
|
|
366
|
+
* options) registered explicitly via `forRoot({ extensions: [...] })`. The host runs them
|
|
367
|
+
* around the core discovery → IR → emit pipeline.
|
|
368
|
+
*
|
|
369
|
+
* Hooks split into **multi** (every extension runs; results accumulate or chain) and
|
|
370
|
+
* **single-slot** (at most one extension may claim it — two claimers is a hard error).
|
|
371
|
+
*
|
|
372
|
+
* @remarks Semver 0.x — the shape may change until 1.0. Out-of-repo extensions should pin
|
|
373
|
+
* a compatible `@dudousxd/nestjs-codegen` peer range.
|
|
374
|
+
*/
|
|
375
|
+
interface CodegenExtension {
|
|
376
|
+
/** Unique id. Used in conflict/collision errors and for deterministic ordering. */
|
|
377
|
+
name: string;
|
|
378
|
+
/**
|
|
379
|
+
* Mutate/augment the route IR before emit. Runs in registration order, chained
|
|
380
|
+
* (each extension sees the previous one's output). Return the new array, or mutate
|
|
381
|
+
* in place and return void. Example: the filter extension attaches `filterFields` to
|
|
382
|
+
* matching routes here.
|
|
383
|
+
*/
|
|
384
|
+
transformRoutes?(routes: RouteDescriptor[], ctx: ExtensionContext): RouteDescriptor[] | undefined | Promise<RouteDescriptor[] | undefined>;
|
|
385
|
+
/**
|
|
386
|
+
* Contribute extra output files (additive). Paths are relative to `outDir`; a path
|
|
387
|
+
* claimed by two extensions is a hard error. Example: the Inertia extension does its
|
|
388
|
+
* own page discovery via `ctx.project()` and emits `pages.d.ts` + `components.json`.
|
|
389
|
+
*/
|
|
390
|
+
emitFiles?(ctx: ExtensionContext): EmittedFile[] | Promise<EmittedFile[]>;
|
|
391
|
+
/**
|
|
392
|
+
* Contribute top-level code to `api.ts` (imports + statements). Runs in registration
|
|
393
|
+
* order; imports are deduped by the host. Example: the Inertia extension adds
|
|
394
|
+
* `import { router } from '@inertiajs/react'` and the `navigate()` helper.
|
|
395
|
+
*/
|
|
396
|
+
apiHeader?(ctx: ExtensionContext): ApiHeaderContribution | undefined;
|
|
397
|
+
/**
|
|
398
|
+
* Add named members to a **handle** leaf. Only runs when a client layer is active
|
|
399
|
+
* (i.e. the leaf is a handle, not a bare callable). Member-name collisions across
|
|
400
|
+
* extensions are a hard error. Example: the filter extension adds `filterQuery` to
|
|
401
|
+
* leaves whose route carries `filterFields`.
|
|
402
|
+
*/
|
|
403
|
+
apiMembers?(leaf: LeafModel, ctx: ExtensionContext): Record<string, string> | undefined;
|
|
404
|
+
/**
|
|
405
|
+
* Claims **how** a single endpoint issues its request. When unset by every extension,
|
|
406
|
+
* the host falls back to the neutral fetcher transport. Example: the Inertia extension
|
|
407
|
+
* routes mutations through the Inertia router while GETs stay fetcher-typed.
|
|
408
|
+
*/
|
|
409
|
+
apiTransport?: ApiTransport;
|
|
410
|
+
/**
|
|
411
|
+
* Claims **what** a leaf returns. When unset, a leaf is a bare callable returning a
|
|
412
|
+
* `Promise`. Example: the TanStack extension wraps each leaf into a handle exposing
|
|
413
|
+
* `{ fetch, queryKey, queryOptions | mutationOptions }`.
|
|
414
|
+
*/
|
|
415
|
+
apiClientLayer?: ApiClientLayer;
|
|
416
|
+
}
|
|
417
|
+
/** Shared, read-only context handed to every extension hook. */
|
|
418
|
+
interface ExtensionContext {
|
|
419
|
+
cwd: string;
|
|
420
|
+
outDir: string;
|
|
421
|
+
routes: readonly RouteDescriptor[];
|
|
422
|
+
config: ResolvedConfig;
|
|
423
|
+
/** Lazily-created shared ts-morph Project for AST work (pages, custom decorators). */
|
|
424
|
+
project(): Project;
|
|
425
|
+
}
|
|
426
|
+
/** A file contributed by an extension's `emitFiles` hook. */
|
|
427
|
+
interface EmittedFile {
|
|
428
|
+
/** Path relative to `outDir`. A collision across extensions throws. */
|
|
429
|
+
path: string;
|
|
430
|
+
contents: string;
|
|
431
|
+
}
|
|
432
|
+
/** Top-level `api.ts` contributions from an extension's `apiHeader` hook. */
|
|
433
|
+
interface ApiHeaderContribution {
|
|
434
|
+
/** Raw import lines (e.g. `import { router } from '@inertiajs/react';`), deduped by the host. */
|
|
435
|
+
imports?: string[];
|
|
436
|
+
/** Top-level statements appended after the api factory (e.g. the `navigate()` helper). */
|
|
437
|
+
statements?: string[];
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* The neutral, per-endpoint request model the host builds for each leaf before any
|
|
441
|
+
* transport/layer runs. Extensions read this to render their output.
|
|
442
|
+
*/
|
|
443
|
+
interface RequestModel {
|
|
444
|
+
/** Dot-path route name, e.g. `users.show`. */
|
|
445
|
+
routeName: string;
|
|
446
|
+
method: 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
447
|
+
isGet: boolean;
|
|
448
|
+
/** True for reads: a GET, or a filter-search route (has `filterFields`) even when POST.
|
|
449
|
+
* Client layers use this (not `isGet`) to decide query vs mutation helpers. */
|
|
450
|
+
isQuery: boolean;
|
|
451
|
+
hasParams: boolean;
|
|
452
|
+
hasBody: boolean;
|
|
453
|
+
/** Type of the leaf's `input` arg, e.g. `{ params: ...; query?: ... }` or `Record<string, never>`. */
|
|
454
|
+
inputType: string;
|
|
455
|
+
/** URL expression, e.g. `route('users.show', input?.params) || '/api/users/:id'`. */
|
|
456
|
+
urlExpr: string;
|
|
457
|
+
/** Request-options expression, e.g. `{ query: ... }` or `{ body: input?.body }`. */
|
|
458
|
+
optsExpr: string;
|
|
459
|
+
/** Response type access, e.g. `ApiRouter['users']['show']['response']`. */
|
|
460
|
+
responseType: string;
|
|
461
|
+
/** Body type access, e.g. `ApiRouter['users']['create']['body']` (for mutation layers). */
|
|
462
|
+
bodyType: string;
|
|
463
|
+
/** Stable query-key expression, e.g. `["users.show", input] as const`. */
|
|
464
|
+
queryKeyExpr: string;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Per-leaf model passed through the api.ts pipeline: transport → layer → member
|
|
468
|
+
* contributors → render. `requestExpr` is set by the transport; `members`, when present,
|
|
469
|
+
* flips the leaf from a bare callable to a handle.
|
|
470
|
+
*/
|
|
471
|
+
interface LeafModel {
|
|
472
|
+
route: RouteDescriptor;
|
|
473
|
+
request: RequestModel;
|
|
474
|
+
/** The expression that issues the request (set by the transport, default = fetcher). */
|
|
475
|
+
requestExpr: string;
|
|
476
|
+
/** When present, the leaf renders as a handle exposing these members (ordered). */
|
|
477
|
+
members?: Record<string, string>;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Top-level `api.ts` imports + helpers a transport or layer depends on. Functions of the
|
|
481
|
+
* context so they can be route-aware (e.g. only import `mutationOptions` when a mutation
|
|
482
|
+
* exists). Imports are deduped by the host across all extensions.
|
|
483
|
+
*/
|
|
484
|
+
interface ApiModuleDeps {
|
|
485
|
+
/** Raw import lines (e.g. `import { queryOptions as _q } from '@tanstack/react-query';`). */
|
|
486
|
+
imports?(ctx: ExtensionContext): string[];
|
|
487
|
+
/** Module-level helper declarations the rendered expressions depend on. */
|
|
488
|
+
helpers?(ctx: ExtensionContext): string[];
|
|
489
|
+
}
|
|
490
|
+
/** Single-slot: decides how an endpoint issues its request. */
|
|
491
|
+
interface ApiTransport extends ApiModuleDeps {
|
|
492
|
+
name: string;
|
|
493
|
+
/** Render the expression that issues this endpoint's request (e.g. `fetcher.get<Res>(url, opts)`). */
|
|
494
|
+
renderRequest(leaf: LeafModel, ctx: ExtensionContext): string;
|
|
495
|
+
}
|
|
496
|
+
/** Single-slot: decides what a leaf returns (the handle members). */
|
|
497
|
+
interface ApiClientLayer extends ApiModuleDeps {
|
|
498
|
+
name: string;
|
|
499
|
+
/**
|
|
500
|
+
* Given the request expression (from the transport) and the leaf, return the handle's
|
|
501
|
+
* members as an ordered `name → value` map (value is the expression after `name: `).
|
|
502
|
+
* Returning members flips the leaf from a bare callable to a handle.
|
|
503
|
+
*/
|
|
504
|
+
buildMembers(requestExpr: string, leaf: LeafModel, ctx: ExtensionContext): Record<string, string>;
|
|
505
|
+
}
|
|
506
|
+
/** Identity helper for authoring extensions with full type inference. */
|
|
507
|
+
declare function defineExtension(ext: CodegenExtension): CodegenExtension;
|
|
508
|
+
|
|
509
|
+
export { type AdapterUsage as A, type CodegenExtension as C, type ExtensionContext as E, type LeafModel as L, type NumberCheck as N, type ResolvedConfig as R, type SchemaModule as S, type TypeRef as T, type UserConfig as U, type ValidationAdapter as V, type RouteDescriptor as a, type ResolvedFormsConfig as b, type ContractDescriptor as c, type ContractSource as d, type ControllerRef as e, type RenderContext as f, type RenderedModule as g, type SchemaNode as h, type ScopeConfig as i, type StringCheck as j, type ValidationOption as k, type ApiClientLayer as l, type ApiHeaderContribution as m, type ApiModuleDeps as n, type ApiTransport as o, type EmittedFile as p, type RequestModel as q, resolveAdapter as r, defineExtension as s };
|