@lark-apaas/devtool-kits 1.1.1-alpha.0 → 1.2.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/dist/error.html +194 -23
- package/dist/index.cjs +691 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +695 -20
- package/dist/index.js.map +1 -1
- package/dist/template/types.ts +8 -21
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -514,6 +514,673 @@ function postprocessDrizzleSchema(targetPath) {
|
|
|
514
514
|
}
|
|
515
515
|
__name(postprocessDrizzleSchema, "postprocessDrizzleSchema");
|
|
516
516
|
|
|
517
|
+
// src/helpers/gen-nest-resource/utils.ts
|
|
518
|
+
function mapDrizzleTypeToTS(field) {
|
|
519
|
+
const typeMap = {
|
|
520
|
+
// String types
|
|
521
|
+
char: "string",
|
|
522
|
+
varchar: "string",
|
|
523
|
+
text: "string",
|
|
524
|
+
// Numeric types
|
|
525
|
+
smallint: "number",
|
|
526
|
+
integer: "number",
|
|
527
|
+
int: "number",
|
|
528
|
+
bigint: "string",
|
|
529
|
+
serial: "number",
|
|
530
|
+
smallserial: "number",
|
|
531
|
+
bigserial: "string",
|
|
532
|
+
// Decimal types
|
|
533
|
+
decimal: "string",
|
|
534
|
+
numeric: "string",
|
|
535
|
+
real: "number",
|
|
536
|
+
doublePrecision: "number",
|
|
537
|
+
// Boolean
|
|
538
|
+
boolean: "boolean",
|
|
539
|
+
// Date/Time types
|
|
540
|
+
timestamp: "Date",
|
|
541
|
+
timestamptz: "Date",
|
|
542
|
+
date: "Date",
|
|
543
|
+
time: "string",
|
|
544
|
+
timetz: "string",
|
|
545
|
+
interval: "string",
|
|
546
|
+
// UUID
|
|
547
|
+
uuid: "string",
|
|
548
|
+
// JSON types
|
|
549
|
+
json: "any",
|
|
550
|
+
jsonb: "any",
|
|
551
|
+
// Binary
|
|
552
|
+
bytea: "Buffer",
|
|
553
|
+
// Network types
|
|
554
|
+
inet: "string",
|
|
555
|
+
cidr: "string",
|
|
556
|
+
macaddr: "string",
|
|
557
|
+
macaddr8: "string",
|
|
558
|
+
// Geometric types
|
|
559
|
+
point: "{ x: number; y: number }",
|
|
560
|
+
line: "string",
|
|
561
|
+
lseg: "string",
|
|
562
|
+
box: "string",
|
|
563
|
+
path: "string",
|
|
564
|
+
polygon: "string",
|
|
565
|
+
circle: "string",
|
|
566
|
+
// Array types (handled by isArray flag)
|
|
567
|
+
array: "any[]",
|
|
568
|
+
// Custom types
|
|
569
|
+
customType: "any",
|
|
570
|
+
customTimestamptz: "Date",
|
|
571
|
+
userProfile: "string",
|
|
572
|
+
fileAttachment: "FileAttachment",
|
|
573
|
+
// Enum (handled separately)
|
|
574
|
+
pgEnum: "string"
|
|
575
|
+
};
|
|
576
|
+
let baseType = typeMap[field.type] || "any";
|
|
577
|
+
if (field.isArray) {
|
|
578
|
+
baseType = baseType.endsWith("[]") ? baseType : `${baseType}[]`;
|
|
579
|
+
}
|
|
580
|
+
if (field.enumValues && field.enumValues.length > 0) {
|
|
581
|
+
baseType = field.enumValues.map((v) => `'${v}'`).join(" | ");
|
|
582
|
+
}
|
|
583
|
+
return baseType;
|
|
584
|
+
}
|
|
585
|
+
__name(mapDrizzleTypeToTS, "mapDrizzleTypeToTS");
|
|
586
|
+
function toPascalCase(str) {
|
|
587
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
588
|
+
}
|
|
589
|
+
__name(toPascalCase, "toPascalCase");
|
|
590
|
+
function toKebabCase(str) {
|
|
591
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/[_\s]/g, "-");
|
|
592
|
+
}
|
|
593
|
+
__name(toKebabCase, "toKebabCase");
|
|
594
|
+
|
|
595
|
+
// src/helpers/gen-nest-resource/generator.ts
|
|
596
|
+
function generateDTO(table) {
|
|
597
|
+
const className = toPascalCase(table.variableName);
|
|
598
|
+
let dto = `// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
599
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
600
|
+
import { IsDefined, IsNumber, IsOptional, IsString, MaxLength, IsInt, IsBoolean, IsUUID, IsDate, IsObject, IsArray } from 'class-validator';
|
|
601
|
+
import { Type } from 'class-transformer';
|
|
602
|
+
import { FileAttachment } from '../../../database/schema';
|
|
603
|
+
|
|
604
|
+
`;
|
|
605
|
+
dto += `export class Create${className}Dto {
|
|
606
|
+
`;
|
|
607
|
+
for (const field of table.fields) {
|
|
608
|
+
if (field.isPrimaryKey || field.name === "id" || field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated")) {
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
612
|
+
const optional = field.nullable || field.hasDefault ? "?" : "";
|
|
613
|
+
const decorators = generateValidationDecorators(field);
|
|
614
|
+
if (decorators) {
|
|
615
|
+
dto += decorators;
|
|
616
|
+
}
|
|
617
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
618
|
+
|
|
619
|
+
`;
|
|
620
|
+
}
|
|
621
|
+
dto += "}\n\n";
|
|
622
|
+
dto += `export class Update${className}Dto {
|
|
623
|
+
`;
|
|
624
|
+
for (const field of table.fields) {
|
|
625
|
+
if (field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated") || field.isPrimaryKey || field.name === "id") {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
629
|
+
const decorators = generateValidationDecorators(field, {
|
|
630
|
+
isUpdate: true
|
|
631
|
+
});
|
|
632
|
+
if (decorators) {
|
|
633
|
+
dto += decorators;
|
|
634
|
+
}
|
|
635
|
+
dto += ` ${field.name}?: ${tsType};
|
|
636
|
+
|
|
637
|
+
`;
|
|
638
|
+
}
|
|
639
|
+
dto += "}\n\n";
|
|
640
|
+
dto += `export class ${className}ResponseDto {
|
|
641
|
+
`;
|
|
642
|
+
for (const field of table.fields) {
|
|
643
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
644
|
+
const optional = field.nullable ? "?" : "";
|
|
645
|
+
const decorators = generateValidationDecorators(field, {
|
|
646
|
+
isResponse: true
|
|
647
|
+
});
|
|
648
|
+
if (decorators) {
|
|
649
|
+
dto += decorators;
|
|
650
|
+
}
|
|
651
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
652
|
+
|
|
653
|
+
`;
|
|
654
|
+
}
|
|
655
|
+
dto += "}\n";
|
|
656
|
+
return dto;
|
|
657
|
+
}
|
|
658
|
+
__name(generateDTO, "generateDTO");
|
|
659
|
+
function generateValidationDecorators(field, { isUpdate = false, isResponse = false } = {}) {
|
|
660
|
+
let decorators = " // \u8BF7\u6309\u7528\u6237\u9700\u6C42\u4FEE\u6539\u4EE5\u4E0B\u88C5\u9970\u5668\u6CE8\u91CA\n";
|
|
661
|
+
if (field.nullable || !isResponse && field.hasDefault || isUpdate) {
|
|
662
|
+
decorators += ` @ApiPropertyOptional({ description: '${field.comment || field.name}' })
|
|
663
|
+
`;
|
|
664
|
+
if (isResponse) {
|
|
665
|
+
return decorators;
|
|
666
|
+
}
|
|
667
|
+
decorators += " @IsOptional()\n";
|
|
668
|
+
} else {
|
|
669
|
+
decorators += ` @ApiProperty({ description: '${field.comment || field.name}' })
|
|
670
|
+
`;
|
|
671
|
+
if (isResponse) {
|
|
672
|
+
return decorators;
|
|
673
|
+
}
|
|
674
|
+
decorators += " @IsDefined()\n";
|
|
675
|
+
}
|
|
676
|
+
switch (field.type) {
|
|
677
|
+
case "varchar":
|
|
678
|
+
case "char":
|
|
679
|
+
case "text":
|
|
680
|
+
decorators += " @IsString()\n";
|
|
681
|
+
if (field.length) {
|
|
682
|
+
decorators += ` @MaxLength(${field.length})
|
|
683
|
+
`;
|
|
684
|
+
}
|
|
685
|
+
break;
|
|
686
|
+
case "integer":
|
|
687
|
+
case "smallint":
|
|
688
|
+
case "serial":
|
|
689
|
+
case "smallserial":
|
|
690
|
+
decorators += " @IsInt()\n";
|
|
691
|
+
break;
|
|
692
|
+
case "decimal":
|
|
693
|
+
case "numeric":
|
|
694
|
+
case "real":
|
|
695
|
+
case "doublePrecision":
|
|
696
|
+
decorators += " @IsNumber()\n";
|
|
697
|
+
break;
|
|
698
|
+
case "boolean":
|
|
699
|
+
decorators += " @IsBoolean()\n";
|
|
700
|
+
break;
|
|
701
|
+
case "uuid":
|
|
702
|
+
decorators += " @IsUUID()\n";
|
|
703
|
+
break;
|
|
704
|
+
case "timestamp":
|
|
705
|
+
case "timestamptz":
|
|
706
|
+
case "date":
|
|
707
|
+
case "customTimestamptz":
|
|
708
|
+
decorators += " @IsDate()\n";
|
|
709
|
+
break;
|
|
710
|
+
case "json":
|
|
711
|
+
case "jsonb":
|
|
712
|
+
decorators += " @IsObject()\n";
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
if (field.isArray) {
|
|
716
|
+
decorators += " @IsArray()\n";
|
|
717
|
+
}
|
|
718
|
+
return decorators;
|
|
719
|
+
}
|
|
720
|
+
__name(generateValidationDecorators, "generateValidationDecorators");
|
|
721
|
+
function generateController(table) {
|
|
722
|
+
const className = toPascalCase(table.variableName);
|
|
723
|
+
const routePath = toKebabCase(table.variableName);
|
|
724
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
725
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
726
|
+
const pkName = pkField ? pkField.name : "id";
|
|
727
|
+
const controller = `
|
|
728
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
729
|
+
import {
|
|
730
|
+
Controller,
|
|
731
|
+
Get,
|
|
732
|
+
Post,
|
|
733
|
+
Put,
|
|
734
|
+
Delete,
|
|
735
|
+
Body,
|
|
736
|
+
Param,
|
|
737
|
+
Query,
|
|
738
|
+
} from '@nestjs/common';
|
|
739
|
+
import {
|
|
740
|
+
ApiTags,
|
|
741
|
+
ApiOperation,
|
|
742
|
+
ApiOkResponse,
|
|
743
|
+
ApiCreatedResponse,
|
|
744
|
+
} from '@nestjs/swagger';
|
|
745
|
+
import {
|
|
746
|
+
Create${className}Dto,
|
|
747
|
+
Update${className}Dto,
|
|
748
|
+
${className}ResponseDto
|
|
749
|
+
} from './dtos/${routePath}.dto';
|
|
750
|
+
import { ${className}Service } from './${routePath}.service';
|
|
751
|
+
|
|
752
|
+
@ApiTags('${toPascalCase(table.variableName)}')
|
|
753
|
+
@Controller('api/${routePath}')
|
|
754
|
+
export class ${className}Controller {
|
|
755
|
+
constructor(private readonly ${table.variableName}Service: ${className}Service) {}
|
|
756
|
+
|
|
757
|
+
@Post()
|
|
758
|
+
@ApiOperation({
|
|
759
|
+
summary: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
760
|
+
description: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
761
|
+
})
|
|
762
|
+
@ApiCreatedResponse({
|
|
763
|
+
description: '\u6210\u529F\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55',
|
|
764
|
+
type: ${className}ResponseDto,
|
|
765
|
+
})
|
|
766
|
+
async create(
|
|
767
|
+
@Body() createDto: Create${className}Dto
|
|
768
|
+
): Promise<${className}ResponseDto> {
|
|
769
|
+
return this.${table.variableName}Service.create(createDto);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
@ApiOperation({
|
|
773
|
+
summary: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
774
|
+
description: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
775
|
+
})
|
|
776
|
+
@ApiOkResponse({
|
|
777
|
+
description: '\u6210\u529F\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55',
|
|
778
|
+
type: ${className}ResponseDto,
|
|
779
|
+
})
|
|
780
|
+
@Get(':${pkName}')
|
|
781
|
+
async findOne(
|
|
782
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
783
|
+
): Promise<${className}ResponseDto> {
|
|
784
|
+
return this.${table.variableName}Service.findOne(${pkName});
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
@ApiOperation({
|
|
788
|
+
summary: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
789
|
+
description: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
790
|
+
})
|
|
791
|
+
@ApiOkResponse({
|
|
792
|
+
description: '\u6210\u529F\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55',
|
|
793
|
+
type: ${className}ResponseDto,
|
|
794
|
+
})
|
|
795
|
+
@Put(':${pkName}')
|
|
796
|
+
async update(
|
|
797
|
+
@Param('${pkName}') ${pkName}: ${pkType},
|
|
798
|
+
@Body() updateDto: Update${className}Dto
|
|
799
|
+
): Promise<${className}ResponseDto> {
|
|
800
|
+
return this.${table.variableName}Service.update(${pkName}, updateDto);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
@ApiOperation({
|
|
804
|
+
summary: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
805
|
+
description: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
806
|
+
})
|
|
807
|
+
@ApiOkResponse({
|
|
808
|
+
description: '\u6210\u529F\u5220\u9664\u4E00\u6761\u8BB0\u5F55',
|
|
809
|
+
})
|
|
810
|
+
@Delete(':${pkName}')
|
|
811
|
+
async remove(
|
|
812
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
813
|
+
): Promise<void> {
|
|
814
|
+
return this.${table.variableName}Service.remove(${pkName});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
`;
|
|
818
|
+
return controller;
|
|
819
|
+
}
|
|
820
|
+
__name(generateController, "generateController");
|
|
821
|
+
function generateService(table) {
|
|
822
|
+
const className = toPascalCase(table.variableName);
|
|
823
|
+
const routePath = toKebabCase(table.variableName);
|
|
824
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
825
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
826
|
+
const pkName = pkField ? pkField.name : "id";
|
|
827
|
+
const service = `
|
|
828
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
829
|
+
import { Injectable, Inject, Logger, NotFoundException } from '@nestjs/common';
|
|
830
|
+
import { eq } from 'drizzle-orm';
|
|
831
|
+
import { DRIZZLE_DATABASE, type PostgresJsDatabase } from '@lark-apaas/fullstack-nestjs-core';
|
|
832
|
+
import { ${table.variableName} } from '../../database/schema';
|
|
833
|
+
import {
|
|
834
|
+
Create${className}Dto,
|
|
835
|
+
Update${className}Dto,
|
|
836
|
+
${className}ResponseDto
|
|
837
|
+
} from './dtos/${routePath}.dto';
|
|
838
|
+
|
|
839
|
+
@Injectable()
|
|
840
|
+
export class ${className}Service {
|
|
841
|
+
private readonly logger = new Logger(${className}Service.name);
|
|
842
|
+
|
|
843
|
+
constructor(@Inject(DRIZZLE_DATABASE) private readonly db: PostgresJsDatabase) {}
|
|
844
|
+
|
|
845
|
+
async create(createDto: Create${className}Dto): Promise<${className}ResponseDto> {
|
|
846
|
+
const [result] = await this.db
|
|
847
|
+
.insert(${table.variableName})
|
|
848
|
+
.values(createDto)
|
|
849
|
+
.returning();
|
|
850
|
+
|
|
851
|
+
this.logger.log(\`Created ${className} with ${pkName} \${result.${pkName}}\`);
|
|
852
|
+
|
|
853
|
+
return result;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
async findAll(options?: { page?: number; limit?: number }): Promise<${className}ResponseDto[]> {
|
|
857
|
+
const { page = 1, limit = 10 } = options || {};
|
|
858
|
+
const offset = (page - 1) * limit;
|
|
859
|
+
|
|
860
|
+
return this.db
|
|
861
|
+
.select()
|
|
862
|
+
.from(${table.variableName})
|
|
863
|
+
.limit(limit)
|
|
864
|
+
.offset(offset);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
async findOne(${pkName}: ${pkType}): Promise<${className}ResponseDto> {
|
|
868
|
+
const [result] = await this.db
|
|
869
|
+
.select()
|
|
870
|
+
.from(${table.variableName})
|
|
871
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
872
|
+
.limit(1);
|
|
873
|
+
|
|
874
|
+
if (!result) {
|
|
875
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return result;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
async update(${pkName}: ${pkType}, updateDto: Update${className}Dto): Promise<${className}ResponseDto> {
|
|
882
|
+
const [result] = await this.db
|
|
883
|
+
.update(${table.variableName})
|
|
884
|
+
.set(updateDto)
|
|
885
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
886
|
+
.returning();
|
|
887
|
+
|
|
888
|
+
if (!result) {
|
|
889
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
return result;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
async remove(${pkName}: ${pkType}): Promise<void> {
|
|
896
|
+
const result = await this.db
|
|
897
|
+
.delete(${table.variableName})
|
|
898
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
899
|
+
.returning();
|
|
900
|
+
|
|
901
|
+
if (result.length === 0) {
|
|
902
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
this.logger.log(\`Deleted ${className} with ${pkName} \${${pkName}}\`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
`;
|
|
909
|
+
return service;
|
|
910
|
+
}
|
|
911
|
+
__name(generateService, "generateService");
|
|
912
|
+
function generateModule(table) {
|
|
913
|
+
const className = toPascalCase(table.variableName);
|
|
914
|
+
const routePath = toKebabCase(table.variableName);
|
|
915
|
+
const module = `
|
|
916
|
+
import { Module } from '@nestjs/common';
|
|
917
|
+
import { ${className}Controller } from './${routePath}.controller';
|
|
918
|
+
import { ${className}Service } from './${routePath}.service';
|
|
919
|
+
|
|
920
|
+
@Module({
|
|
921
|
+
controllers: [${className}Controller],
|
|
922
|
+
providers: [${className}Service],
|
|
923
|
+
})
|
|
924
|
+
export class ${className}Module {}
|
|
925
|
+
`;
|
|
926
|
+
return module;
|
|
927
|
+
}
|
|
928
|
+
__name(generateModule, "generateModule");
|
|
929
|
+
|
|
930
|
+
// src/helpers/gen-nest-resource/schema-parser.ts
|
|
931
|
+
import { Project, Node } from "ts-morph";
|
|
932
|
+
var DrizzleSchemaParser = class DrizzleSchemaParser2 {
|
|
933
|
+
static {
|
|
934
|
+
__name(this, "DrizzleSchemaParser");
|
|
935
|
+
}
|
|
936
|
+
project;
|
|
937
|
+
constructor(projectOptions) {
|
|
938
|
+
this.project = new Project(projectOptions);
|
|
939
|
+
}
|
|
940
|
+
parseSchemaFile(filePath) {
|
|
941
|
+
const sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
942
|
+
const tables = [];
|
|
943
|
+
const variableStatements = sourceFile.getVariableStatements();
|
|
944
|
+
for (const statement of variableStatements) {
|
|
945
|
+
const declarations = statement.getDeclarations();
|
|
946
|
+
for (const declaration of declarations) {
|
|
947
|
+
const initializer = declaration.getInitializer();
|
|
948
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
949
|
+
const expression = initializer.getExpression();
|
|
950
|
+
if (expression.getText() === "pgTable") {
|
|
951
|
+
const tableInfo = this.parsePgTable(declaration.getName(), initializer);
|
|
952
|
+
if (tableInfo) {
|
|
953
|
+
tables.push(tableInfo);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return tables;
|
|
960
|
+
}
|
|
961
|
+
parsePgTable(variableName, callExpr) {
|
|
962
|
+
const args = callExpr.getArguments();
|
|
963
|
+
if (args.length < 2) {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
const tableName = args[0].getText().replace(/['"]/g, "");
|
|
967
|
+
const fieldsArg = args[1];
|
|
968
|
+
if (!Node.isObjectLiteralExpression(fieldsArg)) {
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
const fields = [];
|
|
972
|
+
const properties = fieldsArg.getProperties();
|
|
973
|
+
for (const prop of properties) {
|
|
974
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
975
|
+
const fieldName = prop.getName();
|
|
976
|
+
const initializer = prop.getInitializer();
|
|
977
|
+
const leadingComments = prop.getLeadingCommentRanges();
|
|
978
|
+
let comment;
|
|
979
|
+
if (leadingComments.length > 0) {
|
|
980
|
+
comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
|
|
981
|
+
}
|
|
982
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
983
|
+
const fieldInfo = this.parseField(fieldName, initializer, comment);
|
|
984
|
+
fields.push(fieldInfo);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return {
|
|
989
|
+
tableName,
|
|
990
|
+
variableName,
|
|
991
|
+
fields
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
parseField(fieldName, callExpr, comment) {
|
|
995
|
+
const fieldInfo = {
|
|
996
|
+
name: fieldName,
|
|
997
|
+
columnName: fieldName,
|
|
998
|
+
type: "",
|
|
999
|
+
nullable: true,
|
|
1000
|
+
hasDefault: false,
|
|
1001
|
+
notNull: false,
|
|
1002
|
+
isPrimaryKey: false,
|
|
1003
|
+
isUnique: false,
|
|
1004
|
+
isArray: false,
|
|
1005
|
+
comment
|
|
1006
|
+
};
|
|
1007
|
+
this.parseBaseType(callExpr, fieldInfo);
|
|
1008
|
+
this.parseCallChain(callExpr, fieldInfo);
|
|
1009
|
+
return fieldInfo;
|
|
1010
|
+
}
|
|
1011
|
+
parseBaseType(callExpr, fieldInfo) {
|
|
1012
|
+
let current = callExpr;
|
|
1013
|
+
let baseCall = null;
|
|
1014
|
+
while (Node.isCallExpression(current)) {
|
|
1015
|
+
baseCall = current;
|
|
1016
|
+
const expression2 = current.getExpression();
|
|
1017
|
+
if (Node.isPropertyAccessExpression(expression2)) {
|
|
1018
|
+
current = expression2.getExpression();
|
|
1019
|
+
} else {
|
|
1020
|
+
break;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (!baseCall) {
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
const expression = baseCall.getExpression();
|
|
1027
|
+
let typeName = "";
|
|
1028
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1029
|
+
typeName = expression.getName();
|
|
1030
|
+
} else {
|
|
1031
|
+
typeName = expression.getText();
|
|
1032
|
+
}
|
|
1033
|
+
fieldInfo.type = typeName;
|
|
1034
|
+
const args = baseCall.getArguments();
|
|
1035
|
+
if (args.length > 0) {
|
|
1036
|
+
const firstArg = args[0];
|
|
1037
|
+
if (Node.isStringLiteral(firstArg)) {
|
|
1038
|
+
fieldInfo.columnName = firstArg.getLiteralText();
|
|
1039
|
+
} else if (Node.isObjectLiteralExpression(firstArg)) {
|
|
1040
|
+
this.parseTypeConfig(firstArg, fieldInfo);
|
|
1041
|
+
} else if (Node.isArrayLiteralExpression(firstArg)) {
|
|
1042
|
+
fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
|
|
1046
|
+
this.parseTypeConfig(args[1], fieldInfo);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
parseTypeConfig(objLiteral, fieldInfo) {
|
|
1050
|
+
if (!Node.isObjectLiteralExpression(objLiteral)) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const properties = objLiteral.getProperties();
|
|
1054
|
+
for (const prop of properties) {
|
|
1055
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
1056
|
+
const propName = prop.getName();
|
|
1057
|
+
const value = prop.getInitializer()?.getText();
|
|
1058
|
+
switch (propName) {
|
|
1059
|
+
case "length":
|
|
1060
|
+
fieldInfo.length = value ? parseInt(value) : void 0;
|
|
1061
|
+
break;
|
|
1062
|
+
case "precision":
|
|
1063
|
+
fieldInfo.precision = value ? parseInt(value) : void 0;
|
|
1064
|
+
break;
|
|
1065
|
+
case "scale":
|
|
1066
|
+
fieldInfo.scale = value ? parseInt(value) : void 0;
|
|
1067
|
+
break;
|
|
1068
|
+
case "default":
|
|
1069
|
+
fieldInfo.hasDefault = true;
|
|
1070
|
+
fieldInfo.defaultValue = value;
|
|
1071
|
+
break;
|
|
1072
|
+
// 时间精度(用于 timestamp, time 等)
|
|
1073
|
+
case "withTimezone":
|
|
1074
|
+
fieldInfo.withTimezone = value === "true";
|
|
1075
|
+
break;
|
|
1076
|
+
case "mode":
|
|
1077
|
+
fieldInfo.mode = value?.replace(/['"]/g, "");
|
|
1078
|
+
break;
|
|
1079
|
+
default:
|
|
1080
|
+
throw new Error(`Unsupported property: ${propName}`);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
parseCallChain(callExpr, fieldInfo) {
|
|
1086
|
+
let current = callExpr;
|
|
1087
|
+
while (Node.isCallExpression(current)) {
|
|
1088
|
+
const expression = current.getExpression();
|
|
1089
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1090
|
+
const methodName = expression.getName();
|
|
1091
|
+
const args = current.getArguments();
|
|
1092
|
+
switch (methodName) {
|
|
1093
|
+
case "notNull":
|
|
1094
|
+
fieldInfo.notNull = true;
|
|
1095
|
+
fieldInfo.nullable = false;
|
|
1096
|
+
break;
|
|
1097
|
+
case "default":
|
|
1098
|
+
fieldInfo.hasDefault = true;
|
|
1099
|
+
if (args.length > 0) {
|
|
1100
|
+
fieldInfo.defaultValue = args[0].getText();
|
|
1101
|
+
}
|
|
1102
|
+
break;
|
|
1103
|
+
case "defaultRandom":
|
|
1104
|
+
fieldInfo.hasDefault = true;
|
|
1105
|
+
fieldInfo.defaultValue = "random";
|
|
1106
|
+
break;
|
|
1107
|
+
case "primaryKey":
|
|
1108
|
+
fieldInfo.isPrimaryKey = true;
|
|
1109
|
+
fieldInfo.notNull = true;
|
|
1110
|
+
fieldInfo.nullable = false;
|
|
1111
|
+
break;
|
|
1112
|
+
case "unique":
|
|
1113
|
+
fieldInfo.isUnique = true;
|
|
1114
|
+
break;
|
|
1115
|
+
case "array":
|
|
1116
|
+
fieldInfo.isArray = true;
|
|
1117
|
+
break;
|
|
1118
|
+
case "references":
|
|
1119
|
+
if (args.length > 0) {
|
|
1120
|
+
const refArg = args[0].getText();
|
|
1121
|
+
const match = refArg.match(/=>\s*(\w+)\.(\w+)/);
|
|
1122
|
+
if (match) {
|
|
1123
|
+
fieldInfo.references = {
|
|
1124
|
+
table: match[1],
|
|
1125
|
+
column: match[2]
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
break;
|
|
1130
|
+
default:
|
|
1131
|
+
throw new Error(`Unsupported method: ${methodName}`);
|
|
1132
|
+
}
|
|
1133
|
+
current = expression.getExpression();
|
|
1134
|
+
} else {
|
|
1135
|
+
break;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
|
|
1141
|
+
// src/helpers/gen-nest-resource/index.ts
|
|
1142
|
+
import { join } from "path";
|
|
1143
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
1144
|
+
import { existsSync } from "fs";
|
|
1145
|
+
async function parseAndGenerateNestResourceTemplate(options) {
|
|
1146
|
+
const parser = new DrizzleSchemaParser({
|
|
1147
|
+
tsConfigFilePath: options.tsConfigFilePath
|
|
1148
|
+
});
|
|
1149
|
+
const tables = parser.parseSchemaFile(options.schemaFilePath);
|
|
1150
|
+
for (const table of tables) {
|
|
1151
|
+
console.info(`\u751F\u6210 Nest.js ${table.variableName} \u6A21\u5757`);
|
|
1152
|
+
const routePath = toKebabCase(table.variableName);
|
|
1153
|
+
const moduleDir = join(options.moduleOutputDir, routePath);
|
|
1154
|
+
if (existsSync(moduleDir)) {
|
|
1155
|
+
console.info(`Nest.js \u6A21\u5757 ${routePath} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210\u4EE3\u7801`);
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
const dto = generateDTO(table);
|
|
1159
|
+
const controller = generateController(table);
|
|
1160
|
+
const service = generateService(table);
|
|
1161
|
+
const moduleFilePath = join(moduleDir, `${routePath}.module.ts`);
|
|
1162
|
+
const module = generateModule(table);
|
|
1163
|
+
try {
|
|
1164
|
+
await mkdir(moduleDir, {
|
|
1165
|
+
recursive: true
|
|
1166
|
+
});
|
|
1167
|
+
await mkdir(join(moduleDir, "dtos"), {
|
|
1168
|
+
recursive: true
|
|
1169
|
+
});
|
|
1170
|
+
await writeFile(join(moduleDir, "dtos", `${routePath}.dto.ts`), dto);
|
|
1171
|
+
await writeFile(join(moduleDir, `${routePath}.controller.ts`), controller);
|
|
1172
|
+
await writeFile(join(moduleDir, `${routePath}.service.ts`), service);
|
|
1173
|
+
await writeFile(moduleFilePath, module);
|
|
1174
|
+
} catch (err) {
|
|
1175
|
+
console.error(`\u751F\u6210 Nest.js ${routePath} \u6A21\u5757\u5931\u8D25: ${err.message}`);
|
|
1176
|
+
await rm(moduleDir, {
|
|
1177
|
+
recursive: true
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
__name(parseAndGenerateNestResourceTemplate, "parseAndGenerateNestResourceTemplate");
|
|
1183
|
+
|
|
517
1184
|
// src/helpers/proxy-error/index.ts
|
|
518
1185
|
import fs3 from "fs";
|
|
519
1186
|
import path3 from "path";
|
|
@@ -689,10 +1356,17 @@ function checkForErrors(logs) {
|
|
|
689
1356
|
return false;
|
|
690
1357
|
}
|
|
691
1358
|
__name(checkForErrors, "checkForErrors");
|
|
692
|
-
function
|
|
693
|
-
|
|
1359
|
+
function injectErrorData(template, errorLogs, clientBasePath) {
|
|
1360
|
+
let logsText = "";
|
|
1361
|
+
if (errorLogs.length > 0) {
|
|
1362
|
+
logsText = errorLogs.join("\n");
|
|
1363
|
+
} else {
|
|
1364
|
+
logsText = "\u672A\u627E\u5230\u76F8\u5173\u9519\u8BEF\u65E5\u5FD7";
|
|
1365
|
+
}
|
|
1366
|
+
return template.replace("{{.errorData.message}}", `\u670D\u52A1\u542F\u52A8\u5F02\u5E38\uFF0C\u8BF7\u6839\u636E\u65E5\u5FD7\u4FEE\u590D\u76F8\u5173\u95EE\u9898
|
|
1367
|
+
${JSON.stringify(logsText)}`).replace("{{.clientBasePath}}", clientBasePath);
|
|
694
1368
|
}
|
|
695
|
-
__name(
|
|
1369
|
+
__name(injectErrorData, "injectErrorData");
|
|
696
1370
|
function handleDevProxyError(err, req, res, options) {
|
|
697
1371
|
const { logDir = path3.join(process.cwd(), "logs"), maxErrorLogs = 100, logFileName = "server.log", retryTimeout = 5e3, retryInterval = 500, target = `http://localhost:${process.env.SERVER_PORT || 3e3}`, clientBasePath = process.env.CLIENT_BASE_PATH || "/" } = options || {};
|
|
698
1372
|
const clientBasePathWithoutSlash = normalizeBasePath(clientBasePath);
|
|
@@ -704,7 +1378,7 @@ function handleDevProxyError(err, req, res, options) {
|
|
|
704
1378
|
(async () => {
|
|
705
1379
|
try {
|
|
706
1380
|
const isConnError = isConnectionError(err);
|
|
707
|
-
const { hasCompileError } = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
|
|
1381
|
+
const { logs: errorLogs, hasCompileError } = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
|
|
708
1382
|
if (isConnError && !hasCompileError) {
|
|
709
1383
|
console.log("[Proxy Error]: Connection error without compile errors, possibly server restarting...");
|
|
710
1384
|
try {
|
|
@@ -728,7 +1402,7 @@ function handleDevProxyError(err, req, res, options) {
|
|
|
728
1402
|
console.log("[Proxy Error]: Compile error or non-connection error, showing error page");
|
|
729
1403
|
}
|
|
730
1404
|
const template = getErrorHtmlTemplate();
|
|
731
|
-
const html =
|
|
1405
|
+
const html = injectErrorData(template, errorLogs, clientBasePathWithoutSlash);
|
|
732
1406
|
res.writeHead(200, {
|
|
733
1407
|
"Content-Type": "text/html; charset=utf-8",
|
|
734
1408
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
@@ -878,7 +1552,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
|
|
|
878
1552
|
const startTime = Date.now();
|
|
879
1553
|
const openapiPath = options.openapiPath || path5.resolve(__dirname, "../client/src/api/gen/openapi.json");
|
|
880
1554
|
const serverDir = options.serverDir || path5.resolve(__dirname, "../server");
|
|
881
|
-
const
|
|
1555
|
+
const writeFile2 = options.writeFile !== false;
|
|
882
1556
|
let openapi;
|
|
883
1557
|
if (options.openapiData) {
|
|
884
1558
|
openapi = JSON.parse(JSON.stringify(options.openapiData));
|
|
@@ -889,7 +1563,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
|
|
|
889
1563
|
const controllerFiles = await findControllerFiles(serverDir);
|
|
890
1564
|
const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);
|
|
891
1565
|
const enhanced = enhanceOpenApiPaths(openapi, sourceMap);
|
|
892
|
-
if (
|
|
1566
|
+
if (writeFile2) {
|
|
893
1567
|
await fs5.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + "\n", "utf-8");
|
|
894
1568
|
}
|
|
895
1569
|
const duration = Date.now() - startTime;
|
|
@@ -1072,7 +1746,7 @@ import express2 from "express";
|
|
|
1072
1746
|
|
|
1073
1747
|
// src/middlewares/dev-logs/utils.ts
|
|
1074
1748
|
import { promises as fs7 } from "fs";
|
|
1075
|
-
import { isAbsolute, join, relative } from "path";
|
|
1749
|
+
import { isAbsolute, join as join2, relative } from "path";
|
|
1076
1750
|
|
|
1077
1751
|
// src/middlewares/dev-logs/helper/path-matcher.ts
|
|
1078
1752
|
function pathPatternToRegex(pattern) {
|
|
@@ -1112,9 +1786,9 @@ __name(normalizePathForMatching, "normalizePathForMatching");
|
|
|
1112
1786
|
// src/middlewares/dev-logs/utils.ts
|
|
1113
1787
|
function resolveLogDir(provided) {
|
|
1114
1788
|
if (!provided) {
|
|
1115
|
-
return
|
|
1789
|
+
return join2(process.cwd(), "logs");
|
|
1116
1790
|
}
|
|
1117
|
-
return isAbsolute(provided) ? provided :
|
|
1791
|
+
return isAbsolute(provided) ? provided : join2(process.cwd(), provided);
|
|
1118
1792
|
}
|
|
1119
1793
|
__name(resolveLogDir, "resolveLogDir");
|
|
1120
1794
|
function getRelativePath(filePath) {
|
|
@@ -1173,7 +1847,7 @@ function resolveLogFilePath(baseDir, fileName) {
|
|
|
1173
1847
|
if (segments.some((segment) => segment === "..")) {
|
|
1174
1848
|
throw new Error("Invalid log file path");
|
|
1175
1849
|
}
|
|
1176
|
-
const resolved =
|
|
1850
|
+
const resolved = join2(baseDir, segments.join("/"));
|
|
1177
1851
|
const rel = relative(baseDir, resolved);
|
|
1178
1852
|
if (rel.startsWith("..")) {
|
|
1179
1853
|
throw new Error("Access to the specified log file is denied");
|
|
@@ -1203,7 +1877,7 @@ function serializeError(error) {
|
|
|
1203
1877
|
__name(serializeError, "serializeError");
|
|
1204
1878
|
|
|
1205
1879
|
// src/middlewares/dev-logs/controller.ts
|
|
1206
|
-
import { join as
|
|
1880
|
+
import { join as join3 } from "path";
|
|
1207
1881
|
|
|
1208
1882
|
// src/middlewares/dev-logs/services.ts
|
|
1209
1883
|
import { createReadStream, promises as fs8 } from "fs";
|
|
@@ -1432,7 +2106,7 @@ function handleError(res, error, message = "Failed to read log file") {
|
|
|
1432
2106
|
}
|
|
1433
2107
|
__name(handleError, "handleError");
|
|
1434
2108
|
function createGetTraceEntriesHandler(logDir) {
|
|
1435
|
-
const appLogPath =
|
|
2109
|
+
const appLogPath = join3(logDir, "server.log");
|
|
1436
2110
|
return async (req, res) => {
|
|
1437
2111
|
const traceId = (req.params.traceId || "").trim();
|
|
1438
2112
|
if (!traceId) {
|
|
@@ -1459,7 +2133,7 @@ function createGetTraceEntriesHandler(logDir) {
|
|
|
1459
2133
|
}
|
|
1460
2134
|
__name(createGetTraceEntriesHandler, "createGetTraceEntriesHandler");
|
|
1461
2135
|
function createGetRecentTracesHandler(logDir) {
|
|
1462
|
-
const traceLogPath =
|
|
2136
|
+
const traceLogPath = join3(logDir, "trace.log");
|
|
1463
2137
|
return async (req, res) => {
|
|
1464
2138
|
const page = parsePositiveInt(req.query.page, 1);
|
|
1465
2139
|
const pageSize = parseLimit(req.query.pageSize, 10, 100);
|
|
@@ -1627,17 +2301,17 @@ __name(createDevLogsMiddleware, "createDevLogsMiddleware");
|
|
|
1627
2301
|
import express3 from "express";
|
|
1628
2302
|
|
|
1629
2303
|
// src/middlewares/collect-logs/controller.ts
|
|
1630
|
-
import { join as
|
|
2304
|
+
import { join as join5 } from "path";
|
|
1631
2305
|
import fs10 from "fs";
|
|
1632
2306
|
|
|
1633
2307
|
// src/middlewares/collect-logs/utils.ts
|
|
1634
|
-
import { isAbsolute as isAbsolute2, join as
|
|
2308
|
+
import { isAbsolute as isAbsolute2, join as join4 } from "path";
|
|
1635
2309
|
import fs9 from "fs";
|
|
1636
2310
|
function resolveLogDir2(provided) {
|
|
1637
2311
|
if (!provided) {
|
|
1638
|
-
return
|
|
2312
|
+
return join4(process.cwd(), "logs");
|
|
1639
2313
|
}
|
|
1640
|
-
return isAbsolute2(provided) ? provided :
|
|
2314
|
+
return isAbsolute2(provided) ? provided : join4(process.cwd(), provided);
|
|
1641
2315
|
}
|
|
1642
2316
|
__name(resolveLogDir2, "resolveLogDir");
|
|
1643
2317
|
function ensureDir(dir) {
|
|
@@ -1660,7 +2334,7 @@ __name(serializeError2, "serializeError");
|
|
|
1660
2334
|
|
|
1661
2335
|
// src/middlewares/collect-logs/controller.ts
|
|
1662
2336
|
function collectLogsHandler(logDir, fileName) {
|
|
1663
|
-
const filePath =
|
|
2337
|
+
const filePath = join5(logDir, fileName);
|
|
1664
2338
|
ensureDir(logDir);
|
|
1665
2339
|
return async (req, res) => {
|
|
1666
2340
|
try {
|
|
@@ -1685,7 +2359,7 @@ function collectLogsHandler(logDir, fileName) {
|
|
|
1685
2359
|
}
|
|
1686
2360
|
__name(collectLogsHandler, "collectLogsHandler");
|
|
1687
2361
|
function collectLogsBatchHandler(logDir, fileName) {
|
|
1688
|
-
const filePath =
|
|
2362
|
+
const filePath = join5(logDir, fileName);
|
|
1689
2363
|
ensureDir(logDir);
|
|
1690
2364
|
return async (req, res) => {
|
|
1691
2365
|
try {
|
|
@@ -1840,6 +2514,7 @@ export {
|
|
|
1840
2514
|
createOpenapiMiddleware,
|
|
1841
2515
|
handleDevProxyError,
|
|
1842
2516
|
normalizeBasePath,
|
|
2517
|
+
parseAndGenerateNestResourceTemplate,
|
|
1843
2518
|
postprocessDrizzleSchema,
|
|
1844
2519
|
registerMiddlewares
|
|
1845
2520
|
};
|