@based/schema 2.0.0 → 2.1.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,31 +0,0 @@
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' || args.value === null) {
6
- args.error(ParseError.incorrectFormat)
7
- return
8
- }
9
-
10
- const isArray = Array.isArray(args.value)
11
- if (isArray) {
12
- args.error(ParseError.incorrectFormat)
13
- return
14
- }
15
- args.collect()
16
- return args
17
- }
18
-
19
- export const record: FieldParser<'record'> = async (args) => {
20
- if (typeof args.value !== 'object' || args.value === null) {
21
- args.error(ParseError.incorrectFormat)
22
- return
23
- }
24
- const isArray = Array.isArray(args.value)
25
- if (isArray) {
26
- args.error(ParseError.incorrectFormat)
27
- return
28
- }
29
- args.collect()
30
- return args
31
- }
@@ -1,140 +0,0 @@
1
- import { ParseError } from '../../error'
2
- import { AllowedTypes, 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
- const typeIsAllowed = (
33
- args: ArgsClass<BasedSetTarget, 'reference'>,
34
- type: string
35
- ): boolean => {
36
- if ('allowedTypes' in args.fieldSchema) {
37
- let typeMatches = false
38
- for (const t of args.fieldSchema.allowedTypes) {
39
- if (typeof t === 'string') {
40
- if (t === type) {
41
- return true
42
- }
43
- } else {
44
- if (t.type && t.type === type) {
45
- typeMatches = true
46
- if (t.$filter) {
47
- // stage on requires validation in target
48
- // TODO: ASYNC REQUIRED HOOK
49
- // if(!(await args.target.referenceFilterCondition(value, t.$filter))){
50
- // error(args, ParseError.referenceIsIncorrectType)
51
- // return
52
- // }
53
- }
54
- } else if (!t.type && t.$filter) {
55
- // if(!(await args.target.referenceFilterCondition))
56
- // error(args, ParseError.referenceIsIncorrectType, )
57
- // return
58
- }
59
- }
60
- }
61
- if (typeMatches === false) {
62
- return false
63
- }
64
- }
65
- return true
66
- }
67
-
68
- export const reference: FieldParser<'reference'> = async (args) => {
69
- // TODO: setting an object here , handling $alias (both async hooks)
70
- // Block if path contains $remove (maybe not for $alias)
71
- if (typeof args.value === 'object') {
72
- if (args.root._opts.asyncOperationHandler) {
73
- if (args.value.type && !typeIsAllowed(args, args.value.type)) {
74
- args.error(ParseError.referenceIsIncorrectType)
75
- return
76
- }
77
- if (!args.target.errors.length) {
78
- args.value = await args.root._opts.asyncOperationHandler(
79
- args,
80
- 'modifyObject'
81
- )
82
- }
83
- } else {
84
- args.error(ParseError.nestedModifyObjectNotAllowed)
85
- return
86
- }
87
- }
88
-
89
- if (!isValidId(args.schema, args.value)) {
90
- args.error(ParseError.incorrectFormat)
91
- return
92
- }
93
-
94
- const prefix = args.value.slice(0, 2)
95
- const targetType = args.schema.prefixToTypeMapping[prefix]
96
-
97
- if (typeIsAllowed(args, targetType)) {
98
- args.collect()
99
- } else {
100
- args.error(ParseError.referenceIsIncorrectType)
101
- }
102
- }
103
-
104
- export const references: FieldParser<'references'> = async (args) => {
105
- const { value } = args
106
-
107
- if (typeof value !== 'object' || value === null) {
108
- args.error(ParseError.incorrectFormat)
109
- return
110
- }
111
-
112
- args.stop()
113
-
114
- if (Array.isArray(value)) {
115
- const parseValues = await Promise.all(
116
- value.map(async (id, key) => {
117
- const n = <ArgsClass<BasedSetTarget, 'reference'>>args.create({
118
- value: id,
119
- key,
120
- skipCollection: true,
121
- })
122
- await reference(n)
123
- return n.value
124
- })
125
- )
126
- args.value = { $value: parseValues }
127
- } else {
128
- for (const key in args.value) {
129
- if (key === '$add') {
130
- args.value.$add = await parseOperator(args, key)
131
- } else if (key === '$remove') {
132
- args.value.$remove = await parseOperator(args, key)
133
- } else {
134
- args.create({ key }).error(ParseError.fieldDoesNotExist)
135
- }
136
- }
137
- }
138
-
139
- args.collect()
140
- }
@@ -1,63 +0,0 @@
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
- }
@@ -1,291 +0,0 @@
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
- // --- bla
121
- // if typeof === string
122
- export const text: FieldParser<'text'> = async (args) => {
123
- const value = args.value
124
-
125
- args.stop()
126
-
127
- if (value === null) {
128
- args.error(ParseError.incorrectFormat)
129
- return
130
- }
131
-
132
- if (typeof value === 'object') {
133
- for (const key in value) {
134
- if (key === '$merge') {
135
- if (typeof value.$merge !== 'boolean') {
136
- args.error(ParseError.incorrectFormat)
137
- return
138
- }
139
- } else if (key === '$delete') {
140
- if (value[key] !== true) {
141
- args.error(ParseError.incorrectFormat)
142
- return
143
- }
144
- args.collect({ $delete: true })
145
- return
146
- } else if (key === '$value') {
147
- const valueArgs = args.create({
148
- path: args.path,
149
- value: args.value[key],
150
- })
151
- valueArgs._stopObject = true
152
- await valueArgs.parse()
153
- } else if (key === '$default') {
154
- if (value[key] === null) {
155
- args.error(ParseError.incorrectFormat)
156
- return
157
- }
158
- if (typeof value[key] === 'object') {
159
- for (const k in value[key]) {
160
- if (!validateString(args, args.value[key][k])) {
161
- args.error(ParseError.incorrectFormat)
162
- return
163
- }
164
- args
165
- .create({
166
- key: k,
167
- fieldSchema: { type: 'string' },
168
- value: { $default: args.value[key][k] },
169
- })
170
- .collect()
171
- }
172
- } else if (typeof value[key] !== 'string') {
173
- args.error(ParseError.incorrectFormat)
174
- return
175
- } else if (!args.target.$language) {
176
- args.error(ParseError.noLanguageFound)
177
- return
178
- } else if (!validateString(args, value[key])) {
179
- args.error(ParseError.incorrectFormat)
180
- return
181
- } else {
182
- args
183
- .create({
184
- fieldSchema: { type: 'string' },
185
- key: args.target.$language,
186
- value: { $default: args.value[key] },
187
- })
188
- .collect()
189
- }
190
- } else if (
191
- (args.schema.translations || [])
192
- .concat(args.schema.language)
193
- .includes(<BasedSchemaLanguage>key)
194
- ) {
195
- if (value[key] && typeof value[key] === 'object') {
196
- for (const k in value[key]) {
197
- if (k === '$delete') {
198
- if (value[key].$delete !== true) {
199
- args.error(ParseError.incorrectFormat)
200
- return
201
- }
202
- args
203
- .create({
204
- key,
205
- fieldSchema: { type: 'string' },
206
- value: args.value[key],
207
- })
208
- .collect()
209
- } else if (k === '$value') {
210
- if (!validateString(args, value[key].$value)) {
211
- args.create({ key }).error(ParseError.incorrectFormat)
212
- } else {
213
- args
214
- .create({
215
- key,
216
- fieldSchema: { type: 'string' },
217
- value: args.value[key].$value,
218
- })
219
- .collect()
220
- }
221
- } else if (k === '$default') {
222
- if (!validateString(args, value[key].$default)) {
223
- args.create({ key }).error(ParseError.incorrectFormat)
224
- } else {
225
- args
226
- .create({
227
- key,
228
- fieldSchema: { type: 'string' },
229
- value: { $default: args.value[key].$default },
230
- })
231
- .collect()
232
- }
233
- } else {
234
- args
235
- .create({ path: [...args.path, key, k] })
236
- .error(ParseError.fieldDoesNotExist)
237
- return
238
- }
239
- }
240
- } else {
241
- if (!validateString(args, args.value[key])) {
242
- args.error(ParseError.incorrectFormat)
243
- return
244
- }
245
- args
246
- .create({
247
- key,
248
- fieldSchema: { type: 'string' },
249
- value: args.value[key],
250
- })
251
- .collect()
252
- }
253
- } else {
254
- args.create({ key }).error(ParseError.languageNotSupported)
255
- }
256
- }
257
- if (!args._stopObject) {
258
- args.collect()
259
- }
260
- return
261
- }
262
-
263
- if (typeof value !== 'string') {
264
- args.error(ParseError.incorrectFormat)
265
- return
266
- }
267
-
268
- if (!args.target.$language) {
269
- args.error(ParseError.noLanguageFound)
270
- return
271
- }
272
-
273
- if (!validateString(args, args.value)) {
274
- args.error(ParseError.incorrectFormat)
275
- return
276
- }
277
-
278
- args
279
- .create({
280
- value,
281
- key: args.target.$language,
282
- fieldSchema: { type: 'string' },
283
- })
284
- .collect()
285
-
286
- if (!args._stopObject) {
287
- args.collect({
288
- [args.target.$language]: value,
289
- })
290
- }
291
- }
package/src/set/index.ts DELETED
@@ -1,186 +0,0 @@
1
- import { ParseError } from '../error'
2
- import { BasedSchema, BasedSchemaCollectProps, BasedSetTarget } from '../types'
3
- import { walk, Opts, AsyncOperation } from '../walker'
4
- import { fields } from './fields'
5
- import { isValidId } from './isValidId'
6
-
7
- const opts: Opts<BasedSetTarget> = {
8
- parsers: {
9
- keys: {
10
- $delete: async (args) => {
11
- if (args.prev === args.root) {
12
- args.error(ParseError.cannotDeleteNodeFromModify)
13
- return
14
- }
15
- if (args.value === true) {
16
- args.stop()
17
- args.prev.collect()
18
- args.prev.stop()
19
- return
20
- }
21
- },
22
- $alias: async (args) => {
23
- if (Array.isArray(args.value)) {
24
- for (const field of args.value) {
25
- if (typeof field !== 'string') {
26
- args.error(ParseError.incorrectFormat)
27
- return
28
- }
29
- }
30
- return
31
- }
32
- if (typeof args.value !== 'string') {
33
- args.error(ParseError.incorrectFormat)
34
- }
35
- },
36
- $merge: async (args) => {
37
- if (typeof args.value !== 'boolean') {
38
- args.error(ParseError.incorrectFormat)
39
- return
40
- }
41
-
42
- if (args.prev !== args.root) {
43
- args.prev.collect({ $delete: true })
44
- }
45
-
46
- return
47
- },
48
- $id: async (args) => {
49
- if (!isValidId(args.schema, args.value)) {
50
- args.error(ParseError.incorrectFormat)
51
- return
52
- }
53
- },
54
- $language: async (args) => {
55
- if (
56
- !(args.schema.translations || [])
57
- .concat(args.schema.language)
58
- .includes(args.value)
59
- ) {
60
- args.error(ParseError.languageNotSupported)
61
- return
62
- }
63
- },
64
- $value: async (args) => {
65
- const type = args.fieldSchema?.type
66
- if (type === 'text' || type === 'set' || type == 'references') {
67
- return
68
- }
69
- args.prev.stop()
70
- args.stop()
71
- if (args.prev.value.$default) {
72
- args.error(ParseError.valueAndDefault)
73
- return
74
- }
75
- return {
76
- path: args.path.slice(0, -1),
77
- value: args.value,
78
- }
79
- },
80
- $default: async (args) => {
81
- const type = args.fieldSchema?.type
82
- if (type === 'number' || type === 'integer' || type === 'text') {
83
- // default can exist with $incr and $decr
84
- return
85
- }
86
- args.prev.stop()
87
- args.stop()
88
-
89
- if (type === 'references' || type === 'set' || type === 'array') {
90
- const newArgs = args.create({
91
- path: args.path.slice(0, -1),
92
- skipCollection: true,
93
- })
94
- await newArgs.parse()
95
- newArgs.skipCollection = false
96
- newArgs.value = { $default: newArgs.value }
97
- newArgs.collect()
98
- } else {
99
- const collect = args._collectOverride ?? args.root._opts.collect
100
- const newArgs = args.create({
101
- path: args.path.slice(0, -1),
102
- collect: (a) => {
103
- if (a.path.length === args.path.length - 1) {
104
- collect(a.create({ value: { $default: a.value } }))
105
- } else {
106
- // console.info('hello', a.path) can handle this later
107
- }
108
- },
109
- })
110
- await newArgs.parse()
111
- }
112
- for (const key in args.prev.value) {
113
- if (key !== '$default') {
114
- args.prev.create({ key }).error(ParseError.fieldDoesNotExist)
115
- }
116
- }
117
- },
118
- },
119
- fields,
120
- catch: async (args) => {
121
- args.error(ParseError.fieldDoesNotExist)
122
- },
123
- },
124
- init: async (value, schema, error) => {
125
- let type: string
126
- const target: BasedSetTarget = {
127
- type,
128
- schema,
129
- required: [],
130
- collected: [],
131
- errors: [],
132
- }
133
- if (value.$id) {
134
- if (value.$id === 'root') {
135
- type = 'root'
136
- } else {
137
- type = schema.prefixToTypeMapping[value.$id.slice(0, 2)]
138
- }
139
- if (!type) {
140
- error(ParseError.incorrectFieldType, { target })
141
- return { target }
142
- }
143
- } else if (value.$alias) {
144
- target.$alias = value.$alias
145
- }
146
- if (value.type) {
147
- if (type && value.type !== type) {
148
- error(ParseError.incorrectNodeType, { target })
149
- return { target }
150
- }
151
- type = value.type
152
- }
153
- const typeSchema = type === 'root' ? schema.root : schema.types[type]
154
- if (!typeSchema) {
155
- error(ParseError.incorrectNodeType, { target })
156
- return { target }
157
- }
158
- target.type = type
159
- target.$language = value.$language
160
- target.$id = value.$id
161
- if ('$merge' in value) {
162
- if (typeof value.$merge !== 'boolean') {
163
- error(ParseError.incorrectFormat, { target })
164
- }
165
- target.$merge = value.$merge
166
- }
167
- return { target, typeSchema }
168
- },
169
- error: (code, args) => {
170
- args.target.errors.push({
171
- code,
172
- path: args.path ?? [],
173
- })
174
- },
175
- collect: (args) => {
176
- args.root.target.collected.push(<BasedSchemaCollectProps>args)
177
- },
178
- }
179
-
180
- export const setWalker = (
181
- schema: BasedSchema,
182
- value: any,
183
- asyncOperationHandler?: AsyncOperation<BasedSetTarget>
184
- ): Promise<BasedSetTarget> => {
185
- return walk<BasedSetTarget>(schema, opts, value, asyncOperationHandler)
186
- }