@sanity/diff-patch 5.0.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.
package/src/paths.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type {KeyedSanityObject} from './diffPatch.js'
2
+
3
+ const IS_DOTTABLE_RE = /^[A-Za-z_][A-Za-z0-9_]*$/
4
+
5
+ /**
6
+ * A segment of a path
7
+ *
8
+ * @public
9
+ */
10
+ export type PathSegment =
11
+ | string // Property
12
+ | number // Array index
13
+ | {_key: string} // Array `_key` lookup
14
+ | [number | '', number | ''] // From/to array index
15
+
16
+ /**
17
+ * An array of path segments representing a path in a document
18
+ *
19
+ * @public
20
+ */
21
+ export type Path = PathSegment[]
22
+
23
+ /**
24
+ * Converts an array path to a string path
25
+ *
26
+ * @param path - The array path to convert
27
+ * @returns A stringified path
28
+ * @internal
29
+ */
30
+ export function pathToString(path: Path): string {
31
+ return path.reduce((target: string, segment: PathSegment, i: number) => {
32
+ if (Array.isArray(segment)) {
33
+ return `${target}[${segment.join(':')}]`
34
+ }
35
+
36
+ if (isKeyedObject(segment)) {
37
+ return `${target}[_key=="${segment._key}"]`
38
+ }
39
+
40
+ if (typeof segment === 'number') {
41
+ return `${target}[${segment}]`
42
+ }
43
+
44
+ if (typeof segment === 'string' && !IS_DOTTABLE_RE.test(segment)) {
45
+ return `${target}['${segment}']`
46
+ }
47
+
48
+ if (typeof segment === 'string') {
49
+ const separator = i === 0 ? '' : '.'
50
+ return `${target}${separator}${segment}`
51
+ }
52
+
53
+ throw new Error(`Unsupported path segment "${segment}"`)
54
+ }, '')
55
+ }
56
+
57
+ function isKeyedObject(obj: any): obj is KeyedSanityObject {
58
+ return typeof obj === 'object' && typeof obj._key === 'string'
59
+ }
@@ -0,0 +1,71 @@
1
+ import {DiffError} from './diffError.js'
2
+ import type {Path} from './paths.js'
3
+
4
+ const idPattern = /^[a-z0-9][a-z0-9_.-]+$/i
5
+ const propPattern = /^[a-zA-Z_][a-zA-Z0-9_-]*$/
6
+ const propStartPattern = /^[a-z_]/i
7
+
8
+ /**
9
+ * Validate the given document/subtree for Sanity compatibility
10
+ *
11
+ * @param item - The document or subtree to validate
12
+ * @param path - The path to the current item, for error reporting
13
+ * @returns True if valid, throws otherwise
14
+ * @internal
15
+ */
16
+ export function validateDocument(item: unknown, path: Path = []): boolean {
17
+ if (Array.isArray(item)) {
18
+ return item.every((child, i) => {
19
+ if (Array.isArray(child)) {
20
+ throw new DiffError('Multi-dimensional arrays not supported', path.concat(i))
21
+ }
22
+
23
+ return validateDocument(child, path.concat(i))
24
+ })
25
+ }
26
+
27
+ if (typeof item === 'object' && item !== null) {
28
+ const obj = item as {[key: string]: any}
29
+ return Object.keys(obj).every(
30
+ (key) =>
31
+ validateProperty(key, obj[key], path) && validateDocument(obj[key], path.concat(key)),
32
+ )
33
+ }
34
+
35
+ return true
36
+ }
37
+
38
+ /**
39
+ * Validate a property for Sanity compatibility
40
+ *
41
+ * @param property - The property to valide
42
+ * @param value - The value of the property
43
+ * @param path - The path of the property, for error reporting
44
+ * @returns The property name, if valid
45
+ * @internal
46
+ */
47
+ export function validateProperty(property: string, value: unknown, path: Path): string {
48
+ if (!propStartPattern.test(property)) {
49
+ throw new DiffError('Keys must start with a letter (a-z)', path.concat(property), value)
50
+ }
51
+
52
+ if (!propPattern.test(property)) {
53
+ throw new DiffError(
54
+ 'Keys can only contain letters, numbers and underscores',
55
+ path.concat(property),
56
+ value,
57
+ )
58
+ }
59
+
60
+ if (property === '_key' || property === '_ref' || property === '_type') {
61
+ if (typeof value !== 'string') {
62
+ throw new DiffError('Keys must be strings', path.concat(property), value)
63
+ }
64
+
65
+ if (!idPattern.test(value)) {
66
+ throw new DiffError('Invalid key - use less exotic characters', path.concat(property), value)
67
+ }
68
+ }
69
+
70
+ return property
71
+ }