@dudousxd/nestjs-codegen 0.14.0 → 0.14.2

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,21 @@
1
1
  # @dudousxd/nestjs-codegen
2
2
 
3
+ ## 0.14.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 79d5e73: Fix a drift-guard false positive that permanently blocked incremental regeneration for shared configs: the config hash folded functions in via `toString()`, but the same shared config object yields different function source text per entry point (the CLI loads TS via Node's type stripping; the Nest module runs tsc/SWC-compiled dist), so a genuinely-shared config was flagged as drifted the moment both entry points touched the same outDir. Functions now hash by name only — every setting that can actually diverge is plain data and is still hashed in full. The drift error also now NAMES the top-level keys that differ (via new per-key hashes recorded in the manifest as `configKeyHashes`) instead of a generic example.
8
+
9
+ ## 0.14.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 093f3d5: A bare `@UploadedFile()` route (no `@Body()` DTO) now emits a working multipart leaf.
14
+ `requestShape().hasBody` ignored `multipart`, so while the ApiRouter TYPE promised
15
+ `body: { file: File | Blob }` (the multipart intersection), the generated call accepted no
16
+ body and sent no file. `multipart` now implies a body; routes with a `@Body()` DTO were
17
+ already correct.
18
+
3
19
  ## 0.14.0
4
20
 
5
21
  ### Minor Changes
package/dist/cli/main.cjs CHANGED
@@ -642,7 +642,7 @@ function requestShape(route) {
642
642
  const cs = route.contract?.contractSource;
643
643
  const isGet = route.method.toUpperCase() === "GET";
644
644
  const isQuery = isGet || !!cs?.filterFields?.length || !!cs?.asQuery;
645
- const hasBody = !!cs?.bodyRef || cs?.body != null && cs.body !== "never";
645
+ const hasBody = !!cs?.bodyRef || cs?.body != null && cs.body !== "never" || !!cs?.multipart;
646
646
  const hasQuery = isGet || !!cs?.queryRef || cs?.query != null && cs.query !== "never";
647
647
  return { isGet, isQuery, hasBody, hasQuery };
648
648
  }
@@ -2185,19 +2185,41 @@ function isManifestShape(value) {
2185
2185
  if (typeof candidate.hash !== "string") return false;
2186
2186
  if (candidate.entryPoint !== void 0 && !isEntryPoint(candidate.entryPoint)) return false;
2187
2187
  if (candidate.configHash !== void 0 && typeof candidate.configHash !== "string") return false;
2188
+ if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
2189
+ return false;
2190
+ }
2188
2191
  if (!Array.isArray(candidate.files)) return false;
2189
2192
  return candidate.files.every((entry) => typeof entry === "string");
2190
2193
  }
2194
+ function isStringRecord(value) {
2195
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
2196
+ return Object.values(value).every((entry) => typeof entry === "string");
2197
+ }
2191
2198
  function serializeConfig(config) {
2199
+ return serializeConfigValue(config, `unserializable:${config.codegen.outDir}`);
2200
+ }
2201
+ function serializeConfigValue(value, unserializableMarker) {
2192
2202
  try {
2193
- return JSON.stringify(config, (_key, value) => {
2194
- if (typeof value === "function") return `[fn:${value.name}]${value.toString()}`;
2195
- return value;
2203
+ return JSON.stringify(value, (_key, entry) => {
2204
+ if (typeof entry === "function") return `[fn:${entry.name}]`;
2205
+ return entry;
2196
2206
  });
2197
2207
  } catch {
2198
- return `unserializable:${config.codegen.outDir}:${config.contracts.glob}`;
2208
+ return unserializableMarker;
2199
2209
  }
2200
2210
  }
2211
+ function computeConfigKeyHashes(config) {
2212
+ const hashes = {};
2213
+ for (const [key, value] of Object.entries(config)) {
2214
+ if (value === void 0) continue;
2215
+ hashes[key] = (0, import_node_crypto.createHash)("sha256").update(serializeConfigValue(value, `unserializable:${key}`)).digest("hex");
2216
+ }
2217
+ return hashes;
2218
+ }
2219
+ function diffConfigKeyHashes(previous, current) {
2220
+ const keys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(current)]);
2221
+ return [...keys].filter((key) => previous[key] !== current[key]).sort();
2222
+ }
2201
2223
  async function discoverInputFiles(config) {
2202
2224
  const globs = [config.contracts.glob, config.forms.watch];
2203
2225
  if (config.pages) globs.push(config.pages.glob);
@@ -2232,6 +2254,7 @@ async function readManifest(outDir) {
2232
2254
  hash: parsed.hash,
2233
2255
  ...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
2234
2256
  ...parsed.configHash ? { configHash: parsed.configHash } : {},
2257
+ ...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
2235
2258
  files: parsed.files
2236
2259
  };
2237
2260
  } catch {
@@ -2285,8 +2308,9 @@ function debugWarn(message) {
2285
2308
  }
2286
2309
 
2287
2310
  // src/generate.ts
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.`;
2311
+ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differingKeys) {
2312
+ const differ = differingKeys.length > 0 ? `their resolved configs differ at: ${differingKeys.map((key) => `\`${key}\``).join(", ")}` : "their resolved configs differ (re-run after this generate records per-key hashes to see which keys)";
2313
+ 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 ${differ}. 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
2314
  }
2291
2315
  async function generate(config, inputRoutes = [], entryPoint = "cli") {
2292
2316
  setCodegenDebug(config.debug);
@@ -2297,9 +2321,17 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
2297
2321
  return;
2298
2322
  }
2299
2323
  const configHash = computeConfigHash(config);
2324
+ const configKeyHashes = computeConfigKeyHashes(config);
2300
2325
  if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
2301
2326
  throw new DriftGuardError(
2302
- driftGuardMessage(config.codegen.outDir, manifest.entryPoint, entryPoint)
2327
+ driftGuardMessage(
2328
+ config.codegen.outDir,
2329
+ manifest.entryPoint,
2330
+ entryPoint,
2331
+ // A pre-key-hash manifest can't tell us WHICH keys differ — pass none
2332
+ // rather than diffing against {} (which would name every key).
2333
+ manifest.configKeyHashes ? diffConfigKeyHashes(manifest.configKeyHashes, configKeyHashes) : []
2334
+ )
2303
2335
  );
2304
2336
  }
2305
2337
  const extensions = config.extensions ?? [];
@@ -2369,6 +2401,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
2369
2401
  hash: inputsHash,
2370
2402
  entryPoint,
2371
2403
  configHash,
2404
+ configKeyHashes,
2372
2405
  files: outputFiles
2373
2406
  });
2374
2407
  }
@@ -4801,7 +4834,7 @@ async function watch(config, onChange, options = {}) {
4801
4834
  }
4802
4835
 
4803
4836
  // src/index.ts
4804
- var VERSION = "0.14.0";
4837
+ var VERSION = "0.14.2";
4805
4838
 
4806
4839
  // src/cli/codegen.ts
4807
4840
  async function runCodegen(opts = {}) {