@dudousxd/nestjs-codegen 0.14.2 → 0.16.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 +49 -0
- package/dist/cli/main.cjs +207 -57
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +206 -56
- 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-LQNP7Ms3.d.cts} +30 -0
- package/dist/{index-DT8SgPxp.d.ts → index-LQNP7Ms3.d.ts} +30 -0
- package/dist/index.cjs +207 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +206 -56
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +227 -59
- 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 +222 -57
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -807,6 +807,9 @@ function buildErrorType(c) {
|
|
|
807
807
|
}
|
|
808
808
|
return c.contractSource.error ?? "unknown";
|
|
809
809
|
}
|
|
810
|
+
function filterFieldLiterals(fields) {
|
|
811
|
+
return fields?.length ? fields.map((f) => JSON.stringify(f)) : [];
|
|
812
|
+
}
|
|
810
813
|
function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
811
814
|
const pad = " ".repeat(indent);
|
|
812
815
|
const lines = [];
|
|
@@ -833,7 +836,8 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
|
833
836
|
const params = buildParamsType(c.params);
|
|
834
837
|
const safeMethod = JSON.stringify(method);
|
|
835
838
|
const safeUrl = JSON.stringify(c.path);
|
|
836
|
-
const
|
|
839
|
+
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
840
|
+
const filterFields = filterLiterals.length ? filterLiterals.join(" | ") : "never";
|
|
837
841
|
const stream = c.contractSource.stream ? "true" : "false";
|
|
838
842
|
const binary = c.contractSource.binaryResponse ? "true" : "false";
|
|
839
843
|
lines.push(
|
|
@@ -873,6 +877,7 @@ function buildRequestModel(c) {
|
|
|
873
877
|
const TA = buildRouterTypeAccess(c.name);
|
|
874
878
|
const withParams = hasPathParams(c.params);
|
|
875
879
|
const { isGet, isQuery, hasBody, hasQuery } = requestShape(c.route);
|
|
880
|
+
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
876
881
|
const fields = [];
|
|
877
882
|
if (withParams) fields.push(`params: ${TA}['params']`);
|
|
878
883
|
if (hasQuery) fields.push(`query?: ${TA}['query']`);
|
|
@@ -902,7 +907,12 @@ function buildRequestModel(c) {
|
|
|
902
907
|
// (`[name]` rather than `[name, undefined]`) so the bare `.queryKey()` is a
|
|
903
908
|
// clean prefix that partial-matches every parametrized variant — making it
|
|
904
909
|
// directly usable for `invalidateQueries`.
|
|
905
|
-
queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)
|
|
910
|
+
queryKeyExpr: `(input === undefined ? [${flat}] as const : [${flat}, input] as const)`,
|
|
911
|
+
// Runtime counterpart to the type-level `filterFields` union: the same
|
|
912
|
+
// discovered field list, emitted as a literal `[...] as const` so apps can
|
|
913
|
+
// validate a dynamic/user-supplied field string with `isFilterField(...)`
|
|
914
|
+
// instead of casting. Omitted for routes with no filter.
|
|
915
|
+
...filterLiterals.length ? { filterFieldsExpr: `[${filterLiterals.join(", ")}] as const` } : {}
|
|
906
916
|
};
|
|
907
917
|
}
|
|
908
918
|
function renderFetcherRequest(req, binaryResponse) {
|
|
@@ -937,9 +947,24 @@ function emitReqHelper() {
|
|
|
937
947
|
""
|
|
938
948
|
];
|
|
939
949
|
}
|
|
950
|
+
function emitFilterFieldGuard() {
|
|
951
|
+
return [
|
|
952
|
+
"/** 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. */",
|
|
953
|
+
"export function isFilterField<const K extends string>(",
|
|
954
|
+
" fields: readonly K[],",
|
|
955
|
+
" value: string,",
|
|
956
|
+
"): value is K {",
|
|
957
|
+
" return (fields as readonly string[]).includes(value);",
|
|
958
|
+
"}",
|
|
959
|
+
""
|
|
960
|
+
];
|
|
961
|
+
}
|
|
940
962
|
function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
|
|
941
963
|
const lines = [`${pad}${objKey}: (input?: ${req.inputType}) => ({`];
|
|
942
964
|
lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
|
|
965
|
+
if (req.filterFieldsExpr) {
|
|
966
|
+
lines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
|
|
967
|
+
}
|
|
943
968
|
if (streamExpr) {
|
|
944
969
|
lines.push(`${pad} stream: () => ${streamExpr},`);
|
|
945
970
|
}
|
|
@@ -1211,6 +1236,9 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
1211
1236
|
lines.push("};");
|
|
1212
1237
|
lines.push("");
|
|
1213
1238
|
lines.push(...emitReqHelper());
|
|
1239
|
+
if (contracted.some((r) => r.contract?.contractSource.filterFields?.length)) {
|
|
1240
|
+
lines.push(...emitFilterFieldGuard());
|
|
1241
|
+
}
|
|
1214
1242
|
lines.push("export function createApi(fetcher: Fetcher) {");
|
|
1215
1243
|
lines.push(" return {");
|
|
1216
1244
|
lines.push(
|
|
@@ -2449,7 +2477,7 @@ var import_chokidar = __toESM(require("chokidar"), 1);
|
|
|
2449
2477
|
// src/discovery/contracts-fast.ts
|
|
2450
2478
|
var import_node_path15 = require("path");
|
|
2451
2479
|
var import_fast_glob3 = __toESM(require("fast-glob"), 1);
|
|
2452
|
-
var
|
|
2480
|
+
var import_ts_morph10 = require("ts-morph");
|
|
2453
2481
|
|
|
2454
2482
|
// src/discovery/dto-type-resolver.ts
|
|
2455
2483
|
var import_ts_morph7 = require("ts-morph");
|
|
@@ -3428,6 +3456,12 @@ function toFilterFieldType(name, r) {
|
|
|
3428
3456
|
}
|
|
3429
3457
|
|
|
3430
3458
|
// src/discovery/filter-for.ts
|
|
3459
|
+
function resolveMixinEntityClass(mixin, project) {
|
|
3460
|
+
const entityArg = mixin?.classArgs[0];
|
|
3461
|
+
if (!entityArg) return void 0;
|
|
3462
|
+
const file = project.getSourceFile(entityArg.filePath) ?? project.addSourceFileAtPathIfExists(entityArg.filePath);
|
|
3463
|
+
return file?.getClass(entityArg.name);
|
|
3464
|
+
}
|
|
3431
3465
|
function classifyFilterForHint(typeInit) {
|
|
3432
3466
|
if (import_ts_morph6.Node.isStringLiteral(typeInit)) {
|
|
3433
3467
|
switch (typeInit.getLiteralValue()) {
|
|
@@ -3501,7 +3535,9 @@ function extractFilterForHints(classDecl, project) {
|
|
|
3501
3535
|
}
|
|
3502
3536
|
return hints;
|
|
3503
3537
|
}
|
|
3504
|
-
function extractApplyFilterInfo(method, sourceFile, project) {
|
|
3538
|
+
function extractApplyFilterInfo(method, sourceFile, project, mixin) {
|
|
3539
|
+
const declFile = method.getSourceFile();
|
|
3540
|
+
const mixinEntity = resolveMixinEntityClass(mixin, project);
|
|
3505
3541
|
for (const param of method.getParameters()) {
|
|
3506
3542
|
const filterDecorator = param.getDecorators().find((d) => d.getName() === "ApplyFilter");
|
|
3507
3543
|
if (!filterDecorator) continue;
|
|
@@ -3521,12 +3557,12 @@ function extractApplyFilterInfo(method, sourceFile, project) {
|
|
|
3521
3557
|
}
|
|
3522
3558
|
}
|
|
3523
3559
|
const filterClassName = filterClassArg.getText();
|
|
3524
|
-
const resolved = findType(filterClassName,
|
|
3525
|
-
|
|
3526
|
-
|
|
3560
|
+
const resolved = findType(filterClassName, declFile, project);
|
|
3561
|
+
const classDecl = resolved?.kind === "class" ? resolved.decl : resolveLocalClassDeclaration(filterClassArg);
|
|
3562
|
+
if (classDecl) {
|
|
3527
3563
|
let fieldTypes = extractClassPropertyTypes(classDecl, project);
|
|
3528
3564
|
if (fieldTypes.length === 0) {
|
|
3529
|
-
fieldTypes = extractFilterableEntityFields(classDecl, project);
|
|
3565
|
+
fieldTypes = extractFilterableEntityFields(classDecl, project, mixinEntity);
|
|
3530
3566
|
}
|
|
3531
3567
|
const filterForHints = extractFilterForHints(classDecl, project);
|
|
3532
3568
|
if (filterForHints.size > 0) {
|
|
@@ -3611,22 +3647,29 @@ function extractClassPropertyTypes(classDecl, project) {
|
|
|
3611
3647
|
}
|
|
3612
3648
|
return fields;
|
|
3613
3649
|
}
|
|
3614
|
-
function
|
|
3650
|
+
function resolveLocalClassDeclaration(identifier) {
|
|
3651
|
+
if (!import_ts_morph6.Node.isIdentifier(identifier)) return void 0;
|
|
3652
|
+
return identifier.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => n !== void 0 && import_ts_morph6.Node.isClassDeclaration(n));
|
|
3653
|
+
}
|
|
3654
|
+
function resolveDeclaredEntity(optionsArg, filterClass, project) {
|
|
3655
|
+
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return void 0;
|
|
3656
|
+
const entityProp = optionsArg.getProperty("entity");
|
|
3657
|
+
if (!entityProp || !import_ts_morph6.Node.isPropertyAssignment(entityProp)) return void 0;
|
|
3658
|
+
const entityInit = entityProp.getInitializer();
|
|
3659
|
+
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return void 0;
|
|
3660
|
+
const resolved = findType(entityInit.getText(), filterClass.getSourceFile(), project);
|
|
3661
|
+
if (!resolved || resolved.kind !== "class") return void 0;
|
|
3662
|
+
return resolved.decl;
|
|
3663
|
+
}
|
|
3664
|
+
function extractFilterableEntityFields(filterClass, project, entityOverride) {
|
|
3615
3665
|
const filterableDecorator = filterClass.getDecorators().find((d) => d.getName() === "Filterable");
|
|
3616
3666
|
if (!filterableDecorator) return [];
|
|
3617
3667
|
const args = filterableDecorator.getArguments();
|
|
3618
3668
|
if (args.length === 0) return [];
|
|
3619
3669
|
const optionsArg = args[0];
|
|
3620
3670
|
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) return [];
|
|
3621
|
-
const
|
|
3622
|
-
if (!
|
|
3623
|
-
const entityInit = entityProp.getInitializer();
|
|
3624
|
-
if (!entityInit || !import_ts_morph6.Node.isIdentifier(entityInit)) return [];
|
|
3625
|
-
const entityName = entityInit.getText();
|
|
3626
|
-
const filterSourceFile = filterClass.getSourceFile();
|
|
3627
|
-
const resolvedEntity = findType(entityName, filterSourceFile, project);
|
|
3628
|
-
if (!resolvedEntity || resolvedEntity.kind !== "class") return [];
|
|
3629
|
-
const entityDecl = resolvedEntity.decl;
|
|
3671
|
+
const entityDecl = entityOverride ?? resolveDeclaredEntity(optionsArg, filterClass, project);
|
|
3672
|
+
if (!entityDecl) return [];
|
|
3630
3673
|
const fields = collectEntityFields(
|
|
3631
3674
|
entityDecl,
|
|
3632
3675
|
entityDecl.getSourceFile(),
|
|
@@ -4051,9 +4094,9 @@ function unwrapNamedContainer(node, names) {
|
|
|
4051
4094
|
}
|
|
4052
4095
|
return node;
|
|
4053
4096
|
}
|
|
4054
|
-
function extractDtoContract(method, sourceFile, project) {
|
|
4097
|
+
function extractDtoContract(method, sourceFile, project, mixin) {
|
|
4055
4098
|
let body = extractBodyType(method, sourceFile, project);
|
|
4056
|
-
const filterInfo = extractApplyFilterInfo(method, sourceFile, project);
|
|
4099
|
+
const filterInfo = extractApplyFilterInfo(method, sourceFile, project, mixin);
|
|
4057
4100
|
const query = extractQueryType(method, sourceFile, project);
|
|
4058
4101
|
const uploads = extractUploadedFiles(method);
|
|
4059
4102
|
const multipartBody = uploads.fields ? `{ ${uploads.fields} }` : null;
|
|
@@ -4163,12 +4206,94 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
4163
4206
|
return null;
|
|
4164
4207
|
}
|
|
4165
4208
|
|
|
4166
|
-
// src/discovery/
|
|
4209
|
+
// src/discovery/heritage.ts
|
|
4167
4210
|
var import_ts_morph8 = require("ts-morph");
|
|
4211
|
+
function unwrapExpression(node) {
|
|
4212
|
+
let current = node;
|
|
4213
|
+
while (import_ts_morph8.Node.isAsExpression(current) || import_ts_morph8.Node.isSatisfiesExpression(current) || import_ts_morph8.Node.isParenthesizedExpression(current) || import_ts_morph8.Node.isTypeAssertion(current)) {
|
|
4214
|
+
current = current.getExpression();
|
|
4215
|
+
}
|
|
4216
|
+
return current;
|
|
4217
|
+
}
|
|
4218
|
+
function resolveReturnedClass(factoryDecl) {
|
|
4219
|
+
const body = factoryDecl.getBody();
|
|
4220
|
+
if (!body) return void 0;
|
|
4221
|
+
const returns = body.getDescendants().filter((n) => import_ts_morph8.Node.isReturnStatement(n));
|
|
4222
|
+
for (const ret of returns) {
|
|
4223
|
+
const expr = ret.getExpression();
|
|
4224
|
+
if (!expr) continue;
|
|
4225
|
+
const inner = unwrapExpression(expr);
|
|
4226
|
+
if (import_ts_morph8.Node.isClassExpression(inner)) {
|
|
4227
|
+
return inner;
|
|
4228
|
+
}
|
|
4229
|
+
if (import_ts_morph8.Node.isIdentifier(inner)) {
|
|
4230
|
+
const name = inner.getText();
|
|
4231
|
+
const decl = body.getDescendants().find((n) => import_ts_morph8.Node.isClassDeclaration(n) && n.getName() === name);
|
|
4232
|
+
if (decl) return decl;
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
return void 0;
|
|
4236
|
+
}
|
|
4237
|
+
function resolveInheritedMethods(cls) {
|
|
4238
|
+
const expr = cls.getExtends()?.getExpression();
|
|
4239
|
+
if (!expr || !import_ts_morph8.Node.isCallExpression(expr)) return void 0;
|
|
4240
|
+
const callee = expr.getExpression();
|
|
4241
|
+
if (!import_ts_morph8.Node.isIdentifier(callee)) return void 0;
|
|
4242
|
+
const factoryDecl = callee.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isFunctionDeclaration(n));
|
|
4243
|
+
if (!factoryDecl) return void 0;
|
|
4244
|
+
const returnedClass = resolveReturnedClass(factoryDecl);
|
|
4245
|
+
if (!returnedClass) return void 0;
|
|
4246
|
+
const classArgs = [];
|
|
4247
|
+
for (const arg of expr.getArguments()) {
|
|
4248
|
+
if (!import_ts_morph8.Node.isIdentifier(arg)) continue;
|
|
4249
|
+
const decl = arg.getDefinitions().map((d) => d.getDeclarationNode()).find((n) => import_ts_morph8.Node.isClassDeclaration(n));
|
|
4250
|
+
if (decl) {
|
|
4251
|
+
classArgs.push({
|
|
4252
|
+
name: decl.getName() ?? arg.getText(),
|
|
4253
|
+
filePath: decl.getSourceFile().getFilePath()
|
|
4254
|
+
});
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
return {
|
|
4258
|
+
methods: returnedClass.getMethods(),
|
|
4259
|
+
factoryName: callee.getText(),
|
|
4260
|
+
factoryFilePath: factoryDecl.getSourceFile().getFilePath(),
|
|
4261
|
+
classArgs
|
|
4262
|
+
};
|
|
4263
|
+
}
|
|
4264
|
+
var mixinTypeProject;
|
|
4265
|
+
function getMixinTypeProject() {
|
|
4266
|
+
mixinTypeProject ??= new import_ts_morph8.Project({
|
|
4267
|
+
skipAddingFilesFromTsConfig: true,
|
|
4268
|
+
compilerOptions: { strict: true }
|
|
4269
|
+
});
|
|
4270
|
+
return mixinTypeProject;
|
|
4271
|
+
}
|
|
4272
|
+
function clearMixinTypeProject() {
|
|
4273
|
+
mixinTypeProject = void 0;
|
|
4274
|
+
}
|
|
4275
|
+
function resolveInstantiatedReturnType(cls, methodName) {
|
|
4276
|
+
const filePath = cls.getSourceFile().getFilePath();
|
|
4277
|
+
const className = cls.getName();
|
|
4278
|
+
if (!className) return void 0;
|
|
4279
|
+
const project = getMixinTypeProject();
|
|
4280
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPathIfExists(filePath);
|
|
4281
|
+
const typedCls = sourceFile?.getClass(className);
|
|
4282
|
+
if (!typedCls) return void 0;
|
|
4283
|
+
const prop = typedCls.getType().getProperty(methodName);
|
|
4284
|
+
if (!prop) return void 0;
|
|
4285
|
+
const returnType = prop.getTypeAtLocation(typedCls).getCallSignatures()[0]?.getReturnType();
|
|
4286
|
+
if (!returnType) return void 0;
|
|
4287
|
+
const unwrapped = returnType.getSymbol()?.getName() === "Promise" ? returnType.getTypeArguments()[0] ?? returnType : returnType;
|
|
4288
|
+
return unwrapped.getText(typedCls);
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
4292
|
+
var import_ts_morph9 = require("ts-morph");
|
|
4168
4293
|
function zodAstToTs(node) {
|
|
4169
|
-
if (!
|
|
4294
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return "unknown";
|
|
4170
4295
|
const expr = node.getExpression();
|
|
4171
|
-
if (
|
|
4296
|
+
if (import_ts_morph9.Node.isPropertyAccessExpression(expr)) {
|
|
4172
4297
|
const methodName = expr.getName();
|
|
4173
4298
|
const receiver = expr.getExpression();
|
|
4174
4299
|
if (methodName === "optional") {
|
|
@@ -4192,17 +4317,17 @@ function zodAstToTs(node) {
|
|
|
4192
4317
|
case "literal": {
|
|
4193
4318
|
const lit = args[0];
|
|
4194
4319
|
if (!lit) return "unknown";
|
|
4195
|
-
if (
|
|
4196
|
-
if (
|
|
4197
|
-
if (lit.getKind() ===
|
|
4198
|
-
if (lit.getKind() ===
|
|
4320
|
+
if (import_ts_morph9.Node.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
4321
|
+
if (import_ts_morph9.Node.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
4322
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.TrueKeyword) return "true";
|
|
4323
|
+
if (lit.getKind() === import_ts_morph9.SyntaxKind.FalseKeyword) return "false";
|
|
4199
4324
|
return "unknown";
|
|
4200
4325
|
}
|
|
4201
4326
|
case "enum": {
|
|
4202
4327
|
const arrArg = args[0];
|
|
4203
|
-
if (!arrArg || !
|
|
4328
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4204
4329
|
const members = arrArg.getElements().map(
|
|
4205
|
-
(el) =>
|
|
4330
|
+
(el) => import_ts_morph9.Node.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
4206
4331
|
);
|
|
4207
4332
|
return members.join(" | ");
|
|
4208
4333
|
}
|
|
@@ -4213,10 +4338,10 @@ function zodAstToTs(node) {
|
|
|
4213
4338
|
}
|
|
4214
4339
|
case "object": {
|
|
4215
4340
|
const objArg = args[0];
|
|
4216
|
-
if (!objArg || !
|
|
4341
|
+
if (!objArg || !import_ts_morph9.Node.isObjectLiteralExpression(objArg)) return "unknown";
|
|
4217
4342
|
const lines = [];
|
|
4218
4343
|
for (const prop of objArg.getProperties()) {
|
|
4219
|
-
if (!
|
|
4344
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4220
4345
|
const key = prop.getName();
|
|
4221
4346
|
const valNode = prop.getInitializer();
|
|
4222
4347
|
if (!valNode) continue;
|
|
@@ -4228,7 +4353,7 @@ function zodAstToTs(node) {
|
|
|
4228
4353
|
}
|
|
4229
4354
|
case "union": {
|
|
4230
4355
|
const arrArg = args[0];
|
|
4231
|
-
if (!arrArg || !
|
|
4356
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4232
4357
|
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
4233
4358
|
}
|
|
4234
4359
|
case "record": {
|
|
@@ -4238,7 +4363,7 @@ function zodAstToTs(node) {
|
|
|
4238
4363
|
}
|
|
4239
4364
|
case "tuple": {
|
|
4240
4365
|
const arrArg = args[0];
|
|
4241
|
-
if (!arrArg || !
|
|
4366
|
+
if (!arrArg || !import_ts_morph9.Node.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
4242
4367
|
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
4243
4368
|
}
|
|
4244
4369
|
default:
|
|
@@ -4248,18 +4373,18 @@ function zodAstToTs(node) {
|
|
|
4248
4373
|
return "unknown";
|
|
4249
4374
|
}
|
|
4250
4375
|
function isOptionalChain(node) {
|
|
4251
|
-
if (!
|
|
4376
|
+
if (!import_ts_morph9.Node.isCallExpression(node)) return false;
|
|
4252
4377
|
const expr = node.getExpression();
|
|
4253
|
-
return
|
|
4378
|
+
return import_ts_morph9.Node.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
4254
4379
|
}
|
|
4255
4380
|
function parseDefineContractCall(callExpr) {
|
|
4256
|
-
if (!
|
|
4381
|
+
if (!import_ts_morph9.Node.isCallExpression(callExpr)) return null;
|
|
4257
4382
|
const callee = callExpr.getExpression();
|
|
4258
|
-
const calleeName =
|
|
4383
|
+
const calleeName = import_ts_morph9.Node.isIdentifier(callee) ? callee.getText() : import_ts_morph9.Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
4259
4384
|
if (calleeName !== "defineContract") return null;
|
|
4260
4385
|
const args = callExpr.getArguments();
|
|
4261
4386
|
const optsArg = args[0];
|
|
4262
|
-
if (!optsArg || !
|
|
4387
|
+
if (!optsArg || !import_ts_morph9.Node.isObjectLiteralExpression(optsArg)) return null;
|
|
4263
4388
|
let query = null;
|
|
4264
4389
|
let body = null;
|
|
4265
4390
|
let response = "unknown";
|
|
@@ -4267,7 +4392,7 @@ function parseDefineContractCall(callExpr) {
|
|
|
4267
4392
|
let bodyZodText = null;
|
|
4268
4393
|
let queryZodText = null;
|
|
4269
4394
|
for (const prop of optsArg.getProperties()) {
|
|
4270
|
-
if (!
|
|
4395
|
+
if (!import_ts_morph9.Node.isPropertyAssignment(prop)) continue;
|
|
4271
4396
|
const propName = prop.getName();
|
|
4272
4397
|
const val = prop.getInitializer();
|
|
4273
4398
|
if (!val) continue;
|
|
@@ -4296,6 +4421,7 @@ async function discoverContractsFast(opts) {
|
|
|
4296
4421
|
project.addSourceFileAtPath(f);
|
|
4297
4422
|
}
|
|
4298
4423
|
bindDiscoveryContext(project, cwd, tsconfigPath);
|
|
4424
|
+
clearMixinTypeProject();
|
|
4299
4425
|
return extractAllRoutes(project);
|
|
4300
4426
|
}
|
|
4301
4427
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
@@ -4303,14 +4429,14 @@ function resolveTsconfigPath(cwd, tsconfig) {
|
|
|
4303
4429
|
}
|
|
4304
4430
|
function createDiscoveryProject(tsconfigPath) {
|
|
4305
4431
|
try {
|
|
4306
|
-
return new
|
|
4432
|
+
return new import_ts_morph10.Project({
|
|
4307
4433
|
tsConfigFilePath: tsconfigPath,
|
|
4308
4434
|
skipAddingFilesFromTsConfig: true,
|
|
4309
4435
|
skipLoadingLibFiles: true,
|
|
4310
4436
|
skipFileDependencyResolution: true
|
|
4311
4437
|
});
|
|
4312
4438
|
} catch {
|
|
4313
|
-
return new
|
|
4439
|
+
return new import_ts_morph10.Project({
|
|
4314
4440
|
skipAddingFilesFromTsConfig: true,
|
|
4315
4441
|
skipLoadingLibFiles: true,
|
|
4316
4442
|
skipFileDependencyResolution: true,
|
|
@@ -4417,15 +4543,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
4417
4543
|
runExtraction() {
|
|
4418
4544
|
clearTypeResolutionCaches(this.project);
|
|
4419
4545
|
clearEnumCache(this.project);
|
|
4546
|
+
clearMixinTypeProject();
|
|
4420
4547
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
4421
4548
|
}
|
|
4422
4549
|
};
|
|
4423
4550
|
function decoratorStringArg(decoratorExpr) {
|
|
4424
4551
|
if (!decoratorExpr) return void 0;
|
|
4425
|
-
if (
|
|
4426
|
-
if (
|
|
4552
|
+
if (import_ts_morph10.Node.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
4553
|
+
if (import_ts_morph10.Node.isArrayLiteralExpression(decoratorExpr)) {
|
|
4427
4554
|
const first = decoratorExpr.getElements()[0];
|
|
4428
|
-
if (first &&
|
|
4555
|
+
if (first && import_ts_morph10.Node.isStringLiteral(first)) return first.getLiteralValue();
|
|
4429
4556
|
}
|
|
4430
4557
|
return void 0;
|
|
4431
4558
|
}
|
|
@@ -4447,7 +4574,8 @@ function joinPaths(prefix, suffix) {
|
|
|
4447
4574
|
if (!prefix && !suffix) return "/";
|
|
4448
4575
|
if (!prefix) return suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4449
4576
|
if (!suffix) return prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4450
|
-
const
|
|
4577
|
+
const withLeadingSlash = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
4578
|
+
const p = withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
4451
4579
|
const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
4452
4580
|
const combined = p + s;
|
|
4453
4581
|
return combined === "" ? "/" : combined;
|
|
@@ -4501,7 +4629,8 @@ function buildRoute(args) {
|
|
|
4501
4629
|
methodAs,
|
|
4502
4630
|
sourceFile,
|
|
4503
4631
|
seenNames,
|
|
4504
|
-
contractSource
|
|
4632
|
+
contractSource,
|
|
4633
|
+
mixin
|
|
4505
4634
|
} = args;
|
|
4506
4635
|
const routeName = resolveRouteName(className, methodName, classAs, methodAs);
|
|
4507
4636
|
const qualifiedRef = `${className}.${methodName}`;
|
|
@@ -4517,7 +4646,12 @@ function buildRoute(args) {
|
|
|
4517
4646
|
path: combinedPath,
|
|
4518
4647
|
name: routeName,
|
|
4519
4648
|
params: extractParams(combinedPath),
|
|
4520
|
-
controllerRef: {
|
|
4649
|
+
controllerRef: {
|
|
4650
|
+
className,
|
|
4651
|
+
methodName,
|
|
4652
|
+
filePath: sourceFile.getFilePath(),
|
|
4653
|
+
...mixin ? { mixin } : {}
|
|
4654
|
+
},
|
|
4521
4655
|
contract: { contractSource }
|
|
4522
4656
|
};
|
|
4523
4657
|
}
|
|
@@ -4531,16 +4665,17 @@ function extractContractRoute(args) {
|
|
|
4531
4665
|
className,
|
|
4532
4666
|
sourceFile,
|
|
4533
4667
|
project,
|
|
4534
|
-
seenNames
|
|
4668
|
+
seenNames,
|
|
4669
|
+
mixin
|
|
4535
4670
|
} = args;
|
|
4536
4671
|
const firstDecoratorArg = applyContractDecorator.getArguments()[0];
|
|
4537
4672
|
if (!firstDecoratorArg) return null;
|
|
4538
4673
|
let contractDef = null;
|
|
4539
4674
|
let bodyZodRef = null;
|
|
4540
4675
|
let queryZodRef = null;
|
|
4541
|
-
if (
|
|
4676
|
+
if (import_ts_morph10.Node.isCallExpression(firstDecoratorArg)) {
|
|
4542
4677
|
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
4543
|
-
} else if (
|
|
4678
|
+
} else if (import_ts_morph10.Node.isIdentifier(firstDecoratorArg)) {
|
|
4544
4679
|
const identName = firstDecoratorArg.getText();
|
|
4545
4680
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
4546
4681
|
if (!resolvedVar) {
|
|
@@ -4583,6 +4718,7 @@ function extractContractRoute(args) {
|
|
|
4583
4718
|
methodAs,
|
|
4584
4719
|
sourceFile,
|
|
4585
4720
|
seenNames,
|
|
4721
|
+
mixin,
|
|
4586
4722
|
contractSource: {
|
|
4587
4723
|
query: contractDef.query,
|
|
4588
4724
|
body: contractDef.body,
|
|
@@ -4599,13 +4735,14 @@ function extractContractRoute(args) {
|
|
|
4599
4735
|
});
|
|
4600
4736
|
}
|
|
4601
4737
|
function extractDtoRoute(args) {
|
|
4602
|
-
const { cls, method, verb, prefix, className, sourceFile, project, seenNames } = args;
|
|
4738
|
+
const { cls, method, verb, prefix, className, sourceFile, project, seenNames, mixin } = args;
|
|
4603
4739
|
if (!verb) return null;
|
|
4604
4740
|
const combined = joinPaths(prefix, verb.handlerPath);
|
|
4605
4741
|
const methodName = method.getName();
|
|
4606
4742
|
const classAs = readAsDecorator(cls, `class ${className}`);
|
|
4607
4743
|
const methodAs = readAsDecorator(method, `${className}.${methodName}`);
|
|
4608
|
-
const dtoContract = extractDtoContract(method, sourceFile, project);
|
|
4744
|
+
const dtoContract = extractDtoContract(method, sourceFile, project, mixin);
|
|
4745
|
+
const mixinResponse = mixin ? resolveInstantiatedReturnType(cls, methodName) : void 0;
|
|
4609
4746
|
return buildRoute({
|
|
4610
4747
|
className,
|
|
4611
4748
|
methodName,
|
|
@@ -4615,10 +4752,11 @@ function extractDtoRoute(args) {
|
|
|
4615
4752
|
methodAs,
|
|
4616
4753
|
sourceFile,
|
|
4617
4754
|
seenNames,
|
|
4755
|
+
mixin,
|
|
4618
4756
|
contractSource: {
|
|
4619
4757
|
query: dtoContract?.query ?? null,
|
|
4620
4758
|
body: dtoContract?.body ?? null,
|
|
4621
|
-
response: dtoContract?.response ?? "unknown",
|
|
4759
|
+
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
4622
4760
|
error: dtoContract?.error ?? null,
|
|
4623
4761
|
queryRef: dtoContract?.queryRef ?? null,
|
|
4624
4762
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
@@ -4647,7 +4785,17 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4647
4785
|
const firstArg2 = controllerDecorator.getArguments()[0];
|
|
4648
4786
|
const prefix = decoratorStringArg(firstArg2) ?? "";
|
|
4649
4787
|
const className = cls.getName() ?? "Unknown";
|
|
4650
|
-
|
|
4788
|
+
const inherited = resolveInheritedMethods(cls);
|
|
4789
|
+
const mixinBinding = inherited ? {
|
|
4790
|
+
factoryName: inherited.factoryName,
|
|
4791
|
+
factoryFilePath: inherited.factoryFilePath,
|
|
4792
|
+
classArgs: inherited.classArgs
|
|
4793
|
+
} : void 0;
|
|
4794
|
+
const methods = [
|
|
4795
|
+
...cls.getMethods().map((method) => ({ method })),
|
|
4796
|
+
...(inherited?.methods ?? []).map((method) => ({ method, mixin: mixinBinding }))
|
|
4797
|
+
];
|
|
4798
|
+
for (const { method, mixin } of methods) {
|
|
4651
4799
|
const verb = resolveVerb(method);
|
|
4652
4800
|
const applyContractDecorator = method.getDecorator("ApplyContract");
|
|
4653
4801
|
const route = applyContractDecorator ? extractContractRoute({
|
|
@@ -4659,7 +4807,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4659
4807
|
className,
|
|
4660
4808
|
sourceFile,
|
|
4661
4809
|
project,
|
|
4662
|
-
seenNames
|
|
4810
|
+
seenNames,
|
|
4811
|
+
mixin
|
|
4663
4812
|
}) : extractDtoRoute({
|
|
4664
4813
|
cls,
|
|
4665
4814
|
method,
|
|
@@ -4668,7 +4817,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
4668
4817
|
className,
|
|
4669
4818
|
sourceFile,
|
|
4670
4819
|
project,
|
|
4671
|
-
seenNames
|
|
4820
|
+
seenNames,
|
|
4821
|
+
mixin
|
|
4672
4822
|
});
|
|
4673
4823
|
if (route) routes.push(route);
|
|
4674
4824
|
}
|
|
@@ -4952,7 +5102,7 @@ function createChainModuleRenderer(opts) {
|
|
|
4952
5102
|
}
|
|
4953
5103
|
|
|
4954
5104
|
// src/index.ts
|
|
4955
|
-
var VERSION = "0.
|
|
5105
|
+
var VERSION = "0.16.0";
|
|
4956
5106
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4957
5107
|
0 && (module.exports = {
|
|
4958
5108
|
CodegenError,
|