@based/schema 0.0.15 → 1.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 (112) hide show
  1. package/dist/{set/error.d.ts → error.d.ts} +4 -2
  2. package/dist/{set/error.js → error.js} +2 -0
  3. package/dist/error.js.map +1 -0
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/set/fields/array.d.ts +2 -0
  8. package/dist/set/fields/array.js +96 -0
  9. package/dist/set/fields/array.js.map +1 -0
  10. package/dist/set/fields/index.d.ts +3 -0
  11. package/dist/set/fields/index.js +72 -0
  12. package/dist/set/fields/index.js.map +1 -0
  13. package/dist/set/fields/number.d.ts +4 -0
  14. package/dist/set/fields/number.js +121 -0
  15. package/dist/set/fields/number.js.map +1 -0
  16. package/dist/set/fields/object.d.ts +3 -0
  17. package/dist/set/fields/object.js +33 -0
  18. package/dist/set/fields/object.js.map +1 -0
  19. package/dist/set/fields/references.d.ts +3 -0
  20. package/dist/set/fields/references.js +106 -0
  21. package/dist/set/fields/references.js.map +1 -0
  22. package/dist/set/fields/set.d.ts +2 -0
  23. package/dist/set/fields/set.js +63 -0
  24. package/dist/set/fields/set.js.map +1 -0
  25. package/dist/set/fields/string.d.ts +3 -0
  26. package/dist/set/fields/string.js +190 -0
  27. package/dist/set/fields/string.js.map +1 -0
  28. package/dist/set/index.d.ts +2 -5
  29. package/dist/set/index.js +98 -126
  30. package/dist/set/index.js.map +1 -1
  31. package/dist/set/isValidId.d.ts +2 -0
  32. package/dist/set/isValidId.js +21 -0
  33. package/dist/set/isValidId.js.map +1 -0
  34. package/dist/set/types.d.ts +0 -5
  35. package/dist/set/types.js +0 -2
  36. package/dist/types.d.ts +7 -1
  37. package/dist/types.js.map +1 -1
  38. package/dist/walker/args.d.ts +31 -0
  39. package/dist/walker/args.js +120 -0
  40. package/dist/walker/args.js.map +1 -0
  41. package/dist/walker/index.d.ts +6 -0
  42. package/dist/walker/index.js +40 -0
  43. package/dist/walker/index.js.map +1 -0
  44. package/dist/walker/parse.d.ts +2 -0
  45. package/dist/walker/parse.js +157 -0
  46. package/dist/walker/parse.js.map +1 -0
  47. package/dist/walker/types.d.ts +44 -0
  48. package/dist/walker/types.js +9 -0
  49. package/dist/walker/types.js.map +1 -0
  50. package/package.json +2 -2
  51. package/src/{set/error.ts → error.ts} +3 -1
  52. package/src/index.ts +2 -2
  53. package/src/set/fields/array.ts +111 -0
  54. package/src/set/fields/index.ts +69 -0
  55. package/src/set/fields/number.ts +134 -0
  56. package/src/set/fields/object.ts +30 -0
  57. package/src/set/fields/references.ts +114 -0
  58. package/src/set/fields/set.ts +63 -0
  59. package/src/set/fields/string.ts +199 -0
  60. package/src/set/index.ts +103 -188
  61. package/src/set/isValidId.ts +23 -0
  62. package/src/set/types.ts +0 -20
  63. package/src/types.ts +4 -2
  64. package/src/walker/args.ts +159 -0
  65. package/src/walker/index.ts +35 -0
  66. package/src/walker/parse.ts +193 -0
  67. package/src/walker/types.ts +75 -0
  68. package/test/number.ts +289 -543
  69. package/test/reference.ts +150 -198
  70. package/test/rest.ts +227 -0
  71. package/test/string.ts +139 -183
  72. package/test/utils/index.ts +23 -0
  73. package/test/walker.ts +579 -16
  74. package/dist/set/collections.d.ts +0 -5
  75. package/dist/set/collections.js +0 -229
  76. package/dist/set/collections.js.map +0 -1
  77. package/dist/set/error.js.map +0 -1
  78. package/dist/set/number.d.ts +0 -4
  79. package/dist/set/number.js +0 -124
  80. package/dist/set/number.js.map +0 -1
  81. package/dist/set/parseDefaultAndValue.d.ts +0 -3
  82. package/dist/set/parseDefaultAndValue.js +0 -35
  83. package/dist/set/parseDefaultAndValue.js.map +0 -1
  84. package/dist/set/parsers.d.ts +0 -3
  85. package/dist/set/parsers.js +0 -42
  86. package/dist/set/parsers.js.map +0 -1
  87. package/dist/set/references.d.ts +0 -3
  88. package/dist/set/references.js +0 -84
  89. package/dist/set/references.js.map +0 -1
  90. package/dist/set/rest.d.ts +0 -5
  91. package/dist/set/rest.js +0 -76
  92. package/dist/set/rest.js.map +0 -1
  93. package/dist/set/string.d.ts +0 -3
  94. package/dist/set/string.js +0 -173
  95. package/dist/set/string.js.map +0 -1
  96. package/dist/set2/index.d.ts +0 -0
  97. package/dist/set2/index.js +0 -71
  98. package/dist/set2/index.js.map +0 -1
  99. package/dist/walker.d.ts +0 -49
  100. package/dist/walker.js +0 -120
  101. package/dist/walker.js.map +0 -1
  102. package/src/set/collections.ts +0 -338
  103. package/src/set/number.ts +0 -167
  104. package/src/set/parseDefaultAndValue.ts +0 -54
  105. package/src/set/parsers.ts +0 -20
  106. package/src/set/references.ts +0 -113
  107. package/src/set/rest.ts +0 -135
  108. package/src/set/string.ts +0 -254
  109. package/src/set2/index.ts +0 -71
  110. package/src/walker.ts +0 -191
  111. package/test/setWalker.ts +0 -494
  112. package/test/text.ts +0 -171
@@ -0,0 +1,134 @@
1
+ import { ParseError } from '../../error'
2
+ import { FieldParser, ArgsClass } from '../../walker'
3
+ import { BasedSetTarget } from '../../types'
4
+
5
+ type NumberTypes = 'number' | 'timestamp' | 'integer'
6
+
7
+ const validateNumber = (
8
+ args: ArgsClass<BasedSetTarget, NumberTypes>,
9
+ value: number,
10
+ ignoreMinMax?: boolean
11
+ ): boolean => {
12
+ const { fieldSchema } = args
13
+ if (typeof value !== 'number') {
14
+ args.error(ParseError.incorrectFormat)
15
+ return false
16
+ }
17
+
18
+ if (fieldSchema.type === 'integer' && value - Math.floor(value) !== 0) {
19
+ args.error(ParseError.incorrectFormat)
20
+ return false
21
+ }
22
+
23
+ if (
24
+ fieldSchema.multipleOf &&
25
+ value / fieldSchema.multipleOf -
26
+ Math.floor(value / fieldSchema.multipleOf) !==
27
+ 0
28
+ ) {
29
+ args.error(ParseError.incorrectFormat)
30
+ return false
31
+ }
32
+
33
+ if (ignoreMinMax) {
34
+ // TODO: maybe add async validator getting the actual value from the db OR checking the result of the $incr/$decr operation
35
+ return true
36
+ }
37
+
38
+ if (fieldSchema.maximum) {
39
+ if (fieldSchema.exclusiveMaximum) {
40
+ if (value >= fieldSchema.maximum) {
41
+ args.error(ParseError.exceedsMaximum)
42
+ return false
43
+ }
44
+ } else if (value > fieldSchema.maximum) {
45
+ args.error(ParseError.exceedsMaximum)
46
+ return false
47
+ }
48
+ }
49
+
50
+ if (fieldSchema.minimum) {
51
+ if (fieldSchema.exclusiveMinimum) {
52
+ if (value <= fieldSchema.minimum) {
53
+ args.error(ParseError.subceedsMinimum)
54
+ return false
55
+ }
56
+ } else if (value < fieldSchema.minimum) {
57
+ args.error(ParseError.subceedsMinimum)
58
+ return false
59
+ }
60
+ }
61
+
62
+ return true
63
+ }
64
+
65
+ const validate = (
66
+ args: ArgsClass<BasedSetTarget, NumberTypes>,
67
+ value: any
68
+ ): boolean => {
69
+ if (value === null) {
70
+ return false
71
+ }
72
+
73
+ if (typeof value !== 'object') {
74
+ return validateNumber(args, value)
75
+ }
76
+ if ('$value' in value) {
77
+ return
78
+ }
79
+
80
+ args.stop()
81
+ for (const key in value) {
82
+ if (key === '$default') {
83
+ if (!validateNumber(args, value.$default)) {
84
+ return false
85
+ }
86
+ } else if (key === '$increment') {
87
+ if (!validateNumber(args, value.$increment, true)) {
88
+ return false
89
+ }
90
+ } else if (key === '$decrement') {
91
+ if (!validateNumber(args, value.$decrement, true)) {
92
+ return false
93
+ }
94
+ } else {
95
+ args.create({ key }).error(ParseError.fieldDoesNotExist)
96
+ return false
97
+ }
98
+ }
99
+ return true
100
+ }
101
+
102
+ export const number: FieldParser<'number'> = async (args) => {
103
+ if (!validate(args, args.value)) {
104
+ return
105
+ }
106
+ args.collect()
107
+ }
108
+
109
+ export const integer: FieldParser<'integer'> = async (args) => {
110
+ if (!validate(args, args.value)) {
111
+ return
112
+ }
113
+ args.collect()
114
+ }
115
+
116
+ export const timestamp: FieldParser<'timestamp'> = async (args) => {
117
+ if (typeof args.value === 'string') {
118
+ if (args.value === 'now') {
119
+ // TODO: + 1s + 10s etc
120
+ args.value = Date.now()
121
+ } else {
122
+ const d = new Date(args.value)
123
+ args.value = d.valueOf()
124
+ if (isNaN(args.value)) {
125
+ args.error(ParseError.incorrectFormat)
126
+ return
127
+ }
128
+ }
129
+ }
130
+ if (!validateNumber(args, args.value)) {
131
+ return
132
+ }
133
+ args.collect()
134
+ }
@@ -0,0 +1,30 @@
1
+ import { ParseError } from '../../error'
2
+ import { FieldParser } from '../../walker'
3
+
4
+ export const object: FieldParser<'object'> = async (args) => {
5
+ if (typeof args.value !== 'object') {
6
+ args.error(ParseError.incorrectFormat)
7
+ return
8
+ }
9
+ const isArray = Array.isArray(args.value)
10
+ if (isArray) {
11
+ args.error(ParseError.incorrectFormat)
12
+ return
13
+ }
14
+ args.collect()
15
+ return args
16
+ }
17
+
18
+ export const record: FieldParser<'record'> = async (args) => {
19
+ if (typeof args.value !== 'object') {
20
+ args.error(ParseError.incorrectFormat)
21
+ return
22
+ }
23
+ const isArray = Array.isArray(args.value)
24
+ if (isArray) {
25
+ args.error(ParseError.incorrectFormat)
26
+ return
27
+ }
28
+ args.collect()
29
+ return args
30
+ }
@@ -0,0 +1,114 @@
1
+ import { ParseError } from '../../error'
2
+ import { BasedSetTarget } from '../../types'
3
+ import { ArgsClass, FieldParser } from '../../walker'
4
+ import { isValidId } from '../isValidId'
5
+
6
+ async function parseOperator<T>(
7
+ args: ArgsClass<T, 'references'>,
8
+ key: string
9
+ ): Promise<any[]> {
10
+ if (Array.isArray(args.value[key])) {
11
+ const n = args.create({
12
+ key,
13
+ skipCollection: true,
14
+ value: args.value[key],
15
+ })
16
+ await n.parse()
17
+
18
+ if (n.value?.$value) {
19
+ return n.value.$value
20
+ }
21
+ return []
22
+ }
23
+ const n = <ArgsClass<BasedSetTarget, 'reference'>>args.create({
24
+ value: args.value[key],
25
+ key,
26
+ skipCollection: true,
27
+ })
28
+ await reference(n)
29
+ return [n.value]
30
+ }
31
+
32
+ export const reference: FieldParser<'reference'> = async (args) => {
33
+ // TODO: setting an object here , handling $alias (both async hooks)
34
+
35
+ // block if path contains $remove
36
+
37
+ if (!isValidId(args.schema, args.value)) {
38
+ args.error(ParseError.incorrectFormat)
39
+ return
40
+ }
41
+
42
+ if ('allowedTypes' in args.fieldSchema) {
43
+ const prefix = args.value.slice(0, 2)
44
+ const targetType = args.schema.prefixToTypeMapping[prefix]
45
+ let typeMatches = false
46
+ for (const t of args.fieldSchema.allowedTypes) {
47
+ if (typeof t === 'string') {
48
+ if (t === targetType) {
49
+ typeMatches = true
50
+ break
51
+ }
52
+ } else {
53
+ if (t.type && t.type === targetType) {
54
+ typeMatches = true
55
+ if (t.$filter) {
56
+ // TODO: ASYNC HOOK
57
+ // if(!(await args.target.referenceFilterCondition(value, t.$filter))){
58
+ // error(args, ParseError.referenceIsIncorrectType)
59
+ // return
60
+ // }
61
+ }
62
+ } else if (!t.type && t.$filter) {
63
+ // if(!(await args.target.referenceFilterCondition))
64
+ // error(args, ParseError.referenceIsIncorrectType, )
65
+ // return
66
+ }
67
+ }
68
+ }
69
+ if (typeMatches === false) {
70
+ args.error(ParseError.referenceIsIncorrectType)
71
+ return
72
+ }
73
+ }
74
+
75
+ args.collect()
76
+ }
77
+
78
+ export const references: FieldParser<'references'> = async (args) => {
79
+ const { value } = args
80
+
81
+ if (typeof value !== 'object' || value === null) {
82
+ args.error(ParseError.incorrectFormat)
83
+ return
84
+ }
85
+
86
+ args.stop()
87
+
88
+ if (Array.isArray(value)) {
89
+ const parseValues = await Promise.all(
90
+ value.map(async (id, key) => {
91
+ const n = <ArgsClass<BasedSetTarget, 'reference'>>args.create({
92
+ value: id,
93
+ key,
94
+ skipCollection: true,
95
+ })
96
+ await reference(n)
97
+ return n.value
98
+ })
99
+ )
100
+ args.value = { $value: parseValues }
101
+ } else {
102
+ for (const key in args.value) {
103
+ if (key === '$add') {
104
+ args.value.$add = await parseOperator(args, key)
105
+ } else if (key === '$remove') {
106
+ args.value.$remove = await parseOperator(args, key)
107
+ } else {
108
+ args.create({ key }).error(ParseError.fieldDoesNotExist)
109
+ }
110
+ }
111
+ }
112
+
113
+ args.collect()
114
+ }
@@ -0,0 +1,63 @@
1
+ import { ParseError } from '../../error'
2
+ import { ArgsClass, FieldParser } from '../../walker'
3
+
4
+ async function parseOperator<T>(
5
+ args: ArgsClass<T, 'set'>,
6
+ key: string
7
+ ): Promise<any[]> {
8
+ if (Array.isArray(args.value[key])) {
9
+ const n = args.create({
10
+ key,
11
+ skipCollection: true,
12
+ value: args.value[key],
13
+ })
14
+ await n.parse()
15
+ if (n.value?.$value) {
16
+ return n.value.$value
17
+ }
18
+ return []
19
+ }
20
+ const n = args.create({
21
+ key,
22
+ skipCollection: true,
23
+ value: args.value[key],
24
+ fieldSchema: args.fieldSchema.items,
25
+ })
26
+ await n.parse()
27
+ return [n.value]
28
+ }
29
+
30
+ export const set: FieldParser<'set'> = async (args) => {
31
+ if (typeof args.value !== 'object' || args.value === null) {
32
+ args.error(ParseError.incorrectFormat)
33
+ return
34
+ }
35
+ args.stop()
36
+ const isArray = Array.isArray(args.value)
37
+ if (isArray) {
38
+ const newArgs: ArgsClass<typeof args.target>[] = []
39
+ for (let i = 0; i < args.value.length; i++) {
40
+ newArgs.push(
41
+ args.create({
42
+ key: i,
43
+ value: args.value[i],
44
+ fieldSchema: args.fieldSchema.items,
45
+ skipCollection: true,
46
+ })
47
+ )
48
+ }
49
+ await Promise.all(newArgs.map((args) => args.parse()))
50
+ args.value = { $value: newArgs.map((args) => args.value) }
51
+ } else {
52
+ for (const key in args.value) {
53
+ if (key === '$add') {
54
+ args.value.$add = await parseOperator(args, key)
55
+ } else if (key === '$remove') {
56
+ args.value.$remove = await parseOperator(args, key)
57
+ } else {
58
+ args.create({ key }).error(ParseError.fieldDoesNotExist)
59
+ }
60
+ }
61
+ }
62
+ args.collect()
63
+ }
@@ -0,0 +1,199 @@
1
+ import {
2
+ BasedSchemaFieldString,
3
+ BasedSchemaLanguage,
4
+ BasedSetTarget,
5
+ } from '../../types'
6
+ import { ParseError } from '../../error'
7
+ import { FieldParser, ArgsClass } from '../../walker'
8
+ import validators from 'validator'
9
+ import { deepMerge, setByPath } from '@saulx/utils'
10
+
11
+ type StringTypes = 'string' | 'text'
12
+
13
+ const formatPatterns: Record<
14
+ BasedSchemaFieldString['format'],
15
+ (str: string) => boolean
16
+ > = {
17
+ email: validators.isEmail,
18
+ URL: validators.isURL,
19
+ MACAddress: validators.isMACAddress,
20
+ IP: validators.isIP,
21
+ IPRange: validators.isIPRange,
22
+ FQDN: validators.isFQDN,
23
+ IBAN: validators.isIBAN,
24
+ BIC: validators.isBIC,
25
+ alpha: validators.isAlpha,
26
+ alphaLocales: validators.isAlphaLocales,
27
+ alphanumeric: validators.isAlphanumeric,
28
+ alphanumericLocales: validators.isAlphanumericLocales,
29
+ passportNumber: validators.isPassportNumber,
30
+ port: validators.isPort,
31
+ lowercase: validators.isLowercase,
32
+ uppercase: validators.isUppercase,
33
+ ascii: validators.isAscii,
34
+ semVer: validators.isSemVer,
35
+ surrogatePair: validators.isSurrogatePair,
36
+ IMEI: validators.isIMEI,
37
+ hexadecimal: validators.isHexadecimal,
38
+ octal: validators.isOctal,
39
+ hexColor: validators.isHexColor,
40
+ rgbColor: validators.isRgbColor,
41
+ HSL: validators.isHSL,
42
+ ISRC: validators.isISRC,
43
+ MD5: validators.isMD5,
44
+ JWT: validators.isJWT,
45
+ UUID: validators.isUUID,
46
+ luhnNumber: validators.isLuhnNumber,
47
+ creditCard: validators.isCreditCard,
48
+ identityCard: validators.isIdentityCard,
49
+ EAN: validators.isEAN,
50
+ ISIN: validators.isISIN,
51
+ ISBN: validators.isISBN,
52
+ ISSN: validators.isISSN,
53
+ mobilePhone: validators.isMobilePhone,
54
+ mobilePhoneLocales: validators.isMobilePhoneLocales,
55
+ postalCode: validators.isPostalCode,
56
+ postalCodeLocales: validators.isPostalCodeLocales,
57
+ ethereumAddress: validators.isEthereumAddress,
58
+ currency: validators.isCurrency,
59
+ btcAddress: validators.isBtcAddress,
60
+ ISO6391: validators.isISO6391,
61
+ ISO8601: validators.isISO8601,
62
+ RFC3339: validators.isRFC3339,
63
+ ISO31661Alpha2: validators.isISO31661Alpha2,
64
+ ISO31661Alpha3: validators.isISO31661Alpha3,
65
+ ISO4217: validators.isISO4217,
66
+ base32: validators.isBase32,
67
+ base58: validators.isBase58,
68
+ base64: validators.isBase64,
69
+ dataURI: validators.isDataURI,
70
+ magnetURI: validators.isMagnetURI,
71
+ mimeType: validators.isMimeType,
72
+ latLong: validators.isLatLong,
73
+ slug: validators.isSlug,
74
+ strongPassword: validators.isStrongPassword,
75
+ taxID: validators.isTaxID,
76
+ licensePlate: validators.isLicensePlate,
77
+ VAT: validators.isVAT,
78
+ }
79
+
80
+ const validateString = (
81
+ args: ArgsClass<BasedSetTarget, StringTypes>,
82
+ value: string
83
+ ): boolean => {
84
+ if (typeof value !== 'string') {
85
+ args.error(ParseError.incorrectFormat)
86
+ return false
87
+ }
88
+ if (args.fieldSchema.minLength && value.length < args.fieldSchema.minLength) {
89
+ args.error(ParseError.subceedsMinimum)
90
+ return false
91
+ }
92
+ if (args.fieldSchema.maxLength && value.length > args.fieldSchema.maxLength) {
93
+ args.error(ParseError.exceedsMaximum)
94
+ return false
95
+ }
96
+ if (args.fieldSchema.pattern) {
97
+ const re = new RegExp(args.fieldSchema.pattern)
98
+ if (!re.test(value)) {
99
+ args.error(ParseError.incorrectFormat)
100
+ return false
101
+ }
102
+ }
103
+ if (
104
+ args.fieldSchema.format &&
105
+ !formatPatterns[args.fieldSchema.format](value)
106
+ ) {
107
+ args.error(ParseError.incorrectFormat)
108
+ return false
109
+ }
110
+ return true
111
+ }
112
+
113
+ export const string: FieldParser<'string'> = async (args) => {
114
+ if (!validateString(args, args.value)) {
115
+ return
116
+ }
117
+ args.collect()
118
+ }
119
+
120
+ async function next<T>(args: ArgsClass<T>, key: string): Promise<any> {
121
+ const valueArgs = args.create({
122
+ key,
123
+ value: args.value[key],
124
+ skipCollection: true,
125
+ })
126
+ await valueArgs.parse()
127
+ return valueArgs.value
128
+ }
129
+
130
+ export const text: FieldParser<'text'> = async (args) => {
131
+ const value = args.value
132
+ if (value !== null && typeof value === 'object') {
133
+ args.stop()
134
+ const result: any = {}
135
+ for (const key in value) {
136
+ if (key === '$value') {
137
+ const nValue = await next(args, key)
138
+ if (typeof nValue.value === 'object') {
139
+ deepMerge(result, nValue.value)
140
+ }
141
+ } else if (key === '$default') {
142
+ result.$default = await next(args, key)
143
+ } else if (args.schema.languages.includes(<BasedSchemaLanguage>key)) {
144
+ if (value[key] && typeof value[key] === 'object') {
145
+ for (const k in value[key]) {
146
+ if (k === '$value') {
147
+ if (!validateString(args, value[key].$value)) {
148
+ args.create({ key }).error(ParseError.incorrectFormat)
149
+ } else {
150
+ result[key] = value[key].$value
151
+ }
152
+ } else if (k === '$default') {
153
+ if (!validateString(args, value[key].$default)) {
154
+ args.create({ key }).error(ParseError.incorrectFormat)
155
+ } else {
156
+ setByPath(result, ['$default', key], value[key].$default)
157
+ }
158
+ } else {
159
+ args
160
+ .create({ path: [...args.path, key, k] })
161
+ .error(ParseError.fieldDoesNotExist)
162
+ }
163
+ }
164
+ } else {
165
+ if (!validateString(args, args.value[key])) {
166
+ args.error(ParseError.incorrectFormat)
167
+ return
168
+ }
169
+ result[key] = args.value[key]
170
+ }
171
+ } else {
172
+ args.create({ key }).error(ParseError.languageNotSupported)
173
+ }
174
+ }
175
+ args.collect(result)
176
+ return
177
+ }
178
+
179
+ if (typeof value !== 'string') {
180
+ args.error(ParseError.incorrectFormat)
181
+ return
182
+ }
183
+
184
+ if (!args.target.$language) {
185
+ args.error(ParseError.noLanguageFound)
186
+ return
187
+ }
188
+
189
+ if (!validateString(args, args.value)) {
190
+ args.error(ParseError.incorrectFormat)
191
+ return
192
+ }
193
+
194
+ args.value = {
195
+ [args.target.$language]: value,
196
+ }
197
+
198
+ args.collect()
199
+ }