@dudousxd/nestjs-codegen 0.14.1 → 0.15.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 +13 -0
- package/dist/cli/main.cjs +71 -10
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +71 -10
- package/dist/cli/main.js.map +1 -1
- 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.map +1 -1
- package/dist/{index-DT8SgPxp.d.cts → index-CjIDPMsV.d.cts} +7 -0
- package/dist/{index-DT8SgPxp.d.ts → index-CjIDPMsV.d.ts} +7 -0
- package/dist/index.cjs +71 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +71 -10
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +92 -12
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +56 -3
- package/dist/nest/index.d.ts +56 -3
- package/dist/nest/index.js +88 -11
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/nest/index.cjs
CHANGED
|
@@ -42,7 +42,10 @@ __export(nest_exports, {
|
|
|
42
42
|
CODEGEN_MODULE_OPTIONS: () => CODEGEN_MODULE_OPTIONS,
|
|
43
43
|
NestjsCodegenModule: () => NestjsCodegenModule,
|
|
44
44
|
NestjsCodegenService: () => NestjsCodegenService,
|
|
45
|
-
|
|
45
|
+
QueryList: () => QueryList,
|
|
46
|
+
resolveQueryList: () => resolveQueryList,
|
|
47
|
+
shouldRun: () => shouldRun,
|
|
48
|
+
toStringList: () => toStringList
|
|
46
49
|
});
|
|
47
50
|
module.exports = __toCommonJS(nest_exports);
|
|
48
51
|
|
|
@@ -738,6 +741,9 @@ function buildErrorType(c) {
|
|
|
738
741
|
}
|
|
739
742
|
return c.contractSource.error ?? "unknown";
|
|
740
743
|
}
|
|
744
|
+
function filterFieldLiterals(fields) {
|
|
745
|
+
return fields?.length ? fields.map((f) => JSON.stringify(f)) : [];
|
|
746
|
+
}
|
|
741
747
|
function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
742
748
|
const pad = " ".repeat(indent);
|
|
743
749
|
const lines = [];
|
|
@@ -764,7 +770,8 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
|
764
770
|
const params = buildParamsType(c.params);
|
|
765
771
|
const safeMethod = JSON.stringify(method);
|
|
766
772
|
const safeUrl = JSON.stringify(c.path);
|
|
767
|
-
const
|
|
773
|
+
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
774
|
+
const filterFields = filterLiterals.length ? filterLiterals.join(" | ") : "never";
|
|
768
775
|
const stream = c.contractSource.stream ? "true" : "false";
|
|
769
776
|
const binary = c.contractSource.binaryResponse ? "true" : "false";
|
|
770
777
|
lines.push(
|
|
@@ -804,6 +811,7 @@ function buildRequestModel(c) {
|
|
|
804
811
|
const TA = buildRouterTypeAccess(c.name);
|
|
805
812
|
const withParams = hasPathParams(c.params);
|
|
806
813
|
const { isGet, isQuery, hasBody, hasQuery } = requestShape(c.route);
|
|
814
|
+
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
807
815
|
const fields = [];
|
|
808
816
|
if (withParams) fields.push(`params: ${TA}['params']`);
|
|
809
817
|
if (hasQuery) fields.push(`query?: ${TA}['query']`);
|
|
@@ -833,7 +841,12 @@ function buildRequestModel(c) {
|
|
|
833
841
|
// (`[name]` rather than `[name, undefined]`) so the bare `.queryKey()` is a
|
|
834
842
|
// clean prefix that partial-matches every parametrized variant — making it
|
|
835
843
|
// directly usable for `invalidateQueries`.
|
|
836
|
-
queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)
|
|
844
|
+
queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)`,
|
|
845
|
+
// Runtime counterpart to the type-level `filterFields` union: the same
|
|
846
|
+
// discovered field list, emitted as a literal `[...] as const` so apps can
|
|
847
|
+
// validate a dynamic/user-supplied field string with `isFilterField(...)`
|
|
848
|
+
// instead of casting. Omitted for routes with no filter.
|
|
849
|
+
...filterLiterals.length ? { filterFieldsExpr: `[${filterLiterals.join(", ")}] as const` } : {}
|
|
837
850
|
};
|
|
838
851
|
}
|
|
839
852
|
function renderFetcherRequest(req, binaryResponse) {
|
|
@@ -868,9 +881,24 @@ function emitReqHelper() {
|
|
|
868
881
|
""
|
|
869
882
|
];
|
|
870
883
|
}
|
|
884
|
+
function emitFilterFieldGuard() {
|
|
885
|
+
return [
|
|
886
|
+
"/** Runtime guard: narrows `value` to one of the leaf's `filterFields` (a `readonly K[] as const`), so a dynamic field string can be passed to `.where()` without a cast. */",
|
|
887
|
+
"export function isFilterField<const K extends string>(",
|
|
888
|
+
" fields: readonly K[],",
|
|
889
|
+
" value: string,",
|
|
890
|
+
"): value is K {",
|
|
891
|
+
" return (fields as readonly string[]).includes(value);",
|
|
892
|
+
"}",
|
|
893
|
+
""
|
|
894
|
+
];
|
|
895
|
+
}
|
|
871
896
|
function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
|
|
872
897
|
const lines = [`${pad}${objKey}: (input?: ${req.inputType}) => ({`];
|
|
873
898
|
lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
|
|
899
|
+
if (req.filterFieldsExpr) {
|
|
900
|
+
lines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
|
|
901
|
+
}
|
|
874
902
|
if (streamExpr) {
|
|
875
903
|
lines.push(`${pad} stream: () => ${streamExpr},`);
|
|
876
904
|
}
|
|
@@ -1142,6 +1170,9 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1142
1170
|
lines.push("};");
|
|
1143
1171
|
lines.push("");
|
|
1144
1172
|
lines.push(...emitReqHelper());
|
|
1173
|
+
if (contracted.some((r) => r.contract?.contractSource.filterFields?.length)) {
|
|
1174
|
+
lines.push(...emitFilterFieldGuard());
|
|
1175
|
+
}
|
|
1145
1176
|
lines.push("export function createApi(fetcher: Fetcher) {");
|
|
1146
1177
|
lines.push(" return {");
|
|
1147
1178
|
lines.push(
|
|
@@ -2135,8 +2166,9 @@ function debugWarn(message) {
|
|
|
2135
2166
|
}
|
|
2136
2167
|
|
|
2137
2168
|
// src/generate.ts
|
|
2138
|
-
function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint) {
|
|
2139
|
-
|
|
2169
|
+
function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differingKeys) {
|
|
2170
|
+
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)";
|
|
2171
|
+
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.`;
|
|
2140
2172
|
}
|
|
2141
2173
|
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
2142
2174
|
setCodegenDebug(config.debug);
|
|
@@ -2147,9 +2179,17 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
2147
2179
|
return;
|
|
2148
2180
|
}
|
|
2149
2181
|
const configHash = computeConfigHash(config);
|
|
2182
|
+
const configKeyHashes = computeConfigKeyHashes(config);
|
|
2150
2183
|
if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
|
|
2151
2184
|
throw new DriftGuardError(
|
|
2152
|
-
driftGuardMessage(
|
|
2185
|
+
driftGuardMessage(
|
|
2186
|
+
config.codegen.outDir,
|
|
2187
|
+
manifest.entryPoint,
|
|
2188
|
+
entryPoint,
|
|
2189
|
+
// A pre-key-hash manifest can't tell us WHICH keys differ — pass none
|
|
2190
|
+
// rather than diffing against {} (which would name every key).
|
|
2191
|
+
manifest.configKeyHashes ? diffConfigKeyHashes(manifest.configKeyHashes, configKeyHashes) : []
|
|
2192
|
+
)
|
|
2153
2193
|
);
|
|
2154
2194
|
}
|
|
2155
2195
|
const extensions = config.extensions ?? [];
|
|
@@ -2219,6 +2259,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
2219
2259
|
hash: inputsHash,
|
|
2220
2260
|
entryPoint,
|
|
2221
2261
|
configHash,
|
|
2262
|
+
configKeyHashes,
|
|
2222
2263
|
files: outputFiles
|
|
2223
2264
|
});
|
|
2224
2265
|
}
|
|
@@ -4633,7 +4674,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
4633
4674
|
}
|
|
4634
4675
|
|
|
4635
4676
|
// src/index.ts
|
|
4636
|
-
var VERSION = "0.
|
|
4677
|
+
var VERSION = "0.15.0";
|
|
4637
4678
|
|
|
4638
4679
|
// src/generate-manifest.ts
|
|
4639
4680
|
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
@@ -4654,19 +4695,41 @@ function isManifestShape(value) {
|
|
|
4654
4695
|
if (typeof candidate.hash !== "string") return false;
|
|
4655
4696
|
if (candidate.entryPoint !== void 0 && !isEntryPoint(candidate.entryPoint)) return false;
|
|
4656
4697
|
if (candidate.configHash !== void 0 && typeof candidate.configHash !== "string") return false;
|
|
4698
|
+
if (candidate.configKeyHashes !== void 0 && !isStringRecord(candidate.configKeyHashes)) {
|
|
4699
|
+
return false;
|
|
4700
|
+
}
|
|
4657
4701
|
if (!Array.isArray(candidate.files)) return false;
|
|
4658
4702
|
return candidate.files.every((entry) => typeof entry === "string");
|
|
4659
4703
|
}
|
|
4704
|
+
function isStringRecord(value) {
|
|
4705
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
4706
|
+
return Object.values(value).every((entry) => typeof entry === "string");
|
|
4707
|
+
}
|
|
4660
4708
|
function serializeConfig(config) {
|
|
4709
|
+
return serializeConfigValue(config, `unserializable:${config.codegen.outDir}`);
|
|
4710
|
+
}
|
|
4711
|
+
function serializeConfigValue(value, unserializableMarker) {
|
|
4661
4712
|
try {
|
|
4662
|
-
return JSON.stringify(
|
|
4663
|
-
if (typeof
|
|
4664
|
-
return
|
|
4713
|
+
return JSON.stringify(value, (_key, entry) => {
|
|
4714
|
+
if (typeof entry === "function") return `[fn:${entry.name}]`;
|
|
4715
|
+
return entry;
|
|
4665
4716
|
});
|
|
4666
4717
|
} catch {
|
|
4667
|
-
return
|
|
4718
|
+
return unserializableMarker;
|
|
4668
4719
|
}
|
|
4669
4720
|
}
|
|
4721
|
+
function computeConfigKeyHashes(config) {
|
|
4722
|
+
const hashes = {};
|
|
4723
|
+
for (const [key, value] of Object.entries(config)) {
|
|
4724
|
+
if (value === void 0) continue;
|
|
4725
|
+
hashes[key] = (0, import_node_crypto.createHash)("sha256").update(serializeConfigValue(value, `unserializable:${key}`)).digest("hex");
|
|
4726
|
+
}
|
|
4727
|
+
return hashes;
|
|
4728
|
+
}
|
|
4729
|
+
function diffConfigKeyHashes(previous, current) {
|
|
4730
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(current)]);
|
|
4731
|
+
return [...keys].filter((key) => previous[key] !== current[key]).sort();
|
|
4732
|
+
}
|
|
4670
4733
|
async function discoverInputFiles(config) {
|
|
4671
4734
|
const globs = [config.contracts.glob, config.forms.watch];
|
|
4672
4735
|
if (config.pages) globs.push(config.pages.glob);
|
|
@@ -4701,6 +4764,7 @@ async function readManifest(outDir) {
|
|
|
4701
4764
|
hash: parsed.hash,
|
|
4702
4765
|
...parsed.entryPoint ? { entryPoint: parsed.entryPoint } : {},
|
|
4703
4766
|
...parsed.configHash ? { configHash: parsed.configHash } : {},
|
|
4767
|
+
...parsed.configKeyHashes ? { configKeyHashes: parsed.configKeyHashes } : {},
|
|
4704
4768
|
files: parsed.files
|
|
4705
4769
|
};
|
|
4706
4770
|
} catch {
|
|
@@ -4810,11 +4874,27 @@ var NestjsCodegenModule = class {
|
|
|
4810
4874
|
NestjsCodegenModule = __decorateClass([
|
|
4811
4875
|
(0, import_common.Module)({})
|
|
4812
4876
|
], NestjsCodegenModule);
|
|
4877
|
+
|
|
4878
|
+
// src/nest/query-list.ts
|
|
4879
|
+
var import_common2 = require("@nestjs/common");
|
|
4880
|
+
function toStringList(raw) {
|
|
4881
|
+
if (raw === void 0 || raw === null) return [];
|
|
4882
|
+
const arr = Array.isArray(raw) ? raw : String(raw).split(",");
|
|
4883
|
+
return arr.map((entry) => String(entry).trim()).filter((entry) => entry.length > 0);
|
|
4884
|
+
}
|
|
4885
|
+
function resolveQueryList(key, ctx) {
|
|
4886
|
+
const request = ctx.switchToHttp().getRequest();
|
|
4887
|
+
return toStringList(key ? request.query?.[key] : void 0);
|
|
4888
|
+
}
|
|
4889
|
+
var QueryList = (0, import_common2.createParamDecorator)(resolveQueryList);
|
|
4813
4890
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4814
4891
|
0 && (module.exports = {
|
|
4815
4892
|
CODEGEN_MODULE_OPTIONS,
|
|
4816
4893
|
NestjsCodegenModule,
|
|
4817
4894
|
NestjsCodegenService,
|
|
4818
|
-
|
|
4895
|
+
QueryList,
|
|
4896
|
+
resolveQueryList,
|
|
4897
|
+
shouldRun,
|
|
4898
|
+
toStringList
|
|
4819
4899
|
});
|
|
4820
4900
|
//# sourceMappingURL=index.cjs.map
|