@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.object({
70
+ return z.toJSONSchema(z.array(z
71
+ .object({
33
72
  count: z.number()
34
- }).catchall(z.any())), { target: this.target });
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": "1.4.0",
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": "^1.1.1",
26
- "@drax/common-share": "^1.0.0",
27
- "@drax/identity-share": "^1.0.0",
28
- "@drax/media-back": "^1.4.0",
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.21.0",
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": "^20.12.10",
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.6.2"
47
+ "typescript": "^5.9.3"
48
48
  },
49
- "gitHead": "b957d2b908e730535cf9fb628f0c57e20c007d0f"
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<T extends z.ZodObject<z.ZodRawShape>, TCreate extends z.ZodObject<z.ZodRawShape>, TUpdate extends z.ZodObject<z.ZodRawShape>> {
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
- get getTags(){
35
- if(this.tags.length > 0){
36
- return {tags: this.tags}
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
- return []
70
+
71
+ // 4) Fallback: dejar tal cual
72
+ return f as ZodTypeAny;
39
73
  }
40
74
 
41
- get jsonEntityCreateSchema(){
42
- return z.toJSONSchema(this.entityCreateSchema, {target: this.target})
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 jsonEntityUpdateSchema(){
46
- return z.toJSONSchema(this.entityUpdateSchema, {target: this.target})
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.object({
61
- count: z.number()
62
- }).catchall(z.any())
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(PaginateBodyResponseSchema.extend({
106
- items: z.array(this.entitySchema)
107
- }), {target: this.target})
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
  */