@dudousxd/nestjs-codegen 0.25.2 → 0.27.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/dist/cli/main.cjs CHANGED
@@ -117,11 +117,32 @@ function validateUserConfig(userConfig) {
117
117
  "validation adapter is required \u2014 install @dudousxd/nestjs-codegen-zod and pass zodAdapter, or use @dudousxd/nestjs-codegen-valibot / -arktype"
118
118
  );
119
119
  }
120
- if (userConfig.pages && typeof userConfig.pages.glob !== "string") {
120
+ if (userConfig.pages && !isGlobInput(userConfig.pages.glob)) {
121
121
  throw new ConfigError(
122
- "Config validation failed: `pages.glob` must be a string when `pages` is set"
122
+ "Config validation failed: `pages.glob` must be a string or a non-empty array of strings when `pages` is set"
123
123
  );
124
124
  }
125
+ for (const [scope, scopeConfig] of Object.entries(userConfig.pages?.scopes ?? {})) {
126
+ if (scope === "default") {
127
+ throw new ConfigError(
128
+ 'Config validation failed: `pages.scopes` cannot declare "default" \u2014 the default app is `pages.glob` (InertiaModule.forRoot).'
129
+ );
130
+ }
131
+ if (!isGlobInput(scopeConfig?.glob)) {
132
+ throw new ConfigError(
133
+ `Config validation failed: \`pages.scopes.${scope}.glob\` must be a string or a non-empty array of strings`
134
+ );
135
+ }
136
+ }
137
+ if (userConfig.scopes && Object.keys(userConfig.scopes).length > 0) {
138
+ console.warn(
139
+ "[nestjs-codegen] The top-level `scopes` option has no effect and is deprecated. For several Inertia apps use `pages.scopes` (one entry per InertiaModule.forFeature scope); for several controller sets use one `contracts.glob` (e.g. a brace glob)."
140
+ );
141
+ }
142
+ }
143
+ function isGlobInput(value) {
144
+ if (typeof value === "string") return true;
145
+ return Array.isArray(value) && value.length > 0 && value.every((glob) => typeof glob === "string");
125
146
  }
126
147
  function applyDefaults(userConfig, cwd) {
127
148
  validateUserConfig(userConfig);
@@ -149,7 +170,17 @@ function applyDefaults(userConfig, cwd) {
149
170
  pages: userConfig.pages ? {
150
171
  glob: userConfig.pages.glob,
151
172
  propsExport: userConfig.pages.propsExport ?? "ComponentProps",
152
- componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext"
173
+ componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext",
174
+ scopes: Object.fromEntries(
175
+ Object.entries(userConfig.pages.scopes ?? {}).map(([scope, scopeConfig]) => [
176
+ scope,
177
+ {
178
+ glob: scopeConfig.glob,
179
+ prefix: scopeConfig.prefix ?? `${scope}/`,
180
+ componentNameStrategy: scopeConfig.componentNameStrategy ?? userConfig.pages?.componentNameStrategy ?? "relative-no-ext"
181
+ }
182
+ ])
183
+ )
153
184
  } : null,
154
185
  contracts: {
155
186
  glob: userConfig.contracts?.glob ?? "src/**/*.controller.ts",
@@ -226,23 +257,81 @@ var import_promises2 = require("fs/promises");
226
257
  var import_node_path2 = require("path");
227
258
  var import_fast_glob = __toESM(require("fast-glob"), 1);
228
259
  var NON_PAGE_FILE_RE = /\.(?:test|spec|stories|story)\.(?:tsx?|jsx?|vue|svelte)$/i;
260
+ var MAGIC_SEGMENT_RE = /[*?{}[\]()!]/;
261
+ function globStaticBase(glob) {
262
+ const segments = glob.split("/");
263
+ const staticSegments = [];
264
+ for (const segment of segments.slice(0, -1)) {
265
+ if (MAGIC_SEGMENT_RE.test(segment)) break;
266
+ staticSegments.push(segment);
267
+ }
268
+ return staticSegments.join("/");
269
+ }
229
270
  async function discoverPages(opts) {
230
- const allFiles = await (0, import_fast_glob.default)(opts.glob, { cwd: opts.cwd, absolute: true });
231
- const files = allFiles.filter((f) => !NON_PAGE_FILE_RE.test(f));
232
- files.sort();
233
- const globStatic = opts.glob.split("*")[0]?.replace(/\/$/, "") ?? "";
234
- const pagesBase = (0, import_node_path2.join)(opts.cwd, globStatic);
271
+ const globs = typeof opts.glob === "string" ? [opts.glob] : opts.glob;
272
+ const prefix = opts.prefix ?? "";
273
+ const scope = opts.scope ?? "default";
274
+ const seenFiles = /* @__PURE__ */ new Set();
235
275
  const out = [];
236
- for (const file of files) {
237
- const rel = (0, import_node_path2.relative)(opts.cwd, file);
238
- const nameRel = (0, import_node_path2.relative)(pagesBase, file);
239
- const name = computeName(nameRel, opts.componentNameStrategy);
240
- const source = await (0, import_promises2.readFile)(file, "utf8");
241
- const propsSource = extractPropsSource(source, opts.propsExport);
242
- out.push({ name, absolutePath: file, relativePath: rel, propsSource });
276
+ for (const glob of globs) {
277
+ const files = (await (0, import_fast_glob.default)(glob, { cwd: opts.cwd, absolute: true })).filter((f) => !NON_PAGE_FILE_RE.test(f) && !seenFiles.has(f)).sort();
278
+ const pagesBase = (0, import_node_path2.join)(opts.cwd, globStaticBase(glob));
279
+ for (const file of files) {
280
+ seenFiles.add(file);
281
+ const rel = (0, import_node_path2.relative)(opts.cwd, file);
282
+ const nameRel = (0, import_node_path2.relative)(pagesBase, file).split(import_node_path2.sep).join("/");
283
+ const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
284
+ const source = await (0, import_promises2.readFile)(file, "utf8");
285
+ const propsSource = extractPropsSource(source, opts.propsExport);
286
+ out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
287
+ }
288
+ }
289
+ assertUniqueNames(out);
290
+ return out;
291
+ }
292
+ async function discoverPageScopes(config) {
293
+ const pages = config.pages;
294
+ if (!pages) return [];
295
+ const common = { cwd: config.codegen.cwd, propsExport: pages.propsExport };
296
+ const out = await discoverPages({
297
+ ...common,
298
+ glob: pages.glob,
299
+ componentNameStrategy: pages.componentNameStrategy
300
+ });
301
+ for (const [scope, scopeConfig] of Object.entries(pages.scopes ?? {})) {
302
+ out.push(
303
+ ...await discoverPages({
304
+ ...common,
305
+ glob: scopeConfig.glob,
306
+ componentNameStrategy: scopeConfig.componentNameStrategy,
307
+ prefix: scopeConfig.prefix,
308
+ scope
309
+ })
310
+ );
243
311
  }
312
+ assertUniqueNames(out);
244
313
  return out;
245
314
  }
315
+ function allPageGlobs(config) {
316
+ if (!config.pages) return [];
317
+ const asList = (glob) => typeof glob === "string" ? [glob] : glob;
318
+ return [
319
+ ...asList(config.pages.glob),
320
+ ...Object.values(config.pages.scopes ?? {}).flatMap((scope) => asList(scope.glob))
321
+ ];
322
+ }
323
+ function assertUniqueNames(pages) {
324
+ const byName = /* @__PURE__ */ new Map();
325
+ for (const page of pages) {
326
+ const previous = byName.get(page.name);
327
+ if (previous) {
328
+ throw new Error(
329
+ `Two Inertia pages resolve to the component name "${page.name}": ${previous.relativePath} (scope "${previous.scope}") and ${page.relativePath} (scope "${page.scope}"). Give one of them another name, directory or scope prefix.`
330
+ );
331
+ }
332
+ byName.set(page.name, page);
333
+ }
334
+ }
246
335
  function computeName(rel, strat) {
247
336
  if (typeof strat === "function") return strat(rel);
248
337
  const noExt = rel.replace(/\.(tsx?|vue|svelte)$/, "");
@@ -552,16 +641,16 @@ var import_node_path7 = require("path");
552
641
 
553
642
  // src/extension/registry.ts
554
643
  var import_node_path6 = require("path");
555
- var import_ts_morph10 = require("ts-morph");
644
+ var import_ts_morph13 = require("ts-morph");
556
645
 
557
646
  // src/discovery/contracts-fast.ts
558
647
  var import_node_fs = require("fs");
559
648
  var import_node_path5 = require("path");
560
649
  var import_fast_glob2 = __toESM(require("fast-glob"), 1);
561
- var import_ts_morph9 = require("ts-morph");
650
+ var import_ts_morph12 = require("ts-morph");
562
651
 
563
652
  // src/discovery/dto-type-resolver.ts
564
- var import_ts_morph7 = require("ts-morph");
653
+ var import_ts_morph11 = require("ts-morph");
565
654
 
566
655
  // src/discovery/dto-to-ir.ts
567
656
  var import_ts_morph3 = require("ts-morph");
@@ -2089,6 +2178,672 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
2089
2178
  return fields;
2090
2179
  }
2091
2180
 
2181
+ // src/discovery/pipe-schema.ts
2182
+ var import_ts_morph10 = require("ts-morph");
2183
+
2184
+ // src/discovery/schema-type-inference.ts
2185
+ var import_ts_morph8 = require("ts-morph");
2186
+
2187
+ // src/discovery/schema-type-program.ts
2188
+ var import_ts_morph7 = require("ts-morph");
2189
+ var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
2190
+ var SchemaTypeProgram = class {
2191
+ constructor(discovery) {
2192
+ this.discovery = discovery;
2193
+ const base = discovery.getCompilerOptions();
2194
+ this.options = {
2195
+ ...base,
2196
+ // A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
2197
+ // `Promise` for the schema libraries' declarations to lean on.
2198
+ target: base.target ?? import_ts_morph7.ts.ScriptTarget.ES2022,
2199
+ // With neither set, resolve the way a bundler does: package `exports`
2200
+ // (`zod/v4`) and extensionless or `.js` relative imports all resolve.
2201
+ ...base.module === void 0 && base.moduleResolution === void 0 ? { module: import_ts_morph7.ts.ModuleKind.ESNext, moduleResolution: import_ts_morph7.ts.ModuleResolutionKind.Bundler } : {},
2202
+ // Without it `.nullable()` and `.optional()` would erase to the bare type.
2203
+ strictNullChecks: true,
2204
+ // No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
2205
+ // need them, and they are a hundred-odd files of program to build.
2206
+ types: [],
2207
+ noEmit: true
2208
+ };
2209
+ this.project = new import_ts_morph7.Project({
2210
+ skipAddingFilesFromTsConfig: true,
2211
+ compilerOptions: this.options
2212
+ });
2213
+ for (const sf of discovery.getSourceFiles()) this.request(sf.getFilePath(), { schema: true });
2214
+ this.flush();
2215
+ }
2216
+ project;
2217
+ options;
2218
+ /** Parses a file that is not in discovery's Project, without adding it there. */
2219
+ scratch = new import_ts_morph7.Project({
2220
+ skipAddingFilesFromTsConfig: true,
2221
+ skipLoadingLibFiles: true,
2222
+ skipFileDependencyResolution: true
2223
+ });
2224
+ needs = /* @__PURE__ */ new Map();
2225
+ /** Pruned text per file, as last written into {@link project}. */
2226
+ written = /* @__PURE__ */ new Map();
2227
+ /**
2228
+ * The typed copy of a discovery file, with its schema positions kept. Adding one
2229
+ * that was not there yet (a contract file discovery reached lazily) rebuilds the
2230
+ * program once.
2231
+ */
2232
+ sourceFileFor(sf) {
2233
+ this.request(sf.getFilePath(), { schema: true });
2234
+ this.flush();
2235
+ return this.project.getSourceFile(sf.getFilePath());
2236
+ }
2237
+ // -------------------------------------------------------------------------
2238
+ queue = [];
2239
+ request(path, want) {
2240
+ let needs = this.needs.get(path);
2241
+ const isNew = !needs;
2242
+ needs ??= { schema: false, all: false, names: /* @__PURE__ */ new Set() };
2243
+ let grew = isNew;
2244
+ if (want.schema && !needs.schema) {
2245
+ needs.schema = true;
2246
+ grew = true;
2247
+ }
2248
+ if (want.all && !needs.all) {
2249
+ needs.all = true;
2250
+ grew = true;
2251
+ }
2252
+ for (const name of want.names ?? []) {
2253
+ if (!needs.names.has(name)) {
2254
+ needs.names.add(name);
2255
+ grew = true;
2256
+ }
2257
+ }
2258
+ this.needs.set(path, needs);
2259
+ if (grew) this.queue.push(path);
2260
+ }
2261
+ /** Run the worklist to a fixpoint, then write every changed file into the program. */
2262
+ flush() {
2263
+ const changed = /* @__PURE__ */ new Map();
2264
+ while (this.queue.length > 0) {
2265
+ const path = this.queue.pop();
2266
+ const original = this.original(path);
2267
+ if (!original) continue;
2268
+ const needs = this.needs.get(path);
2269
+ const text = this.prune(original, needs);
2270
+ if (this.written.get(path) !== text) changed.set(path, text);
2271
+ }
2272
+ for (const [path, text] of changed) {
2273
+ this.project.createSourceFile(path, text, { overwrite: true });
2274
+ this.written.set(path, text);
2275
+ }
2276
+ }
2277
+ /** The file as discovery sees it (its in-memory text), else as it is on disk. */
2278
+ original(path) {
2279
+ return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
2280
+ }
2281
+ /**
2282
+ * `sf`'s text with every import/re-export nothing in `needs` references blanked,
2283
+ * requesting from each kept one's target the names it brings in.
2284
+ */
2285
+ prune(sf, needs) {
2286
+ const locals = topLevelDeclarations(sf);
2287
+ const roots = [];
2288
+ if (needs.all) roots.push(...sf.getStatements());
2289
+ if (needs.schema) roots.push(...schemaPositions(sf));
2290
+ const reexported = /* @__PURE__ */ new Map();
2291
+ for (const exp of sf.getExportDeclarations()) {
2292
+ const spec = exp.getModuleSpecifierValue();
2293
+ if (spec === void 0) {
2294
+ for (const named of exp.getNamedExports()) {
2295
+ const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
2296
+ if (needs.all || needs.names.has(exposed)) roots.push(named.getNameNode());
2297
+ }
2298
+ continue;
2299
+ }
2300
+ if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) continue;
2301
+ for (const named of exp.getNamedExports()) {
2302
+ const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
2303
+ const list = reexported.get(exposed) ?? [];
2304
+ list.push({ from: spec, name: named.getNameNode().getText() });
2305
+ reexported.set(exposed, list);
2306
+ }
2307
+ }
2308
+ const unresolvedNames = [];
2309
+ for (const name of needs.names) {
2310
+ const decls = name === "default" ? defaultExport(sf) : locals.get(name) ?? [];
2311
+ if (decls.length > 0) roots.push(...decls);
2312
+ else if (!reexported.has(name)) unresolvedNames.push(name);
2313
+ }
2314
+ const referenced = /* @__PURE__ */ new Set();
2315
+ const queue = [...roots];
2316
+ const seen = new Set(queue);
2317
+ while (queue.length > 0) {
2318
+ const node = queue.pop();
2319
+ const ids = import_ts_morph7.Node.isIdentifier(node) ? [node] : node.getDescendantsOfKind(import_ts_morph7.ts.SyntaxKind.Identifier);
2320
+ for (const id of ids) {
2321
+ const name = id.getText();
2322
+ if (referenced.has(name)) continue;
2323
+ referenced.add(name);
2324
+ for (const local of locals.get(name) ?? []) {
2325
+ if (seen.has(local)) continue;
2326
+ seen.add(local);
2327
+ queue.push(local);
2328
+ }
2329
+ }
2330
+ }
2331
+ const chars = sf.getFullText().split("");
2332
+ const blank = (node) => {
2333
+ for (let i = node.getStart(); i < node.getEnd(); i++) {
2334
+ if (chars[i] !== "\n" && chars[i] !== "\r") chars[i] = " ";
2335
+ }
2336
+ };
2337
+ for (const decl of sf.getImportDeclarations()) {
2338
+ const target = this.resolve(decl.getModuleSpecifierValue(), sf);
2339
+ const namespace = decl.getNamespaceImport()?.getText();
2340
+ const def = decl.getDefaultImport()?.getText();
2341
+ const named = decl.getNamedImports().map((n) => ({
2342
+ local: (n.getAliasNode() ?? n.getNameNode()).getText(),
2343
+ imported: n.getName()
2344
+ })).filter((n) => referenced.has(n.local));
2345
+ const wantsNamespace = namespace !== void 0 && referenced.has(namespace);
2346
+ const wantsDefault = def !== void 0 && referenced.has(def);
2347
+ if (!wantsNamespace && !wantsDefault && named.length === 0) {
2348
+ blank(decl);
2349
+ continue;
2350
+ }
2351
+ if (target) {
2352
+ this.request(target, {
2353
+ all: wantsNamespace,
2354
+ names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
2355
+ });
2356
+ }
2357
+ }
2358
+ for (const exp of sf.getExportDeclarations()) {
2359
+ const spec = exp.getModuleSpecifierValue();
2360
+ if (spec === void 0) continue;
2361
+ const target = this.resolve(spec, sf);
2362
+ if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
2363
+ if (!needs.all && unresolvedNames.length === 0) {
2364
+ blank(exp);
2365
+ } else if (target) {
2366
+ this.request(target, { all: needs.all, names: unresolvedNames });
2367
+ }
2368
+ continue;
2369
+ }
2370
+ const wanted = exp.getNamedExports().filter(
2371
+ (n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
2372
+ ).map((n) => n.getNameNode().getText());
2373
+ if (wanted.length === 0) blank(exp);
2374
+ else if (target) this.request(target, { names: wanted });
2375
+ }
2376
+ return chars.join("");
2377
+ }
2378
+ /**
2379
+ * The project source file a module specifier resolves to, or undefined for a
2380
+ * package (read unpruned by the program itself) or an unresolvable one.
2381
+ */
2382
+ resolve(specifier, from) {
2383
+ const resolved = import_ts_morph7.ts.resolveModuleName(
2384
+ specifier,
2385
+ from.getFilePath(),
2386
+ this.options,
2387
+ import_ts_morph7.ts.sys
2388
+ ).resolvedModule;
2389
+ if (!resolved || resolved.isExternalLibraryImport) return void 0;
2390
+ const file = resolved.resolvedFileName;
2391
+ if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
2392
+ return file;
2393
+ }
2394
+ };
2395
+ function schemaPositions(sf) {
2396
+ const roots = [];
2397
+ sf.forEachDescendant((node) => {
2398
+ if (import_ts_morph7.Node.isDecorator(node)) {
2399
+ const name = node.getName();
2400
+ if (name === "ApplyContract") {
2401
+ roots.push(...node.getArguments());
2402
+ } else if (PARAM_DECORATORS.has(name)) {
2403
+ for (const arg of node.getArguments()) {
2404
+ if (import_ts_morph7.Node.isNewExpression(arg) || import_ts_morph7.Node.isCallExpression(arg)) {
2405
+ roots.push(...arg.getArguments());
2406
+ }
2407
+ }
2408
+ }
2409
+ } else if (import_ts_morph7.Node.isCallExpression(node)) {
2410
+ const callee = node.getExpression();
2411
+ const name = import_ts_morph7.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph7.Node.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
2412
+ if (name === "defineContract") roots.push(...node.getArguments());
2413
+ }
2414
+ });
2415
+ return roots;
2416
+ }
2417
+ function topLevelDeclarations(sf) {
2418
+ const byName = /* @__PURE__ */ new Map();
2419
+ const add = (name, node) => {
2420
+ if (!name) return;
2421
+ const list = byName.get(name);
2422
+ if (list) list.push(node);
2423
+ else byName.set(name, [node]);
2424
+ };
2425
+ for (const decl of sf.getVariableDeclarations()) add(decl.getName(), decl);
2426
+ for (const decl of sf.getFunctions()) add(decl.getName(), decl);
2427
+ for (const decl of sf.getClasses()) add(decl.getName(), decl);
2428
+ for (const decl of sf.getEnums()) add(decl.getName(), decl);
2429
+ for (const decl of sf.getInterfaces()) add(decl.getName(), decl);
2430
+ for (const decl of sf.getTypeAliases()) add(decl.getName(), decl);
2431
+ for (const decl of sf.getModules()) add(decl.getName(), decl);
2432
+ return byName;
2433
+ }
2434
+ function defaultExport(sf) {
2435
+ const nodes = [];
2436
+ for (const assignment of sf.getExportAssignments()) nodes.push(assignment);
2437
+ for (const fn of sf.getFunctions()) if (fn.isDefaultExport()) nodes.push(fn);
2438
+ for (const cls of sf.getClasses()) if (cls.isDefaultExport()) nodes.push(cls);
2439
+ return nodes;
2440
+ }
2441
+
2442
+ // src/discovery/schema-type-inference.ts
2443
+ var programs = /* @__PURE__ */ new WeakMap();
2444
+ function programFor(discovery) {
2445
+ let program = programs.get(discovery);
2446
+ if (!program) {
2447
+ program = new SchemaTypeProgram(discovery);
2448
+ programs.set(discovery, program);
2449
+ }
2450
+ return program;
2451
+ }
2452
+ function clearSchemaTypeProject(discovery) {
2453
+ programs.delete(discovery);
2454
+ }
2455
+ function counterpart(typedSf, node) {
2456
+ const start = node.getStart();
2457
+ const end = node.getEnd();
2458
+ const kind = node.getKind();
2459
+ let candidate = typedSf.getDescendantAtPos(start);
2460
+ while (candidate) {
2461
+ if (candidate.getStart() === start && candidate.getEnd() === end && candidate.getKind() === kind) {
2462
+ return candidate;
2463
+ }
2464
+ if (candidate.getStart() < start || candidate.getEnd() > end) return void 0;
2465
+ candidate = candidate.getParent();
2466
+ }
2467
+ return void 0;
2468
+ }
2469
+ function propType(type, name, at) {
2470
+ return type.getProperty(name)?.getTypeAtLocation(at);
2471
+ }
2472
+ function isAnyOrUnknown(type) {
2473
+ return type.isAny() || type.isUnknown();
2474
+ }
2475
+ function inferSchema(expr, discovery) {
2476
+ try {
2477
+ const typedSf = programFor(discovery).sourceFileFor(expr.getSourceFile());
2478
+ const at = typedSf && counterpart(typedSf, expr);
2479
+ if (!at) return { kind: "unresolved" };
2480
+ const schemaType = at.getType();
2481
+ if (isAnyOrUnknown(schemaType)) return { kind: "unresolved" };
2482
+ const standard = propType(schemaType, "~standard", at);
2483
+ const stdTypes = standard ? propType(standard, "types", at)?.getNonNullableType() : void 0;
2484
+ const stdInput = stdTypes ? propType(stdTypes, "input", at) : void 0;
2485
+ const stdOutput = stdTypes ? propType(stdTypes, "output", at) : void 0;
2486
+ if (stdInput && stdOutput) {
2487
+ return { kind: "schema", types: { input: stdInput, output: stdOutput, at } };
2488
+ }
2489
+ const zodInput = propType(schemaType, "_input", at);
2490
+ const zodOutput = propType(schemaType, "_output", at);
2491
+ if (zodInput && zodOutput) {
2492
+ return { kind: "schema", types: { input: zodInput, output: zodOutput, at } };
2493
+ }
2494
+ return { kind: "not-schema" };
2495
+ } catch (err) {
2496
+ dbg("schema inference failed:", err);
2497
+ return { kind: "unresolved" };
2498
+ }
2499
+ }
2500
+ var GLOBAL_NAMED_TYPES = /* @__PURE__ */ new Set([
2501
+ "Date",
2502
+ "File",
2503
+ "Blob",
2504
+ "FormData",
2505
+ "URL",
2506
+ "RegExp",
2507
+ "Map",
2508
+ "Set",
2509
+ "ArrayBuffer",
2510
+ "Uint8Array"
2511
+ ]);
2512
+ var MAX_DEPTH = 12;
2513
+ function objectKey(name) {
2514
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
2515
+ }
2516
+ function isOptionalSymbol(sym) {
2517
+ return (sym.getFlags() & import_ts_morph8.ts.SymbolFlags.Optional) !== 0;
2518
+ }
2519
+ function isSymbolKeyed(sym) {
2520
+ return sym.getName().startsWith("__@");
2521
+ }
2522
+ function orderedProperties(type) {
2523
+ const position = (sym) => {
2524
+ const decl = sym.getDeclarations()[0];
2525
+ return decl ? [decl.getSourceFile().getFilePath(), decl.getStart()] : void 0;
2526
+ };
2527
+ return type.getProperties().map((sym, index) => ({ sym, index, at: position(sym) })).sort((a, b) => {
2528
+ if (a.at && b.at) {
2529
+ if (a.at[0] !== b.at[0]) return a.at[0] < b.at[0] ? -1 : 1;
2530
+ if (a.at[1] !== b.at[1]) return a.at[1] - b.at[1];
2531
+ } else if (a.at || b.at) {
2532
+ return a.at ? -1 : 1;
2533
+ }
2534
+ return a.index - b.index;
2535
+ }).map((entry) => entry.sym);
2536
+ }
2537
+ function isFromDeclarationFile(sym) {
2538
+ const decl = sym?.getDeclarations()[0];
2539
+ return !!decl && decl.getSourceFile().isDeclarationFile();
2540
+ }
2541
+ function render(type, state, paired) {
2542
+ if (state.depth > MAX_DEPTH) return "unknown";
2543
+ if (isAnyOrUnknown(type)) {
2544
+ return paired && !isAnyOrUnknown(paired) ? render(paired, state) : "unknown";
2545
+ }
2546
+ if (type.isNever()) return "never";
2547
+ if (type.isString() || type.isTemplateLiteral()) return "string";
2548
+ if (type.isNumber()) return "number";
2549
+ if (type.isBoolean()) return "boolean";
2550
+ if (type.isBigInt()) return "bigint";
2551
+ if (type.isNull()) return "null";
2552
+ if (type.isUndefined() || type.isVoid()) return "undefined";
2553
+ if (type.isBooleanLiteral()) return type.getText();
2554
+ if (type.isStringLiteral() || type.isNumberLiteral() || type.isEnumLiteral()) {
2555
+ const value = type.getLiteralValue();
2556
+ return typeof value === "string" ? JSON.stringify(value) : String(value);
2557
+ }
2558
+ if (type.isBigIntLiteral()) return type.getText();
2559
+ if (type.isTypeParameter()) return "unknown";
2560
+ if (type.isUnion()) return renderUnion(type, state, paired);
2561
+ if (type.isIntersection()) return renderIntersection(type, state);
2562
+ const next = { ...state, depth: state.depth + 1 };
2563
+ if (type.isArray() || type.isReadonlyArray()) {
2564
+ const element = type.getArrayElementType() ?? type.getTypeArguments()[0];
2565
+ const pairedElement = paired?.getArrayElementType() ?? paired?.getTypeArguments()[0];
2566
+ return element ? `Array<${render(element, next, pairedElement)}>` : "Array<unknown>";
2567
+ }
2568
+ if (type.isTuple()) {
2569
+ const elements = type.getTupleElements();
2570
+ const pairedElements = paired?.isTuple() ? paired.getTupleElements() : [];
2571
+ return `[${elements.map((e, i) => render(e, next, pairedElements[i])).join(", ")}]`;
2572
+ }
2573
+ if (type.isObject()) return renderObject(type, next, paired);
2574
+ return "unknown";
2575
+ }
2576
+ function renderUnion(type, state, paired) {
2577
+ const members = type.getUnionTypes();
2578
+ const hasTrue = members.some((m) => m.isBooleanLiteral() && m.getText() === "true");
2579
+ const hasFalse = members.some((m) => m.isBooleanLiteral() && m.getText() === "false");
2580
+ const pairedNonNull = paired?.getNonNullableType();
2581
+ const arms = [];
2582
+ let sawBoolean = false;
2583
+ let hasNull = false;
2584
+ let hasUndefined = false;
2585
+ for (const member of members) {
2586
+ if (member.isNull()) {
2587
+ hasNull = true;
2588
+ continue;
2589
+ }
2590
+ if (member.isUndefined()) {
2591
+ hasUndefined = true;
2592
+ continue;
2593
+ }
2594
+ if (member.isBooleanLiteral() && hasTrue && hasFalse) {
2595
+ if (!sawBoolean) arms.push("boolean");
2596
+ sawBoolean = true;
2597
+ continue;
2598
+ }
2599
+ arms.push(render(member, state, members.length === 1 ? paired : pairedNonNull));
2600
+ }
2601
+ if (hasNull) arms.push("null");
2602
+ if (hasUndefined) arms.push("undefined");
2603
+ const unique = [...new Set(arms)];
2604
+ if (unique.includes("unknown")) return "unknown";
2605
+ return unique.join(" | ");
2606
+ }
2607
+ function renderIntersection(type, state) {
2608
+ const members = type.getIntersectionTypes();
2609
+ const primitive = members.find((m) => !m.isObject());
2610
+ if (primitive) return render(primitive, state);
2611
+ return renderObject(type, { ...state, depth: state.depth + 1 }, void 0);
2612
+ }
2613
+ function renderObject(type, state, paired) {
2614
+ const symbol = type.getSymbol() ?? type.getAliasSymbol();
2615
+ const name = symbol?.getName();
2616
+ if (name && GLOBAL_NAMED_TYPES.has(name) && isFromDeclarationFile(symbol)) {
2617
+ const args = type.getTypeArguments();
2618
+ return args.length ? `${name}<${args.map((a) => render(a, state)).join(", ")}>` : name;
2619
+ }
2620
+ if (type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0) {
2621
+ return "unknown";
2622
+ }
2623
+ if (type.isClassOrInterface() && isFromDeclarationFile(symbol)) return "unknown";
2624
+ if (state.stack.has(type.compilerType)) return "unknown";
2625
+ const stack = new Set(state.stack).add(type.compilerType);
2626
+ const inner = { ...state, stack };
2627
+ const fields = [];
2628
+ for (const prop of orderedProperties(type)) {
2629
+ if (isSymbolKeyed(prop)) continue;
2630
+ const propName = prop.getName();
2631
+ const propT = prop.getTypeAtLocation(state.at);
2632
+ if (propT.getCallSignatures().length > 0 && !propT.isUnion()) continue;
2633
+ const pairedProp = paired?.getProperty(propName)?.getTypeAtLocation(state.at);
2634
+ const optional = isOptionalSymbol(prop) ? "?" : "";
2635
+ fields.push(`${objectKey(propName)}${optional}: ${render(propT, inner, pairedProp)}`);
2636
+ }
2637
+ const stringIndex = type.getStringIndexType();
2638
+ if (stringIndex) {
2639
+ const pairedIndex = paired?.getStringIndexType();
2640
+ const value = render(stringIndex, inner, pairedIndex);
2641
+ if (fields.length === 0) return `Record<string, ${value}>`;
2642
+ fields.push(`[key: string]: ${value}`);
2643
+ }
2644
+ return fields.length ? `{ ${fields.join("; ")} }` : "{}";
2645
+ }
2646
+ function stateAt(at) {
2647
+ return { at, stack: /* @__PURE__ */ new Set(), depth: 0 };
2648
+ }
2649
+ function requestTypeOf(types) {
2650
+ return render(types.input, stateAt(types.at), types.output);
2651
+ }
2652
+ function responseTypeOf(types) {
2653
+ return render(types.output, stateAt(types.at));
2654
+ }
2655
+ function requestFieldsOf(types) {
2656
+ const { input, output, at } = types;
2657
+ const objectInput = input.getNonNullableType();
2658
+ if (!objectInput.isObject() || objectInput.isArray() || objectInput.isTuple()) return null;
2659
+ const state = stateAt(at);
2660
+ return orderedProperties(objectInput).filter((prop) => !isSymbolKeyed(prop)).map((prop) => ({
2661
+ name: prop.getName(),
2662
+ type: render(
2663
+ prop.getTypeAtLocation(at),
2664
+ state,
2665
+ output.getProperty(prop.getName())?.getTypeAtLocation(at)
2666
+ ),
2667
+ optional: isOptionalSymbol(prop)
2668
+ }));
2669
+ }
2670
+ function acceptsUndefined(types) {
2671
+ const { input, output } = types;
2672
+ const accepted = isAnyOrUnknown(input) ? output : input;
2673
+ if (isAnyOrUnknown(accepted) || accepted.isUndefined()) return true;
2674
+ return accepted.isUnion() && accepted.getUnionTypes().some((t) => t.isUndefined());
2675
+ }
2676
+
2677
+ // src/discovery/zod-ast-to-ts.ts
2678
+ var import_ts_morph9 = require("ts-morph");
2679
+ function zodAstToTs(node) {
2680
+ if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
2681
+ const expr = node.getExpression();
2682
+ if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
2683
+ const methodName = expr.getName();
2684
+ const receiver = expr.getExpression();
2685
+ if (methodName === "optional") {
2686
+ return `${zodAstToTs(receiver)} | undefined`;
2687
+ }
2688
+ if (methodName === "nullable") {
2689
+ return `${zodAstToTs(receiver)} | null`;
2690
+ }
2691
+ const args = node.getArguments();
2692
+ switch (methodName) {
2693
+ case "string":
2694
+ return "string";
2695
+ case "number":
2696
+ return "number";
2697
+ case "boolean":
2698
+ return "boolean";
2699
+ case "unknown":
2700
+ return "unknown";
2701
+ case "any":
2702
+ return "unknown";
2703
+ case "literal": {
2704
+ const lit = args[0];
2705
+ if (!lit) return "unknown";
2706
+ if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
2707
+ if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
2708
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
2709
+ if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
2710
+ return "unknown";
2711
+ }
2712
+ case "enum": {
2713
+ const arrArg = args[0];
2714
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2715
+ const members = arrArg.getElements().map(
2716
+ (el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
2717
+ );
2718
+ return members.join(" | ");
2719
+ }
2720
+ case "array": {
2721
+ const inner = args[0];
2722
+ if (!inner) return "unknown";
2723
+ return `Array<${zodAstToTs(inner)}>`;
2724
+ }
2725
+ case "object": {
2726
+ const objArg = args[0];
2727
+ if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
2728
+ const lines = [];
2729
+ for (const prop of objArg.getProperties()) {
2730
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
2731
+ const key = prop.getName();
2732
+ const valNode = prop.getInitializer();
2733
+ if (!valNode) continue;
2734
+ const tsType = zodAstToTs(valNode);
2735
+ const isOpt = isOptionalChain(valNode);
2736
+ lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
2737
+ }
2738
+ return `{ ${lines.join("; ")} }`;
2739
+ }
2740
+ case "union": {
2741
+ const arrArg = args[0];
2742
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2743
+ return arrArg.getElements().map(zodAstToTs).join(" | ");
2744
+ }
2745
+ case "record": {
2746
+ const valArg = args.length === 1 ? args[0] : args[1];
2747
+ if (!valArg) return "unknown";
2748
+ return `Record<string, ${zodAstToTs(valArg)}>`;
2749
+ }
2750
+ case "tuple": {
2751
+ const arrArg = args[0];
2752
+ if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2753
+ return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
2754
+ }
2755
+ default:
2756
+ return "unknown";
2757
+ }
2758
+ }
2759
+ return "unknown";
2760
+ }
2761
+ function isOptionalChain(node) {
2762
+ if (!import_ts_morph9.Node.isCallExpression(node)) return false;
2763
+ const expr = node.getExpression();
2764
+ return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
2765
+ }
2766
+ function memberType(val, position, project) {
2767
+ if (project) {
2768
+ const inferred = inferSchema(val, project);
2769
+ if (inferred.kind === "schema") {
2770
+ return position === "request" ? requestTypeOf(inferred.types) : responseTypeOf(inferred.types);
2771
+ }
2772
+ }
2773
+ return zodAstToTs(val);
2774
+ }
2775
+ function parseDefineContractCall(callExpr, project) {
2776
+ if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
2777
+ const callee = callExpr.getExpression();
2778
+ const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
2779
+ if (calleeName !== "defineContract") return null;
2780
+ const args = callExpr.getArguments();
2781
+ const optsArg = args[0];
2782
+ if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
2783
+ let query = null;
2784
+ let body = null;
2785
+ let response = "unknown";
2786
+ let error = null;
2787
+ let bodyZodText = null;
2788
+ let queryZodText = null;
2789
+ for (const prop of optsArg.getProperties()) {
2790
+ if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
2791
+ const propName = prop.getName();
2792
+ const val = prop.getInitializer();
2793
+ if (!val) continue;
2794
+ if (propName === "query") {
2795
+ query = memberType(val, "request", project);
2796
+ queryZodText = val.getText();
2797
+ } else if (propName === "body") {
2798
+ body = memberType(val, "request", project);
2799
+ bodyZodText = val.getText();
2800
+ } else if (propName === "response") {
2801
+ response = memberType(val, "response", project);
2802
+ } else if (propName === "error") {
2803
+ error = memberType(val, "response", project);
2804
+ }
2805
+ }
2806
+ return { query, body, response, error, bodyZodText, queryZodText };
2807
+ }
2808
+
2809
+ // src/discovery/pipe-schema.ts
2810
+ function pipeSchemaOf(decorator, project) {
2811
+ for (const pipe of decorator.getArguments()) {
2812
+ if (!import_ts_morph10.Node.isNewExpression(pipe) && !import_ts_morph10.Node.isCallExpression(pipe)) continue;
2813
+ for (const arg of pipe.getArguments()) {
2814
+ if (!couldBeSchema(arg)) continue;
2815
+ const inferred = inferSchema(arg, project);
2816
+ if (inferred.kind === "schema") {
2817
+ return {
2818
+ type: requestTypeOf(inferred.types),
2819
+ optional: acceptsUndefined(inferred.types),
2820
+ fields: requestFieldsOf(inferred.types)
2821
+ };
2822
+ }
2823
+ if (inferred.kind === "unresolved") {
2824
+ const fallback = syntacticZodType(arg, project);
2825
+ if (fallback) {
2826
+ return { type: fallback, optional: fallback.endsWith("| undefined"), fields: null };
2827
+ }
2828
+ }
2829
+ }
2830
+ }
2831
+ return null;
2832
+ }
2833
+ function couldBeSchema(arg) {
2834
+ return import_ts_morph10.Node.isIdentifier(arg) || import_ts_morph10.Node.isPropertyAccessExpression(arg) || import_ts_morph10.Node.isCallExpression(arg) || import_ts_morph10.Node.isAsExpression(arg) || import_ts_morph10.Node.isSatisfiesExpression(arg) || import_ts_morph10.Node.isParenthesizedExpression(arg);
2835
+ }
2836
+ function syntacticZodType(arg, project) {
2837
+ let expr = arg;
2838
+ if (import_ts_morph10.Node.isIdentifier(arg)) {
2839
+ const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
2840
+ expr = resolved?.decl.getInitializer();
2841
+ }
2842
+ if (!expr || !import_ts_morph10.Node.isCallExpression(expr)) return null;
2843
+ const type = zodAstToTs(expr);
2844
+ return type === "unknown" ? null : type;
2845
+ }
2846
+
2092
2847
  // src/discovery/dto-type-resolver.ts
2093
2848
  var WRAPPER_TYPES = {
2094
2849
  // MikroORM Ref/Reference/LoadedReference/IdentifiedReference are server-side
@@ -2120,22 +2875,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
2120
2875
  ]);
2121
2876
  function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
2122
2877
  if (depth <= 0) return "unknown";
2123
- if (import_ts_morph7.Node.isArrayTypeNode(typeNode)) {
2878
+ if (import_ts_morph11.Node.isArrayTypeNode(typeNode)) {
2124
2879
  const elementType = typeNode.getElementTypeNode();
2125
2880
  return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
2126
2881
  }
2127
- if (import_ts_morph7.Node.isUnionTypeNode(typeNode)) {
2882
+ if (import_ts_morph11.Node.isUnionTypeNode(typeNode)) {
2128
2883
  return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
2129
2884
  }
2130
- if (import_ts_morph7.Node.isIntersectionTypeNode(typeNode)) {
2885
+ if (import_ts_morph11.Node.isIntersectionTypeNode(typeNode)) {
2131
2886
  return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
2132
2887
  }
2133
- if (import_ts_morph7.Node.isParenthesizedTypeNode(typeNode)) {
2888
+ if (import_ts_morph11.Node.isParenthesizedTypeNode(typeNode)) {
2134
2889
  return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
2135
2890
  }
2136
- if (import_ts_morph7.Node.isTypeReference(typeNode)) {
2891
+ if (import_ts_morph11.Node.isTypeReference(typeNode)) {
2137
2892
  const typeName = typeNode.getTypeName();
2138
- const name = import_ts_morph7.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
2893
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
2139
2894
  const bound = subst.get(name);
2140
2895
  if (bound !== void 0) return bound;
2141
2896
  if (name === "string" || name === "number" || name === "boolean") return name;
@@ -2158,13 +2913,13 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
2158
2913
  dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
2159
2914
  return "unknown";
2160
2915
  }
2161
- if (import_ts_morph7.Node.isTypeLiteral(typeNode)) {
2916
+ if (import_ts_morph11.Node.isTypeLiteral(typeNode)) {
2162
2917
  const members = [];
2163
2918
  for (const member of typeNode.getMembers()) {
2164
- if (import_ts_morph7.Node.isPropertySignature(member)) {
2919
+ if (import_ts_morph11.Node.isPropertySignature(member)) {
2165
2920
  const memberTypeNode = member.getTypeNode();
2166
- const memberType = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
2167
- members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType}`);
2921
+ const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
2922
+ members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
2168
2923
  } else {
2169
2924
  members.push(member.getText());
2170
2925
  }
@@ -2172,11 +2927,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
2172
2927
  return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
2173
2928
  }
2174
2929
  const kind = typeNode.getKind();
2175
- if (kind === import_ts_morph7.SyntaxKind.StringKeyword) return "string";
2176
- if (kind === import_ts_morph7.SyntaxKind.NumberKeyword) return "number";
2177
- if (kind === import_ts_morph7.SyntaxKind.BooleanKeyword) return "boolean";
2178
- if (kind === import_ts_morph7.SyntaxKind.UnknownKeyword) return "unknown";
2179
- if (kind === import_ts_morph7.SyntaxKind.AnyKeyword) return "unknown";
2930
+ if (kind === import_ts_morph11.SyntaxKind.StringKeyword) return "string";
2931
+ if (kind === import_ts_morph11.SyntaxKind.NumberKeyword) return "number";
2932
+ if (kind === import_ts_morph11.SyntaxKind.BooleanKeyword) return "boolean";
2933
+ if (kind === import_ts_morph11.SyntaxKind.UnknownKeyword) return "unknown";
2934
+ if (kind === import_ts_morph11.SyntaxKind.AnyKeyword) return "unknown";
2180
2935
  return typeNode.getText();
2181
2936
  }
2182
2937
  function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
@@ -2224,20 +2979,24 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
2224
2979
  const propName = prop.getName();
2225
2980
  const isOptional = prop.hasQuestionToken();
2226
2981
  const propTypeNode = prop.getTypeNode();
2227
- let propType = "unknown";
2982
+ let propType2 = "unknown";
2228
2983
  if (propTypeNode) {
2229
- propType = resolveTypeNodeToString(propTypeNode, sourceFile, project, depth, subst);
2984
+ propType2 = resolveTypeNodeToString(propTypeNode, sourceFile, project, depth, subst);
2230
2985
  }
2231
- lines.push(`${propName}${isOptional ? "?" : ""}: ${propType}`);
2986
+ lines.push(`${propName}${isOptional ? "?" : ""}: ${propType2}`);
2232
2987
  }
2233
2988
  return `{ ${lines.join("; ")} }`;
2234
2989
  }
2990
+ function namedField(decorator) {
2991
+ const first = decorator.getArguments()[0];
2992
+ return first && import_ts_morph11.Node.isStringLiteral(first) ? first.getLiteralValue() : null;
2993
+ }
2235
2994
  function extractBodyType(method, sourceFile, project) {
2236
2995
  for (const param of method.getParameters()) {
2237
2996
  const bodyDecorator = param.getDecorators().find((d) => d.getName() === "Body");
2238
- if (!bodyDecorator) continue;
2239
- const bodyArgs = bodyDecorator.getArguments();
2240
- if (bodyArgs.length > 0) continue;
2997
+ if (!bodyDecorator || namedField(bodyDecorator) !== null) continue;
2998
+ const piped = pipeSchemaOf(bodyDecorator, project);
2999
+ if (piped) return piped.type;
2241
3000
  const typeNode = param.getTypeNode();
2242
3001
  if (typeNode) {
2243
3002
  return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
@@ -2245,11 +3004,16 @@ function extractBodyType(method, sourceFile, project) {
2245
3004
  }
2246
3005
  return null;
2247
3006
  }
3007
+ function hasPipeSchema(param, decoratorName, project) {
3008
+ const decorator = param.getDecorators().find((d) => d.getName() === decoratorName);
3009
+ return !!decorator && pipeSchemaOf(decorator, project) !== null;
3010
+ }
2248
3011
  function extractQueryType(method, sourceFile, project) {
2249
3012
  for (const param of method.getParameters()) {
2250
3013
  const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
2251
- if (!queryDecorator) continue;
2252
- if (queryDecorator.getArguments().length > 0) continue;
3014
+ if (!queryDecorator || namedField(queryDecorator) !== null) continue;
3015
+ const piped = pipeSchemaOf(queryDecorator, project);
3016
+ if (piped) return piped.type;
2253
3017
  const typeNode = param.getTypeNode();
2254
3018
  if (typeNode) {
2255
3019
  return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
@@ -2259,10 +3023,13 @@ function extractQueryType(method, sourceFile, project) {
2259
3023
  for (const param of method.getParameters()) {
2260
3024
  const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
2261
3025
  if (!queryDecorator) continue;
2262
- const queryArgs = queryDecorator.getArguments();
2263
- const nameArg = queryArgs[0];
2264
- if (!nameArg || !import_ts_morph7.Node.isStringLiteral(nameArg)) continue;
2265
- const queryName = nameArg.getLiteralValue();
3026
+ const queryName = namedField(queryDecorator);
3027
+ if (queryName === null) continue;
3028
+ const piped = pipeSchemaOf(queryDecorator, project);
3029
+ if (piped) {
3030
+ entries.push(`${queryName}${piped.optional ? "?" : ""}: ${piped.type}`);
3031
+ continue;
3032
+ }
2266
3033
  const typeNode = param.getTypeNode();
2267
3034
  const queryType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
2268
3035
  entries.push(`${queryName}${isParamOptional(param) ? "?" : ""}: ${queryType}`);
@@ -2272,8 +3039,8 @@ function extractQueryType(method, sourceFile, project) {
2272
3039
  function isParamOptional(param) {
2273
3040
  if (param.hasQuestionToken() || param.hasInitializer()) return true;
2274
3041
  const typeNode = param.getTypeNode();
2275
- if (typeNode && import_ts_morph7.Node.isUnionTypeNode(typeNode)) {
2276
- return typeNode.getTypeNodes().some((t) => t.getKind() === import_ts_morph7.SyntaxKind.UndefinedKeyword);
3042
+ if (typeNode && import_ts_morph11.Node.isUnionTypeNode(typeNode)) {
3043
+ return typeNode.getTypeNodes().some((t) => t.getKind() === import_ts_morph11.SyntaxKind.UndefinedKeyword);
2277
3044
  }
2278
3045
  return false;
2279
3046
  }
@@ -2285,7 +3052,7 @@ function extractParamsType(method, sourceFile, project) {
2285
3052
  const paramArgs = paramDecorator.getArguments();
2286
3053
  if (paramArgs.length === 0) continue;
2287
3054
  const nameArg = paramArgs[0];
2288
- if (!import_ts_morph7.Node.isStringLiteral(nameArg)) continue;
3055
+ if (!import_ts_morph11.Node.isStringLiteral(nameArg)) continue;
2289
3056
  const paramName = nameArg.getLiteralValue();
2290
3057
  const typeNode = param.getTypeNode();
2291
3058
  const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
@@ -2293,6 +3060,22 @@ function extractParamsType(method, sourceFile, project) {
2293
3060
  }
2294
3061
  return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
2295
3062
  }
3063
+ function extractPipedParamTypes(method, project) {
3064
+ const types = {};
3065
+ for (const param of method.getParameters()) {
3066
+ const paramDecorator = param.getDecorators().find((d) => d.getName() === "Param");
3067
+ if (!paramDecorator) continue;
3068
+ const piped = pipeSchemaOf(paramDecorator, project);
3069
+ if (!piped) continue;
3070
+ const name = namedField(paramDecorator);
3071
+ if (name !== null) {
3072
+ types[name] = piped.type;
3073
+ } else {
3074
+ for (const field of piped.fields ?? []) types[field.name] = field.type;
3075
+ }
3076
+ }
3077
+ return Object.keys(types).length > 0 ? types : null;
3078
+ }
2296
3079
  function extractUploadedFiles(method) {
2297
3080
  const FILE = "File | Blob";
2298
3081
  const entries = [];
@@ -2306,28 +3089,28 @@ function extractUploadedFiles(method) {
2306
3089
  for (const decorator of method.getDecorators()) {
2307
3090
  if (decorator.getName() !== "UseInterceptors") continue;
2308
3091
  for (const arg of decorator.getArguments()) {
2309
- if (!import_ts_morph7.Node.isCallExpression(arg)) continue;
3092
+ if (!import_ts_morph11.Node.isCallExpression(arg)) continue;
2310
3093
  const interceptor = arg.getExpression().getText();
2311
3094
  const callArgs = arg.getArguments();
2312
3095
  const firstArg2 = callArgs[0];
2313
3096
  if (interceptor === "FileInterceptor") {
2314
- if (firstArg2 && import_ts_morph7.Node.isStringLiteral(firstArg2)) {
3097
+ if (firstArg2 && import_ts_morph11.Node.isStringLiteral(firstArg2)) {
2315
3098
  entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
2316
3099
  multipart = true;
2317
3100
  }
2318
3101
  } else if (interceptor === "FilesInterceptor") {
2319
- if (firstArg2 && import_ts_morph7.Node.isStringLiteral(firstArg2)) {
3102
+ if (firstArg2 && import_ts_morph11.Node.isStringLiteral(firstArg2)) {
2320
3103
  entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
2321
3104
  multipart = true;
2322
3105
  }
2323
3106
  } else if (interceptor === "FileFieldsInterceptor") {
2324
- if (firstArg2 && import_ts_morph7.Node.isArrayLiteralExpression(firstArg2)) {
3107
+ if (firstArg2 && import_ts_morph11.Node.isArrayLiteralExpression(firstArg2)) {
2325
3108
  for (const el of firstArg2.getElements()) {
2326
- if (!import_ts_morph7.Node.isObjectLiteralExpression(el)) continue;
3109
+ if (!import_ts_morph11.Node.isObjectLiteralExpression(el)) continue;
2327
3110
  const nameProp = el.getProperty("name");
2328
- if (nameProp && import_ts_morph7.Node.isPropertyAssignment(nameProp)) {
3111
+ if (nameProp && import_ts_morph11.Node.isPropertyAssignment(nameProp)) {
2329
3112
  const init = nameProp.getInitializer();
2330
- if (init && import_ts_morph7.Node.isStringLiteral(init)) {
3113
+ if (init && import_ts_morph11.Node.isStringLiteral(init)) {
2331
3114
  entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
2332
3115
  }
2333
3116
  }
@@ -2347,13 +3130,13 @@ function extractResponseType(method, sourceFile, project) {
2347
3130
  if (apiResponseDecorator) {
2348
3131
  const args = apiResponseDecorator.getArguments();
2349
3132
  const optsArg = args[0];
2350
- if (optsArg && import_ts_morph7.Node.isObjectLiteralExpression(optsArg)) {
3133
+ if (optsArg && import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) {
2351
3134
  for (const prop of optsArg.getProperties()) {
2352
- if (!import_ts_morph7.Node.isPropertyAssignment(prop)) continue;
3135
+ if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
2353
3136
  if (prop.getName() !== "type") continue;
2354
3137
  const val = prop.getInitializer();
2355
3138
  if (!val) continue;
2356
- if (import_ts_morph7.Node.isArrayLiteralExpression(val)) {
3139
+ if (import_ts_morph11.Node.isArrayLiteralExpression(val)) {
2357
3140
  const elements = val.getElements();
2358
3141
  const firstEl = elements[0];
2359
3142
  if (elements.length > 0 && firstEl !== void 0) {
@@ -2374,24 +3157,24 @@ function extractResponseType(method, sourceFile, project) {
2374
3157
  }
2375
3158
  function apiResponseStatus(decorator) {
2376
3159
  const optsArg = decorator.getArguments()[0];
2377
- if (!optsArg || !import_ts_morph7.Node.isObjectLiteralExpression(optsArg)) return null;
3160
+ if (!optsArg || !import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) return null;
2378
3161
  for (const prop of optsArg.getProperties()) {
2379
- if (!import_ts_morph7.Node.isPropertyAssignment(prop)) continue;
3162
+ if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
2380
3163
  if (prop.getName() !== "status") continue;
2381
3164
  const val = prop.getInitializer();
2382
- if (val && import_ts_morph7.Node.isNumericLiteral(val)) return Number(val.getLiteralValue());
3165
+ if (val && import_ts_morph11.Node.isNumericLiteral(val)) return Number(val.getLiteralValue());
2383
3166
  }
2384
3167
  return null;
2385
3168
  }
2386
3169
  function apiResponseTypeNode(decorator) {
2387
3170
  const optsArg = decorator.getArguments()[0];
2388
- if (!optsArg || !import_ts_morph7.Node.isObjectLiteralExpression(optsArg)) return null;
3171
+ if (!optsArg || !import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) return null;
2389
3172
  for (const prop of optsArg.getProperties()) {
2390
- if (!import_ts_morph7.Node.isPropertyAssignment(prop)) continue;
3173
+ if (!import_ts_morph11.Node.isPropertyAssignment(prop)) continue;
2391
3174
  if (prop.getName() !== "type") continue;
2392
3175
  const val = prop.getInitializer();
2393
3176
  if (!val) return null;
2394
- if (import_ts_morph7.Node.isArrayLiteralExpression(val)) {
3177
+ if (import_ts_morph11.Node.isArrayLiteralExpression(val)) {
2395
3178
  const first = val.getElements()[0];
2396
3179
  return first ? { node: first, isArray: true } : null;
2397
3180
  }
@@ -2409,7 +3192,7 @@ function extractErrorType(method, sourceFile, project) {
2409
3192
  const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
2410
3193
  const type = typeInfo.isArray ? `Array<${inner}>` : inner;
2411
3194
  let ref = null;
2412
- if (import_ts_morph7.Node.isIdentifier(typeInfo.node)) {
3195
+ if (import_ts_morph11.Node.isIdentifier(typeInfo.node)) {
2413
3196
  const name = typeInfo.node.getText();
2414
3197
  const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
2415
3198
  if (localDecl?.isExported()) {
@@ -2426,7 +3209,7 @@ function extractErrorType(method, sourceFile, project) {
2426
3209
  return null;
2427
3210
  }
2428
3211
  function resolveIdentifierToClassType(node, sourceFile, project, depth) {
2429
- if (!import_ts_morph7.Node.isIdentifier(node)) return "unknown";
3212
+ if (!import_ts_morph11.Node.isIdentifier(node)) return "unknown";
2430
3213
  const name = node.getText();
2431
3214
  const resolved = findType(name, sourceFile, project);
2432
3215
  if (resolved) {
@@ -2446,9 +3229,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
2446
3229
  var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
2447
3230
  function detectBinaryResponse(method) {
2448
3231
  const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
2449
- if (!node || !import_ts_morph7.Node.isTypeReference(node)) return false;
3232
+ if (!node || !import_ts_morph11.Node.isTypeReference(node)) return false;
2450
3233
  const typeName = node.getTypeName();
2451
- const name = import_ts_morph7.Node.isIdentifier(typeName) ? typeName.getText() : "";
3234
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
2452
3235
  return BINARY_RESPONSE_TYPES.has(name);
2453
3236
  }
2454
3237
  function detectStreamElement(method) {
@@ -2463,9 +3246,9 @@ function detectStreamElement(method) {
2463
3246
  return null;
2464
3247
  }
2465
3248
  function streamContainerElement(node) {
2466
- if (!node || !import_ts_morph7.Node.isTypeReference(node)) return null;
3249
+ if (!node || !import_ts_morph11.Node.isTypeReference(node)) return null;
2467
3250
  const typeName = node.getTypeName();
2468
- const name = import_ts_morph7.Node.isIdentifier(typeName) ? typeName.getText() : "";
3251
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
2469
3252
  if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
2470
3253
  return node.getTypeArguments()[0] ?? null;
2471
3254
  }
@@ -2475,9 +3258,9 @@ function hasAsQueryDecorator(method) {
2475
3258
  return method.getDecorators().some((d) => d.getName() === "AsQuery");
2476
3259
  }
2477
3260
  function unwrapNamedContainer(node, names) {
2478
- if (!node || !import_ts_morph7.Node.isTypeReference(node)) return node;
3261
+ if (!node || !import_ts_morph11.Node.isTypeReference(node)) return node;
2479
3262
  const typeName = node.getTypeName();
2480
- const name = import_ts_morph7.Node.isIdentifier(typeName) ? typeName.getText() : "";
3263
+ const name = import_ts_morph11.Node.isIdentifier(typeName) ? typeName.getText() : "";
2481
3264
  if (names.has(name)) {
2482
3265
  return node.getTypeArguments()[0] ?? node;
2483
3266
  }
@@ -2498,19 +3281,20 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
2498
3281
  body = body ?? bodyType;
2499
3282
  }
2500
3283
  const paramsType = extractParamsType(method, sourceFile, project);
3284
+ const paramTypes = extractPipedParamTypes(method, project);
2501
3285
  const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
2502
3286
  const errorInfo = extractErrorType(method, sourceFile, project);
2503
- if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
3287
+ if (body === null && query === null && paramsType === null && paramTypes === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
2504
3288
  return null;
2505
3289
  }
2506
3290
  let bodyRef = null;
2507
3291
  let queryRef = null;
2508
3292
  let responseRef = null;
2509
3293
  for (const param of method.getParameters()) {
2510
- if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
3294
+ if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode() && !hasPipeSchema(param, "Body", project)) {
2511
3295
  bodyRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
2512
3296
  }
2513
- if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
3297
+ if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode() && !hasPipeSchema(param, "Query", project)) {
2514
3298
  queryRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
2515
3299
  }
2516
3300
  }
@@ -2523,11 +3307,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
2523
3307
  if (apiResp) {
2524
3308
  const args = apiResp.getArguments();
2525
3309
  const optsArg = args[0];
2526
- if (optsArg && import_ts_morph7.Node.isObjectLiteralExpression(optsArg)) {
3310
+ if (optsArg && import_ts_morph11.Node.isObjectLiteralExpression(optsArg)) {
2527
3311
  for (const prop of optsArg.getProperties()) {
2528
- if (import_ts_morph7.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
3312
+ if (import_ts_morph11.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
2529
3313
  const val = prop.getInitializer();
2530
- if (val && import_ts_morph7.Node.isIdentifier(val)) {
3314
+ if (val && import_ts_morph11.Node.isIdentifier(val)) {
2531
3315
  const name = val.getText();
2532
3316
  const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
2533
3317
  if (localDecl?.isExported()) {
@@ -2563,6 +3347,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
2563
3347
  response,
2564
3348
  error: errorInfo?.type ?? null,
2565
3349
  params: paramsType,
3350
+ paramTypes,
2566
3351
  queryRef,
2567
3352
  bodyRef,
2568
3353
  responseRef,
@@ -2583,6 +3368,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
2583
3368
  function resolveParamClass(method, decoratorName, sourceFile, project) {
2584
3369
  for (const param of method.getParameters()) {
2585
3370
  if (!param.getDecorators().some((d) => d.getName() === decoratorName)) continue;
3371
+ if (hasPipeSchema(param, decoratorName, project)) continue;
2586
3372
  const typeNode = param.getTypeNode();
2587
3373
  if (!typeNode) continue;
2588
3374
  const text = typeNode.getText().replace(/\[\]$/, "");
@@ -2595,129 +3381,6 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
2595
3381
  return null;
2596
3382
  }
2597
3383
 
2598
- // src/discovery/zod-ast-to-ts.ts
2599
- var import_ts_morph8 = require("ts-morph");
2600
- function zodAstToTs(node) {
2601
- if (!import_ts_morph8.Node.isCallExpression(node)) return "unknown";
2602
- const expr = node.getExpression();
2603
- if (import_ts_morph8.Node.isPropertyAccessExpression(expr)) {
2604
- const methodName = expr.getName();
2605
- const receiver = expr.getExpression();
2606
- if (methodName === "optional") {
2607
- return `${zodAstToTs(receiver)} | undefined`;
2608
- }
2609
- if (methodName === "nullable") {
2610
- return `${zodAstToTs(receiver)} | null`;
2611
- }
2612
- const args = node.getArguments();
2613
- switch (methodName) {
2614
- case "string":
2615
- return "string";
2616
- case "number":
2617
- return "number";
2618
- case "boolean":
2619
- return "boolean";
2620
- case "unknown":
2621
- return "unknown";
2622
- case "any":
2623
- return "unknown";
2624
- case "literal": {
2625
- const lit = args[0];
2626
- if (!lit) return "unknown";
2627
- if (import_ts_morph8.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
2628
- if (import_ts_morph8.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
2629
- if (lit.getKind() === import_ts_morph8.SyntaxKind.TrueKeyword) return "true";
2630
- if (lit.getKind() === import_ts_morph8.SyntaxKind.FalseKeyword) return "false";
2631
- return "unknown";
2632
- }
2633
- case "enum": {
2634
- const arrArg = args[0];
2635
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2636
- const members = arrArg.getElements().map(
2637
- (el) => import_ts_morph8.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
2638
- );
2639
- return members.join(" | ");
2640
- }
2641
- case "array": {
2642
- const inner = args[0];
2643
- if (!inner) return "unknown";
2644
- return `Array<${zodAstToTs(inner)}>`;
2645
- }
2646
- case "object": {
2647
- const objArg = args[0];
2648
- if (!objArg || !import_ts_morph8.Node.isObjectLiteralExpression(objArg)) return "unknown";
2649
- const lines = [];
2650
- for (const prop of objArg.getProperties()) {
2651
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
2652
- const key = prop.getName();
2653
- const valNode = prop.getInitializer();
2654
- if (!valNode) continue;
2655
- const tsType = zodAstToTs(valNode);
2656
- const isOpt = isOptionalChain(valNode);
2657
- lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
2658
- }
2659
- return `{ ${lines.join("; ")} }`;
2660
- }
2661
- case "union": {
2662
- const arrArg = args[0];
2663
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2664
- return arrArg.getElements().map(zodAstToTs).join(" | ");
2665
- }
2666
- case "record": {
2667
- const valArg = args.length === 1 ? args[0] : args[1];
2668
- if (!valArg) return "unknown";
2669
- return `Record<string, ${zodAstToTs(valArg)}>`;
2670
- }
2671
- case "tuple": {
2672
- const arrArg = args[0];
2673
- if (!arrArg || !import_ts_morph8.Node.isArrayLiteralExpression(arrArg)) return "unknown";
2674
- return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
2675
- }
2676
- default:
2677
- return "unknown";
2678
- }
2679
- }
2680
- return "unknown";
2681
- }
2682
- function isOptionalChain(node) {
2683
- if (!import_ts_morph8.Node.isCallExpression(node)) return false;
2684
- const expr = node.getExpression();
2685
- return import_ts_morph8.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
2686
- }
2687
- function parseDefineContractCall(callExpr) {
2688
- if (!import_ts_morph8.Node.isCallExpression(callExpr)) return null;
2689
- const callee = callExpr.getExpression();
2690
- const calleeName = import_ts_morph8.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph8.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
2691
- if (calleeName !== "defineContract") return null;
2692
- const args = callExpr.getArguments();
2693
- const optsArg = args[0];
2694
- if (!optsArg || !import_ts_morph8.Node.isObjectLiteralExpression(optsArg)) return null;
2695
- let query = null;
2696
- let body = null;
2697
- let response = "unknown";
2698
- let error = null;
2699
- let bodyZodText = null;
2700
- let queryZodText = null;
2701
- for (const prop of optsArg.getProperties()) {
2702
- if (!import_ts_morph8.Node.isPropertyAssignment(prop)) continue;
2703
- const propName = prop.getName();
2704
- const val = prop.getInitializer();
2705
- if (!val) continue;
2706
- if (propName === "query") {
2707
- query = zodAstToTs(val);
2708
- queryZodText = val.getText();
2709
- } else if (propName === "body") {
2710
- body = zodAstToTs(val);
2711
- bodyZodText = val.getText();
2712
- } else if (propName === "response") {
2713
- response = zodAstToTs(val);
2714
- } else if (propName === "error") {
2715
- error = zodAstToTs(val);
2716
- }
2717
- }
2718
- return { query, body, response, error, bodyZodText, queryZodText };
2719
- }
2720
-
2721
3384
  // src/discovery/contracts-fast.ts
2722
3385
  async function discoverContractsFast(opts) {
2723
3386
  const { cwd, glob, tsconfig } = opts;
@@ -2730,7 +3393,9 @@ async function discoverContractsFast(opts) {
2730
3393
  }
2731
3394
  bindDiscoveryContext(project, cwd, tsconfigPath, loaded);
2732
3395
  clearMixinTypeProject();
2733
- return extractAllRoutes(project);
3396
+ const routes = extractAllRoutes(project);
3397
+ clearSchemaTypeProject(project);
3398
+ return routes;
2734
3399
  }
2735
3400
  function resolveTsconfigPath(cwd, tsconfig) {
2736
3401
  return tsconfig ? (0, import_node_path5.resolve)(tsconfig) : (0, import_node_path5.join)(cwd, "tsconfig.json");
@@ -2742,25 +3407,25 @@ var NO_TSCONFIG_OPTIONS = {
2742
3407
  };
2743
3408
  function loadDiscoveryTsconfig(tsconfigPath) {
2744
3409
  if (!(0, import_node_fs.existsSync)(tsconfigPath)) return { options: null, files: [] };
2745
- const read = import_ts_morph9.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph9.ts.sys.readFile(path));
3410
+ const read = import_ts_morph12.ts.readConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
2746
3411
  if (read.error) {
2747
3412
  return {
2748
3413
  options: null,
2749
3414
  files: [tsconfigPath],
2750
- error: import_ts_morph9.ts.flattenDiagnosticMessageText(read.error.messageText, " ")
3415
+ error: import_ts_morph12.ts.flattenDiagnosticMessageText(read.error.messageText, " ")
2751
3416
  };
2752
3417
  }
2753
3418
  const host = {
2754
- useCaseSensitiveFileNames: import_ts_morph9.ts.sys.useCaseSensitiveFileNames,
3419
+ useCaseSensitiveFileNames: import_ts_morph12.ts.sys.useCaseSensitiveFileNames,
2755
3420
  // The one line that keeps a tsconfig from being a filesystem walk.
2756
3421
  readDirectory: () => [],
2757
- fileExists: (path) => import_ts_morph9.ts.sys.fileExists(path),
3422
+ fileExists: (path) => import_ts_morph12.ts.sys.fileExists(path),
2758
3423
  // `extends` is resolved through the host, so a consumer whose `paths` live
2759
3424
  // in a base tsconfig keeps working.
2760
- readFile: (path) => import_ts_morph9.ts.sys.readFile(path)
3425
+ readFile: (path) => import_ts_morph12.ts.sys.readFile(path)
2761
3426
  };
2762
- const sourceFile = import_ts_morph9.ts.readJsonConfigFile(tsconfigPath, (path) => import_ts_morph9.ts.sys.readFile(path));
2763
- const parsed = import_ts_morph9.ts.parseJsonSourceFileConfigFileContent(
3427
+ const sourceFile = import_ts_morph12.ts.readJsonConfigFile(tsconfigPath, (path) => import_ts_morph12.ts.sys.readFile(path));
3428
+ const parsed = import_ts_morph12.ts.parseJsonSourceFileConfigFileContent(
2764
3429
  sourceFile,
2765
3430
  host,
2766
3431
  (0, import_node_path5.dirname)(tsconfigPath),
@@ -2783,7 +3448,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
2783
3448
  `[nestjs-codegen/fast] Could not load ${tsconfigPath}: ${tsconfig.error} \u2014 continuing WITHOUT its compiler options, so path alias imports (e.g. '@/...') will not resolve and controllers extending an alias-imported factory will contribute NO routes to the generated client.`
2784
3449
  );
2785
3450
  }
2786
- return new import_ts_morph9.Project({
3451
+ return new import_ts_morph12.Project({
2787
3452
  compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
2788
3453
  skipAddingFilesFromTsConfig: true,
2789
3454
  skipLoadingLibFiles: true,
@@ -2895,15 +3560,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
2895
3560
  clearTypeResolutionCaches(this.project);
2896
3561
  clearEnumCache(this.project);
2897
3562
  clearMixinTypeProject();
3563
+ clearSchemaTypeProject(this.project);
2898
3564
  return extractRoutesFrom(this.project, this.controllerPaths);
2899
3565
  }
2900
3566
  };
2901
3567
  function decoratorStringArg(decoratorExpr) {
2902
3568
  if (!decoratorExpr) return void 0;
2903
- if (import_ts_morph9.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
2904
- if (import_ts_morph9.Node.isArrayLiteralExpression(decoratorExpr)) {
3569
+ if (import_ts_morph12.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
3570
+ if (import_ts_morph12.Node.isArrayLiteralExpression(decoratorExpr)) {
2905
3571
  const first = decoratorExpr.getElements()[0];
2906
- if (first && import_ts_morph9.Node.isStringLiteral(first)) return first.getLiteralValue();
3572
+ if (first && import_ts_morph12.Node.isStringLiteral(first)) return first.getLiteralValue();
2907
3573
  }
2908
3574
  return void 0;
2909
3575
  }
@@ -3024,9 +3690,9 @@ function extractContractRoute(args) {
3024
3690
  let contractDef = null;
3025
3691
  let bodyZodRef = null;
3026
3692
  let queryZodRef = null;
3027
- if (import_ts_morph9.Node.isCallExpression(firstDecoratorArg)) {
3028
- contractDef = parseDefineContractCall(firstDecoratorArg);
3029
- } else if (import_ts_morph9.Node.isIdentifier(firstDecoratorArg)) {
3693
+ if (import_ts_morph12.Node.isCallExpression(firstDecoratorArg)) {
3694
+ contractDef = parseDefineContractCall(firstDecoratorArg, project);
3695
+ } else if (import_ts_morph12.Node.isIdentifier(firstDecoratorArg)) {
3030
3696
  const identName = firstDecoratorArg.getText();
3031
3697
  const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
3032
3698
  if (!resolvedVar) {
@@ -3038,7 +3704,7 @@ function extractContractRoute(args) {
3038
3704
  const { decl: varDecl, file: declFile } = resolvedVar;
3039
3705
  const initializer = varDecl.getInitializer();
3040
3706
  if (!initializer) return null;
3041
- contractDef = parseDefineContractCall(initializer);
3707
+ contractDef = parseDefineContractCall(initializer, project);
3042
3708
  if (contractDef && varDecl.isExported()) {
3043
3709
  const filePath = declFile.getFilePath();
3044
3710
  if (contractDef.body !== null) {
@@ -3109,6 +3775,7 @@ function extractDtoRoute(args) {
3109
3775
  body: dtoContract?.body ?? null,
3110
3776
  response: mixinResponse ?? dtoContract?.response ?? "unknown",
3111
3777
  error: dtoContract?.error ?? null,
3778
+ paramTypes: dtoContract?.paramTypes ?? null,
3112
3779
  queryRef: dtoContract?.queryRef ?? null,
3113
3780
  bodyRef: dtoContract?.bodyRef ?? null,
3114
3781
  responseRef: dtoContract?.responseRef ?? null,
@@ -3231,7 +3898,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
3231
3898
  },
3232
3899
  project() {
3233
3900
  if (!project) {
3234
- project = new import_ts_morph10.Project({
3901
+ project = new import_ts_morph13.Project({
3235
3902
  skipAddingFilesFromTsConfig: true,
3236
3903
  skipLoadingLibFiles: true,
3237
3904
  skipFileDependencyResolution: true,
@@ -3349,10 +4016,11 @@ function insertIntoTree(tree, segments, leaf, fullName) {
3349
4016
  insertIntoTree(branch.children, rest, leaf, fullName);
3350
4017
  }
3351
4018
  }
3352
- function buildParamsType(params) {
4019
+ function buildParamsType(params, paramTypes) {
3353
4020
  const pathParams = params.filter((p) => p.source === "path");
3354
4021
  if (pathParams.length === 0) return "never";
3355
- return `{ ${pathParams.map((p) => `${p.name}: string`).join("; ")} }`;
4022
+ const typeOf = (name) => paramTypes && Object.hasOwn(paramTypes, name) && paramTypes[name] || "string";
4023
+ return `{ ${pathParams.map((p) => `${p.name}: ${typeOf(p.name)}`).join("; ")} }`;
3356
4024
  }
3357
4025
  function hasPathParams(params) {
3358
4026
  return params.some((p) => p.source === "path");
@@ -3446,7 +4114,7 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
3446
4114
  }
3447
4115
  const response = buildResponseType(c, outDir, serialization);
3448
4116
  const error = buildErrorType(c);
3449
- const params = buildParamsType(c.params);
4117
+ const params = buildParamsType(c.params, c.contractSource.paramTypes);
3450
4118
  const safeMethod = JSON.stringify(method);
3451
4119
  const safeUrl = JSON.stringify(c.path);
3452
4120
  const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
@@ -3534,28 +4202,28 @@ function renderFetcherRequest(req, binaryResponse) {
3534
4202
  }
3535
4203
  function emitReqHelper() {
3536
4204
  return [
3537
- "/** Awaitable request handle. `await api.x.y({...})` runs the fetch; extensions add query/mutation helpers. */",
3538
- "type __Req<R> = {",
3539
- " then<T1 = R, T2 = never>(",
4205
+ "/** An awaitable request handle: a lazy, memoized `Promise` that also carries `fetch()` and the members extensions add. */",
4206
+ "export type RequestHandle<R, M = {}> = Promise<R> & { fetch(): Promise<R> } & M;",
4207
+ "class __Request<R> extends Promise<R> {",
4208
+ " static override get [Symbol.species](): PromiseConstructor {",
4209
+ " return Promise;",
4210
+ " }",
4211
+ " private __run: () => Promise<R>;",
4212
+ " private __p: Promise<R> | undefined;",
4213
+ " constructor(run: () => Promise<R>) {",
4214
+ " super(() => {});",
4215
+ " this.__run = run;",
4216
+ " }",
4217
+ " override then<T1 = R, T2 = never>(",
3540
4218
  " onfulfilled?: ((value: R) => T1 | PromiseLike<T1>) | null,",
3541
4219
  " onrejected?: ((reason: unknown) => T2 | PromiseLike<T2>) | null,",
3542
- " ): Promise<T1 | T2>;",
3543
- " catch<T = never>(onrejected?: ((reason: unknown) => T | PromiseLike<T>) | null): Promise<R | T>;",
3544
- " finally(onfinally?: (() => void) | null): Promise<R>;",
3545
- " fetch(): Promise<R>;",
3546
- "};",
3547
- "function __req<R>(run: () => Promise<R>): __Req<R> {",
3548
- " let __p: Promise<R> | undefined;",
3549
- " const __promise = () => {",
3550
- " __p ??= run();",
3551
- " return __p;",
3552
- " };",
3553
- " return {",
3554
- " then: (onfulfilled, onrejected) => __promise().then(onfulfilled, onrejected),",
3555
- " catch: (onrejected) => __promise().catch(onrejected),",
3556
- " finally: (onfinally) => __promise().finally(onfinally),",
3557
- " fetch: run,",
3558
- " };",
4220
+ " ): Promise<T1 | T2> {",
4221
+ " this.__p ??= this.__run();",
4222
+ " return this.__p.then(onfulfilled, onrejected);",
4223
+ " }",
4224
+ "}",
4225
+ "function __req<R, M extends object = {}>(run: () => Promise<R>, members?: M): RequestHandle<R, M> {",
4226
+ " return Object.assign(new __Request<R>(run), { fetch: run }, members) as RequestHandle<R, M>;",
3559
4227
  "}",
3560
4228
  ""
3561
4229
  ];
@@ -3573,18 +4241,26 @@ function emitFilterFieldGuard() {
3573
4241
  ];
3574
4242
  }
3575
4243
  function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
3576
- const lines = [`${pad}${objKey}: (input?: ${req.inputType}) => ({`];
3577
- lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
4244
+ const memberLines = [];
3578
4245
  if (req.filterFieldsExpr) {
3579
- lines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
4246
+ memberLines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
3580
4247
  }
3581
4248
  if (streamExpr) {
3582
- lines.push(`${pad} stream: () => ${streamExpr},`);
4249
+ memberLines.push(`${pad} stream: () => ${streamExpr},`);
3583
4250
  }
3584
4251
  for (const [name, value] of Object.entries(members)) {
3585
- lines.push(`${pad} ${name}: ${value},`);
3586
- }
3587
- lines.push(`${pad}}),`);
4252
+ memberLines.push(`${pad} ${name}: ${value},`);
4253
+ }
4254
+ const body = [requestExpr, ...memberLines].join("\n");
4255
+ const param = /\binput\b/.test(body) ? `input?: ${req.inputType}` : "";
4256
+ const lines = [`${pad}${objKey}: (${param}) =>`];
4257
+ if (memberLines.length === 0) {
4258
+ lines.push(`${pad} __req<${req.responseType}>(() => ${requestExpr}),`);
4259
+ return lines;
4260
+ }
4261
+ lines.push(`${pad} __req(() => ${requestExpr}, {`);
4262
+ lines.push(...memberLines);
4263
+ lines.push(`${pad} }),`);
3588
4264
  return lines;
3589
4265
  }
3590
4266
  function renderStreamExpr(req) {
@@ -3715,6 +4391,26 @@ var EMPTY_PATH_NAMESPACE = [
3715
4391
  "}",
3716
4392
  ""
3717
4393
  ];
4394
+ function isClientCallable(route) {
4395
+ return route.method.toUpperCase() !== "ALL";
4396
+ }
4397
+ var ROUTES_IMPORT_PLACEHOLDER = "/* @@routes-import@@ */";
4398
+ var ROUTES_EXPORTS = [
4399
+ // Called, never mentioned in prose: match the call so a comment saying "route" doesn't count.
4400
+ { name: "route", typeOnly: false },
4401
+ { name: "ROUTES", typeOnly: false },
4402
+ { name: "RouteName", typeOnly: true },
4403
+ { name: "ExtractParams", typeOnly: true },
4404
+ { name: "RouteParams", typeOnly: true }
4405
+ ];
4406
+ function finishRoutesImport(lines) {
4407
+ const body = lines.filter((l) => l !== ROUTES_IMPORT_PLACEHOLDER).join("\n");
4408
+ const used = ROUTES_EXPORTS.filter(
4409
+ ({ name }) => new RegExp(name === "route" ? "\\broute\\(" : `\\b${name}\\b`).test(body)
4410
+ ).map(({ name, typeOnly }) => typeOnly ? `type ${name}` : name);
4411
+ const importLine = used.length ? `import { ${used.join(", ")} } from './routes.js';` : null;
4412
+ return lines.flatMap((l) => l === ROUTES_IMPORT_PLACEHOLDER ? importLine ? [importLine] : [] : [l]).join("\n");
4413
+ }
3718
4414
  function buildApiFile(routes, outDir, opts = {}) {
3719
4415
  const fetcherImportPath = opts.fetcherImportPath;
3720
4416
  const serialization = opts.serialization ?? "json";
@@ -3722,7 +4418,7 @@ function buildApiFile(routes, outDir, opts = {}) {
3722
4418
  const { layer } = resolveApiSlots(extensions);
3723
4419
  const memberExts = extensions.filter((e) => e.apiMembers);
3724
4420
  const headerExts = extensions.filter((e) => e.apiHeader);
3725
- const contracted = routes.filter((r) => r.contract);
4421
+ const contracted = routes.filter((r) => r.contract && isClientCallable(r));
3726
4422
  const ctx = opts.ctx ?? {
3727
4423
  cwd: outDir ?? "",
3728
4424
  outDir: outDir ?? "",
@@ -3774,9 +4470,7 @@ function buildApiFile(routes, outDir, opts = {}) {
3774
4470
  for (const imp of ext.apiHeader?.(ctx)?.imports ?? []) pushImport(imp);
3775
4471
  }
3776
4472
  lines.push(...extImports);
3777
- lines.push(
3778
- "import { route, ROUTES, type RouteName, type ExtractParams, type RouteParams } from './routes.js';"
3779
- );
4473
+ lines.push(ROUTES_IMPORT_PLACEHOLDER);
3780
4474
  const runtimeImport = fetcherImportPath ?? "@dudousxd/nestjs-client";
3781
4475
  lines.push(`import type { Fetcher } from '${runtimeImport}';`);
3782
4476
  if (serialization === "json" && contracted.length > 0) {
@@ -3827,7 +4521,7 @@ function buildApiFile(routes, outDir, opts = {}) {
3827
4521
  lines.push(...statements, "");
3828
4522
  }
3829
4523
  }
3830
- return lines.join("\n");
4524
+ return finishRoutesImport(lines);
3831
4525
  }
3832
4526
  const tree = /* @__PURE__ */ new Map();
3833
4527
  for (const r of contracted) {
@@ -3880,7 +4574,7 @@ function buildApiFile(routes, outDir, opts = {}) {
3880
4574
  lines.push(...statements, "");
3881
4575
  }
3882
4576
  }
3883
- return lines.join("\n");
4577
+ return finishRoutesImport(lines);
3884
4578
  }
3885
4579
 
3886
4580
  // src/emit/emit-cache.ts
@@ -3893,6 +4587,7 @@ async function emitCache(pages, outDir) {
3893
4587
  const s = await (0, import_promises4.stat)(p.absolutePath);
3894
4588
  return {
3895
4589
  name: p.name,
4590
+ scope: p.scope ?? "default",
3896
4591
  relativePath: p.relativePath,
3897
4592
  mtime: s.mtime.toISOString()
3898
4593
  };
@@ -4640,6 +5335,7 @@ function buildOpenApiSpec(routes, opts = {}) {
4640
5335
  const paths = {};
4641
5336
  for (const route of routes) {
4642
5337
  if (!route.contract) continue;
5338
+ if (route.method.toUpperCase() === "ALL") continue;
4643
5339
  const oaPath = toOpenApiPath(route.path);
4644
5340
  const method = route.method.toLowerCase();
4645
5341
  let pathItem = paths[oaPath];
@@ -4683,10 +5379,11 @@ async function emitPages(pages, outDir, _options = {}) {
4683
5379
  }).join("\n");
4684
5380
  const propsHelper = "\nexport type InertiaProps<K extends InertiaPageName> = import('@dudousxd/nestjs-inertia').InertiaPages[K];\n";
4685
5381
  const sharedPropsBlock = buildSharedPropsBlock(_options.sharedProps ?? null);
5382
+ const scopesBlock = buildScopesBlock(pages);
4686
5383
  const content = `// Generated by @dudousxd/nestjs-codegen. Do not edit.
4687
5384
 
4688
5385
  export type InertiaPageName = ${pageNameUnion};
4689
- ${propsHelper}
5386
+ ${propsHelper}${scopesBlock}
4690
5387
  declare module '@dudousxd/nestjs-inertia' {
4691
5388
  interface InertiaPages {
4692
5389
  ${augBody}
@@ -4695,6 +5392,31 @@ ${sharedPropsBlock}}
4695
5392
  `;
4696
5393
  await (0, import_promises9.writeFile)((0, import_node_path13.join)(outDir, "pages.d.ts"), content, "utf8");
4697
5394
  }
5395
+ function buildScopesBlock(pages) {
5396
+ const byScope = /* @__PURE__ */ new Map();
5397
+ for (const page of pages) {
5398
+ const scope = page.scope ?? "default";
5399
+ const names = byScope.get(scope) ?? [];
5400
+ names.push(JSON.stringify(page.name));
5401
+ byScope.set(scope, names);
5402
+ }
5403
+ if (![...byScope.keys()].some((scope) => scope !== "default")) return "";
5404
+ if (!byScope.has("default")) byScope.set("default", []);
5405
+ const scopes = ["default", ...[...byScope.keys()].filter((s) => s !== "default")];
5406
+ const body = scopes.map((scope) => {
5407
+ const key = needsQuotes(scope) ? JSON.stringify(scope) : scope;
5408
+ const names = byScope.get(scope) ?? [];
5409
+ return ` ${key}: ${names.length ? names.join(" | ") : "never"};`;
5410
+ }).join("\n");
5411
+ return `
5412
+ /** The page names of each Inertia app: \`default\` (forRoot) and every \`pages.scopes\` entry. */
5413
+ export interface InertiaScopePages {
5414
+ ${body}
5415
+ }
5416
+
5417
+ export type InertiaScopePageName<S extends keyof InertiaScopePages> = InertiaScopePages[S];
5418
+ `;
5419
+ }
4698
5420
  function buildSharedPropsBlock(sharedProps) {
4699
5421
  if (!sharedProps) return "";
4700
5422
  if (sharedProps.isImportRef) {
@@ -4902,8 +5624,7 @@ function diffConfigKeyHashes(previous, current) {
4902
5624
  return [...keys].filter((key) => previous[key] !== current[key]).sort();
4903
5625
  }
4904
5626
  async function discoverInputFiles(config) {
4905
- const globs = [config.contracts.glob, config.forms.watch];
4906
- if (config.pages) globs.push(config.pages.glob);
5627
+ const globs = [config.contracts.glob, config.forms.watch, ...allPageGlobs(config)];
4907
5628
  const cwd = config.codegen.cwd;
4908
5629
  const matched = await (0, import_fast_glob3.default)(globs, { cwd, absolute: true, onlyFiles: true });
4909
5630
  return [...new Set(matched)].sort();
@@ -5034,12 +5755,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
5034
5755
  }
5035
5756
  if (config.pages) {
5036
5757
  const pagesConfig = config.pages;
5037
- const pages = await discoverPages({
5038
- glob: pagesConfig.glob,
5039
- cwd: config.codegen.cwd,
5040
- propsExport: pagesConfig.propsExport,
5041
- componentNameStrategy: pagesConfig.componentNameStrategy
5042
- });
5758
+ const pages = await discoverPageScopes(config);
5043
5759
  const sharedProps = discoverSharedPropsFromConfig(config);
5044
5760
  await emitPages(pages, config.codegen.outDir, {
5045
5761
  propsExport: pagesConfig.propsExport,
@@ -5214,12 +5930,17 @@ async function watch(config, onChange, options = {}) {
5214
5930
  await runInitialPass();
5215
5931
  }
5216
5932
  let pagesDebounceTimer;
5217
- const pagesGlob = config.pages?.glob ?? ".nestjs-codegen-no-pages";
5218
- const pagesWatcher = import_chokidar.default.watch((0, import_node_path18.join)(config.codegen.cwd, pagesGlob), {
5219
- ignoreInitial: true,
5220
- persistent: true,
5221
- awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5222
- });
5933
+ const pageGlobs = allPageGlobs(config);
5934
+ const pagesWatcher = import_chokidar.default.watch(
5935
+ (pageGlobs.length ? pageGlobs : [".nestjs-codegen-no-pages"]).map(
5936
+ (glob) => (0, import_node_path18.join)(config.codegen.cwd, glob)
5937
+ ),
5938
+ {
5939
+ ignoreInitial: true,
5940
+ persistent: true,
5941
+ awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
5942
+ }
5943
+ );
5223
5944
  function schedulePagesRegenerate() {
5224
5945
  if (pagesDebounceTimer !== void 0) {
5225
5946
  clearTimeout(pagesDebounceTimer);
@@ -5299,7 +6020,7 @@ async function watch(config, onChange, options = {}) {
5299
6020
  }
5300
6021
 
5301
6022
  // src/index.ts
5302
- var VERSION = "0.25.2";
6023
+ var VERSION = "0.27.0";
5303
6024
 
5304
6025
  // src/cli/codegen.ts
5305
6026
  async function runCodegen(opts = {}) {