@valbuild/core 0.15.0 → 0.17.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.
Files changed (133) hide show
  1. package/ROADMAP.md +106 -0
  2. package/dist/declarations/src/ValApi.d.ts +26 -0
  3. package/dist/declarations/src/expr/eval.d.ts +1 -1
  4. package/dist/declarations/src/future/fetchVal.d.ts +5 -0
  5. package/dist/declarations/src/index.d.ts +38 -9
  6. package/dist/declarations/src/initSchema.d.ts +2 -10
  7. package/dist/declarations/src/initVal.d.ts +3 -21
  8. package/dist/declarations/src/module.d.ts +2 -14
  9. package/dist/declarations/src/patch/index.d.ts +1 -1
  10. package/dist/declarations/src/patch/util.d.ts +2 -0
  11. package/dist/declarations/src/schema/array.d.ts +3 -2
  12. package/dist/declarations/src/schema/boolean.d.ts +3 -2
  13. package/dist/declarations/src/schema/image.d.ts +4 -3
  14. package/dist/declarations/src/schema/index.d.ts +8 -7
  15. package/dist/declarations/src/schema/keyOf.d.ts +23 -0
  16. package/dist/declarations/src/schema/literal.d.ts +3 -2
  17. package/dist/declarations/src/schema/number.d.ts +3 -2
  18. package/dist/declarations/src/schema/object.d.ts +3 -2
  19. package/dist/declarations/src/schema/record.d.ts +19 -0
  20. package/dist/declarations/src/schema/richtext.d.ts +3 -2
  21. package/dist/declarations/src/schema/string.d.ts +3 -2
  22. package/dist/declarations/src/schema/union.d.ts +4 -2
  23. package/dist/declarations/src/schema/validation/ValidationError.d.ts +14 -0
  24. package/dist/declarations/src/schema/validation/ValidationFix.d.ts +2 -0
  25. package/dist/declarations/src/selector/SelectorProxy.d.ts +7 -0
  26. package/dist/declarations/src/selector/array.d.ts +2 -13
  27. package/dist/declarations/src/selector/file.d.ts +1 -4
  28. package/dist/declarations/src/selector/future/array.d.ts +17 -0
  29. package/dist/declarations/src/selector/future/boolean.d.ts +2 -0
  30. package/dist/declarations/src/selector/future/file.d.ts +9 -0
  31. package/dist/declarations/src/selector/{i18n.d.ts → future/i18n.d.ts} +2 -2
  32. package/dist/declarations/src/selector/future/index.d.ts +81 -0
  33. package/dist/declarations/src/selector/future/number.d.ts +2 -0
  34. package/dist/declarations/src/selector/future/object.d.ts +10 -0
  35. package/dist/declarations/src/selector/future/primitive.d.ts +9 -0
  36. package/dist/declarations/src/selector/{remote.d.ts → future/remote.d.ts} +1 -1
  37. package/dist/declarations/src/selector/future/string.d.ts +2 -0
  38. package/dist/declarations/src/selector/index.d.ts +5 -32
  39. package/dist/declarations/src/selector/object.d.ts +2 -9
  40. package/dist/declarations/src/selector/primitive.d.ts +3 -9
  41. package/dist/declarations/src/source/{i18n.d.ts → future/i18n.d.ts} +2 -2
  42. package/dist/declarations/src/source/{remote.d.ts → future/remote.d.ts} +3 -3
  43. package/dist/declarations/src/source/index.d.ts +4 -3
  44. package/dist/declarations/src/val/index.d.ts +2 -2
  45. package/dist/index-369caccf.esm.js +550 -0
  46. package/dist/{index-06df0a5b.esm.js → index-3e3e839e.esm.js} +190 -555
  47. package/dist/{index-9663f28a.cjs.dev.js → index-486c7fbf.cjs.dev.js} +224 -617
  48. package/dist/{index-b2270f8f.cjs.prod.js → index-601a7d73.cjs.prod.js} +224 -617
  49. package/dist/index-8706c87e.cjs.prod.js +582 -0
  50. package/dist/index-a6e642dd.cjs.dev.js +582 -0
  51. package/dist/ops-0d09f8ee.cjs.prod.js +684 -0
  52. package/dist/ops-23a5abb2.esm.js +671 -0
  53. package/dist/ops-f3015423.cjs.dev.js +684 -0
  54. package/dist/valbuild-core.cjs.dev.js +640 -633
  55. package/dist/valbuild-core.cjs.prod.js +640 -633
  56. package/dist/valbuild-core.esm.js +578 -572
  57. package/expr/dist/valbuild-core-expr.cjs.dev.js +8 -8
  58. package/expr/dist/valbuild-core-expr.cjs.prod.js +8 -8
  59. package/expr/dist/valbuild-core-expr.esm.js +2 -2
  60. package/package.json +2 -2
  61. package/patch/dist/valbuild-core-patch.cjs.dev.js +32 -23
  62. package/patch/dist/valbuild-core-patch.cjs.prod.js +32 -23
  63. package/patch/dist/valbuild-core-patch.esm.js +12 -4
  64. package/src/ValApi.ts +85 -0
  65. package/src/expr/eval.test.ts +2 -2
  66. package/src/expr/eval.ts +2 -2
  67. package/src/expr/repl.ts +2 -2
  68. package/src/{fetchVal.test.ts → future/fetchVal.test.ts} +57 -57
  69. package/src/{fetchVal.ts → future/fetchVal.ts} +17 -22
  70. package/src/index.ts +51 -14
  71. package/src/initSchema.ts +11 -12
  72. package/src/initVal.ts +42 -52
  73. package/src/module.test.ts +40 -40
  74. package/src/module.ts +53 -43
  75. package/src/patch/deref.test.ts +1 -1
  76. package/src/patch/deref.ts +1 -1
  77. package/src/patch/index.ts +1 -0
  78. package/src/patch/json.test.ts +0 -1
  79. package/src/patch/util.ts +7 -0
  80. package/src/schema/array.ts +45 -4
  81. package/src/schema/boolean.ts +14 -3
  82. package/src/schema/{i18n.ts → future/i18n.ts} +15 -11
  83. package/src/schema/{oneOf.ts → future/oneOf.ts} +21 -18
  84. package/src/schema/image.ts +66 -6
  85. package/src/schema/index.ts +37 -13
  86. package/src/schema/keyOf.ts +167 -0
  87. package/src/schema/literal.ts +24 -3
  88. package/src/schema/number.ts +14 -3
  89. package/src/schema/object.ts +50 -7
  90. package/src/schema/record.ts +103 -0
  91. package/src/schema/richtext.ts +63 -3
  92. package/src/schema/string.ts +14 -3
  93. package/src/schema/union.ts +4 -3
  94. package/src/schema/validation/ValidationError.ts +16 -0
  95. package/src/schema/validation/ValidationFix.ts +6 -0
  96. package/src/schema/validation.test.ts +291 -0
  97. package/src/selector/SelectorProxy.ts +16 -16
  98. package/src/selector/array.ts +2 -26
  99. package/src/selector/file.ts +1 -9
  100. package/src/selector/{ExprProxy.test.ts → future/ExprProxy.test.ts} +2 -2
  101. package/src/selector/{ExprProxy.ts → future/ExprProxy.ts} +9 -2
  102. package/src/selector/{SelectorProxy.test.ts → future/SelectorProxy.test.ts} +4 -4
  103. package/src/selector/future/SelectorProxy.ts +238 -0
  104. package/src/selector/future/array.ts +37 -0
  105. package/src/selector/future/boolean.ts +4 -0
  106. package/src/selector/future/file.ts +14 -0
  107. package/src/selector/{i18n.ts → future/i18n.ts} +2 -2
  108. package/src/selector/future/index.ts +165 -0
  109. package/src/selector/future/number.ts +4 -0
  110. package/src/selector/future/object.ts +22 -0
  111. package/src/selector/future/primitive.ts +17 -0
  112. package/src/selector/{remote.ts → future/remote.ts} +1 -1
  113. package/src/selector/{selector.test.ts → future/selector.test.ts} +8 -28
  114. package/src/selector/{selectorOf.ts → future/selectorOf.ts} +1 -1
  115. package/src/selector/future/string.ts +4 -0
  116. package/src/selector/index.ts +4 -46
  117. package/src/selector/object.ts +2 -19
  118. package/src/selector/primitive.ts +3 -16
  119. package/src/source/{i18n.ts → future/i18n.ts} +2 -2
  120. package/src/source/{remote.ts → future/remote.ts} +3 -3
  121. package/src/source/index.ts +3 -2
  122. package/src/val/array.ts +1 -1
  123. package/src/val/index.ts +2 -2
  124. package/src/val/object.ts +1 -1
  125. package/dist/createClass-012eebbf.esm.js +0 -109
  126. package/dist/createClass-a436dbfe.cjs.dev.js +0 -116
  127. package/dist/createClass-de7426aa.cjs.prod.js +0 -116
  128. package/dist/declarations/src/fetchVal.d.ts +0 -5
  129. package/dist/declarations/src/schema/i18n.d.ts +0 -21
  130. package/dist/declarations/src/schema/oneOf.d.ts +0 -22
  131. package/dist/ops-6fae92a1.esm.js +0 -12
  132. package/dist/ops-87cdbafc.cjs.dev.js +0 -14
  133. package/dist/ops-ae4d1bc2.cjs.prod.js +0 -14
@@ -0,0 +1,167 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { Schema, SerializedSchema, SourceObject } from "..";
3
+ import { ValModuleBrand } from "../module";
4
+ import { GenericSelector, GetSchema } from "../selector";
5
+ import { SourceArray } from "../source";
6
+ import { SourcePath, getValPath } from "../val";
7
+ import { ValidationErrors } from "./validation/ValidationError";
8
+
9
+ export type SerializedKeyOfSchema = {
10
+ type: "keyOf";
11
+ selector: SourcePath;
12
+ opt: boolean;
13
+ };
14
+
15
+ type KeyOfSelector<Sel extends GenericSelector<SourceArray | SourceObject>> =
16
+ Sel extends GenericSelector<infer S>
17
+ ? // TODO: remove any:
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ S extends readonly any[]
20
+ ? number
21
+ : S extends SourceObject
22
+ ? keyof S
23
+ : // TODO: remove any:
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ S extends Record<string, any>
26
+ ? string
27
+ : never
28
+ : never;
29
+
30
+ export class KeyOfSchema<
31
+ Sel extends GenericSelector<SourceArray | SourceObject>
32
+ > extends Schema<KeyOfSelector<Sel>> {
33
+ constructor(readonly selector: Sel, readonly opt: boolean = false) {
34
+ super();
35
+ }
36
+ validate(path: SourcePath, src: KeyOfSelector<Sel>): ValidationErrors {
37
+ if (this.opt && (src === null || src === undefined)) {
38
+ return false;
39
+ }
40
+ const schema = this.selector[GetSchema];
41
+ if (!schema) {
42
+ return {
43
+ [path]: [
44
+ {
45
+ message: `Schema not found for module. keyOf must be used with a Val Module`,
46
+ },
47
+ ],
48
+ };
49
+ }
50
+ const serializedSchema = schema.serialize();
51
+
52
+ if (
53
+ !(
54
+ serializedSchema.type === "array" ||
55
+ serializedSchema.type === "object" ||
56
+ serializedSchema.type === "record"
57
+ )
58
+ ) {
59
+ return {
60
+ [path]: [
61
+ {
62
+ message: `Schema in keyOf must be an 'array', 'object' or 'record'. Found '${serializedSchema.type}'`,
63
+ },
64
+ ],
65
+ };
66
+ }
67
+ if (serializedSchema.opt && (src === null || src === undefined)) {
68
+ return false;
69
+ }
70
+ if (serializedSchema.type === "array" && typeof src !== "number") {
71
+ return {
72
+ [path]: [
73
+ {
74
+ message: "Type of value in keyof (array) must be 'number'",
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ if (serializedSchema.type === "record" && typeof src !== "string") {
80
+ return {
81
+ [path]: [
82
+ {
83
+ message: "Type of value in keyof (record) must be 'string'",
84
+ },
85
+ ],
86
+ };
87
+ }
88
+ if (serializedSchema.type === "object") {
89
+ const keys = Object.keys(serializedSchema.items);
90
+ if (!keys.includes(src as string)) {
91
+ return {
92
+ [path]: [
93
+ {
94
+ message: `Value of keyOf (object) must be: ${keys.join(
95
+ ", "
96
+ )}. Found: ${src}`,
97
+ },
98
+ ],
99
+ };
100
+ }
101
+ }
102
+ return false;
103
+ }
104
+
105
+ assert(src: KeyOfSelector<Sel>): boolean {
106
+ if (this.opt && (src === null || src === undefined)) {
107
+ return true;
108
+ }
109
+ const schema = this.selector[GetSchema];
110
+ if (!schema) {
111
+ return false;
112
+ }
113
+ const serializedSchema = schema.serialize();
114
+
115
+ if (
116
+ !(
117
+ serializedSchema.type === "array" ||
118
+ serializedSchema.type === "object" ||
119
+ serializedSchema.type === "record"
120
+ )
121
+ ) {
122
+ return false;
123
+ }
124
+ if (serializedSchema.opt && (src === null || src === undefined)) {
125
+ return true;
126
+ }
127
+ if (serializedSchema.type === "array" && typeof src !== "number") {
128
+ return false;
129
+ }
130
+ if (serializedSchema.type === "record" && typeof src !== "string") {
131
+ return false;
132
+ }
133
+ if (serializedSchema.type === "object") {
134
+ const keys = Object.keys(serializedSchema.items);
135
+ if (!keys.includes(src as string)) {
136
+ return false;
137
+ }
138
+ }
139
+ return true;
140
+ }
141
+
142
+ optional(): Schema<KeyOfSelector<Sel> | null> {
143
+ return new KeyOfSchema(this.selector, true);
144
+ }
145
+
146
+ serialize(): SerializedSchema {
147
+ const path = getValPath(this.selector);
148
+ if (!path) {
149
+ throw new Error(
150
+ "Cannot serialize oneOf schema with empty selector. keyOf must be used with a Val Module."
151
+ );
152
+ }
153
+ return {
154
+ type: "keyOf",
155
+ selector: path,
156
+ opt: this.opt,
157
+ };
158
+ }
159
+ }
160
+
161
+ export const keyOf = <
162
+ Src extends GenericSelector<SourceArray | SourceObject> & ValModuleBrand // ValModuleBrand enforces call site to pass in a val module - selectors are not allowed. The reason is that this should make it easier to patch. We might be able to relax this constraint in the future
163
+ >(
164
+ valModule: Src
165
+ ): Schema<KeyOfSelector<Src>> => {
166
+ return new KeyOfSchema(valModule);
167
+ };
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { Schema, SerializedSchema } from ".";
3
3
  import { SourcePath } from "../val";
4
+ import { ValidationErrors } from "./validation/ValidationError";
4
5
 
5
6
  export type SerializedLiteralSchema = {
6
7
  type: "literal";
@@ -13,11 +14,31 @@ export class LiteralSchema<Src extends string | null> extends Schema<Src> {
13
14
  super();
14
15
  }
15
16
 
16
- validate(src: Src): false | Record<SourcePath, string[]> {
17
- throw new Error("Method not implemented.");
17
+ validate(path: SourcePath, src: Src): ValidationErrors {
18
+ if (this.opt && (src === null || src === undefined)) {
19
+ return false;
20
+ }
21
+ if (typeof src !== "string") {
22
+ return {
23
+ [path]: [
24
+ { message: `Expected 'string', got '${typeof src}'`, value: src },
25
+ ],
26
+ } as ValidationErrors;
27
+ }
28
+ if (src !== this.value) {
29
+ return {
30
+ [path]: [
31
+ {
32
+ message: `Expected literal '${this.value}', got '${src}'`,
33
+ value: src,
34
+ },
35
+ ],
36
+ } as ValidationErrors;
37
+ }
38
+ return false;
18
39
  }
19
40
 
20
- match(src: Src): boolean {
41
+ assert(src: Src): boolean {
21
42
  if (this.opt && (src === null || src === undefined)) {
22
43
  return true;
23
44
  }
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { Schema, SerializedSchema } from ".";
3
3
  import { SourcePath } from "../val";
4
+ import { ValidationErrors } from "./validation/ValidationError";
4
5
 
5
6
  type NumberOptions = {
6
7
  max?: number;
@@ -17,11 +18,21 @@ export class NumberSchema<Src extends number | null> extends Schema<Src> {
17
18
  constructor(readonly options?: NumberOptions, readonly opt: boolean = false) {
18
19
  super();
19
20
  }
20
- validate(src: Src): false | Record<SourcePath, string[]> {
21
- throw new Error("Method not implemented.");
21
+ validate(path: SourcePath, src: Src): ValidationErrors {
22
+ if (this.opt && (src === null || src === undefined)) {
23
+ return false;
24
+ }
25
+ if (typeof src !== "number") {
26
+ return {
27
+ [path]: [
28
+ { message: `Expected 'number', got '${typeof src}'`, value: src },
29
+ ],
30
+ } as ValidationErrors;
31
+ }
32
+ return false;
22
33
  }
23
34
 
24
- match(src: Src): boolean {
35
+ assert(src: Src): boolean {
25
36
  if (this.opt && (src === null || src === undefined)) {
26
37
  return true;
27
38
  }
@@ -1,7 +1,9 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { Schema, SchemaTypeOf, SerializedSchema } from ".";
3
3
  import { SelectorSource } from "../selector";
4
+ import { createValPathOfItem } from "../selector/SelectorProxy";
4
5
  import { SourcePath } from "../val";
6
+ import { ValidationErrors } from "./validation/ValidationError";
5
7
 
6
8
  export type SerializedObjectSchema = {
7
9
  type: "object";
@@ -21,13 +23,51 @@ export class ObjectSchema<Props extends ObjectSchemaProps> extends Schema<
21
23
  super();
22
24
  }
23
25
 
24
- validate(
25
- src: ObjectSchemaSrcOf<Props>
26
- ): false | Record<SourcePath, string[]> {
27
- throw new Error("Method not implemented.");
26
+ validate(path: SourcePath, src: ObjectSchemaSrcOf<Props>): ValidationErrors {
27
+ let error: ValidationErrors = false;
28
+
29
+ if (this.opt && (src === null || src === undefined)) {
30
+ return false;
31
+ }
32
+
33
+ if (typeof src !== "object") {
34
+ return {
35
+ [path]: [{ message: `Expected 'object', got '${typeof src}'` }],
36
+ } as ValidationErrors;
37
+ } else if (Array.isArray(src)) {
38
+ return {
39
+ [path]: [{ message: `Expected 'object', got 'array'` }],
40
+ } as ValidationErrors;
41
+ }
42
+
43
+ Object.entries(this.items).forEach(([key, schema]) => {
44
+ const subPath = createValPathOfItem(path, key);
45
+ if (!subPath) {
46
+ error = this.appendValidationError(
47
+ error,
48
+ path,
49
+ `Internal error: could not create path at ${
50
+ !path && typeof path === "string" ? "<empty string>" : path
51
+ } at key ${key}`, // Should! never happen
52
+ src
53
+ );
54
+ } else {
55
+ const subError = schema.validate(subPath, src[key]);
56
+ if (subError && error) {
57
+ error = {
58
+ ...subError,
59
+ ...error,
60
+ };
61
+ } else if (subError) {
62
+ error = subError;
63
+ }
64
+ }
65
+ });
66
+
67
+ return error;
28
68
  }
29
69
 
30
- match(src: ObjectSchemaSrcOf<Props>): boolean {
70
+ assert(src: ObjectSchemaSrcOf<Props>): boolean {
31
71
  if (this.opt && (src === null || src === undefined)) {
32
72
  return true;
33
73
  }
@@ -35,8 +75,11 @@ export class ObjectSchema<Props extends ObjectSchemaProps> extends Schema<
35
75
  return false;
36
76
  }
37
77
 
38
- // TODO: checks all props
39
-
78
+ for (const [key, schema] of Object.entries(this.items)) {
79
+ if (!schema.assert(src[key])) {
80
+ return false;
81
+ }
82
+ }
40
83
  return typeof src === "object" && !Array.isArray(src);
41
84
  }
42
85
 
@@ -0,0 +1,103 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { Schema, SchemaTypeOf, SerializedSchema } from ".";
3
+ import { initVal } from "../initVal";
4
+ import { SelectorSource } from "../selector";
5
+ import { createValPathOfItem } from "../selector/SelectorProxy";
6
+ import { SourcePath } from "../val";
7
+ import { string } from "./string";
8
+ import { ValidationErrors } from "./validation/ValidationError";
9
+
10
+ export type SerializedRecordSchema = {
11
+ type: "record";
12
+ item: SerializedSchema;
13
+ opt: boolean;
14
+ };
15
+
16
+ export class RecordSchema<T extends Schema<SelectorSource>> extends Schema<
17
+ Record<string, SchemaTypeOf<T>>
18
+ > {
19
+ constructor(readonly item: T, readonly opt: boolean = false) {
20
+ super();
21
+ }
22
+
23
+ validate(
24
+ path: SourcePath,
25
+ src: Record<string, SchemaTypeOf<T>>
26
+ ): ValidationErrors {
27
+ let error: ValidationErrors = false;
28
+
29
+ if (this.opt && (src === null || src === undefined)) {
30
+ return false;
31
+ }
32
+
33
+ if (typeof src !== "object") {
34
+ return {
35
+ [path]: [{ message: `Expected 'object', got '${typeof src}'` }],
36
+ } as ValidationErrors;
37
+ }
38
+ if (Array.isArray(src)) {
39
+ return {
40
+ [path]: [{ message: `Expected 'object', got 'array'` }],
41
+ } as ValidationErrors;
42
+ }
43
+ Object.entries(src).forEach(([key, elem]) => {
44
+ const subPath = createValPathOfItem(path, key);
45
+ if (!subPath) {
46
+ error = this.appendValidationError(
47
+ error,
48
+ path,
49
+ `Internal error: could not create path at ${
50
+ !path && typeof path === "string" ? "<empty string>" : path
51
+ } at key ${elem}`, // Should! never happen
52
+ src
53
+ );
54
+ } else {
55
+ const subError = this.item.validate(subPath, elem as SelectorSource);
56
+ if (subError && error) {
57
+ error = {
58
+ ...subError,
59
+ ...error,
60
+ };
61
+ } else if (subError) {
62
+ error = subError;
63
+ }
64
+ }
65
+ });
66
+
67
+ return error;
68
+ }
69
+
70
+ assert(src: Record<string, SchemaTypeOf<T>>): boolean {
71
+ if (this.opt && (src === null || src === undefined)) {
72
+ return true;
73
+ }
74
+ if (!src) {
75
+ return false;
76
+ }
77
+
78
+ for (const [, item] of Object.entries(src)) {
79
+ if (!this.item.assert(item)) {
80
+ return false;
81
+ }
82
+ }
83
+ return typeof src === "object" && !Array.isArray(src);
84
+ }
85
+
86
+ optional(): Schema<Record<string, SchemaTypeOf<T>> | null> {
87
+ return new RecordSchema(this.item, true);
88
+ }
89
+
90
+ serialize(): SerializedRecordSchema {
91
+ return {
92
+ type: "record",
93
+ item: this.item.serialize(),
94
+ opt: this.opt,
95
+ };
96
+ }
97
+ }
98
+
99
+ export const record = <S extends Schema<SelectorSource>>(
100
+ schema: S
101
+ ): Schema<Record<string, SchemaTypeOf<S>>> => {
102
+ return new RecordSchema(schema);
103
+ };
@@ -1,7 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { Schema, SerializedSchema } from ".";
3
+ import { VAL_EXTENSION } from "../source";
3
4
  import { RichTextSource } from "../source/richtext";
4
5
  import { SourcePath } from "../val";
6
+ import {
7
+ ValidationError,
8
+ ValidationErrors,
9
+ } from "./validation/ValidationError";
5
10
 
6
11
  export type SerializedRichTextSchema = {
7
12
  type: "richtext";
@@ -11,10 +16,65 @@ export type SerializedRichTextSchema = {
11
16
  export class RichTextSchema<
12
17
  Src extends RichTextSource | null
13
18
  > extends Schema<Src> {
14
- validate(src: Src): false | Record<SourcePath, string[]> {
15
- throw new Error("Method not implemented.");
19
+ validate(path: SourcePath, src: Src): ValidationErrors {
20
+ if (this.opt && (src === null || src === undefined)) {
21
+ return false;
22
+ }
23
+ if (src === null || src === undefined) {
24
+ return {
25
+ [path]: [
26
+ { message: `Expected non-nullable got '${src}'` } as ValidationError,
27
+ ],
28
+ } as ValidationErrors;
29
+ }
30
+
31
+ if (typeof src !== "object" && !Array.isArray(src)) {
32
+ return {
33
+ [path]: [
34
+ {
35
+ message: `Expected 'object' (that is not of an array) or 'string', got '${typeof src}'`,
36
+ value: src,
37
+ } as ValidationError,
38
+ ],
39
+ } as ValidationErrors;
40
+ }
41
+
42
+ if (src[VAL_EXTENSION] !== "richtext") {
43
+ return {
44
+ [path]: [
45
+ {
46
+ message: `Expected _type key with value 'richtext' got '${src[VAL_EXTENSION]}'`,
47
+ value: src,
48
+ } as ValidationError,
49
+ ],
50
+ } as ValidationErrors;
51
+ }
52
+
53
+ if (src.type !== "root") {
54
+ return {
55
+ [path]: [
56
+ {
57
+ message: `Expected type key with value 'root' got '${src.type}'`,
58
+ value: src,
59
+ } as ValidationError,
60
+ ],
61
+ } as ValidationErrors;
62
+ }
63
+
64
+ if (typeof src.children !== "object" && !Array.isArray(src.children)) {
65
+ return {
66
+ [path]: [
67
+ {
68
+ message: `Expected children to be an array, but got '${src.type}'`,
69
+ value: src,
70
+ } as ValidationError,
71
+ ],
72
+ } as ValidationErrors;
73
+ }
74
+
75
+ return false;
16
76
  }
17
- match(src: Src): boolean {
77
+ assert(src: Src): boolean {
18
78
  // TODO:
19
79
  return true;
20
80
  }
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { Schema, SerializedSchema } from ".";
3
3
  import { SourcePath } from "../val";
4
+ import { ValidationErrors } from "./validation/ValidationError";
4
5
 
5
6
  type StringOptions = {
6
7
  maxLength?: number;
@@ -18,11 +19,21 @@ export class StringSchema<Src extends string | null> extends Schema<Src> {
18
19
  super();
19
20
  }
20
21
 
21
- validate(src: Src): false | Record<SourcePath, string[]> {
22
- throw new Error("Method not implemented.");
22
+ validate(path: SourcePath, src: Src): ValidationErrors {
23
+ if (this.opt && (src === null || src === undefined)) {
24
+ return false;
25
+ }
26
+ if (typeof src !== "string") {
27
+ return {
28
+ [path]: [
29
+ { message: `Expected 'string', got '${typeof src}'`, value: src },
30
+ ],
31
+ } as ValidationErrors;
32
+ }
33
+ return false;
23
34
  }
24
35
 
25
- match(src: Src): boolean {
36
+ assert(src: Src): boolean {
26
37
  if (this.opt && (src === null || src === undefined)) {
27
38
  return true;
28
39
  }
@@ -1,8 +1,9 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { Schema, SerializedSchema } from ".";
3
- import { SelectorSource } from "../selector";
3
+ import { SelectorSource } from "../selector/index";
4
4
  import { SourceObject } from "../source";
5
5
  import { SourcePath } from "../val";
6
+ import { ValidationErrors } from "./validation/ValidationError";
6
7
 
7
8
  export type SerializedUnionSchema = {
8
9
  type: "union";
@@ -24,10 +25,10 @@ export class UnionSchema<
24
25
  Key extends string,
25
26
  T extends Schema<SourceObject & { [k in Key]: string }>[]
26
27
  > extends Schema<SourceOf<Key, T>> {
27
- validate(src: SourceOf<Key, T>): false | Record<SourcePath, string[]> {
28
+ validate(path: SourcePath, src: SourceOf<Key, T>): ValidationErrors {
28
29
  throw new Error("Method not implemented.");
29
30
  }
30
- match(src: SourceOf<Key, T>): boolean {
31
+ assert(src: SourceOf<Key, T>): boolean {
31
32
  throw new Error("Method not implemented.");
32
33
  }
33
34
  optional(): Schema<SourceOf<Key, T> | null> {
@@ -0,0 +1,16 @@
1
+ import { SourcePath } from "../../val";
2
+ import { ValidationFix } from "./ValidationFix";
3
+
4
+ export type ValidationError = {
5
+ message: string;
6
+ value?: unknown;
7
+ fixes?: ValidationFix[];
8
+ };
9
+
10
+ /**
11
+ * Equals `false` if no validation errors were found.
12
+ * Errors are indexed by the full source path.
13
+ *
14
+ * Global errors have the path `"/"`.
15
+ */
16
+ export type ValidationErrors = false | Record<SourcePath, ValidationError[]>;
@@ -0,0 +1,6 @@
1
+ export const ValidationFix = [
2
+ "image:add-metadata",
3
+ "image:replace-metadata",
4
+ ] as const;
5
+
6
+ export type ValidationFix = (typeof ValidationFix)[number];