@based/schema 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 (63) hide show
  1. package/README.md +21 -0
  2. package/dist/deepPartial.d.ts +0 -0
  3. package/dist/deepPartial.js +3 -0
  4. package/dist/deepPartial.js.map +1 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.js +20 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/languages.d.ts +187 -0
  9. package/dist/languages.js +190 -0
  10. package/dist/languages.js.map +1 -0
  11. package/dist/schema.d.ts +1 -0
  12. package/dist/schema.js +3 -0
  13. package/dist/schema.js.map +1 -0
  14. package/dist/set/enum.d.ts +2 -0
  15. package/dist/set/enum.js +15 -0
  16. package/dist/set/enum.js.map +1 -0
  17. package/dist/set/fieldValidator.d.ts +6 -0
  18. package/dist/set/fieldValidator.js +144 -0
  19. package/dist/set/fieldValidator.js.map +1 -0
  20. package/dist/set/handleError.d.ts +1 -0
  21. package/dist/set/handleError.js +9 -0
  22. package/dist/set/handleError.js.map +1 -0
  23. package/dist/set/index.d.ts +5 -0
  24. package/dist/set/index.js +93 -0
  25. package/dist/set/index.js.map +1 -0
  26. package/dist/set/parsers.d.ts +6 -0
  27. package/dist/set/parsers.js +287 -0
  28. package/dist/set/parsers.js.map +1 -0
  29. package/dist/setWalker.d.ts +11 -0
  30. package/dist/setWalker.js +189 -0
  31. package/dist/setWalker.js.map +1 -0
  32. package/dist/transformers.d.ts +3 -0
  33. package/dist/transformers.js +18 -0
  34. package/dist/transformers.js.map +1 -0
  35. package/dist/typeWalker.d.ts +3 -0
  36. package/dist/typeWalker.js +18 -0
  37. package/dist/typeWalker.js.map +1 -0
  38. package/dist/types.d.ts +163 -0
  39. package/dist/types.js +8 -0
  40. package/dist/types.js.map +1 -0
  41. package/dist/validate.d.ts +4 -0
  42. package/dist/validate.js +34 -0
  43. package/dist/validate.js.map +1 -0
  44. package/dist/validateFields.d.ts +4 -0
  45. package/dist/validateFields.js +34 -0
  46. package/dist/validateFields.js.map +1 -0
  47. package/dist/validateSchema copy.d.ts +4 -0
  48. package/dist/validateSchema copy.js +34 -0
  49. package/dist/validateSchema copy.js.map +1 -0
  50. package/dist/validateSchema.d.ts +4 -0
  51. package/dist/validateSchema.js +34 -0
  52. package/dist/validateSchema.js.map +1 -0
  53. package/package.json +35 -0
  54. package/src/index.ts +5 -0
  55. package/src/languages.ts +188 -0
  56. package/src/set/handleError.ts +15 -0
  57. package/src/set/index.ts +135 -0
  58. package/src/set/parsers.ts +448 -0
  59. package/src/types.ts +277 -0
  60. package/src/validateSchema.ts +56 -0
  61. package/test/setWalker.ts +197 -0
  62. package/test/validateSchema.ts +42 -0
  63. package/tsconfig.json +9 -0
@@ -0,0 +1,135 @@
1
+ import {
2
+ BasedSchemaField,
3
+ BasedSchemaType,
4
+ BasedSetHandlers,
5
+ BasedSchema,
6
+ BasedSetTarget,
7
+ } from '../types'
8
+ import { createError } from './handleError'
9
+ import parsers from './parsers'
10
+
11
+ // Collect is a pretty good place for checking if a user is allowed to set something
12
+ // also make collect async
13
+
14
+ // add extra function for loading required
15
+
16
+ export const fieldWalker = async (
17
+ path: (string | number)[],
18
+ value: any,
19
+ fieldSchema: BasedSchemaField,
20
+ typeSchema: BasedSchemaType,
21
+ target: BasedSetTarget,
22
+ handlers: BasedSetHandlers
23
+ ): Promise<void> => {
24
+ if ('$ref' in fieldSchema) {
25
+ // TODO: when we have this it has to get it from the schema and redo the parsing with the correct fieldSchema
26
+ return
27
+ }
28
+ const valueType = typeof value
29
+
30
+ const valueIsObject = value && valueType === 'object'
31
+ if (valueIsObject && value.$delete === true) {
32
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
33
+ return
34
+ }
35
+
36
+ const typeDef =
37
+ 'type' in fieldSchema
38
+ ? fieldSchema.type
39
+ : 'enum' in fieldSchema
40
+ ? 'enum'
41
+ : ''
42
+
43
+ if (!typeDef) {
44
+ throw createError(path, target.type, typeDef, path[path.length - 1])
45
+ }
46
+
47
+ if ('customValidator' in fieldSchema) {
48
+ const customValidator = fieldSchema.customValidator
49
+ if (!(await customValidator(value, path, target))) {
50
+ throw createError(path, target.type, typeDef, value)
51
+ }
52
+ }
53
+
54
+ const parse = parsers[typeDef]
55
+
56
+ await parse(path, value, fieldSchema, typeSchema, target, handlers)
57
+
58
+ return
59
+ }
60
+
61
+ export const setWalker = async (
62
+ schema: BasedSchema,
63
+ value: { [key: string]: any },
64
+ handlers: BasedSetHandlers
65
+ ): Promise<BasedSetTarget> => {
66
+ let type: string
67
+
68
+ if (value.$id) {
69
+ type = schema.prefixToTypeMapping[value.$id.slice(0, 2)]
70
+ if (!type) {
71
+ throw new Error(`Cannot find type for $id ${value.$id}`)
72
+ }
73
+ }
74
+
75
+ if (value.type) {
76
+ if (type && value.type !== type) {
77
+ throw new Error(
78
+ `type from "$id" ${value.$id} does not match "type" field ${value.type}`
79
+ )
80
+ }
81
+ type = value.type
82
+ }
83
+
84
+ const schemaType = schema.types[type]
85
+
86
+ if (!schemaType) {
87
+ throw new Error(`Cannot find schema definition for type ${type}`)
88
+ }
89
+
90
+ const target: BasedSetTarget = {
91
+ type,
92
+ schema,
93
+ }
94
+
95
+ if (value.$id) {
96
+ target.$id = value.$id
97
+ } else if (value.$alias) {
98
+ target.$alias = value.$alias
99
+ }
100
+
101
+ const q: Promise<void>[] = []
102
+
103
+ for (const key in value) {
104
+ if (key[0] === '$') {
105
+ console.info('key is operator', key)
106
+ } else {
107
+ const fieldSchema = schemaType.fields[key]
108
+ if (!fieldSchema) {
109
+ throw new Error(
110
+ `Field does not exist in schema "${key}" on type "${type}"`
111
+ )
112
+ } else {
113
+ q.push(
114
+ fieldWalker(
115
+ [key],
116
+ value[key],
117
+ fieldSchema,
118
+ schemaType,
119
+ target,
120
+ handlers
121
+ )
122
+ )
123
+ }
124
+ }
125
+ }
126
+
127
+ await Promise.all(q)
128
+
129
+ // required fields (collect them!)
130
+ // if (!(await handlers.requiredFields(value, [], target))) {
131
+ // throw new Error('Missing required fields')
132
+ // }
133
+
134
+ return target
135
+ }
@@ -0,0 +1,448 @@
1
+ import {
2
+ BasedSchemaField,
3
+ BasedSchemaType,
4
+ BasedSetHandlers,
5
+ BasedSetTarget,
6
+ } from '../types'
7
+ import { deepEqual } from '@saulx/utils'
8
+ import { createError } from './handleError'
9
+ import { fieldWalker } from '.'
10
+
11
+ type Parser = (
12
+ path: (string | number)[],
13
+ value: any,
14
+ fieldSchema: BasedSchemaField,
15
+ typeSchema: BasedSchemaType,
16
+ target: BasedSetTarget,
17
+ handlers: BasedSetHandlers
18
+ ) => Promise<void>
19
+
20
+ const reference: Parser = async (
21
+ path,
22
+ value,
23
+ fieldSchema,
24
+ typeSchema,
25
+ target,
26
+ handlers
27
+ ) => {
28
+ // $no root
29
+
30
+ // prob pass these as options
31
+ // value .default
32
+ // $value
33
+
34
+ if (typeof value !== 'string') {
35
+ throw createError(path, target.type, 'reference', value)
36
+ }
37
+ if ('allowedTypes' in fieldSchema) {
38
+ const prefix = value.slice(0, 2)
39
+ const targetType = target.schema.prefixToTypeMapping[prefix]
40
+ if (!targetType) {
41
+ throw createError(
42
+ path,
43
+ target.type,
44
+ 'reference',
45
+ value,
46
+ '',
47
+ `${prefix} does not exist in database`
48
+ )
49
+ }
50
+ let typeMatches = false
51
+ for (const t of fieldSchema.allowedTypes) {
52
+ if (typeof t === 'string') {
53
+ if (t === targetType) {
54
+ typeMatches = true
55
+ break
56
+ }
57
+ } else {
58
+ if (t.type && t.type === targetType) {
59
+ typeMatches = true
60
+ if (t.$filter) {
61
+ if (!(await handlers.referenceFilterCondition(value, t.$filter))) {
62
+ throw createError(
63
+ path,
64
+ target.type,
65
+ 'reference',
66
+ value,
67
+ '',
68
+ `${targetType} does not match allowedReferences`
69
+ )
70
+ }
71
+ }
72
+ } else if (!t.type && t.$filter) {
73
+ if (!(await handlers.referenceFilterCondition(value, t.$filter))) {
74
+ throw createError(
75
+ path,
76
+ target.type,
77
+ 'reference',
78
+ value,
79
+ '',
80
+ `${targetType} does not match allowedReferences`
81
+ )
82
+ }
83
+ }
84
+ }
85
+ }
86
+ if (typeMatches === false) {
87
+ throw createError(
88
+ path,
89
+ target.type,
90
+ 'reference',
91
+ value,
92
+ '',
93
+ `${targetType} does not match allowedReferences`
94
+ )
95
+ }
96
+ }
97
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
98
+ }
99
+
100
+ const parsers: {
101
+ [key: string]: Parser
102
+ } = {
103
+ enum: async (path, value, fieldSchema, typeSchema, target, handlers) => {
104
+ // value .default
105
+
106
+ // @ts-ignore
107
+ const enumValues = fieldSchema.enum
108
+ for (let i = 0; i < enumValues.length; i++) {
109
+ if (deepEqual(enumValues[i], value)) {
110
+ handlers.collect({ path, value: i, typeSchema, fieldSchema, target })
111
+ return
112
+ }
113
+ }
114
+ throw createError(path, target.type, 'enum', value)
115
+ },
116
+
117
+ array: async (path, value, fieldSchema, typeSchema, target, handlers) => {
118
+ // value .default
119
+
120
+ // TODO: ADD ARRAY OPERATORS
121
+ const isArray = Array.isArray(value)
122
+
123
+ if (typeof value === 'object' && !isArray) {
124
+ const q: Promise<void>[] = []
125
+
126
+ if (value.$insert) {
127
+ }
128
+
129
+ if (value.$remove) {
130
+ }
131
+
132
+ if (value.$push) {
133
+ }
134
+
135
+ if (value.$assign) {
136
+ }
137
+
138
+ // value.$value :/
139
+ // fix
140
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
141
+
142
+ return
143
+ }
144
+
145
+ if (!isArray) {
146
+ throw createError(path, target.type, 'array', value)
147
+ }
148
+ const q: Promise<void>[] = []
149
+ for (let i = 0; i < value.length; i++) {
150
+ q.push(
151
+ fieldWalker(
152
+ [...path, i],
153
+ value[i],
154
+ // @ts-ignore
155
+ fieldSchema.values,
156
+ typeSchema,
157
+ target,
158
+ handlers
159
+ )
160
+ )
161
+ }
162
+ await Promise.all(q)
163
+ },
164
+
165
+ object: async (path, value, fieldSchema, typeSchema, target, handlers) => {
166
+ // value .default
167
+
168
+ if (typeof value !== 'object') {
169
+ throw createError(path, target.type, 'object', value)
170
+ }
171
+ const isArray = Array.isArray(value)
172
+ if (isArray) {
173
+ throw createError(path, target.type, 'object', value)
174
+ }
175
+ const q: Promise<void>[] = []
176
+ for (const key in value) {
177
+ // @ts-ignore
178
+ const propDef = fieldSchema.properties[key]
179
+ if (!propDef) {
180
+ throw createError(
181
+ [...path, key],
182
+ target.type,
183
+ 'object',
184
+ value[key],
185
+ key
186
+ )
187
+ }
188
+ q.push(
189
+ fieldWalker(
190
+ [...path, key],
191
+ value[key],
192
+ propDef,
193
+ typeSchema,
194
+ target,
195
+ handlers
196
+ )
197
+ )
198
+ }
199
+ await Promise.all(q)
200
+ },
201
+
202
+ set: async (path, value, fieldSchema, typeSchema, target, handlers) => {
203
+ // value .default
204
+
205
+ const q: Promise<void>[] = []
206
+ // @ts-ignore
207
+ const fieldDef = fieldSchema.items
208
+
209
+ if (Array.isArray(value)) {
210
+ const handlerNest = {
211
+ ...handlers,
212
+ collect: ({ path, value }) => {
213
+ parsedArray.push(value)
214
+ },
215
+ }
216
+ const parsedArray = []
217
+ for (let i = 0; i < value.length; i++) {
218
+ q.push(
219
+ fieldWalker(
220
+ [...path, i],
221
+ value[i],
222
+ fieldDef,
223
+ typeSchema,
224
+ target,
225
+ handlerNest
226
+ )
227
+ )
228
+ }
229
+ await Promise.all(q)
230
+ handlers.collect({
231
+ path,
232
+ value: parsedArray,
233
+ typeSchema,
234
+ fieldSchema,
235
+ target,
236
+ })
237
+ } else {
238
+ const handlerNest = {
239
+ ...handlers,
240
+ collect: ({ path, value }) => {},
241
+ }
242
+ if (value.$add) {
243
+ for (let i = 0; i < value.$add.length; i++) {
244
+ q.push(
245
+ fieldWalker(
246
+ [...path, '$add', i],
247
+ value.$add[i],
248
+ fieldDef,
249
+ typeSchema,
250
+ target,
251
+ handlerNest
252
+ )
253
+ )
254
+ }
255
+ }
256
+ if (value.$delete) {
257
+ for (let i = 0; i < value.$add.length; i++) {
258
+ q.push(
259
+ fieldWalker(
260
+ [...path, '$delete', i],
261
+ value.$delete[i],
262
+ fieldDef,
263
+ typeSchema,
264
+ target,
265
+ handlerNest
266
+ )
267
+ )
268
+ }
269
+ }
270
+ await Promise.all(q)
271
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
272
+ }
273
+ },
274
+
275
+ json: async (path, value, fieldSchema, typeSchema, target, handlers) => {
276
+ // value .default
277
+
278
+ try {
279
+ const parsedValue = JSON.stringify(value)
280
+ handlers.collect({
281
+ path,
282
+ value: parsedValue,
283
+ typeSchema,
284
+ fieldSchema,
285
+ target,
286
+ })
287
+ } catch (err) {
288
+ throw createError(path, target.type, 'json', value)
289
+ }
290
+ },
291
+
292
+ number: async (path, value, fieldSchema, typeSchema, target, handlers) => {
293
+ // value .default
294
+ // $increment / $decrement
295
+
296
+ if (typeof value !== 'number') {
297
+ throw createError(path, target.type, 'number', value)
298
+ }
299
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
300
+ },
301
+
302
+ integer: async (path, value, fieldSchema, typeSchema, target, handlers) => {
303
+ // value .default
304
+ // $increment / $decrement
305
+
306
+ if (typeof value !== 'number' || value - Math.floor(value) !== 0) {
307
+ throw createError(path, target.type, 'integer', value)
308
+ }
309
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
310
+ },
311
+
312
+ string: async (path, value, fieldSchema, typeSchema, target, handlers) => {
313
+ // default
314
+
315
+ if (typeof value !== 'string') {
316
+ throw createError(path, target.type, 'string', value)
317
+ }
318
+ // @ts-ignore
319
+ if (fieldSchema.minLength && value.length < fieldSchema.minLength) {
320
+ throw createError(path, target.type, 'string', value)
321
+ }
322
+ // @ts-ignore
323
+ if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) {
324
+ throw createError(path, target.type, 'string', value)
325
+ }
326
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
327
+ },
328
+
329
+ text: async (path, value, fieldSchema, typeSchema, target, handlers) => {
330
+ // default
331
+
332
+ const valueType = typeof value
333
+ if (target.$language && valueType === 'string') {
334
+ // @ts-ignore
335
+ if (fieldSchema.minLength && value.length < fieldSchema.minLength) {
336
+ throw createError(path, target.type, 'text', value)
337
+ }
338
+ // @ts-ignore
339
+ if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) {
340
+ throw createError(path, target.type, 'text', value)
341
+ }
342
+
343
+ handlers.collect({
344
+ path,
345
+ value: { [target.$language]: value },
346
+ typeSchema,
347
+ fieldSchema,
348
+ target,
349
+ })
350
+ return
351
+ }
352
+
353
+ if (valueType !== 'object') {
354
+ throw createError(path, target.type, 'text', value)
355
+ }
356
+
357
+ for (const key in value) {
358
+ // @ts-ignore
359
+ if (fieldSchema.minLength && value[key].length < fieldSchema.minLength) {
360
+ throw createError([...path, key], target.type, 'text', value)
361
+ }
362
+
363
+ // @ts-ignore
364
+ if (fieldSchema.maxLength && value[key].length > fieldSchema.maxLength) {
365
+ throw createError([...path, key], target.type, 'text', value)
366
+ }
367
+
368
+ if (typeof value[key] === 'object' && value[key].$delete === true) {
369
+ handlers.collect({
370
+ path: [...path, key],
371
+ value: null,
372
+ typeSchema,
373
+ fieldSchema,
374
+ target,
375
+ })
376
+ continue
377
+ }
378
+
379
+ if (typeof value[key] !== 'string') {
380
+ throw createError([...path, key], target.type, 'text', value)
381
+ }
382
+
383
+ handlers.collect({
384
+ path: [...path, key],
385
+ value: value[key],
386
+ typeSchema,
387
+ fieldSchema,
388
+ target,
389
+ })
390
+ }
391
+ },
392
+
393
+ reference,
394
+
395
+ references: async (
396
+ path,
397
+ value,
398
+ fieldSchema,
399
+ typeSchema,
400
+ target,
401
+ handlers
402
+ ) => {
403
+ // default
404
+ // $no root
405
+
406
+ if (Array.isArray(value)) {
407
+ const handler = {
408
+ ...handlers,
409
+ collect: () => {},
410
+ }
411
+ await Promise.all(
412
+ value.map((v, i) => {
413
+ return reference(
414
+ [...path, i],
415
+ v,
416
+ fieldSchema,
417
+ typeSchema,
418
+ target,
419
+ handler
420
+ )
421
+ })
422
+ )
423
+ } else if (typeof value === 'object') {
424
+ if (value.$add) {
425
+ const handler = {
426
+ ...handlers,
427
+ collect: () => {},
428
+ }
429
+ await Promise.all(
430
+ value.$add.map((v, i) => {
431
+ return reference(
432
+ [...path, '$add', i],
433
+ v,
434
+ fieldSchema,
435
+ typeSchema,
436
+ target,
437
+ handler
438
+ )
439
+ })
440
+ )
441
+ }
442
+ }
443
+
444
+ handlers.collect({ path, value, typeSchema, fieldSchema, target })
445
+ },
446
+ }
447
+
448
+ export default parsers