@sanity/validation 2.33.4-shopify.8 → 3.0.0-canary.186
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.
- package/lib/dts/src/index.d.ts +36 -0
- package/lib/index.js +1547 -30
- package/lib/index.js.map +1 -0
- package/lib/index.mjs +1542 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +26 -10
- package/src/Rule.ts +6 -2
- package/src/index.ts +3 -1
- package/src/inferFromSchema.ts +7 -1
- package/src/util/convertToValidationMarker.ts +5 -3
- package/src/util/requestIdleCallback.ts +31 -0
- package/src/validateDocument.test.ts +35 -101
- package/src/validateDocument.ts +119 -50
- package/src/validators/dateValidator.ts +4 -4
- package/src/validators/slugValidator.ts +19 -5
- package/src/validators/stringValidator.ts +2 -1
- package/dist/dts/Rule.d.ts +0 -4
- package/dist/dts/Rule.d.ts.map +0 -1
- package/dist/dts/ValidationError.d.ts +0 -4
- package/dist/dts/ValidationError.d.ts.map +0 -1
- package/dist/dts/getClient.d.ts +0 -3
- package/dist/dts/getClient.d.ts.map +0 -1
- package/dist/dts/index.d.ts +0 -13
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/dts/inferFromSchema.d.ts +0 -4
- package/dist/dts/inferFromSchema.d.ts.map +0 -1
- package/dist/dts/inferFromSchemaType.d.ts +0 -5
- package/dist/dts/inferFromSchemaType.d.ts.map +0 -1
- package/dist/dts/util/convertToValidationMarker.d.ts +0 -6
- package/dist/dts/util/convertToValidationMarker.d.ts.map +0 -1
- package/dist/dts/util/deepEquals.d.ts +0 -6
- package/dist/dts/util/deepEquals.d.ts.map +0 -1
- package/dist/dts/util/escapeRegex.d.ts +0 -3
- package/dist/dts/util/escapeRegex.d.ts.map +0 -1
- package/dist/dts/util/normalizeValidationRules.d.ts +0 -6
- package/dist/dts/util/normalizeValidationRules.d.ts.map +0 -1
- package/dist/dts/util/normalizeValidationRules.test.d.ts +0 -2
- package/dist/dts/util/normalizeValidationRules.test.d.ts.map +0 -1
- package/dist/dts/util/pathToString.d.ts +0 -3
- package/dist/dts/util/pathToString.d.ts.map +0 -1
- package/dist/dts/util/typeString.d.ts +0 -2
- package/dist/dts/util/typeString.d.ts.map +0 -1
- package/dist/dts/util/typeString.test.d.ts +0 -2
- package/dist/dts/util/typeString.test.d.ts.map +0 -1
- package/dist/dts/validateDocument.d.ts +0 -22
- package/dist/dts/validateDocument.d.ts.map +0 -1
- package/dist/dts/validateDocument.test.d.ts +0 -2
- package/dist/dts/validateDocument.test.d.ts.map +0 -1
- package/dist/dts/validators/arrayValidator.d.ts +0 -4
- package/dist/dts/validators/arrayValidator.d.ts.map +0 -1
- package/dist/dts/validators/booleanValidator.d.ts +0 -4
- package/dist/dts/validators/booleanValidator.d.ts.map +0 -1
- package/dist/dts/validators/dateValidator.d.ts +0 -5
- package/dist/dts/validators/dateValidator.d.ts.map +0 -1
- package/dist/dts/validators/genericValidator.d.ts +0 -4
- package/dist/dts/validators/genericValidator.d.ts.map +0 -1
- package/dist/dts/validators/numberValidator.d.ts +0 -4
- package/dist/dts/validators/numberValidator.d.ts.map +0 -1
- package/dist/dts/validators/objectValidator.d.ts +0 -4
- package/dist/dts/validators/objectValidator.d.ts.map +0 -1
- package/dist/dts/validators/slugValidator.d.ts +0 -9
- package/dist/dts/validators/slugValidator.d.ts.map +0 -1
- package/dist/dts/validators/stringValidator.d.ts +0 -4
- package/dist/dts/validators/stringValidator.d.ts.map +0 -1
- package/jest.config.js +0 -5
- package/lib/Rule.js +0 -491
- package/lib/ValidationError.js +0 -39
- package/lib/getClient.js +0 -20
- package/lib/inferFromSchema.js +0 -22
- package/lib/inferFromSchemaType.js +0 -86
- package/lib/util/convertToValidationMarker.js +0 -65
- package/lib/util/deepEquals.js +0 -87
- package/lib/util/escapeRegex.js +0 -14
- package/lib/util/normalizeValidationRules.js +0 -115
- package/lib/util/pathToString.js +0 -30
- package/lib/util/typeString.js +0 -29
- package/lib/validateDocument.js +0 -231
- package/lib/validators/arrayValidator.js +0 -123
- package/lib/validators/booleanValidator.js +0 -29
- package/lib/validators/dateValidator.js +0 -120
- package/lib/validators/genericValidator.js +0 -135
- package/lib/validators/numberValidator.js +0 -70
- package/lib/validators/objectValidator.js +0 -93
- package/lib/validators/slugValidator.js +0 -133
- package/lib/validators/stringValidator.js +0 -131
- package/src/getClient.ts +0 -13
- package/tsconfig.json +0 -10
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type {SanityClient} from '@sanity/client'
|
|
3
2
|
import {Rule, SchemaType, SanityDocument, Schema, ArraySchemaType, Block} from '@sanity/types'
|
|
4
|
-
import createSchema from '
|
|
3
|
+
import {createSchema} from '../test/createSchema'
|
|
4
|
+
import {createMockSanityClient} from '../test/mocks/mockSanityClient'
|
|
5
5
|
import validateDocument, {resolveTypeForArrayItem, validateItem} from './validateDocument'
|
|
6
|
-
import convertToValidationMarker from './util/convertToValidationMarker'
|
|
6
|
+
import {convertToValidationMarker} from './util/convertToValidationMarker'
|
|
7
7
|
|
|
8
8
|
jest.mock('./util/convertToValidationMarker', () => {
|
|
9
|
-
return
|
|
9
|
+
return {
|
|
10
|
+
convertToValidationMarker: jest.fn(
|
|
11
|
+
jest.requireActual('./util/convertToValidationMarker').convertToValidationMarker
|
|
12
|
+
),
|
|
13
|
+
}
|
|
10
14
|
})
|
|
11
15
|
|
|
12
16
|
beforeEach(() => {
|
|
13
17
|
;(convertToValidationMarker as jest.Mock).mockClear()
|
|
14
18
|
})
|
|
15
19
|
|
|
20
|
+
// mock client
|
|
21
|
+
const client = createMockSanityClient() as any as SanityClient
|
|
22
|
+
const getClient = (options: {apiVersion: string}) => client
|
|
23
|
+
|
|
16
24
|
describe('resolveTypeForArrayItem', () => {
|
|
17
25
|
const schema: Schema = createSchema({
|
|
18
26
|
types: [
|
|
@@ -39,7 +47,7 @@ describe('resolveTypeForArrayItem', () => {
|
|
|
39
47
|
_key: 'exampleKey',
|
|
40
48
|
title: 5,
|
|
41
49
|
},
|
|
42
|
-
[fooType
|
|
50
|
+
[fooType!, barType!]
|
|
43
51
|
)
|
|
44
52
|
|
|
45
53
|
expect(resolved).toBe(barType)
|
|
@@ -52,7 +60,7 @@ describe('resolveTypeForArrayItem', () => {
|
|
|
52
60
|
_key: 'exampleKey',
|
|
53
61
|
title: 5,
|
|
54
62
|
},
|
|
55
|
-
[fooType]
|
|
63
|
+
[fooType!]
|
|
56
64
|
)
|
|
57
65
|
|
|
58
66
|
expect(resolved).toBe(fooType)
|
|
@@ -87,10 +95,9 @@ describe('validateDocument', () => {
|
|
|
87
95
|
title: null,
|
|
88
96
|
}
|
|
89
97
|
|
|
90
|
-
const result = await validateDocument(document, schema)
|
|
98
|
+
const result = await validateDocument(getClient, document, schema)
|
|
91
99
|
expect(result).toMatchObject([
|
|
92
100
|
{
|
|
93
|
-
type: 'validation',
|
|
94
101
|
level: 'error',
|
|
95
102
|
item: {
|
|
96
103
|
message: 'Expected type "String", got "null"',
|
|
@@ -99,7 +106,6 @@ describe('validateDocument', () => {
|
|
|
99
106
|
path: ['title'],
|
|
100
107
|
},
|
|
101
108
|
{
|
|
102
|
-
type: 'validation',
|
|
103
109
|
level: 'error',
|
|
104
110
|
item: {
|
|
105
111
|
message: 'Required',
|
|
@@ -163,6 +169,9 @@ describe('validateItem', () => {
|
|
|
163
169
|
|
|
164
170
|
await expect(
|
|
165
171
|
validateItem({
|
|
172
|
+
client,
|
|
173
|
+
getClient,
|
|
174
|
+
schema,
|
|
166
175
|
value: {},
|
|
167
176
|
document: undefined,
|
|
168
177
|
path: [],
|
|
@@ -172,37 +181,31 @@ describe('validateItem', () => {
|
|
|
172
181
|
})
|
|
173
182
|
).resolves.toMatchObject([
|
|
174
183
|
{
|
|
175
|
-
type: 'validation',
|
|
176
184
|
level: 'error',
|
|
177
185
|
item: {message: 'This is required!'},
|
|
178
186
|
path: ['registeredString'],
|
|
179
187
|
},
|
|
180
188
|
{
|
|
181
|
-
type: 'validation',
|
|
182
189
|
level: 'error',
|
|
183
190
|
item: {message: 'This is required!'},
|
|
184
191
|
path: ['inlineString'],
|
|
185
192
|
},
|
|
186
193
|
{
|
|
187
|
-
type: 'validation',
|
|
188
194
|
level: 'error',
|
|
189
195
|
item: {message: 'Required'},
|
|
190
196
|
path: ['registeredObject'],
|
|
191
197
|
},
|
|
192
198
|
{
|
|
193
|
-
type: 'validation',
|
|
194
199
|
level: 'error',
|
|
195
200
|
item: {message: 'This is required!'},
|
|
196
201
|
path: ['registeredObject', 'foo'],
|
|
197
202
|
},
|
|
198
203
|
{
|
|
199
|
-
type: 'validation',
|
|
200
204
|
level: 'error',
|
|
201
205
|
item: {message: 'Required'},
|
|
202
206
|
path: ['inlineObject'],
|
|
203
207
|
},
|
|
204
208
|
{
|
|
205
|
-
type: 'validation',
|
|
206
209
|
level: 'error',
|
|
207
210
|
item: {message: 'This is required!'},
|
|
208
211
|
path: ['inlineObject', 'foo'],
|
|
@@ -237,6 +240,9 @@ describe('validateItem', () => {
|
|
|
237
240
|
|
|
238
241
|
await expect(
|
|
239
242
|
validateItem({
|
|
243
|
+
client,
|
|
244
|
+
getClient,
|
|
245
|
+
schema,
|
|
240
246
|
document: undefined,
|
|
241
247
|
parent: undefined,
|
|
242
248
|
path: undefined,
|
|
@@ -249,18 +255,17 @@ describe('validateItem', () => {
|
|
|
249
255
|
item: {message: 'Expected type "String", got "Number"'},
|
|
250
256
|
level: 'error',
|
|
251
257
|
path: ['foo'],
|
|
252
|
-
type: 'validation',
|
|
253
258
|
},
|
|
254
259
|
{
|
|
255
260
|
item: {message: 'Required'},
|
|
256
261
|
level: 'error',
|
|
257
262
|
path: ['bar'],
|
|
258
|
-
type: 'validation',
|
|
259
263
|
},
|
|
260
264
|
])
|
|
261
265
|
})
|
|
262
266
|
|
|
263
|
-
|
|
267
|
+
// @todo this one fails, needs investigation for what is actually the expected outcome
|
|
268
|
+
it.skip('runs nested validation for markDefs', async () => {
|
|
264
269
|
const linkValidationSpy = jest.fn(() => true as const)
|
|
265
270
|
const internalLinkSpy = jest.fn(() => 'mock invalid response')
|
|
266
271
|
|
|
@@ -403,6 +408,9 @@ describe('validateItem', () => {
|
|
|
403
408
|
}
|
|
404
409
|
|
|
405
410
|
const result = await validateItem({
|
|
411
|
+
client,
|
|
412
|
+
getClient,
|
|
413
|
+
schema,
|
|
406
414
|
document: document,
|
|
407
415
|
parent: undefined,
|
|
408
416
|
path: [],
|
|
@@ -416,7 +424,6 @@ describe('validateItem', () => {
|
|
|
416
424
|
item: {message: 'mock invalid response', paths: []},
|
|
417
425
|
level: 'error',
|
|
418
426
|
path: ['content', {_key: 'block-key'}, 'markDefs', {_key: '1'}],
|
|
419
|
-
type: 'validation',
|
|
420
427
|
},
|
|
421
428
|
// this tests for nested markDef validation
|
|
422
429
|
{
|
|
@@ -436,7 +443,6 @@ describe('validateItem', () => {
|
|
|
436
443
|
{_key: 'example-annotation-key'},
|
|
437
444
|
'value',
|
|
438
445
|
],
|
|
439
|
-
type: 'validation',
|
|
440
446
|
},
|
|
441
447
|
])
|
|
442
448
|
|
|
@@ -506,6 +512,9 @@ describe('validateItem', () => {
|
|
|
506
512
|
|
|
507
513
|
await expect(
|
|
508
514
|
validateItem({
|
|
515
|
+
client,
|
|
516
|
+
getClient,
|
|
517
|
+
schema,
|
|
509
518
|
document: undefined,
|
|
510
519
|
parent: undefined,
|
|
511
520
|
path: [],
|
|
@@ -515,7 +524,6 @@ describe('validateItem', () => {
|
|
|
515
524
|
})
|
|
516
525
|
).resolves.toEqual([
|
|
517
526
|
{
|
|
518
|
-
type: 'validation',
|
|
519
527
|
level: 'error',
|
|
520
528
|
item: {
|
|
521
529
|
message: 'Expected type "String", got "Number"',
|
|
@@ -594,9 +602,9 @@ describe('validateItem', () => {
|
|
|
594
602
|
}
|
|
595
603
|
|
|
596
604
|
const rootType = schema.get('root')
|
|
597
|
-
const level1ObjectType = getField({in: rootType
|
|
605
|
+
const level1ObjectType = getField({in: rootType!, name: 'level1Object'})
|
|
598
606
|
const level2StringType = getField({in: level1ObjectType, name: 'level2String'})
|
|
599
|
-
const level1ArrayType = getField({in: rootType
|
|
607
|
+
const level1ArrayType = getField({in: rootType!, name: 'level1Array'})
|
|
600
608
|
const level2NumberType = getField({
|
|
601
609
|
in: (level1ArrayType as ArraySchemaType).of[0],
|
|
602
610
|
name: 'level2Number',
|
|
@@ -604,6 +612,9 @@ describe('validateItem', () => {
|
|
|
604
612
|
|
|
605
613
|
await expect(
|
|
606
614
|
validateItem({
|
|
615
|
+
client,
|
|
616
|
+
getClient,
|
|
617
|
+
schema,
|
|
607
618
|
value,
|
|
608
619
|
type: rootType,
|
|
609
620
|
document,
|
|
@@ -687,81 +698,4 @@ describe('validateItem', () => {
|
|
|
687
698
|
},
|
|
688
699
|
])
|
|
689
700
|
})
|
|
690
|
-
|
|
691
|
-
it('runs all nested validation checks concurrently', async () => {
|
|
692
|
-
type Resolver = (value: true) => void
|
|
693
|
-
const resolvers = new Set<Resolver>()
|
|
694
|
-
const customValidationFn = () => new Promise<true>((resolve) => resolvers.add(resolve))
|
|
695
|
-
|
|
696
|
-
const schema: Schema = createSchema({
|
|
697
|
-
types: [
|
|
698
|
-
{
|
|
699
|
-
name: 'root',
|
|
700
|
-
type: 'object',
|
|
701
|
-
fields: [
|
|
702
|
-
{
|
|
703
|
-
name: 'level1Object',
|
|
704
|
-
type: 'object',
|
|
705
|
-
validation: (rule: Rule) => [
|
|
706
|
-
rule.custom(customValidationFn),
|
|
707
|
-
rule.fields({
|
|
708
|
-
level2String: (r) => r.custom(customValidationFn),
|
|
709
|
-
}),
|
|
710
|
-
],
|
|
711
|
-
fields: [
|
|
712
|
-
{
|
|
713
|
-
name: 'level2String',
|
|
714
|
-
type: 'string',
|
|
715
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
716
|
-
},
|
|
717
|
-
],
|
|
718
|
-
},
|
|
719
|
-
{
|
|
720
|
-
name: 'level1Array',
|
|
721
|
-
type: 'array',
|
|
722
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
723
|
-
of: [
|
|
724
|
-
{
|
|
725
|
-
type: 'object',
|
|
726
|
-
fields: [
|
|
727
|
-
{
|
|
728
|
-
name: 'level2Number',
|
|
729
|
-
type: 'number',
|
|
730
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
731
|
-
},
|
|
732
|
-
],
|
|
733
|
-
},
|
|
734
|
-
],
|
|
735
|
-
},
|
|
736
|
-
],
|
|
737
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
738
|
-
},
|
|
739
|
-
],
|
|
740
|
-
})
|
|
741
|
-
|
|
742
|
-
const value = {
|
|
743
|
-
level1Object: {level3String: 'a string'},
|
|
744
|
-
level1Array: [{level2Number: 5}],
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
const resultPromise = validateItem({
|
|
748
|
-
value,
|
|
749
|
-
document: undefined,
|
|
750
|
-
parent: undefined,
|
|
751
|
-
path: undefined,
|
|
752
|
-
type: schema.get('root'),
|
|
753
|
-
getDocumentExists: undefined,
|
|
754
|
-
})
|
|
755
|
-
|
|
756
|
-
// after `validateItem(...)` has been initiated, all of the custom
|
|
757
|
-
// validation calls should have fired so the resolver size should equal the
|
|
758
|
-
// amount of total fields
|
|
759
|
-
expect(resolvers.size).toBe(6)
|
|
760
|
-
|
|
761
|
-
for (const resolver of resolvers) {
|
|
762
|
-
resolver(true)
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
await resultPromise
|
|
766
|
-
})
|
|
767
701
|
})
|
package/src/validateDocument.ts
CHANGED
|
@@ -10,8 +10,12 @@ import {
|
|
|
10
10
|
isBlockSchemaType,
|
|
11
11
|
isSpanSchemaType,
|
|
12
12
|
} from '@sanity/types'
|
|
13
|
-
import {
|
|
13
|
+
import {concat, defer, merge, Observable, of} from 'rxjs'
|
|
14
|
+
import {catchError, map, mergeAll, mergeMap, toArray} from 'rxjs/operators'
|
|
15
|
+
import {flatten, uniqBy} from 'lodash'
|
|
16
|
+
import type {SanityClient} from '@sanity/client'
|
|
14
17
|
import typeString from './util/typeString'
|
|
18
|
+
import {cancelIdleCallback, requestIdleCallback} from './util/requestIdleCallback'
|
|
15
19
|
import ValidationErrorClass from './ValidationError'
|
|
16
20
|
import normalizeValidationRules from './util/normalizeValidationRules'
|
|
17
21
|
|
|
@@ -45,38 +49,78 @@ export function resolveTypeForArrayItem(
|
|
|
45
49
|
candidates.find((candidate) => candidate.name === 'object' && primitive === 'object')
|
|
46
50
|
)
|
|
47
51
|
}
|
|
52
|
+
const EMPTY_MARKERS: ValidationMarker[] = []
|
|
48
53
|
|
|
49
54
|
export default async function validateDocument(
|
|
55
|
+
getClient: (options: {apiVersion: string}) => SanityClient,
|
|
50
56
|
doc: SanityDocument,
|
|
51
57
|
schema: Schema,
|
|
52
58
|
context?: Pick<ValidationContext, 'getDocumentExists'>
|
|
53
59
|
): Promise<ValidationMarker[]> {
|
|
60
|
+
return validateDocumentObservable(getClient, doc, schema, context).toPromise()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function validateDocumentObservable(
|
|
64
|
+
getClient: (options: {apiVersion: string}) => SanityClient,
|
|
65
|
+
doc: SanityDocument,
|
|
66
|
+
schema: Schema,
|
|
67
|
+
context?: Pick<ValidationContext, 'getDocumentExists'>
|
|
68
|
+
): Observable<ValidationMarker[]> {
|
|
54
69
|
const documentType = schema.get(doc._type)
|
|
55
70
|
if (!documentType) {
|
|
56
71
|
console.warn('Schema type for object type "%s" not found, skipping validation', doc._type)
|
|
57
|
-
return
|
|
72
|
+
return of(EMPTY_MARKERS)
|
|
58
73
|
}
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return [
|
|
72
|
-
{
|
|
73
|
-
type: 'validation',
|
|
74
|
-
level: 'error',
|
|
75
|
-
path: [],
|
|
76
|
-
item: new ValidationErrorClass(err?.message),
|
|
77
|
-
},
|
|
78
|
-
]
|
|
75
|
+
const client = getClient({apiVersion: '2021-06-07'})
|
|
76
|
+
const validationOptions: ValidateItemOptions = {
|
|
77
|
+
client,
|
|
78
|
+
getClient,
|
|
79
|
+
schema,
|
|
80
|
+
parent: undefined,
|
|
81
|
+
value: doc,
|
|
82
|
+
path: [],
|
|
83
|
+
document: doc,
|
|
84
|
+
type: documentType,
|
|
85
|
+
getDocumentExists: context?.getDocumentExists,
|
|
79
86
|
}
|
|
87
|
+
|
|
88
|
+
// <TEMPORARY UGLY HACK TO PRINT DEPRECATION WARNINGS ON USE>
|
|
89
|
+
/* eslint-disable no-proto */
|
|
90
|
+
const wrappedClient = client as any
|
|
91
|
+
validationOptions.client = [
|
|
92
|
+
...Object.keys(client),
|
|
93
|
+
...Object.keys(wrappedClient.__proto__),
|
|
94
|
+
].reduce((acc, key) => {
|
|
95
|
+
const original = Object.hasOwnProperty.call(client, key)
|
|
96
|
+
? wrappedClient[key]
|
|
97
|
+
: wrappedClient.__proto__[key]
|
|
98
|
+
|
|
99
|
+
return Object.defineProperty(acc, key, {
|
|
100
|
+
get() {
|
|
101
|
+
console.warn(
|
|
102
|
+
'`configContext.client` is deprecated and will be removed in the next release! Use `context.getClient({apiVersion: "2021-06-07"})` instead'
|
|
103
|
+
)
|
|
104
|
+
return original
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
}, {}) as any as SanityClient
|
|
108
|
+
/* eslint-enable no-proto */
|
|
109
|
+
// </TEMPORARY UGLY HACK TO PRINT DEPRECATION WARNINGS ON USE>
|
|
110
|
+
|
|
111
|
+
return validateItemObservable(validationOptions).pipe(
|
|
112
|
+
catchError((err) => {
|
|
113
|
+
console.error(err)
|
|
114
|
+
return of([
|
|
115
|
+
{
|
|
116
|
+
type: 'validation' as const,
|
|
117
|
+
level: 'error' as const,
|
|
118
|
+
path: [],
|
|
119
|
+
item: new ValidationErrorClass(err?.message),
|
|
120
|
+
},
|
|
121
|
+
])
|
|
122
|
+
})
|
|
123
|
+
)
|
|
80
124
|
}
|
|
81
125
|
|
|
82
126
|
/**
|
|
@@ -94,27 +138,32 @@ type ValidateItemOptions = {
|
|
|
94
138
|
value: unknown
|
|
95
139
|
} & ExplicitUndefined<ValidationContext>
|
|
96
140
|
|
|
97
|
-
export
|
|
141
|
+
export function validateItem(opts: ValidateItemOptions): Promise<ValidationMarker[]> {
|
|
142
|
+
return validateItemObservable(opts).toPromise()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validateItemObservable({
|
|
98
146
|
value,
|
|
99
147
|
type,
|
|
100
148
|
path = [],
|
|
101
149
|
parent,
|
|
102
150
|
...restOfContext
|
|
103
|
-
}: ValidateItemOptions):
|
|
151
|
+
}: ValidateItemOptions): Observable<ValidationMarker[]> {
|
|
104
152
|
const rules = normalizeValidationRules(type)
|
|
105
|
-
|
|
106
153
|
// run validation for the current value
|
|
107
154
|
const selfChecks = rules.map((rule) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
155
|
+
defer(() =>
|
|
156
|
+
rule.validate(value, {
|
|
157
|
+
...restOfContext,
|
|
158
|
+
parent,
|
|
159
|
+
path,
|
|
160
|
+
type,
|
|
161
|
+
})
|
|
162
|
+
)
|
|
114
163
|
)
|
|
115
164
|
|
|
116
165
|
// run validation for nested values (conditionally)
|
|
117
|
-
let nestedChecks: Array<
|
|
166
|
+
let nestedChecks: Array<Observable<ValidationMarker[]>> = []
|
|
118
167
|
|
|
119
168
|
const selfIsRequired = rules.some((rule) => rule.isRequired())
|
|
120
169
|
const shouldRunNestedObjectValidation =
|
|
@@ -141,12 +190,14 @@ export async function validateItem({
|
|
|
141
190
|
const fieldType = fieldTypes[name]
|
|
142
191
|
return normalizeValidationRules({...fieldType, validation}).map((subRule) => {
|
|
143
192
|
const nestedValue = isRecord(value) ? value[name] : undefined
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
193
|
+
return defer(() =>
|
|
194
|
+
subRule.validate(nestedValue, {
|
|
195
|
+
...restOfContext,
|
|
196
|
+
parent: value,
|
|
197
|
+
path: path.concat(name),
|
|
198
|
+
type: fieldType,
|
|
199
|
+
})
|
|
200
|
+
)
|
|
150
201
|
})
|
|
151
202
|
})
|
|
152
203
|
)
|
|
@@ -154,7 +205,7 @@ export async function validateItem({
|
|
|
154
205
|
// Validation from each field's schema `validation: Rule => {/* ... */}` function
|
|
155
206
|
nestedChecks = nestedChecks.concat(
|
|
156
207
|
type.fields.map((field) =>
|
|
157
|
-
|
|
208
|
+
validateItemObservable({
|
|
158
209
|
...restOfContext,
|
|
159
210
|
parent: value,
|
|
160
211
|
value: isRecord(value) ? value[field.name] : undefined,
|
|
@@ -174,7 +225,7 @@ export async function validateItem({
|
|
|
174
225
|
if (shouldRunNestedValidationForArrays) {
|
|
175
226
|
nestedChecks = nestedChecks.concat(
|
|
176
227
|
value.map((item) =>
|
|
177
|
-
|
|
228
|
+
validateItemObservable({
|
|
178
229
|
...restOfContext,
|
|
179
230
|
parent: value,
|
|
180
231
|
value: item,
|
|
@@ -195,16 +246,16 @@ export async function validateItem({
|
|
|
195
246
|
const spanType = spanChildrenField.type.of.find(isSpanSchemaType)
|
|
196
247
|
|
|
197
248
|
const annotations = (spanType?.annotations || []).reduce<Map<string, SchemaType>>(
|
|
198
|
-
(
|
|
199
|
-
|
|
200
|
-
return
|
|
249
|
+
(acc, annotationType) => {
|
|
250
|
+
acc.set(annotationType.name, annotationType)
|
|
251
|
+
return acc
|
|
201
252
|
},
|
|
202
253
|
new Map()
|
|
203
254
|
)
|
|
204
255
|
|
|
205
256
|
nestedChecks = nestedChecks.concat(
|
|
206
257
|
value.markDefs.map((markDef) =>
|
|
207
|
-
|
|
258
|
+
validateItemObservable({
|
|
208
259
|
...restOfContext,
|
|
209
260
|
parent: value,
|
|
210
261
|
value: markDef,
|
|
@@ -215,13 +266,31 @@ export async function validateItem({
|
|
|
215
266
|
)
|
|
216
267
|
}
|
|
217
268
|
|
|
218
|
-
|
|
269
|
+
return defer(() => merge([...selfChecks, ...nestedChecks])).pipe(
|
|
270
|
+
mergeMap((validateNode) => concat(idle(), validateNode), 40),
|
|
271
|
+
mergeAll(),
|
|
272
|
+
toArray(),
|
|
273
|
+
map(flatten),
|
|
274
|
+
map((results) => {
|
|
275
|
+
// run `uniqBy` if `_fieldRules` are present because they can
|
|
276
|
+
// cause repeat markers
|
|
277
|
+
if (rules.some((rule) => rule._fieldRules)) {
|
|
278
|
+
return uniqBy(results, (rule) => JSON.stringify(rule))
|
|
279
|
+
}
|
|
280
|
+
return results
|
|
281
|
+
})
|
|
282
|
+
)
|
|
283
|
+
}
|
|
219
284
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
285
|
+
function idle(timeout?: number): Observable<never> {
|
|
286
|
+
return new Observable<never>((observer) => {
|
|
287
|
+
const handle = requestIdleCallback(
|
|
288
|
+
() => {
|
|
289
|
+
observer.complete()
|
|
290
|
+
},
|
|
291
|
+
timeout ? {timeout} : undefined
|
|
292
|
+
)
|
|
225
293
|
|
|
226
|
-
|
|
294
|
+
return () => cancelIdleCallback(handle)
|
|
295
|
+
})
|
|
227
296
|
}
|
|
@@ -6,8 +6,8 @@ export function isRecord(obj: unknown): obj is Record<string, unknown> {
|
|
|
6
6
|
return typeof obj === 'object' && obj !== null && !Array.isArray(obj)
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const isoDate =
|
|
10
|
+
/^(?:[-+]\d{2})?(?:\d{4}(?!\d{2}\b))(?:(-?)(?:(?:0[1-9]|1[0-2])(?:\1(?:[12]\d|0[1-9]|3[01]))?|W(?:[0-4]\d|5[0-2])(?:-?[1-7])?|(?:00[1-9]|0[1-9]\d|[12]\d{2}|3(?:[0-5]\d|6[1-6])))(?![T]$|[T][\d]+Z$)(?:[T\s](?:(?:(?:[01]\d|2[0-3])(?:(:?)[0-5]\d)?|24:?00)(?:[.,]\d+(?!:))?)(?:\2[0-5]\d(?:[.,]\d+)?)?(?:[Z]|(?:[+-])(?:[01]\d|2[0-3])(?::?[0-5]\d)?)?)?)?$/
|
|
11
11
|
|
|
12
12
|
// eslint-disable-next-line no-warning-comments
|
|
13
13
|
// TODO (eventually): move these to schema type package
|
|
@@ -79,7 +79,7 @@ const dateValidators: Validators = {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const dateTimeOptions: DateTimeOptions = isRecord(context.type.options)
|
|
82
|
-
? context.type.options
|
|
82
|
+
? (context.type.options as DateTimeOptions)
|
|
83
83
|
: {}
|
|
84
84
|
|
|
85
85
|
const date = getFormattedDate(context.type.name, minDate, dateTimeOptions)
|
|
@@ -102,7 +102,7 @@ const dateValidators: Validators = {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const dateTimeOptions: DateTimeOptions = isRecord(context.type.options)
|
|
105
|
-
? context.type.options
|
|
105
|
+
? (context.type.options as DateTimeOptions)
|
|
106
106
|
: {}
|
|
107
107
|
|
|
108
108
|
const date = getFormattedDate(context.type.name, maxDate, dateTimeOptions)
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
SlugIsUniqueValidator,
|
|
3
|
+
Path,
|
|
4
|
+
CustomValidator,
|
|
5
|
+
isKeyedObject,
|
|
6
|
+
SlugValidationContext,
|
|
7
|
+
SlugParent,
|
|
8
|
+
SlugSchemaType,
|
|
9
|
+
} from '@sanity/types'
|
|
2
10
|
import {memoize} from 'lodash'
|
|
3
|
-
import getClient from '../getClient'
|
|
11
|
+
// import getClient from '../getClient'
|
|
4
12
|
|
|
5
13
|
const memoizedWarnOnArraySlug = memoize(warnOnArraySlug)
|
|
6
14
|
|
|
@@ -23,7 +31,7 @@ function serializePath(path: Path): string {
|
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
const defaultIsUnique: SlugIsUniqueValidator = (slug, context) => {
|
|
26
|
-
const {document, path, type} = context
|
|
34
|
+
const {getClient, document, path, type} = context
|
|
27
35
|
const schemaOptions = type?.options as {disableArrayWarning?: boolean} | undefined
|
|
28
36
|
|
|
29
37
|
if (!document) {
|
|
@@ -48,7 +56,7 @@ const defaultIsUnique: SlugIsUniqueValidator = (slug, context) => {
|
|
|
48
56
|
`${atPath} == $slug`,
|
|
49
57
|
].join(' && ')
|
|
50
58
|
|
|
51
|
-
return getClient().fetch<boolean>(
|
|
59
|
+
return getClient({apiVersion: '2022-09-09'}).fetch<boolean>(
|
|
52
60
|
`!defined(*[${constraints}][0]._id)`,
|
|
53
61
|
{
|
|
54
62
|
docType,
|
|
@@ -94,7 +102,13 @@ export const slugValidator: CustomValidator = async (value, context) => {
|
|
|
94
102
|
const options = context?.type?.options as {isUnique?: SlugIsUniqueValidator} | undefined
|
|
95
103
|
const isUnique = options?.isUnique || defaultIsUnique
|
|
96
104
|
|
|
97
|
-
const
|
|
105
|
+
const slugContext: SlugValidationContext = {
|
|
106
|
+
...context,
|
|
107
|
+
parent: context.parent as SlugParent,
|
|
108
|
+
type: context.type as SlugSchemaType,
|
|
109
|
+
defaultIsUnique,
|
|
110
|
+
}
|
|
111
|
+
const wasUnique = await isUnique(slugValue, slugContext)
|
|
98
112
|
if (wasUnique) {
|
|
99
113
|
return true
|
|
100
114
|
}
|
|
@@ -2,7 +2,8 @@ import {Validators} from '@sanity/types'
|
|
|
2
2
|
import genericValidator from './genericValidator'
|
|
3
3
|
|
|
4
4
|
const DUMMY_ORIGIN = 'http://sanity'
|
|
5
|
-
const emailRegex =
|
|
5
|
+
const emailRegex =
|
|
6
|
+
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
6
7
|
const isRelativeUrl = (url: string) => /^\.*\//.test(url)
|
|
7
8
|
|
|
8
9
|
const stringValidators: Validators = {
|
package/dist/dts/Rule.d.ts
DELETED
package/dist/dts/Rule.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Rule.d.ts","sourceRoot":"","sources":["../../src/Rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,SAAS,EAUV,MAAM,eAAe,CAAA;AA0DtB,QAAA,MAAM,IAAI,EAAE,SAkWX,CAAA;AAED,eAAe,IAAI,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationError.d.ts","sourceRoot":"","sources":["../../src/ValidationError.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,oBAAoB,EACrB,MAAM,eAAe,CAAA;AAGtB,QAAA,MAAM,eAAe,EAAE,oBAoBtB,CAAA;AAED,eAAe,eAAe,CAAA"}
|
package/dist/dts/getClient.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getClient.d.ts","sourceRoot":"","sources":["../../src/getClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAIhD,MAAM,CAAC,OAAO,UAAU,SAAS,IAAI,YAAY,CAQhD"}
|
package/dist/dts/index.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import RuleClass from './Rule';
|
|
2
|
-
import validateDocument from './validateDocument';
|
|
3
|
-
import inferFromSchema from './inferFromSchema';
|
|
4
|
-
import inferFromSchemaType from './inferFromSchemaType';
|
|
5
|
-
declare const _default: {
|
|
6
|
-
Rule: import("@sanity/types/dist/dts").RuleClass;
|
|
7
|
-
validateDocument: typeof validateDocument;
|
|
8
|
-
inferFromSchema: typeof inferFromSchema;
|
|
9
|
-
inferFromSchemaType: typeof inferFromSchemaType;
|
|
10
|
-
};
|
|
11
|
-
export default _default;
|
|
12
|
-
export { RuleClass as Rule, validateDocument, inferFromSchema, inferFromSchemaType };
|
|
13
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/dts/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,QAAQ,CAAA;AAC9B,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AACjD,OAAO,eAAe,MAAM,mBAAmB,CAAA;AAC/C,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;;;;;;;AAEvD,wBAAwF;AACxF,OAAO,EAAC,SAAS,IAAI,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,mBAAmB,EAAC,CAAA"}
|