@drax/crud-back 1.4.0 → 2.0.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.
|
@@ -1,15 +1,53 @@
|
|
|
1
|
-
import z from 'zod';
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
import { IdParamSchema, DeleteBodyResponseSchema, PaginateQuerySchema, PaginateBodyResponseSchema, FindQuerySchema, SearchQuerySchema, FindByParamSchema, ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema, ExportBodyResponseSchema, GroupByQuerySchema } from '../index.js';
|
|
3
3
|
export class CrudSchemaBuilder {
|
|
4
4
|
constructor(entitySchema, entityCreateSchema, entityUpdateSchema, entityName, target = 'openapi-3.0', tags = []) {
|
|
5
5
|
this.target = 'openapi-3.0'; //"jsonSchema7" | "jsonSchema2019-09" | "openapi-3.0" | "openAi"
|
|
6
|
-
this.entitySchema = entitySchema;
|
|
6
|
+
this.entitySchema = this.schemaAdapter(entitySchema);
|
|
7
7
|
this.entityCreateSchema = entityCreateSchema;
|
|
8
8
|
this.entityUpdateSchema = entityUpdateSchema;
|
|
9
9
|
this.entityName = entityName;
|
|
10
10
|
this.tags = tags;
|
|
11
11
|
this.target = target;
|
|
12
12
|
}
|
|
13
|
+
getTypeName(field) {
|
|
14
|
+
// Zod v4 suele tener constructor.name útil en runtime
|
|
15
|
+
return field?.constructor?.name;
|
|
16
|
+
}
|
|
17
|
+
fieldAdapter(field) {
|
|
18
|
+
const f = field;
|
|
19
|
+
const typeName = this.getTypeName(f);
|
|
20
|
+
// 1) Desenrollar wrappers por "duck typing" (evita líos de tipos en Zod 4)
|
|
21
|
+
if (typeof f?.unwrap === 'function' && typeName === 'ZodOptional') {
|
|
22
|
+
return this.fieldAdapter(f.unwrap()).optional();
|
|
23
|
+
}
|
|
24
|
+
if (typeof f?.unwrap === 'function' && typeName === 'ZodNullable') {
|
|
25
|
+
return this.fieldAdapter(f.unwrap()).nullable();
|
|
26
|
+
}
|
|
27
|
+
// 2) Tipos compuestos
|
|
28
|
+
if (typeName === 'ZodArray' && f?.element) {
|
|
29
|
+
return z.array(this.fieldAdapter(f.element));
|
|
30
|
+
}
|
|
31
|
+
if (typeName === 'ZodObject' && f?.shape) {
|
|
32
|
+
return this.schemaAdapter(f);
|
|
33
|
+
}
|
|
34
|
+
// 3) Date -> ISO datetime (string)
|
|
35
|
+
if (typeName === 'ZodDate') {
|
|
36
|
+
return z.iso.datetime();
|
|
37
|
+
}
|
|
38
|
+
// 4) Fallback: dejar tal cual
|
|
39
|
+
return f;
|
|
40
|
+
}
|
|
41
|
+
schemaAdapter(entitySchema) {
|
|
42
|
+
const shape = entitySchema.shape;
|
|
43
|
+
const newShape = {};
|
|
44
|
+
for (const key of Object.keys(shape)) {
|
|
45
|
+
newShape[key] = this.fieldAdapter(shape[key]);
|
|
46
|
+
}
|
|
47
|
+
// Re-creamos el objeto con el nuevo shape.
|
|
48
|
+
// Tipamos como TObj para mantener la firma genérica del builder.
|
|
49
|
+
return z.object(newShape);
|
|
50
|
+
}
|
|
13
51
|
get getTags() {
|
|
14
52
|
if (this.tags.length > 0) {
|
|
15
53
|
return { tags: this.tags };
|
|
@@ -29,9 +67,11 @@ export class CrudSchemaBuilder {
|
|
|
29
67
|
return z.toJSONSchema(z.array(this.entitySchema), { target: this.target });
|
|
30
68
|
}
|
|
31
69
|
get jsonEntityGroupBySchema() {
|
|
32
|
-
return z.toJSONSchema(z.array(z
|
|
70
|
+
return z.toJSONSchema(z.array(z
|
|
71
|
+
.object({
|
|
33
72
|
count: z.number()
|
|
34
|
-
})
|
|
73
|
+
})
|
|
74
|
+
.catchall(z.any())), { target: this.target });
|
|
35
75
|
}
|
|
36
76
|
get jsonExportBodyResponse() {
|
|
37
77
|
return z.toJSONSchema(ExportBodyResponseSchema, { target: this.target });
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "2.0.0",
|
|
7
7
|
"description": "Crud utils across modules",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"author": "Cristian Incarnato & Drax Team",
|
|
23
23
|
"license": "ISC",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@drax/common-back": "^
|
|
26
|
-
"@drax/common-share": "^
|
|
27
|
-
"@drax/identity-share": "^
|
|
28
|
-
"@drax/media-back": "^
|
|
25
|
+
"@drax/common-back": "^2.0.0",
|
|
26
|
+
"@drax/common-share": "^2.0.0",
|
|
27
|
+
"@drax/identity-share": "^2.0.0",
|
|
28
|
+
"@drax/media-back": "^2.0.0",
|
|
29
29
|
"@graphql-tools/load-files": "^7.0.0",
|
|
30
30
|
"@graphql-tools/merge": "^9.0.4",
|
|
31
|
-
"mongoose": "^8.
|
|
31
|
+
"mongoose": "^8.23.0",
|
|
32
32
|
"mongoose-lean-virtuals": "^1.1.0",
|
|
33
33
|
"mongoose-paginate-v2": "^1.8.3"
|
|
34
34
|
},
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"zod": "^4.3.6"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@types/node": "^
|
|
41
|
+
"@types/node": "^25.2.3",
|
|
42
42
|
"copyfiles": "^2.4.1",
|
|
43
43
|
"mongoose-paginate-v2": "^1.8.3",
|
|
44
44
|
"nodemon": "^3.1.0",
|
|
45
45
|
"ts-node": "^10.9.2",
|
|
46
46
|
"tsc-alias": "^1.8.10",
|
|
47
|
-
"typescript": "^5.
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "2713061af9f137c81afe01e36350f6a10dd0ad1d"
|
|
50
50
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import z from 'zod';
|
|
1
|
+
import { z, type ZodTypeAny } from 'zod';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
IdParamSchema,
|
|
@@ -14,7 +14,11 @@ import {
|
|
|
14
14
|
GroupByQuerySchema
|
|
15
15
|
} from '../index.js';
|
|
16
16
|
|
|
17
|
-
export class CrudSchemaBuilder<
|
|
17
|
+
export class CrudSchemaBuilder<
|
|
18
|
+
T extends z.ZodObject<z.ZodRawShape>,
|
|
19
|
+
TCreate extends z.ZodObject<z.ZodRawShape>,
|
|
20
|
+
TUpdate extends z.ZodObject<z.ZodRawShape>
|
|
21
|
+
> {
|
|
18
22
|
private entitySchema: T;
|
|
19
23
|
private entityCreateSchema: TCreate;
|
|
20
24
|
private entityUpdateSchema: TUpdate;
|
|
@@ -23,94 +27,150 @@ export class CrudSchemaBuilder<T extends z.ZodObject<z.ZodRawShape>, TCreate ext
|
|
|
23
27
|
private target: string = 'openapi-3.0'; //"jsonSchema7" | "jsonSchema2019-09" | "openapi-3.0" | "openAi"
|
|
24
28
|
|
|
25
29
|
constructor(entitySchema: T, entityCreateSchema: TCreate, entityUpdateSchema: TUpdate, entityName: string, target:string = 'openapi-3.0', tags: string[] = []) {
|
|
26
|
-
this.entitySchema = entitySchema;
|
|
30
|
+
this.entitySchema = this.schemaAdapter(entitySchema);
|
|
27
31
|
this.entityCreateSchema = entityCreateSchema;
|
|
28
32
|
this.entityUpdateSchema = entityUpdateSchema;
|
|
29
33
|
this.entityName = entityName;
|
|
30
34
|
this.tags = tags
|
|
31
|
-
this.target = target
|
|
35
|
+
this.target = target;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
private getTypeName(field: any): string | undefined {
|
|
39
|
+
// Zod v4 suele tener constructor.name útil en runtime
|
|
40
|
+
return field?.constructor?.name;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fieldAdapter(field: unknown): ZodTypeAny {
|
|
44
|
+
const f: any = field;
|
|
45
|
+
|
|
46
|
+
const typeName = this.getTypeName(f);
|
|
47
|
+
|
|
48
|
+
// 1) Desenrollar wrappers por "duck typing" (evita líos de tipos en Zod 4)
|
|
49
|
+
if (typeof f?.unwrap === 'function' && typeName === 'ZodOptional') {
|
|
50
|
+
return this.fieldAdapter(f.unwrap()).optional();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof f?.unwrap === 'function' && typeName === 'ZodNullable') {
|
|
54
|
+
return this.fieldAdapter(f.unwrap()).nullable();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2) Tipos compuestos
|
|
58
|
+
if (typeName === 'ZodArray' && f?.element) {
|
|
59
|
+
return z.array(this.fieldAdapter(f.element));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeName === 'ZodObject' && f?.shape) {
|
|
63
|
+
return this.schemaAdapter(f);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 3) Date -> ISO datetime (string)
|
|
67
|
+
if (typeName === 'ZodDate') {
|
|
68
|
+
return z.iso.datetime();
|
|
37
69
|
}
|
|
38
|
-
|
|
70
|
+
|
|
71
|
+
// 4) Fallback: dejar tal cual
|
|
72
|
+
return f as ZodTypeAny;
|
|
39
73
|
}
|
|
40
74
|
|
|
41
|
-
|
|
42
|
-
|
|
75
|
+
schemaAdapter<TObj extends z.ZodObject<z.ZodRawShape>>(entitySchema: TObj): TObj {
|
|
76
|
+
const shape = (entitySchema as any).shape as Record<string, unknown>;
|
|
77
|
+
const newShape: Record<string, ZodTypeAny> = {};
|
|
78
|
+
|
|
79
|
+
for (const key of Object.keys(shape)) {
|
|
80
|
+
newShape[key] = this.fieldAdapter(shape[key]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Re-creamos el objeto con el nuevo shape.
|
|
84
|
+
// Tipamos como TObj para mantener la firma genérica del builder.
|
|
85
|
+
return z.object(newShape) as unknown as TObj;
|
|
43
86
|
}
|
|
44
87
|
|
|
45
|
-
get
|
|
46
|
-
|
|
88
|
+
get getTags() {
|
|
89
|
+
if (this.tags.length > 0) {
|
|
90
|
+
return { tags: this.tags };
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get jsonEntityCreateSchema() {
|
|
96
|
+
return z.toJSONSchema(this.entityCreateSchema, { target: this.target });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get jsonEntityUpdateSchema() {
|
|
100
|
+
return z.toJSONSchema(this.entityUpdateSchema, { target: this.target });
|
|
47
101
|
}
|
|
48
102
|
|
|
49
103
|
get jsonEntitySchema() {
|
|
50
|
-
return z.toJSONSchema(this.entitySchema, {target: this.target})
|
|
104
|
+
return z.toJSONSchema(this.entitySchema, { target: this.target });
|
|
51
105
|
}
|
|
52
106
|
|
|
53
107
|
get jsonEntityArraySchema() {
|
|
54
|
-
return z.toJSONSchema(z.array(this.entitySchema), {target: this.target})
|
|
108
|
+
return z.toJSONSchema(z.array(this.entitySchema), { target: this.target });
|
|
55
109
|
}
|
|
56
110
|
|
|
57
111
|
get jsonEntityGroupBySchema() {
|
|
58
112
|
return z.toJSONSchema(
|
|
59
113
|
z.array(
|
|
60
|
-
z
|
|
61
|
-
|
|
62
|
-
|
|
114
|
+
z
|
|
115
|
+
.object({
|
|
116
|
+
count: z.number()
|
|
117
|
+
})
|
|
118
|
+
.catchall(z.any())
|
|
63
119
|
),
|
|
64
|
-
{target: this.target}
|
|
65
|
-
)
|
|
120
|
+
{ target: this.target }
|
|
121
|
+
);
|
|
66
122
|
}
|
|
67
123
|
|
|
68
124
|
get jsonExportBodyResponse() {
|
|
69
|
-
return z.toJSONSchema(ExportBodyResponseSchema, {target: this.target})
|
|
125
|
+
return z.toJSONSchema(ExportBodyResponseSchema, { target: this.target });
|
|
70
126
|
}
|
|
71
127
|
|
|
72
128
|
get jsonErrorBodyResponse() {
|
|
73
|
-
return z.toJSONSchema(ErrorBodyResponseSchema, {target: this.target})
|
|
129
|
+
return z.toJSONSchema(ErrorBodyResponseSchema, { target: this.target });
|
|
74
130
|
}
|
|
75
131
|
|
|
76
132
|
get jsonValidationErrorBodyResponse() {
|
|
77
|
-
return z.toJSONSchema(ValidationErrorBodyResponseSchema, {target: this.target})
|
|
133
|
+
return z.toJSONSchema(ValidationErrorBodyResponseSchema, { target: this.target });
|
|
78
134
|
}
|
|
79
135
|
|
|
80
|
-
get jsonFindQuerySchema(){
|
|
81
|
-
return z.toJSONSchema(FindQuerySchema, {target: this.target})
|
|
136
|
+
get jsonFindQuerySchema() {
|
|
137
|
+
return z.toJSONSchema(FindQuerySchema, { target: this.target });
|
|
82
138
|
}
|
|
83
139
|
|
|
84
|
-
get jsonGroupByQuerySchema(){
|
|
85
|
-
return z.toJSONSchema(GroupByQuerySchema, {target: this.target})
|
|
140
|
+
get jsonGroupByQuerySchema() {
|
|
141
|
+
return z.toJSONSchema(GroupByQuerySchema, { target: this.target });
|
|
86
142
|
}
|
|
87
143
|
|
|
88
|
-
get jsonSearchQuerySchema(){
|
|
89
|
-
return z.toJSONSchema(SearchQuerySchema, {target: this.target})
|
|
144
|
+
get jsonSearchQuerySchema() {
|
|
145
|
+
return z.toJSONSchema(SearchQuerySchema, { target: this.target });
|
|
90
146
|
}
|
|
91
147
|
|
|
92
|
-
get jsonPaginateQuerySchema(){
|
|
93
|
-
return z.toJSONSchema(PaginateQuerySchema, {target: this.target})
|
|
148
|
+
get jsonPaginateQuerySchema() {
|
|
149
|
+
return z.toJSONSchema(PaginateQuerySchema, { target: this.target });
|
|
94
150
|
}
|
|
95
151
|
|
|
96
|
-
get jsonDeleteBodyResponseSchema(){
|
|
97
|
-
return z.toJSONSchema(DeleteBodyResponseSchema, {target: this.target})
|
|
152
|
+
get jsonDeleteBodyResponseSchema() {
|
|
153
|
+
return z.toJSONSchema(DeleteBodyResponseSchema, { target: this.target });
|
|
98
154
|
}
|
|
99
155
|
|
|
100
|
-
get jsonFindByParamSchema(){
|
|
101
|
-
return z.toJSONSchema(FindByParamSchema, {target: this.target})
|
|
156
|
+
get jsonFindByParamSchema() {
|
|
157
|
+
return z.toJSONSchema(FindByParamSchema, { target: this.target });
|
|
102
158
|
}
|
|
103
159
|
|
|
104
|
-
get jsonPaginateBodyResponseSchema(){
|
|
105
|
-
return z.toJSONSchema(
|
|
106
|
-
|
|
107
|
-
|
|
160
|
+
get jsonPaginateBodyResponseSchema() {
|
|
161
|
+
return z.toJSONSchema(
|
|
162
|
+
PaginateBodyResponseSchema.extend({
|
|
163
|
+
items: z.array(this.entitySchema)
|
|
164
|
+
}),
|
|
165
|
+
{ target: this.target }
|
|
166
|
+
);
|
|
108
167
|
}
|
|
109
168
|
|
|
110
|
-
get jsonIdParamSchema(){
|
|
111
|
-
return z.toJSONSchema(IdParamSchema, {target: this.target})
|
|
169
|
+
get jsonIdParamSchema() {
|
|
170
|
+
return z.toJSONSchema(IdParamSchema, { target: this.target });
|
|
112
171
|
}
|
|
113
172
|
|
|
173
|
+
|
|
114
174
|
/**
|
|
115
175
|
* Get JSON schema for export
|
|
116
176
|
*/
|