@dudousxd/nestjs-codegen 0.13.2 → 0.14.1
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 +42 -0
- package/dist/cli/main.cjs +92 -18
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +92 -18
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.cjs +2 -2
- package/dist/extension/index.cjs.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js +2 -2
- package/dist/extension/index.js.map +1 -1
- package/dist/{index-D8RIMVpU.d.cts → index-DT8SgPxp.d.cts} +35 -2
- package/dist/{index-D8RIMVpU.d.ts → index-DT8SgPxp.d.ts} +35 -2
- package/dist/index.cjs +92 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -19
- package/dist/index.d.ts +64 -19
- package/dist/index.js +91 -16
- package/dist/index.js.map +1 -1
- package/dist/markers.cjs +34 -0
- package/dist/markers.cjs.map +1 -0
- package/dist/markers.d.cts +32 -0
- package/dist/markers.d.ts +32 -0
- package/dist/markers.js +9 -0
- package/dist/markers.js.map +1 -0
- package/dist/nest/index.cjs +4027 -3946
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +4049 -3968
- package/dist/nest/index.js.map +1 -1
- package/package.json +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @dudousxd/nestjs-codegen
|
|
2
2
|
|
|
3
|
+
## 0.14.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 093f3d5: A bare `@UploadedFile()` route (no `@Body()` DTO) now emits a working multipart leaf.
|
|
8
|
+
`requestShape().hasBody` ignored `multipart`, so while the ApiRouter TYPE promised
|
|
9
|
+
`body: { file: File | Blob }` (the multipart intersection), the generated call accepted no
|
|
10
|
+
body and sent no file. `multipart` now implies a body; routes with a `@Body()` DTO were
|
|
11
|
+
already correct.
|
|
12
|
+
|
|
13
|
+
## 0.14.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- fc78a39: feat: binary (blob) response mode, `@AsQuery()` marker, CLI↔module config-drift guard, and a `handleQuery` TanStack helper.
|
|
18
|
+
|
|
19
|
+
- **Binary (blob) response mode.** A handler returning NestJS `StreamableFile` or Node `Buffer`
|
|
20
|
+
(including `Promise<StreamableFile>`) is now discovered as `binaryResponse: true` and emitted
|
|
21
|
+
with `response: RawResponse<Blob>` (never `Jsonify<...>`) — the leaf issues its request via
|
|
22
|
+
`fetcher.fetchBlob(...)` instead of the verb method, so callers get `{ data, status, headers }`
|
|
23
|
+
and can read `content-disposition` etc. Works on any HTTP method (`fetchBlob` already accepted
|
|
24
|
+
a `method` opt); a non-GET binary route passes it explicitly since `fetchBlob` defaults to GET.
|
|
25
|
+
`Observable`/`ReadableStream` handlers are unaffected — they stay on the existing SSE/stream
|
|
26
|
+
path. Each `ApiRouter` leaf now also carries a `binary` flag (`Route.Binary<K>` /
|
|
27
|
+
`Path.Binary<M, U>` type helpers), mirroring `stream`.
|
|
28
|
+
- **`@AsQuery()` marker** (new `@dudousxd/nestjs-codegen/markers` subpath — zero-import, runtime
|
|
29
|
+
no-op). Marks a non-GET route whose semantics are a read (e.g. a POST with a query-shaped
|
|
30
|
+
payload) so codegen emits `queryOptions` for it, exactly like a GET or a filter-search route.
|
|
31
|
+
- **CLI↔module config-drift guard.** The CLI (`nestjs-codegen.config.ts`) and the Nest module
|
|
32
|
+
(`NestjsCodegenModule.forRoot()`) can target the same `outDir` from independently-resolved
|
|
33
|
+
configs; if they genuinely differ (e.g. `serialization` `'json'` vs `'superjson'`), each run
|
|
34
|
+
used to silently overwrite the other's `api.ts` shape. `generate()` now throws a
|
|
35
|
+
`DriftGuardError` _before writing anything_ when the manifest's `entryPoint` differs from the
|
|
36
|
+
current run's AND the resolved configs' hashes differ — naming both entry points and
|
|
37
|
+
instructing how to fix it (share one config object, or set `driftGuard: false`). Same entry
|
|
38
|
+
point (a normal config edit) or same config across entry points both proceed as before.
|
|
39
|
+
- **TanStack: `handleQuery` helper**, emitted into `api.ts` whenever the TanStack layer is
|
|
40
|
+
active. Wraps any `{ queryKey, fetch }`-shaped handle (a POST-as-query handle, or a runtime
|
|
41
|
+
pick between two different handles) into a plain `{ queryKey, queryFn }` pair — solves the
|
|
42
|
+
useQuery-overload break from spreading a ternary of `queryOptions()` calls. Also: binary GET
|
|
43
|
+
routes get `queryOptions` but never `infiniteQueryOptions` (a download isn't paginated data).
|
|
44
|
+
|
|
3
45
|
## 0.13.2
|
|
4
46
|
|
|
5
47
|
### 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,8 +641,8 @@ 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 hasBody = !!cs?.bodyRef || cs?.body != null && cs.body !== "never";
|
|
644
|
+
const isQuery = isGet || !!cs?.filterFields?.length || !!cs?.asQuery;
|
|
645
|
+
const hasBody = !!cs?.bodyRef || cs?.body != null && cs.body !== "never" || !!cs?.multipart;
|
|
645
646
|
const hasQuery = isGet || !!cs?.queryRef || cs?.query != null && cs.query !== "never";
|
|
646
647
|
return { isGet, isQuery, hasBody, hasQuery };
|
|
647
648
|
}
|
|
@@ -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 {
|
|
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
|
-
|
|
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
|
}
|
|
@@ -3891,6 +3943,14 @@ function resolveBodyQueryResponseRef(typeNode, sourceFile, project) {
|
|
|
3891
3943
|
var STREAM_CONTAINERS = /* @__PURE__ */ new Set(["Observable", "AsyncIterable", "AsyncIterableIterator"]);
|
|
3892
3944
|
var STREAM_CONTAINERS_GENERATOR = /* @__PURE__ */ new Set(["AsyncGenerator"]);
|
|
3893
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
|
+
}
|
|
3894
3954
|
function detectStreamElement(method) {
|
|
3895
3955
|
const hasSse = method.getDecorators().some((d) => d.getName() === "Sse");
|
|
3896
3956
|
let node = method.getReturnTypeNode();
|
|
@@ -3911,6 +3971,9 @@ function streamContainerElement(node) {
|
|
|
3911
3971
|
}
|
|
3912
3972
|
return null;
|
|
3913
3973
|
}
|
|
3974
|
+
function hasAsQueryDecorator(method) {
|
|
3975
|
+
return method.getDecorators().some((d) => d.getName() === "AsQuery");
|
|
3976
|
+
}
|
|
3914
3977
|
function unwrapNamedContainer(node, names) {
|
|
3915
3978
|
if (!node || !import_ts_morph7.Node.isTypeReference(node)) return node;
|
|
3916
3979
|
const typeName = node.getTypeName();
|
|
@@ -3928,6 +3991,8 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3928
3991
|
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
3929
3992
|
const streamElement = detectStreamElement(method);
|
|
3930
3993
|
const isStream = streamElement !== null;
|
|
3994
|
+
const binaryResponse = detectBinaryResponse(method);
|
|
3995
|
+
const asQuery = hasAsQueryDecorator(method);
|
|
3931
3996
|
if (filterInfo && filterInfo.source === "body") {
|
|
3932
3997
|
const bodyType = "import('@dudousxd/nestjs-filter-client').FilterQueryResult";
|
|
3933
3998
|
body = body ?? bodyType;
|
|
@@ -3935,7 +4000,7 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
3935
4000
|
const paramsType = extractParamsType(method, sourceFile, project);
|
|
3936
4001
|
const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
|
|
3937
4002
|
const errorInfo = extractErrorType(method, sourceFile, project);
|
|
3938
|
-
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) {
|
|
3939
4004
|
return null;
|
|
3940
4005
|
}
|
|
3941
4006
|
let bodyRef = null;
|
|
@@ -4010,7 +4075,9 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
4010
4075
|
querySchema,
|
|
4011
4076
|
stream: isStream,
|
|
4012
4077
|
multipart: uploads.multipart,
|
|
4013
|
-
multipartBody
|
|
4078
|
+
multipartBody,
|
|
4079
|
+
binaryResponse,
|
|
4080
|
+
asQuery
|
|
4014
4081
|
};
|
|
4015
4082
|
}
|
|
4016
4083
|
function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
@@ -4497,7 +4564,9 @@ function extractDtoRoute(args) {
|
|
|
4497
4564
|
querySchema: dtoContract?.querySchema ?? null,
|
|
4498
4565
|
stream: dtoContract?.stream ?? false,
|
|
4499
4566
|
multipart: dtoContract?.multipart ?? false,
|
|
4500
|
-
multipartBody: dtoContract?.multipartBody ?? null
|
|
4567
|
+
multipartBody: dtoContract?.multipartBody ?? null,
|
|
4568
|
+
binaryResponse: dtoContract?.binaryResponse ?? false,
|
|
4569
|
+
asQuery: dtoContract?.asQuery ?? false
|
|
4501
4570
|
}
|
|
4502
4571
|
});
|
|
4503
4572
|
}
|
|
@@ -4590,6 +4659,7 @@ var PAGES_DEBOUNCE_MS = 150;
|
|
|
4590
4659
|
var NO_OP_WATCHER = { close: async () => {
|
|
4591
4660
|
} };
|
|
4592
4661
|
async function watch(config, onChange, options = {}) {
|
|
4662
|
+
const entryPoint = options.entryPoint ?? "cli";
|
|
4593
4663
|
const lock = await acquireLock(config.codegen.outDir);
|
|
4594
4664
|
if (lock === null) {
|
|
4595
4665
|
let holderPid = "unknown";
|
|
@@ -4621,13 +4691,17 @@ async function watch(config, onChange, options = {}) {
|
|
|
4621
4691
|
try {
|
|
4622
4692
|
const initialRoutes = (await getDiscovery()).discover();
|
|
4623
4693
|
lastRoutes = initialRoutes;
|
|
4624
|
-
await generate(config, initialRoutes);
|
|
4694
|
+
await generate(config, initialRoutes, entryPoint);
|
|
4625
4695
|
} catch (err) {
|
|
4696
|
+
if (err instanceof DriftGuardError) {
|
|
4697
|
+
console.error(err.message);
|
|
4698
|
+
return;
|
|
4699
|
+
}
|
|
4626
4700
|
console.warn(
|
|
4627
4701
|
`[nestjs-codegen] Initial route discovery failed, falling back to pages-only: ${err instanceof Error ? err.message : String(err)}`
|
|
4628
4702
|
);
|
|
4629
4703
|
try {
|
|
4630
|
-
await generate(config, lastRoutes);
|
|
4704
|
+
await generate(config, lastRoutes, entryPoint);
|
|
4631
4705
|
} catch {
|
|
4632
4706
|
}
|
|
4633
4707
|
}
|
|
@@ -4655,7 +4729,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4655
4729
|
pagesDebounceTimer = setTimeout(async () => {
|
|
4656
4730
|
pagesDebounceTimer = void 0;
|
|
4657
4731
|
try {
|
|
4658
|
-
await generate(config, lastRoutes);
|
|
4732
|
+
await generate(config, lastRoutes, entryPoint);
|
|
4659
4733
|
} catch (err) {
|
|
4660
4734
|
console.error(
|
|
4661
4735
|
"[nestjs-codegen] Pages generation failed:",
|
|
@@ -4687,7 +4761,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4687
4761
|
try {
|
|
4688
4762
|
const routes = await (await getDiscovery()).rediscover(changed);
|
|
4689
4763
|
lastRoutes = routes;
|
|
4690
|
-
await generate(config, routes);
|
|
4764
|
+
await generate(config, routes, entryPoint);
|
|
4691
4765
|
} catch (err) {
|
|
4692
4766
|
console.error(
|
|
4693
4767
|
"[nestjs-codegen] Contracts generation failed:",
|
|
@@ -4727,14 +4801,14 @@ async function watch(config, onChange, options = {}) {
|
|
|
4727
4801
|
}
|
|
4728
4802
|
|
|
4729
4803
|
// src/index.ts
|
|
4730
|
-
var VERSION = "0.
|
|
4804
|
+
var VERSION = "0.14.1";
|
|
4731
4805
|
|
|
4732
4806
|
// src/cli/codegen.ts
|
|
4733
4807
|
async function runCodegen(opts = {}) {
|
|
4734
4808
|
const cwd = opts.cwd ?? process.cwd();
|
|
4735
4809
|
const config = await loadConfig(cwd);
|
|
4736
4810
|
if (opts.watch) {
|
|
4737
|
-
const watcher = await watch(config);
|
|
4811
|
+
const watcher = await watch(config, void 0, { entryPoint: "cli" });
|
|
4738
4812
|
await new Promise((resolve4) => {
|
|
4739
4813
|
function onSignal() {
|
|
4740
4814
|
watcher.close().then(resolve4).catch(resolve4);
|
|
@@ -4749,7 +4823,7 @@ async function runCodegen(opts = {}) {
|
|
|
4749
4823
|
glob: config.contracts.glob,
|
|
4750
4824
|
...config.app?.tsconfig ? { tsconfig: config.app.tsconfig } : {}
|
|
4751
4825
|
});
|
|
4752
|
-
await generate(config, routes);
|
|
4826
|
+
await generate(config, routes, "cli");
|
|
4753
4827
|
console.log("\u2713 Codegen generated artifacts in", config.codegen.outDir);
|
|
4754
4828
|
}
|
|
4755
4829
|
|