@dudousxd/nestjs-codegen 0.13.1 → 0.14.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 CHANGED
@@ -1,5 +1,43 @@
1
1
  # @dudousxd/nestjs-codegen
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fc78a39: feat: binary (blob) response mode, `@AsQuery()` marker, CLI↔module config-drift guard, and a `handleQuery` TanStack helper.
8
+
9
+ - **Binary (blob) response mode.** A handler returning NestJS `StreamableFile` or Node `Buffer`
10
+ (including `Promise<StreamableFile>`) is now discovered as `binaryResponse: true` and emitted
11
+ with `response: RawResponse<Blob>` (never `Jsonify<...>`) — the leaf issues its request via
12
+ `fetcher.fetchBlob(...)` instead of the verb method, so callers get `{ data, status, headers }`
13
+ and can read `content-disposition` etc. Works on any HTTP method (`fetchBlob` already accepted
14
+ a `method` opt); a non-GET binary route passes it explicitly since `fetchBlob` defaults to GET.
15
+ `Observable`/`ReadableStream` handlers are unaffected — they stay on the existing SSE/stream
16
+ path. Each `ApiRouter` leaf now also carries a `binary` flag (`Route.Binary<K>` /
17
+ `Path.Binary<M, U>` type helpers), mirroring `stream`.
18
+ - **`@AsQuery()` marker** (new `@dudousxd/nestjs-codegen/markers` subpath — zero-import, runtime
19
+ no-op). Marks a non-GET route whose semantics are a read (e.g. a POST with a query-shaped
20
+ payload) so codegen emits `queryOptions` for it, exactly like a GET or a filter-search route.
21
+ - **CLI↔module config-drift guard.** The CLI (`nestjs-codegen.config.ts`) and the Nest module
22
+ (`NestjsCodegenModule.forRoot()`) can target the same `outDir` from independently-resolved
23
+ configs; if they genuinely differ (e.g. `serialization` `'json'` vs `'superjson'`), each run
24
+ used to silently overwrite the other's `api.ts` shape. `generate()` now throws a
25
+ `DriftGuardError` _before writing anything_ when the manifest's `entryPoint` differs from the
26
+ current run's AND the resolved configs' hashes differ — naming both entry points and
27
+ instructing how to fix it (share one config object, or set `driftGuard: false`). Same entry
28
+ point (a normal config edit) or same config across entry points both proceed as before.
29
+ - **TanStack: `handleQuery` helper**, emitted into `api.ts` whenever the TanStack layer is
30
+ active. Wraps any `{ queryKey, fetch }`-shaped handle (a POST-as-query handle, or a runtime
31
+ pick between two different handles) into a plain `{ queryKey, queryFn }` pair — solves the
32
+ useQuery-overload break from spreading a ternary of `queryOptions()` calls. Also: binary GET
33
+ routes get `queryOptions` but never `infiniteQueryOptions` (a download isn't paginated data).
34
+
35
+ ## 0.13.2
36
+
37
+ ### Patch Changes
38
+
39
+ - 889af1f: Resolve the members of an inline object-literal type (`{ a: Foo; b: Bar }`) in response/stream types instead of emitting the node's raw text. A named type nested in an object literal — most commonly an SSE payload's `Observable<{ data: SomeType }>`, where `SomeType` is imported from another package — was previously copied verbatim, leaving a bare, unimported identifier that is undefined in the generated file. Each member's type is now resolved (expanded inline, or reduced to `unknown` when unresolvable) like any other named reference.
40
+
3
41
  ## 0.13.1
4
42
 
5
43
  ### Patch Changes
package/dist/cli/main.cjs CHANGED
@@ -180,7 +180,8 @@ function applyDefaults(userConfig, cwd) {
180
180
  fileName: userConfig.mocks?.fileName ?? "mocks.ts",
181
181
  seed: userConfig.mocks?.seed ?? 1,
182
182
  baseUrl: userConfig.mocks?.baseUrl ?? ""
183
- }
183
+ },
184
+ driftGuard: userConfig.driftGuard ?? true
184
185
  };
185
186
  }
186
187
  async function loadConfig(cwd) {
@@ -640,7 +641,7 @@ async function collectEmittedFiles(extensions, ctx) {
640
641
  function requestShape(route) {
641
642
  const cs = route.contract?.contractSource;
642
643
  const isGet = route.method.toUpperCase() === "GET";
643
- const isQuery = isGet || !!cs?.filterFields?.length;
644
+ const isQuery = isGet || !!cs?.filterFields?.length || !!cs?.asQuery;
644
645
  const hasBody = !!cs?.bodyRef || cs?.body != null && cs.body !== "never";
645
646
  const hasQuery = isGet || !!cs?.queryRef || cs?.query != null && cs.query !== "never";
646
647
  return { isGet, isQuery, hasBody, hasQuery };
@@ -747,6 +748,7 @@ function emitFilterQueryType(c) {
747
748
  return `import('@dudousxd/nestjs-filter-client').TypedFilterQuery<${emitFilterQueryTypeArgs(c)}>`;
748
749
  }
749
750
  function buildResponseType(c, outDir, serialization) {
751
+ if (c.contractSource.binaryResponse) return "RawResponse<Blob>";
750
752
  const raw = rawResponseType(c, outDir);
751
753
  return serialization === "json" ? `Jsonify<${raw}>` : raw;
752
754
  }
@@ -801,8 +803,9 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
801
803
  const safeUrl = JSON.stringify(c.path);
802
804
  const filterFields = c.contractSource.filterFields?.length ? c.contractSource.filterFields.map((f) => JSON.stringify(f)).join(" | ") : "never";
803
805
  const stream = c.contractSource.stream ? "true" : "false";
806
+ const binary = c.contractSource.binaryResponse ? "true" : "false";
804
807
  lines.push(
805
- `${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; params: ${params}; query: ${query}; body: ${body}; response: ${response}; error: ${error}; filterFields: ${filterFields}; stream: ${stream} };`
808
+ `${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; params: ${params}; query: ${query}; body: ${body}; response: ${response}; error: ${error}; filterFields: ${filterFields}; stream: ${stream}; binary: ${binary} };`
806
809
  );
807
810
  } else {
808
811
  lines.push(`${pad}${objKey}: {`);
@@ -848,6 +851,9 @@ function buildRequestModel(c) {
848
851
  if (hasQuery) optsParts.push("query: input?.query as Record<string, unknown> | undefined");
849
852
  if (hasBody) optsParts.push("body: input?.body");
850
853
  if (hasBody && c.contractSource.multipart) optsParts.push("multipart: true");
854
+ if (c.contractSource.binaryResponse && m !== "get") {
855
+ optsParts.unshift(`method: ${JSON.stringify(m.toUpperCase())}`);
856
+ }
851
857
  const optsExpr = optsParts.length ? `{ ${optsParts.join(", ")} }` : "{}";
852
858
  return {
853
859
  routeName: c.name,
@@ -867,7 +873,8 @@ function buildRequestModel(c) {
867
873
  queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)`
868
874
  };
869
875
  }
870
- function renderFetcherRequest(req) {
876
+ function renderFetcherRequest(req, binaryResponse) {
877
+ if (binaryResponse) return `fetcher.fetchBlob(${req.urlExpr}, ${req.optsExpr})`;
871
878
  return `fetcher.${req.method}<${req.responseType}>(${req.urlExpr}, ${req.optsExpr})`;
872
879
  }
873
880
  function emitReqHelper() {
@@ -928,7 +935,7 @@ function emitApiObjectBlock(tree, indent, p) {
928
935
  const leaf = {
929
936
  route: node.route,
930
937
  request: req,
931
- requestExpr: renderFetcherRequest(req)
938
+ requestExpr: renderFetcherRequest(req, node.contractSource.binaryResponse === true)
932
939
  };
933
940
  const owned = /* @__PURE__ */ new Map();
934
941
  if (p.layer) {
@@ -988,6 +995,8 @@ var ROUTE_NAMESPACE = [
988
995
  ' export type FilterFields<K extends string> = ResolveByName<K, "filterFields">;',
989
996
  " /** The streamed element type of an `@Sse()`/streaming route \u2014 the type yielded by its `stream()` AsyncIterable. */",
990
997
  ' export type Stream<K extends string> = ResolveByName<K, "response">;',
998
+ " /** True for a binary/blob route (`StreamableFile`/`Buffer` handler return type). */",
999
+ ' export type Binary<K extends string> = ResolveByName<K, "binary">;',
991
1000
  " export type Request<K extends string> = {",
992
1001
  " body: Body<K>;",
993
1002
  " query: Query<K>;",
@@ -1005,6 +1014,7 @@ var PATH_NAMESPACE = [
1005
1014
  ' export type Error<M extends string, U extends string> = ResolveByPath<M, U, "error">;',
1006
1015
  ' export type FilterFields<M extends string, U extends string> = ResolveByPath<M, U, "filterFields">;',
1007
1016
  ' export type Stream<M extends string, U extends string> = ResolveByPath<M, U, "response">;',
1017
+ ' export type Binary<M extends string, U extends string> = ResolveByPath<M, U, "binary">;',
1008
1018
  "}",
1009
1019
  ""
1010
1020
  ];
@@ -1017,6 +1027,7 @@ var EMPTY_ROUTE_NAMESPACE = [
1017
1027
  " export type Error<K extends string> = never;",
1018
1028
  " export type FilterFields<K extends string> = never;",
1019
1029
  " export type Stream<K extends string> = never;",
1030
+ " export type Binary<K extends string> = never;",
1020
1031
  " export type Request<K extends string> = { body: never; query: never; params: never };",
1021
1032
  "}",
1022
1033
  ""
@@ -1030,6 +1041,7 @@ var EMPTY_PATH_NAMESPACE = [
1030
1041
  " export type Error<M extends string, U extends string> = never;",
1031
1042
  " export type FilterFields<M extends string, U extends string> = never;",
1032
1043
  " export type Stream<M extends string, U extends string> = never;",
1044
+ " export type Binary<M extends string, U extends string> = never;",
1033
1045
  "}",
1034
1046
  ""
1035
1047
  ];
@@ -1095,6 +1107,9 @@ function buildApiFile(routes, outDir, opts = {}) {
1095
1107
  if (serialization === "json" && contracted.length > 0) {
1096
1108
  lines.push(`import type { Jsonify } from '${runtimeImport}';`);
1097
1109
  }
1110
+ if (contracted.some((r) => r.contract?.contractSource.binaryResponse)) {
1111
+ lines.push(`import type { RawResponse } from '${runtimeImport}';`);
1112
+ }
1098
1113
  if (importsByFile.size > 0 && outDir) {
1099
1114
  lines.push("");
1100
1115
  const emittedNames = /* @__PURE__ */ new Set();
@@ -1131,6 +1146,12 @@ function buildApiFile(routes, outDir, opts = {}) {
1131
1146
  lines.push("");
1132
1147
  lines.push(...EMPTY_ROUTE_NAMESPACE);
1133
1148
  lines.push(...EMPTY_PATH_NAMESPACE);
1149
+ for (const ext of headerExts) {
1150
+ const statements = ext.apiHeader?.(ctx)?.statements;
1151
+ if (statements?.length) {
1152
+ lines.push(...statements, "");
1153
+ }
1154
+ }
1134
1155
  return lines.join("\n");
1135
1156
  }
1136
1157
  const tree = /* @__PURE__ */ new Map();
@@ -2148,11 +2169,22 @@ var import_node_path12 = require("path");
2148
2169
  var import_fast_glob2 = __toESM(require("fast-glob"), 1);
2149
2170
  var MANIFEST_FILE = ".codegen-manifest.json";
2150
2171
  var LOCK_FILE = ".watcher.lock";
2172
+ var DriftGuardError = class extends Error {
2173
+ constructor(message) {
2174
+ super(message);
2175
+ this.name = "DriftGuardError";
2176
+ }
2177
+ };
2178
+ function isEntryPoint(value) {
2179
+ return value === "cli" || value === "module";
2180
+ }
2151
2181
  function isManifestShape(value) {
2152
2182
  if (typeof value !== "object" || value === null) return false;
2153
2183
  const candidate = value;
2154
2184
  if (typeof candidate.version !== "string") return false;
2155
2185
  if (typeof candidate.hash !== "string") return false;
2186
+ if (candidate.entryPoint !== void 0 && !isEntryPoint(candidate.entryPoint)) return false;
2187
+ if (candidate.configHash !== void 0 && typeof candidate.configHash !== "string") return false;
2156
2188
  if (!Array.isArray(candidate.files)) return false;
2157
2189
  return candidate.files.every((entry) => typeof entry === "string");
2158
2190
  }
@@ -2195,11 +2227,20 @@ async function readManifest(outDir) {
2195
2227
  const raw = await (0, import_promises11.readFile)((0, import_node_path12.join)(outDir, MANIFEST_FILE), "utf8");
2196
2228
  const parsed = JSON.parse(raw);
2197
2229
  if (!isManifestShape(parsed)) return null;
2198
- return { version: parsed.version, hash: parsed.hash, files: parsed.files };
2230
+ return {
2231
+ version: parsed.version,
2232
+ hash: parsed.hash,
2233
+ ...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
2234
+ ...parsed.configHash ? { configHash: parsed.configHash } : {},
2235
+ files: parsed.files
2236
+ };
2199
2237
  } catch {
2200
2238
  return null;
2201
2239
  }
2202
2240
  }
2241
+ function computeConfigHash(config) {
2242
+ return (0, import_node_crypto.createHash)("sha256").update(serializeConfig(config)).digest("hex");
2243
+ }
2203
2244
  async function writeManifest(outDir, manifest) {
2204
2245
  await (0, import_promises11.writeFile)((0, import_node_path12.join)(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
2205
2246
  `, "utf8");
@@ -2244,7 +2285,10 @@ function debugWarn(message) {
2244
2285
  }
2245
2286
 
2246
2287
  // src/generate.ts
2247
- async function generate(config, inputRoutes = []) {
2288
+ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint) {
2289
+ return `[nestjs-codegen] Config drift detected in "${outDir}": the last generate ran from the "${previousEntryPoint}" entry point, this run is from the "${currentEntryPoint}" entry point, and their resolved configs differ (e.g. \`serialization: "json"\` vs \`"superjson"\`). Both entry points must read the SAME config \u2014 export a shared config object (e.g. codegen.config.ts) and import it from BOTH nestjs-codegen.config.ts (CLI) and NestjsCodegenModule.forRoot() (Nest module), or set \`driftGuard: false\` on either config to opt out of this check.`;
2290
+ }
2291
+ async function generate(config, inputRoutes = [], entryPoint = "cli") {
2248
2292
  setCodegenDebug(config.debug);
2249
2293
  const inputsHash = await computeInputsHash(config);
2250
2294
  const manifest = await readManifest(config.codegen.outDir);
@@ -2252,6 +2296,12 @@ async function generate(config, inputRoutes = []) {
2252
2296
  console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
2253
2297
  return;
2254
2298
  }
2299
+ const configHash = computeConfigHash(config);
2300
+ if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
2301
+ throw new DriftGuardError(
2302
+ driftGuardMessage(config.codegen.outDir, manifest.entryPoint, entryPoint)
2303
+ );
2304
+ }
2255
2305
  const extensions = config.extensions ?? [];
2256
2306
  let routes = inputRoutes;
2257
2307
  const ctx = createExtensionContext(config, () => routes);
@@ -2317,6 +2367,8 @@ async function generate(config, inputRoutes = []) {
2317
2367
  await writeManifest(config.codegen.outDir, {
2318
2368
  version: VERSION,
2319
2369
  hash: inputsHash,
2370
+ entryPoint,
2371
+ configHash,
2320
2372
  files: outputFiles
2321
2373
  });
2322
2374
  }
@@ -3606,6 +3658,19 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
3606
3658
  dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
3607
3659
  return "unknown";
3608
3660
  }
3661
+ if (import_ts_morph7.Node.isTypeLiteral(typeNode)) {
3662
+ const members = [];
3663
+ for (const member of typeNode.getMembers()) {
3664
+ if (import_ts_morph7.Node.isPropertySignature(member)) {
3665
+ const memberTypeNode = member.getTypeNode();
3666
+ const memberType = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
3667
+ members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType}`);
3668
+ } else {
3669
+ members.push(member.getText());
3670
+ }
3671
+ }
3672
+ return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
3673
+ }
3609
3674
  const kind = typeNode.getKind();
3610
3675
  if (kind === import_ts_morph7.SyntaxKind.StringKeyword) return "string";
3611
3676
  if (kind === import_ts_morph7.SyntaxKind.NumberKeyword) return "number";
@@ -3878,6 +3943,14 @@ function resolveBodyQueryResponseRef(typeNode, sourceFile, project) {
3878
3943
  var STREAM_CONTAINERS = /* @__PURE__ */ new Set(["Observable", "AsyncIterable", "AsyncIterableIterator"]);
3879
3944
  var STREAM_CONTAINERS_GENERATOR = /* @__PURE__ */ new Set(["AsyncGenerator"]);
3880
3945
  var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLike"]);
3946
+ var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
3947
+ function detectBinaryResponse(method) {
3948
+ const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
3949
+ if (!node || !import_ts_morph7.Node.isTypeReference(node)) return false;
3950
+ const typeName = node.getTypeName();
3951
+ const name = import_ts_morph7.Node.isIdentifier(typeName) ? typeName.getText() : "";
3952
+ return BINARY_RESPONSE_TYPES.has(name);
3953
+ }
3881
3954
  function detectStreamElement(method) {
3882
3955
  const hasSse = method.getDecorators().some((d) => d.getName() === "Sse");
3883
3956
  let node = method.getReturnTypeNode();
@@ -3898,6 +3971,9 @@ function streamContainerElement(node) {
3898
3971
  }
3899
3972
  return null;
3900
3973
  }
3974
+ function hasAsQueryDecorator(method) {
3975
+ return method.getDecorators().some((d) => d.getName() === "AsQuery");
3976
+ }
3901
3977
  function unwrapNamedContainer(node, names) {
3902
3978
  if (!node || !import_ts_morph7.Node.isTypeReference(node)) return node;
3903
3979
  const typeName = node.getTypeName();
@@ -3915,6 +3991,8 @@ function extractDtoContract(method, sourceFile, project) {
3915
3991
  const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
3916
3992
  const streamElement = detectStreamElement(method);
3917
3993
  const isStream = streamElement !== null;
3994
+ const binaryResponse = detectBinaryResponse(method);
3995
+ const asQuery = hasAsQueryDecorator(method);
3918
3996
  if (filterInfo && filterInfo.source === "body") {
3919
3997
  const bodyType = "import('@dudousxd/nestjs-filter-client').FilterQueryResult";
3920
3998
  body = body ?? bodyType;
@@ -3922,7 +4000,7 @@ function extractDtoContract(method, sourceFile, project) {
3922
4000
  const paramsType = extractParamsType(method, sourceFile, project);
3923
4001
  const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
3924
4002
  const errorInfo = extractErrorType(method, sourceFile, project);
3925
- if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart) {
4003
+ if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
3926
4004
  return null;
3927
4005
  }
3928
4006
  let bodyRef = null;
@@ -3997,7 +4075,9 @@ function extractDtoContract(method, sourceFile, project) {
3997
4075
  querySchema,
3998
4076
  stream: isStream,
3999
4077
  multipart: uploads.multipart,
4000
- multipartBody
4078
+ multipartBody,
4079
+ binaryResponse,
4080
+ asQuery
4001
4081
  };
4002
4082
  }
4003
4083
  function resolveParamClass(method, decoratorName, sourceFile, project) {
@@ -4484,7 +4564,9 @@ function extractDtoRoute(args) {
4484
4564
  querySchema: dtoContract?.querySchema ?? null,
4485
4565
  stream: dtoContract?.stream ?? false,
4486
4566
  multipart: dtoContract?.multipart ?? false,
4487
- multipartBody: dtoContract?.multipartBody ?? null
4567
+ multipartBody: dtoContract?.multipartBody ?? null,
4568
+ binaryResponse: dtoContract?.binaryResponse ?? false,
4569
+ asQuery: dtoContract?.asQuery ?? false
4488
4570
  }
4489
4571
  });
4490
4572
  }
@@ -4577,6 +4659,7 @@ var PAGES_DEBOUNCE_MS = 150;
4577
4659
  var NO_OP_WATCHER = { close: async () => {
4578
4660
  } };
4579
4661
  async function watch(config, onChange, options = {}) {
4662
+ const entryPoint = options.entryPoint ?? "cli";
4580
4663
  const lock = await acquireLock(config.codegen.outDir);
4581
4664
  if (lock === null) {
4582
4665
  let holderPid = "unknown";
@@ -4608,13 +4691,17 @@ async function watch(config, onChange, options = {}) {
4608
4691
  try {
4609
4692
  const initialRoutes = (await getDiscovery()).discover();
4610
4693
  lastRoutes = initialRoutes;
4611
- await generate(config, initialRoutes);
4694
+ await generate(config, initialRoutes, entryPoint);
4612
4695
  } catch (err) {
4696
+ if (err instanceof DriftGuardError) {
4697
+ console.error(err.message);
4698
+ return;
4699
+ }
4613
4700
  console.warn(
4614
4701
  `[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
4615
4702
  );
4616
4703
  try {
4617
- await generate(config, lastRoutes);
4704
+ await generate(config, lastRoutes, entryPoint);
4618
4705
  } catch {
4619
4706
  }
4620
4707
  }
@@ -4642,7 +4729,7 @@ async function watch(config, onChange, options = {}) {
4642
4729
  pagesDebounceTimer = setTimeout(async () => {
4643
4730
  pagesDebounceTimer = void 0;
4644
4731
  try {
4645
- await generate(config, lastRoutes);
4732
+ await generate(config, lastRoutes, entryPoint);
4646
4733
  } catch (err) {
4647
4734
  console.error(
4648
4735
  "[nestjs-codegen] Pages generation failed:",
@@ -4674,7 +4761,7 @@ async function watch(config, onChange, options = {}) {
4674
4761
  try {
4675
4762
  const routes = await (await getDiscovery()).rediscover(changed);
4676
4763
  lastRoutes = routes;
4677
- await generate(config, routes);
4764
+ await generate(config, routes, entryPoint);
4678
4765
  } catch (err) {
4679
4766
  console.error(
4680
4767
  "[nestjs-codegen] Contracts generation failed:",
@@ -4714,14 +4801,14 @@ async function watch(config, onChange, options = {}) {
4714
4801
  }
4715
4802
 
4716
4803
  // src/index.ts
4717
- var VERSION = "0.13.1";
4804
+ var VERSION = "0.14.0";
4718
4805
 
4719
4806
  // src/cli/codegen.ts
4720
4807
  async function runCodegen(opts = {}) {
4721
4808
  const cwd = opts.cwd ?? process.cwd();
4722
4809
  const config = await loadConfig(cwd);
4723
4810
  if (opts.watch) {
4724
- const watcher = await watch(config);
4811
+ const watcher = await watch(config, void 0, { entryPoint: "cli" });
4725
4812
  await new Promise((resolve4) => {
4726
4813
  function onSignal() {
4727
4814
  watcher.close().then(resolve4).catch(resolve4);
@@ -4736,7 +4823,7 @@ async function runCodegen(opts = {}) {
4736
4823
  glob: config.contracts.glob,
4737
4824
  ...config.app?.tsconfig ? { tsconfig: config.app.tsconfig } : {}
4738
4825
  });
4739
- await generate(config, routes);
4826
+ await generate(config, routes, "cli");
4740
4827
  console.log("\u2713 Codegen generated artifacts in", config.codegen.outDir);
4741
4828
  }
4742
4829