@palmares/schemas 0.0.1

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.
Files changed (219) hide show
  1. package/.turbo/turbo-build$colon$watch.log +424 -0
  2. package/.turbo/turbo-build.log +13 -0
  3. package/.turbo/turbo-build:watch.log +26 -0
  4. package/CHANGELOG.md +11 -0
  5. package/LICENSE +21 -0
  6. package/dist/cjs/src/adapter/fields/array.js +157 -0
  7. package/dist/cjs/src/adapter/fields/boolean.js +167 -0
  8. package/dist/cjs/src/adapter/fields/datetime.js +167 -0
  9. package/dist/cjs/src/adapter/fields/index.js +311 -0
  10. package/dist/cjs/src/adapter/fields/number.js +167 -0
  11. package/dist/cjs/src/adapter/fields/object.js +167 -0
  12. package/dist/cjs/src/adapter/fields/string.js +167 -0
  13. package/dist/cjs/src/adapter/fields/union.js +167 -0
  14. package/dist/cjs/src/adapter/index.js +198 -0
  15. package/dist/cjs/src/adapter/types.js +4 -0
  16. package/dist/cjs/src/compile.js +262 -0
  17. package/dist/cjs/src/conf.js +27 -0
  18. package/dist/cjs/src/constants.js +42 -0
  19. package/dist/cjs/src/domain.js +12 -0
  20. package/dist/cjs/src/exceptions.js +168 -0
  21. package/dist/cjs/src/index.js +365 -0
  22. package/dist/cjs/src/model.js +628 -0
  23. package/dist/cjs/src/parsers/convert-from-number.js +20 -0
  24. package/dist/cjs/src/parsers/convert-from-string.js +24 -0
  25. package/dist/cjs/src/parsers/index.js +25 -0
  26. package/dist/cjs/src/schema/array.js +890 -0
  27. package/dist/cjs/src/schema/boolean.js +826 -0
  28. package/dist/cjs/src/schema/datetime.js +778 -0
  29. package/dist/cjs/src/schema/index.js +17 -0
  30. package/dist/cjs/src/schema/number.js +960 -0
  31. package/dist/cjs/src/schema/object.js +999 -0
  32. package/dist/cjs/src/schema/schema.js +1788 -0
  33. package/dist/cjs/src/schema/string.js +948 -0
  34. package/dist/cjs/src/schema/types.js +4 -0
  35. package/dist/cjs/src/schema/union.js +952 -0
  36. package/dist/cjs/src/types.js +4 -0
  37. package/dist/cjs/src/utils.js +627 -0
  38. package/dist/cjs/src/validators/array.js +457 -0
  39. package/dist/cjs/src/validators/boolean.js +199 -0
  40. package/dist/cjs/src/validators/datetime.js +287 -0
  41. package/dist/cjs/src/validators/number.js +403 -0
  42. package/dist/cjs/src/validators/object.js +290 -0
  43. package/dist/cjs/src/validators/schema.js +318 -0
  44. package/dist/cjs/src/validators/string.js +439 -0
  45. package/dist/cjs/src/validators/types.js +4 -0
  46. package/dist/cjs/src/validators/union.js +232 -0
  47. package/dist/cjs/src/validators/utils.js +426 -0
  48. package/dist/cjs/tsconfig.types.tsbuildinfo +1 -0
  49. package/dist/cjs/types/adapter/fields/array.d.ts +20 -0
  50. package/dist/cjs/types/adapter/fields/array.d.ts.map +1 -0
  51. package/dist/cjs/types/adapter/fields/boolean.d.ts +25 -0
  52. package/dist/cjs/types/adapter/fields/boolean.d.ts.map +1 -0
  53. package/dist/cjs/types/adapter/fields/datetime.d.ts +25 -0
  54. package/dist/cjs/types/adapter/fields/datetime.d.ts.map +1 -0
  55. package/dist/cjs/types/adapter/fields/index.d.ts +31 -0
  56. package/dist/cjs/types/adapter/fields/index.d.ts.map +1 -0
  57. package/dist/cjs/types/adapter/fields/number.d.ts +25 -0
  58. package/dist/cjs/types/adapter/fields/number.d.ts.map +1 -0
  59. package/dist/cjs/types/adapter/fields/object.d.ts +25 -0
  60. package/dist/cjs/types/adapter/fields/object.d.ts.map +1 -0
  61. package/dist/cjs/types/adapter/fields/string.d.ts +25 -0
  62. package/dist/cjs/types/adapter/fields/string.d.ts.map +1 -0
  63. package/dist/cjs/types/adapter/fields/union.d.ts +25 -0
  64. package/dist/cjs/types/adapter/fields/union.d.ts.map +1 -0
  65. package/dist/cjs/types/adapter/index.d.ts +25 -0
  66. package/dist/cjs/types/adapter/index.d.ts.map +1 -0
  67. package/dist/cjs/types/adapter/types.d.ts +144 -0
  68. package/dist/cjs/types/adapter/types.d.ts.map +1 -0
  69. package/dist/cjs/types/compile.d.ts +3 -0
  70. package/dist/cjs/types/compile.d.ts.map +1 -0
  71. package/dist/cjs/types/conf.d.ts +16 -0
  72. package/dist/cjs/types/conf.d.ts.map +1 -0
  73. package/dist/cjs/types/constants.d.ts +6 -0
  74. package/dist/cjs/types/constants.d.ts.map +1 -0
  75. package/dist/cjs/types/domain.d.ts +21 -0
  76. package/dist/cjs/types/domain.d.ts.map +1 -0
  77. package/dist/cjs/types/exceptions.d.ts +13 -0
  78. package/dist/cjs/types/exceptions.d.ts.map +1 -0
  79. package/dist/cjs/types/index.d.ts +240 -0
  80. package/dist/cjs/types/index.d.ts.map +1 -0
  81. package/dist/cjs/types/model.d.ts +136 -0
  82. package/dist/cjs/types/model.d.ts.map +1 -0
  83. package/dist/cjs/types/parsers/convert-from-number.d.ts +15 -0
  84. package/dist/cjs/types/parsers/convert-from-number.d.ts.map +1 -0
  85. package/dist/cjs/types/parsers/convert-from-string.d.ts +9 -0
  86. package/dist/cjs/types/parsers/convert-from-string.d.ts.map +1 -0
  87. package/dist/cjs/types/parsers/index.d.ts +3 -0
  88. package/dist/cjs/types/parsers/index.d.ts.map +1 -0
  89. package/dist/cjs/types/schema/array.d.ts +429 -0
  90. package/dist/cjs/types/schema/array.d.ts.map +1 -0
  91. package/dist/cjs/types/schema/boolean.d.ts +501 -0
  92. package/dist/cjs/types/schema/boolean.d.ts.map +1 -0
  93. package/dist/cjs/types/schema/datetime.d.ts +474 -0
  94. package/dist/cjs/types/schema/datetime.d.ts.map +1 -0
  95. package/dist/cjs/types/schema/index.d.ts +4 -0
  96. package/dist/cjs/types/schema/index.d.ts.map +1 -0
  97. package/dist/cjs/types/schema/number.d.ts +667 -0
  98. package/dist/cjs/types/schema/number.d.ts.map +1 -0
  99. package/dist/cjs/types/schema/object.d.ts +450 -0
  100. package/dist/cjs/types/schema/object.d.ts.map +1 -0
  101. package/dist/cjs/types/schema/schema.d.ts +646 -0
  102. package/dist/cjs/types/schema/schema.d.ts.map +1 -0
  103. package/dist/cjs/types/schema/string.d.ts +606 -0
  104. package/dist/cjs/types/schema/string.d.ts.map +1 -0
  105. package/dist/cjs/types/schema/types.d.ts +70 -0
  106. package/dist/cjs/types/schema/types.d.ts.map +1 -0
  107. package/dist/cjs/types/schema/union.d.ts +388 -0
  108. package/dist/cjs/types/schema/union.d.ts.map +1 -0
  109. package/dist/cjs/types/types.d.ts +11 -0
  110. package/dist/cjs/types/types.d.ts.map +1 -0
  111. package/dist/cjs/types/utils.d.ts +79 -0
  112. package/dist/cjs/types/utils.d.ts.map +1 -0
  113. package/dist/cjs/types/validators/array.d.ts +8 -0
  114. package/dist/cjs/types/validators/array.d.ts.map +1 -0
  115. package/dist/cjs/types/validators/boolean.d.ts +4 -0
  116. package/dist/cjs/types/validators/boolean.d.ts.map +1 -0
  117. package/dist/cjs/types/validators/datetime.d.ts +7 -0
  118. package/dist/cjs/types/validators/datetime.d.ts.map +1 -0
  119. package/dist/cjs/types/validators/number.d.ts +10 -0
  120. package/dist/cjs/types/validators/number.d.ts.map +1 -0
  121. package/dist/cjs/types/validators/object.d.ts +6 -0
  122. package/dist/cjs/types/validators/object.d.ts.map +1 -0
  123. package/dist/cjs/types/validators/schema.d.ts +10 -0
  124. package/dist/cjs/types/validators/schema.d.ts.map +1 -0
  125. package/dist/cjs/types/validators/string.d.ts +12 -0
  126. package/dist/cjs/types/validators/string.d.ts.map +1 -0
  127. package/dist/cjs/types/validators/types.d.ts +2 -0
  128. package/dist/cjs/types/validators/types.d.ts.map +1 -0
  129. package/dist/cjs/types/validators/union.d.ts +4 -0
  130. package/dist/cjs/types/validators/union.d.ts.map +1 -0
  131. package/dist/cjs/types/validators/utils.d.ts +83 -0
  132. package/dist/cjs/types/validators/utils.d.ts.map +1 -0
  133. package/dist/esm/src/adapter/fields/array.js +13 -0
  134. package/dist/esm/src/adapter/fields/boolean.js +20 -0
  135. package/dist/esm/src/adapter/fields/datetime.js +20 -0
  136. package/dist/esm/src/adapter/fields/index.js +37 -0
  137. package/dist/esm/src/adapter/fields/number.js +20 -0
  138. package/dist/esm/src/adapter/fields/object.js +20 -0
  139. package/dist/esm/src/adapter/fields/string.js +20 -0
  140. package/dist/esm/src/adapter/fields/union.js +20 -0
  141. package/dist/esm/src/adapter/index.js +18 -0
  142. package/dist/esm/src/adapter/types.js +1 -0
  143. package/dist/esm/src/compile.js +10 -0
  144. package/dist/esm/src/conf.js +19 -0
  145. package/dist/esm/src/constants.js +5 -0
  146. package/dist/esm/src/domain.js +2 -0
  147. package/dist/esm/src/exceptions.js +15 -0
  148. package/dist/esm/src/index.js +160 -0
  149. package/dist/esm/src/model.js +255 -0
  150. package/dist/esm/src/parsers/convert-from-number.js +8 -0
  151. package/dist/esm/src/parsers/convert-from-string.js +14 -0
  152. package/dist/esm/src/parsers/index.js +2 -0
  153. package/dist/esm/src/schema/array.js +403 -0
  154. package/dist/esm/src/schema/boolean.js +465 -0
  155. package/dist/esm/src/schema/datetime.js +423 -0
  156. package/dist/esm/src/schema/index.js +3 -0
  157. package/dist/esm/src/schema/number.js +592 -0
  158. package/dist/esm/src/schema/object.js +464 -0
  159. package/dist/esm/src/schema/schema.js +728 -0
  160. package/dist/esm/src/schema/string.js +579 -0
  161. package/dist/esm/src/schema/types.js +1 -0
  162. package/dist/esm/src/schema/union.js +388 -0
  163. package/dist/esm/src/types.js +1 -0
  164. package/dist/esm/src/utils.js +175 -0
  165. package/dist/esm/src/validators/array.js +135 -0
  166. package/dist/esm/src/validators/boolean.js +35 -0
  167. package/dist/esm/src/validators/datetime.js +85 -0
  168. package/dist/esm/src/validators/number.js +162 -0
  169. package/dist/esm/src/validators/object.js +38 -0
  170. package/dist/esm/src/validators/schema.js +114 -0
  171. package/dist/esm/src/validators/string.js +174 -0
  172. package/dist/esm/src/validators/types.js +1 -0
  173. package/dist/esm/src/validators/union.js +38 -0
  174. package/dist/esm/src/validators/utils.js +120 -0
  175. package/package.json +48 -0
  176. package/src/adapter/fields/array.ts +31 -0
  177. package/src/adapter/fields/boolean.ts +48 -0
  178. package/src/adapter/fields/datetime.ts +49 -0
  179. package/src/adapter/fields/index.ts +72 -0
  180. package/src/adapter/fields/number.ts +49 -0
  181. package/src/adapter/fields/object.ts +49 -0
  182. package/src/adapter/fields/string.ts +49 -0
  183. package/src/adapter/fields/union.ts +49 -0
  184. package/src/adapter/index.ts +34 -0
  185. package/src/adapter/types.ts +261 -0
  186. package/src/compile.ts +14 -0
  187. package/src/conf.ts +27 -0
  188. package/src/constants.ts +9 -0
  189. package/src/domain.ts +3 -0
  190. package/src/exceptions.ts +17 -0
  191. package/src/index.ts +338 -0
  192. package/src/model.ts +501 -0
  193. package/src/parsers/convert-from-number.ts +13 -0
  194. package/src/parsers/convert-from-string.ts +19 -0
  195. package/src/parsers/index.ts +2 -0
  196. package/src/schema/array.ts +633 -0
  197. package/src/schema/boolean.ts +700 -0
  198. package/src/schema/datetime.ts +613 -0
  199. package/src/schema/index.ts +5 -0
  200. package/src/schema/number.ts +885 -0
  201. package/src/schema/object.ts +699 -0
  202. package/src/schema/schema.ts +1093 -0
  203. package/src/schema/string.ts +807 -0
  204. package/src/schema/types.ts +126 -0
  205. package/src/schema/union.ts +596 -0
  206. package/src/types.ts +13 -0
  207. package/src/utils.ts +322 -0
  208. package/src/validators/array.ts +164 -0
  209. package/src/validators/boolean.ts +46 -0
  210. package/src/validators/datetime.ts +113 -0
  211. package/src/validators/number.ts +188 -0
  212. package/src/validators/object.ts +55 -0
  213. package/src/validators/schema.ts +134 -0
  214. package/src/validators/string.ts +215 -0
  215. package/src/validators/types.ts +1 -0
  216. package/src/validators/union.ts +52 -0
  217. package/src/validators/utils.ts +200 -0
  218. package/tsconfig.json +9 -0
  219. package/tsconfig.types.json +10 -0
package/src/model.ts ADDED
@@ -0,0 +1,501 @@
1
+ import {
2
+ AutoField,
3
+ BigAutoField,
4
+ BooleanField,
5
+ CharField,
6
+ DateField,
7
+ DecimalField,
8
+ EnumField,
9
+ type Field,
10
+ ForeignKeyField,
11
+ IntegerField,
12
+ type InternalModelClass_DoNotUse,
13
+ type Model,
14
+ type ModelFields,
15
+ TextField,
16
+ TranslatableField,
17
+ UuidField
18
+ } from '@palmares/databases';
19
+
20
+ import { TranslatableFieldNotImplementedError } from './exceptions';
21
+ import { number } from './schema';
22
+ import ArraySchema from './schema/array';
23
+ import { boolean } from './schema/boolean';
24
+ import { datetime } from './schema/datetime';
25
+ import ObjectSchema from './schema/object';
26
+ import Schema from './schema/schema';
27
+ import { string } from './schema/string';
28
+ import { union } from './schema/union';
29
+
30
+ import type { DefinitionsOfSchemaType, ExtractTypeFromObjectOfSchemas } from './schema/types';
31
+
32
+ async function getSchemaFromModelField(
33
+ model: ReturnType<typeof Model>,
34
+ field: Field<any, any, any, any, any, any, any, any>,
35
+ parent: Schema<any, any> | undefined,
36
+ definedFields: Record<any, Schema<any, DefinitionsOfSchemaType>> | undefined,
37
+ engineInstanceName?: string,
38
+ options?: {
39
+ foreignKeyRelation?: {
40
+ schema?: Schema<any, any>;
41
+ isArray: boolean;
42
+ model: ReturnType<typeof Model>;
43
+ fieldToSearchOnModel: string;
44
+ fieldToGetFromData: string;
45
+ relationOrRelatedName: string;
46
+ };
47
+ }
48
+ ) {
49
+ let schema: Schema<any, any> | undefined = undefined;
50
+ if (field instanceof AutoField || field instanceof BigAutoField) schema = number().integer().optional();
51
+ else if (field instanceof DecimalField)
52
+ schema = number().decimalPlaces(field.decimalPlaces).maxDigits(field.maxDigits);
53
+ else if (field instanceof IntegerField) schema = number().integer();
54
+ else if (field instanceof BooleanField) schema = boolean();
55
+ else if (field instanceof TextField || field instanceof CharField || field instanceof UuidField) {
56
+ schema = string();
57
+ if (field.allowBlank === false) schema = (schema as ReturnType<typeof string>).minLength(1);
58
+ if (field instanceof CharField && typeof field.maxLength === 'number')
59
+ schema = (schema as ReturnType<typeof string>).maxLength(field.maxLength);
60
+ if (field instanceof UuidField) {
61
+ schema = (schema as ReturnType<typeof string>).uuid();
62
+ if (field.autoGenerate) schema = (schema as ReturnType<typeof string>).optional();
63
+ }
64
+ } else if (field instanceof DateField) {
65
+ schema = datetime().allowString();
66
+ if (field.autoNow || field.autoNowAdd) schema = (schema as ReturnType<typeof datetime>).optional();
67
+ } else if (field instanceof EnumField) {
68
+ const allChoicesOfTypeStrings = field.choices.filter((choice: any) => typeof choice === 'string');
69
+ const allChoicesOfTypeNumbers = field.choices.filter((choice: any) => typeof choice === 'number');
70
+
71
+ let schemaForChoicesAsStrings: Schema<any, any> | undefined = undefined;
72
+ let schemaForChoicesAsNumbers: Schema<any, any> | undefined = undefined;
73
+ if (allChoicesOfTypeStrings.length > 0) schemaForChoicesAsStrings = string().is([...allChoicesOfTypeStrings]);
74
+ if (allChoicesOfTypeNumbers.length > 0) schemaForChoicesAsNumbers = number().is([...allChoicesOfTypeNumbers]);
75
+ if (schemaForChoicesAsStrings && schemaForChoicesAsNumbers)
76
+ schema = union([schemaForChoicesAsStrings, schemaForChoicesAsNumbers]);
77
+ else if (schemaForChoicesAsStrings) schema = schemaForChoicesAsStrings;
78
+ else if (schemaForChoicesAsNumbers) schema = schemaForChoicesAsNumbers;
79
+ } else if (field instanceof ForeignKeyField) {
80
+ const doesADefinedFieldExistWithRelatedName =
81
+ parent && field.relatedName && (parent as any).__data?.[field.relatedName];
82
+ const doesADefinedFieldExistWithRelationName =
83
+ definedFields && field.relationName && definedFields[field.relationName];
84
+ const fieldWithRelatedName = doesADefinedFieldExistWithRelatedName
85
+ ? (parent as any).__data?.[field.relatedName]
86
+ : undefined;
87
+ const fieldWithRelationName = doesADefinedFieldExistWithRelationName
88
+ ? definedFields[field.relationName]
89
+ : undefined;
90
+ const isFieldWithRelatedNameAModelField =
91
+ fieldWithRelatedName instanceof Schema && (fieldWithRelatedName as any).__model !== undefined;
92
+ const isFieldWithRelationNameAModelField =
93
+ fieldWithRelationName instanceof Schema && (fieldWithRelationName as any).__model !== undefined;
94
+ const relatedToModel = field.relatedTo;
95
+ const toField = field.toField;
96
+ const engineInstance = await model.default.getEngineInstance(engineInstanceName);
97
+ const relatedToModelInstance = engineInstance.__modelsOfEngine[relatedToModel];
98
+ const modelFieldsOfRelatedModel = (relatedToModelInstance as any).__cachedFields[toField];
99
+ if (isFieldWithRelatedNameAModelField) {
100
+ if (typeof options !== 'object') options = {};
101
+ options.foreignKeyRelation = {
102
+ schema: parent,
103
+ isArray: fieldWithRelatedName instanceof ArraySchema,
104
+ model: (fieldWithRelatedName as any).__model,
105
+ fieldToSearchOnModel: field.fieldName,
106
+ fieldToGetFromData: field.toField,
107
+ relationOrRelatedName: field.relatedName!
108
+ };
109
+ } else if (isFieldWithRelationNameAModelField) {
110
+ if (typeof options !== 'object') options = {};
111
+ options.foreignKeyRelation = {
112
+ isArray: fieldWithRelationName instanceof ArraySchema,
113
+ model: (fieldWithRelationName as any).__model,
114
+ fieldToSearchOnModel: field.toField,
115
+ fieldToGetFromData: field.fieldName,
116
+ relationOrRelatedName: field.relationName!
117
+ };
118
+ }
119
+
120
+ return getSchemaFromModelField(
121
+ relatedToModelInstance,
122
+ modelFieldsOfRelatedModel,
123
+ parent,
124
+ definedFields,
125
+ engineInstanceName,
126
+ options
127
+ );
128
+ } else if (field instanceof TranslatableField && field.customAttributes.schema) {
129
+ if (field.customAttributes.schema instanceof Schema === false)
130
+ throw new TranslatableFieldNotImplementedError(field.fieldName);
131
+ schema = field.customAttributes.schema;
132
+ }
133
+
134
+ if (field.allowNull && schema) schema = schema.nullable().optional();
135
+ if (field.defaultValue && schema) schema = schema.default(field.defaultValue);
136
+
137
+ return schema || string();
138
+ }
139
+
140
+ /**
141
+ * Different from other schemas, this function is a factory function that returns either an ObjectSchema or an ArraySchema.
142
+ * The idea is to build the schema of a model dynamically based on its fields.
143
+ *
144
+ * Another feature is that it can automatically add the foreign key relation to the schema, but for that you need to define
145
+ * the fields of the related model in the fields object.
146
+ *
147
+ * For example: A User model have a field `companyId` that is a ForeignKeyField to the Company model. The `relationName`
148
+ * is the direct relation from the User model to the Company model, and the `relatedName` is the reverse relation from the
149
+ * Company model to the User model. If you define the fieldName as either the relatedName or the relationName it will fetch
150
+ * the data automatically.
151
+ *
152
+ * **Important**: We build the schema dynamically but also lazily, if you don't try to parse or validate the schema, it won't be built.
153
+ * After the first time it's built, it's cached and never built again.
154
+ *
155
+ * **Important 2**: If you want to use the automatic relation feature, you need to define guarantee that the foreignKey field fieldName
156
+ * exists on `show` array, or that it doesn't exist on `omit` array.
157
+ *
158
+ * Like: `{ options: { show: ['id', 'name', 'companyId'] }}` or `{ options: { omit: ['id'] }}` it **will work**.
159
+ *
160
+ * If you do `{ options: { show: ['id', 'name'] }}` or `{ options: { omit: ['companyId']} }` it **won't work**.
161
+ *
162
+ * **Important 3**: If you want to return an array instead of an object, you need to pass the `many` option as true.
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * import { auto, choice, foreignKey, Model, define } from '@palmares/databases';
167
+ * import * as p from '@palmares/schemas';
168
+ *
169
+ * const Company = define('Company', {
170
+ * fields: {
171
+ * id: auto(),
172
+ * name: text(),
173
+ * },
174
+ * options: {
175
+ * tableName: 'company',
176
+ * }
177
+ * });
178
+ *
179
+ * class User extends Model<User>() {
180
+ * fields = {
181
+ * id: auto(),
182
+ * type: choice({ choices: ['user', 'admin'] }),
183
+ * companyId: foreignKey({
184
+ * relatedTo: Company,
185
+ * relationName: 'company',
186
+ * relatedName: 'usersOfCompany',
187
+ * toField: 'id',
188
+ * onDelete: 'CASCADE',
189
+ * }),
190
+ * }
191
+ *
192
+ * options = {
193
+ * tableName: 'user',
194
+ * }
195
+ * }
196
+ *
197
+ * const userSchema = p.modelSchema(User, {
198
+ * fields: {
199
+ * company: p.modelSchema(Company).optional({ outputOnly: true });
200
+ * },
201
+ * show: ['type', 'companyId'], // 'companyId' is required for the automatic relation to work, otherwise it won't show
202
+ * omitRelation: ['company']
203
+ * });
204
+ *
205
+ * const companySchema = p.modelSchema(Company, {
206
+ * fields: {
207
+ * usersOfCompany: p.modelSchema(User, { many: true }).optional({ outputOnly: true });
208
+ * },
209
+ * show: ['id', 'type'] // The `companyId` field on the 'User' model is tied to the `id` field on the 'Company' model so 'id' is required.
210
+ * });
211
+ *```
212
+ * @param model - The model that you want to build the schema from.
213
+ * @param options - The options to build the schema.
214
+ * @param options.ignoreExtraneousFields - If you want to ignore extraneous fields set this to true.
215
+ * @param options.engineInstance - What engine instance you want to use to fetch the data. Defaults to the first one.
216
+ * @param options.omitRelation - Fields that you want to omit from the relation. For example, on the example above, on the
217
+ * `userSchema` you can omit the `companyId` field from the relation by just passing `['company']`, on the `companySchema`
218
+ * you can omit the `id` field from company by passing `['usersOfCompany']`.
219
+ *
220
+ * @param options.fields - Extra fields that you want to add to the schema. If it has the same name as the model field,
221
+ * We will not create a schema for that field and use the one you have defined here.
222
+ * @param options.omit - Fields that you want to omit from the schema. If that is defined, we ignore `show` option.
223
+ * @param options.show - Fields that you want to show on the schema. If that is defined, we ignore `omit` option.
224
+ * @param options.many - If you want to return an array instead of an object, set this to true. With that we create
225
+ * an ArraySchema instead of an ObjectSchema.
226
+ *
227
+ * @returns - If you pass the `many` option as true, we return an ArraySchema, otherwise we return an ObjectSchema.
228
+ */
229
+ export function modelSchema<
230
+ TModel extends ReturnType<typeof Model>,
231
+ const TOmit extends readonly (keyof ModelFields<InstanceType<TModel>>)[] | undefined[] = undefined[],
232
+ const TShow extends readonly (keyof ModelFields<InstanceType<TModel>>)[] | undefined[] = undefined[],
233
+ TMany extends boolean = false,
234
+ TFields extends Record<any, Schema<any, DefinitionsOfSchemaType>> | undefined = undefined,
235
+ TAllModelFields = ModelFields<InstanceType<TModel>>,
236
+ TDefinitionsOfSchemaType extends DefinitionsOfSchemaType = DefinitionsOfSchemaType,
237
+ TFieldsOnModel = TOmit extends undefined[]
238
+ ? TShow extends undefined[]
239
+ ? TAllModelFields
240
+ : Pick<TAllModelFields, TShow[number] extends keyof TAllModelFields ? TShow[number] : never>
241
+ : Omit<TAllModelFields, TOmit[number] extends keyof TAllModelFields ? TOmit[number] : never>,
242
+ TReturnType extends {
243
+ input: any;
244
+ output: any;
245
+ validate: any;
246
+ internal: any;
247
+ representation: any;
248
+ } = {
249
+ input: TFields extends undefined
250
+ ? TFieldsOnModel
251
+ : Omit<
252
+ TFieldsOnModel,
253
+ keyof ExtractTypeFromObjectOfSchemas<
254
+ // eslint-disable-next-line ts/ban-types
255
+ TFields extends undefined ? {} : TFields,
256
+ 'input'
257
+ >
258
+ > &
259
+ ExtractTypeFromObjectOfSchemas<
260
+ // eslint-disable-next-line ts/ban-types
261
+ TFields extends undefined ? {} : TFields,
262
+ 'input'
263
+ >;
264
+ output: TFields extends undefined
265
+ ? TFieldsOnModel
266
+ : Omit<
267
+ TFieldsOnModel,
268
+ keyof ExtractTypeFromObjectOfSchemas<
269
+ // eslint-disable-next-line ts/ban-types
270
+ TFields extends undefined ? {} : TFields,
271
+ 'output'
272
+ >
273
+ > &
274
+ ExtractTypeFromObjectOfSchemas<
275
+ // eslint-disable-next-line ts/ban-types
276
+ TFields extends undefined ? {} : TFields,
277
+ 'output'
278
+ >;
279
+ internal: TFields extends undefined
280
+ ? TFieldsOnModel
281
+ : Omit<
282
+ TFieldsOnModel,
283
+ keyof ExtractTypeFromObjectOfSchemas<
284
+ // eslint-disable-next-line ts/ban-types
285
+ TFields extends undefined ? {} : TFields,
286
+ 'internal'
287
+ >
288
+ > &
289
+ ExtractTypeFromObjectOfSchemas<
290
+ // eslint-disable-next-line ts/ban-types
291
+ TFields extends undefined ? {} : TFields,
292
+ 'internal'
293
+ >;
294
+ representation: TFields extends undefined
295
+ ? TFieldsOnModel
296
+ : Omit<
297
+ TFieldsOnModel,
298
+ keyof ExtractTypeFromObjectOfSchemas<
299
+ // eslint-disable-next-line ts/ban-types
300
+ TFields extends Record<any, Schema<any, DefinitionsOfSchemaType>> ? TFields : {},
301
+ 'representation'
302
+ >
303
+ > &
304
+ ExtractTypeFromObjectOfSchemas<
305
+ // eslint-disable-next-line ts/ban-types
306
+ TFields extends Record<any, Schema<any, DefinitionsOfSchemaType>> ? TFields : {},
307
+ 'representation'
308
+ >;
309
+ validate: TFields extends undefined
310
+ ? TFieldsOnModel
311
+ : Omit<
312
+ TFieldsOnModel,
313
+ keyof ExtractTypeFromObjectOfSchemas<
314
+ // eslint-disable-next-line ts/ban-types
315
+ TFields extends Record<any, Schema<any, DefinitionsOfSchemaType>> ? TFields : {},
316
+ 'validate'
317
+ >
318
+ > &
319
+ ExtractTypeFromObjectOfSchemas<
320
+ // eslint-disable-next-line ts/ban-types
321
+ TFields extends Record<any, Schema<any, DefinitionsOfSchemaType>> ? TFields : {},
322
+ 'validate'
323
+ >;
324
+ }
325
+ >(
326
+ model: TModel,
327
+ options?: {
328
+ ignoreExtraneousFields?: boolean;
329
+ engineInstance?: string;
330
+ fields?: TFields;
331
+ omit?: TOmit;
332
+ show?: TShow;
333
+ omitRelation?: readonly (keyof TFields)[];
334
+ many?: TMany;
335
+ }
336
+ ): TMany extends true
337
+ ? ArraySchema<
338
+ {
339
+ input: TReturnType['input'][];
340
+ output: TReturnType['output'][];
341
+ internal: TReturnType['internal'][];
342
+ representation: TReturnType['representation'][];
343
+ validate: TReturnType['validate'][];
344
+ },
345
+ TDefinitionsOfSchemaType,
346
+ [
347
+ ObjectSchema<
348
+ {
349
+ input: TReturnType['input'];
350
+ output: TReturnType['output'];
351
+ internal: TReturnType['internal'];
352
+ representation: TReturnType['representation'];
353
+ validate: TReturnType['validate'];
354
+ },
355
+ TDefinitionsOfSchemaType,
356
+ Record<any, any>
357
+ >[]
358
+ ]
359
+ >
360
+ : ObjectSchema<
361
+ {
362
+ input: TReturnType['input'];
363
+ output: TReturnType['output'];
364
+ internal: TReturnType['internal'];
365
+ representation: TReturnType['representation'];
366
+ validate: TReturnType['validate'];
367
+ },
368
+ TDefinitionsOfSchemaType,
369
+ Record<any, any>
370
+ > {
371
+ const lazyModelSchema = ObjectSchema.new({} as any) as ObjectSchema<any, any, any> & {
372
+ __runBeforeParseAndData: Required<Schema<any, any>['__runBeforeParseAndData']>;
373
+ };
374
+ const parentSchema = options?.many === true ? ArraySchema.new([lazyModelSchema]) : (lazyModelSchema as any);
375
+ const omitRelationAsSet = new Set(options?.omitRelation || []);
376
+ const omitAsSet = new Set(options?.omit || []);
377
+ const showAsSet = new Set(options?.show || []);
378
+ const fieldsAsObject = options?.fields || {};
379
+ const customFieldValues = Object.values(fieldsAsObject);
380
+
381
+ // We need to add it to the instance to be able to access it on the `toRepresentation` callback
382
+ (lazyModelSchema as any).__omitRelation = omitRelationAsSet;
383
+ parentSchema.__model = model;
384
+ (lazyModelSchema as any).__model = model;
385
+
386
+ // Add this callback to transform the model fields
387
+ parentSchema.__runBeforeParseAndData = async () => {
388
+ if (parentSchema.__alreadyAppliedModel) return;
389
+ parentSchema.__alreadyAppliedModel = true;
390
+ const fieldsOfModels = (model as unknown as typeof InternalModelClass_DoNotUse)._fields();
391
+ const fieldsAsEntries = Object.entries(fieldsOfModels);
392
+ const fieldsWithAutomaticRelations = new Map<
393
+ Schema<any, any>,
394
+ {
395
+ relationOrRelatedName: string;
396
+ isArray: boolean;
397
+ model: ReturnType<typeof Model>;
398
+ fieldToSearchOnModel: string;
399
+ fieldToGetFromData: string;
400
+ }[]
401
+ >();
402
+
403
+ const fields = await fieldsAsEntries.reduce(
404
+ async (accumulatorAsPromise, [key, value]) => {
405
+ if (omitAsSet.has(key as any)) return accumulatorAsPromise;
406
+ if (showAsSet.size > 0 && !showAsSet.has(key as any)) return accumulatorAsPromise;
407
+
408
+ let schema = (fieldsAsObject as any)[key as any];
409
+ const optionsForForeignKeyRelation: any = {};
410
+ if (!schema || value instanceof ForeignKeyField) {
411
+ const newSchema = await getSchemaFromModelField(
412
+ model,
413
+ value,
414
+ parentSchema?.__getParent?.(),
415
+ options?.fields,
416
+ options?.engineInstance,
417
+ optionsForForeignKeyRelation
418
+ );
419
+ if (!schema) schema = newSchema;
420
+ }
421
+
422
+ // Appends the foreign key relation to the schema automatically.
423
+ if (optionsForForeignKeyRelation.foreignKeyRelation) {
424
+ const rootSchema = optionsForForeignKeyRelation?.foreignKeyRelation?.schema || lazyModelSchema;
425
+ const existingRelations =
426
+ fieldsWithAutomaticRelations.get(rootSchema) ||
427
+ ([] as {
428
+ relationOrRelatedName: string;
429
+ isArray: boolean;
430
+ model: ReturnType<typeof Model>;
431
+ fieldToSearchOnModel: string;
432
+ fieldToGetFromData: string;
433
+ }[]);
434
+ existingRelations.push({
435
+ relationOrRelatedName: optionsForForeignKeyRelation.foreignKeyRelation.relationOrRelatedName,
436
+ isArray: optionsForForeignKeyRelation.foreignKeyRelation.isArray,
437
+ model: optionsForForeignKeyRelation.foreignKeyRelation.model,
438
+ fieldToSearchOnModel: optionsForForeignKeyRelation.foreignKeyRelation.fieldToSearchOnModel,
439
+ fieldToGetFromData: optionsForForeignKeyRelation.foreignKeyRelation.fieldToGetFromData
440
+ });
441
+ fieldsWithAutomaticRelations.set(rootSchema, existingRelations);
442
+ }
443
+
444
+ const accumulator = await accumulatorAsPromise;
445
+ accumulator[key] = schema;
446
+ return accumulator;
447
+ },
448
+ Promise.resolve(fieldsAsObject as Record<any, Schema<any, any>>)
449
+ );
450
+
451
+ if (fieldsWithAutomaticRelations.size > 0) {
452
+ // This way we can get all of the relations concurrently with Promise.all
453
+ for (const [schema, relations] of fieldsWithAutomaticRelations.entries()) {
454
+ schema.toRepresentation(
455
+ async (data: any | any[]) => {
456
+ const allData = Array.isArray(data) ? data : [data];
457
+ // since we are changing the data by reference, just return the data itself.
458
+ await Promise.all(
459
+ allData.map(async (data) =>
460
+ Promise.all(
461
+ relations.map(async (relation) => {
462
+ // Ignore if the data of the relation already exists
463
+ if (relation.relationOrRelatedName in data) return;
464
+
465
+ let relationData: any | any[] = await relation.model.default.get({
466
+ search: {
467
+ [relation.fieldToSearchOnModel]: data[relation.fieldToGetFromData]
468
+ }
469
+ });
470
+ if (relation.isArray !== true) relationData = relationData[0];
471
+ data[relation.relationOrRelatedName] = relationData;
472
+
473
+ if ((schema as any).__omitRelation.has(relation.relationOrRelatedName as any))
474
+ delete data[relation.fieldToGetFromData];
475
+ })
476
+ )
477
+ )
478
+ );
479
+
480
+ return data;
481
+ },
482
+ {
483
+ after: true
484
+ }
485
+ );
486
+ }
487
+ }
488
+
489
+ (lazyModelSchema as any).__data = fields as any;
490
+
491
+ await Promise.all(
492
+ customFieldValues.map(async (schema: any) => {
493
+ schema['__getParent'] = () => lazyModelSchema;
494
+ if (schema['__runBeforeParseAndData']) await schema['__runBeforeParseAndData'](schema);
495
+ })
496
+ );
497
+ };
498
+
499
+ if (options?.ignoreExtraneousFields !== true) lazyModelSchema.removeExtraneous();
500
+ return parentSchema;
501
+ }
@@ -0,0 +1,13 @@
1
+ import type Schema from '../schema/schema';
2
+
3
+ /**
4
+ * This will convert a value from a number to any other type.
5
+ *
6
+ * @param callback
7
+ * @returns
8
+ */
9
+ export default function convertFromNumberBuilder(
10
+ callback: (value: number) => Awaited<ReturnType<Parameters<Schema['__parsers']['high']['set']>[1]>>
11
+ ) {
12
+ return (value: any) => callback(value);
13
+ }
@@ -0,0 +1,19 @@
1
+ import type Schema from '../schema/schema';
2
+
3
+ /**
4
+ * This will convert a value from a string to any other type.
5
+ *
6
+ * @param callback
7
+ * @returns
8
+ */
9
+ export default function convertFromStringBuilder(
10
+ callback: (value: string) => Awaited<ReturnType<Parameters<Schema['__parsers']['high']['set']>[1]>>
11
+ ): Parameters<Schema['__parsers']['high']['set']>[1] {
12
+ return (value: any) => {
13
+ if (typeof value === 'string') return callback(value);
14
+ return {
15
+ value,
16
+ preventNextParsers: false,
17
+ };
18
+ };
19
+ }
@@ -0,0 +1,2 @@
1
+ export { default as convertFromNumberBuilder } from './convert-from-number';
2
+ export { default as convertFromStringBuilder } from './convert-from-string';