@cleverbrush/schema 1.1.11 → 2.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/README.md +1314 -101
- package/dist/builders/AnySchemaBuilder.d.ts +54 -14
- package/dist/builders/AnySchemaBuilder.js +2 -112
- package/dist/builders/AnySchemaBuilder.js.map +1 -0
- package/dist/builders/ArraySchemaBuilder.d.ts +124 -23
- package/dist/builders/ArraySchemaBuilder.js +2 -284
- package/dist/builders/ArraySchemaBuilder.js.map +1 -0
- package/dist/builders/BooleanSchemaBuilder.d.ts +94 -19
- package/dist/builders/BooleanSchemaBuilder.js +2 -150
- package/dist/builders/BooleanSchemaBuilder.js.map +1 -0
- package/dist/builders/DateSchemaBuilder.d.ts +134 -34
- package/dist/builders/DateSchemaBuilder.js +2 -433
- package/dist/builders/DateSchemaBuilder.js.map +1 -0
- package/dist/builders/ExternSchemaBuilder.d.ts +200 -0
- package/dist/builders/ExternSchemaBuilder.js +2 -0
- package/dist/builders/ExternSchemaBuilder.js.map +1 -0
- package/dist/builders/FunctionSchemaBuilder.d.ts +57 -14
- package/dist/builders/FunctionSchemaBuilder.js +2 -113
- package/dist/builders/FunctionSchemaBuilder.js.map +1 -0
- package/dist/builders/LazySchemaBuilder.d.ts +167 -0
- package/dist/builders/NullSchemaBuilder.d.ts +162 -0
- package/dist/builders/NumberSchemaBuilder.d.ts +143 -31
- package/dist/builders/NumberSchemaBuilder.js +2 -386
- package/dist/builders/NumberSchemaBuilder.js.map +1 -0
- package/dist/builders/ObjectSchemaBuilder.d.ts +266 -61
- package/dist/builders/ObjectSchemaBuilder.js +2 -589
- package/dist/builders/ObjectSchemaBuilder.js.map +1 -0
- package/dist/builders/PropertyValidationResult.d.ts +68 -0
- package/dist/builders/RecordSchemaBuilder.d.ts +341 -0
- package/dist/builders/RecordSchemaBuilder.js +2 -0
- package/dist/builders/RecordSchemaBuilder.js.map +1 -0
- package/dist/builders/SchemaBuilder.d.ts +818 -30
- package/dist/builders/StringSchemaBuilder.d.ts +152 -37
- package/dist/builders/StringSchemaBuilder.js +2 -414
- package/dist/builders/StringSchemaBuilder.js.map +1 -0
- package/dist/builders/TupleSchemaBuilder.d.ts +248 -0
- package/dist/builders/TupleSchemaBuilder.js +2 -0
- package/dist/builders/TupleSchemaBuilder.js.map +1 -0
- package/dist/builders/UnionSchemaBuilder.d.ts +133 -39
- package/dist/builders/UnionSchemaBuilder.js +2 -216
- package/dist/builders/UnionSchemaBuilder.js.map +1 -0
- package/dist/chunk-BFCMDDTX.js +2 -0
- package/dist/chunk-BFCMDDTX.js.map +1 -0
- package/dist/chunk-CTP4RHDG.js +2 -0
- package/dist/chunk-CTP4RHDG.js.map +1 -0
- package/dist/chunk-EUQ5AE3H.js +2 -0
- package/dist/chunk-EUQ5AE3H.js.map +1 -0
- package/dist/chunk-FWUEUW2N.js +2 -0
- package/dist/chunk-FWUEUW2N.js.map +1 -0
- package/dist/chunk-IJ6FO37G.js +2 -0
- package/dist/chunk-IJ6FO37G.js.map +1 -0
- package/dist/chunk-KESLT5EE.js +2 -0
- package/dist/chunk-KESLT5EE.js.map +1 -0
- package/dist/chunk-KFTO7TMB.js +2 -0
- package/dist/chunk-KFTO7TMB.js.map +1 -0
- package/dist/chunk-MKAYFPAR.js +2 -0
- package/dist/chunk-MKAYFPAR.js.map +1 -0
- package/dist/chunk-NXPH3ZUW.js +2 -0
- package/dist/chunk-NXPH3ZUW.js.map +1 -0
- package/dist/chunk-S5TC6NSU.js +2 -0
- package/dist/chunk-S5TC6NSU.js.map +1 -0
- package/dist/chunk-YWBNVHR6.js +2 -0
- package/dist/chunk-YWBNVHR6.js.map +1 -0
- package/dist/chunk-Z7QZ7IXT.js +2 -0
- package/dist/chunk-Z7QZ7IXT.js.map +1 -0
- package/dist/chunk-ZAN4ZHCJ.js +2 -0
- package/dist/chunk-ZAN4ZHCJ.js.map +1 -0
- package/dist/chunk-ZUPYV5TI.js +2 -0
- package/dist/chunk-ZUPYV5TI.js.map +1 -0
- package/dist/core.d.ts +23 -0
- package/dist/core.js +2 -0
- package/dist/core.js.map +1 -0
- package/dist/extension.d.ts +413 -0
- package/dist/extensions/array.d.ts +112 -0
- package/dist/extensions/enum.d.ts +190 -0
- package/dist/extensions/index.d.ts +108 -0
- package/dist/extensions/nullable.d.ts +26 -0
- package/dist/extensions/number.d.ts +228 -0
- package/dist/extensions/string.d.ts +332 -0
- package/dist/extensions/util.d.ts +45 -0
- package/dist/index.d.ts +7 -20
- package/dist/index.js +2 -19
- package/dist/index.js.map +1 -0
- package/dist/utils/transaction.d.ts +27 -4
- package/package.json +74 -6
- package/dist/builders/SchemaBuilder.js +0 -275
- package/dist/utils/transaction.js +0 -178
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for customizing transaction behavior.
|
|
3
|
+
*/
|
|
1
4
|
export type TransactionOptions = {
|
|
2
5
|
/**
|
|
3
6
|
* An optional callback returning boolean. Called for every
|
|
@@ -12,6 +15,11 @@ export type TransactionOptions = {
|
|
|
12
15
|
*/
|
|
13
16
|
shouldNotWrapWithTransaction?: (child: any) => boolean;
|
|
14
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* A transaction wrapper around an object. Provides copy-on-write semantics:
|
|
20
|
+
* modifications to `object` are isolated from the original until `commit()` is called.
|
|
21
|
+
* Use `rollback()` to discard changes, and `isDirty()` to check for pending modifications.
|
|
22
|
+
*/
|
|
15
23
|
export type Transaction<T> = {
|
|
16
24
|
/**
|
|
17
25
|
* Transaction object you can modify (equals to `initial` right after the call).
|
|
@@ -30,17 +38,32 @@ export type Transaction<T> = {
|
|
|
30
38
|
*/
|
|
31
39
|
rollback: () => T;
|
|
32
40
|
/**
|
|
33
|
-
* Returns `true` if there are any changes to `object` which
|
|
41
|
+
* Returns `true` if there are any changes to `object` which make it different from `initial`.
|
|
34
42
|
*/
|
|
35
43
|
isDirty: () => boolean;
|
|
36
44
|
};
|
|
37
45
|
/**
|
|
38
|
-
* Starts transaction over the `initial` object.
|
|
46
|
+
* Starts a transaction over the `initial` object. The returned `object` is a
|
|
47
|
+
* proxy (for plain objects) or a shallow copy (for arrays) that tracks mutations
|
|
48
|
+
* without modifying `initial`. Call `commit()` to apply or `rollback()` to discard changes.
|
|
49
|
+
*
|
|
50
|
+
* @param initial - the object or array to wrap in a transaction
|
|
51
|
+
* @param options - optional configuration to control which nested values are wrapped
|
|
52
|
+
* @returns a {@link Transaction} with `object`, `commit`, `rollback`, and `isDirty` members
|
|
39
53
|
*/
|
|
40
54
|
export declare const transaction: <T extends {}>(initial: T, options?: TransactionOptions) => Transaction<T>;
|
|
41
55
|
/**
|
|
42
|
-
*
|
|
56
|
+
* Creates a lightweight no-op transaction that wraps the `initial` value
|
|
57
|
+
* without any Proxy or copy-on-write overhead. `commit()` and `rollback()`
|
|
58
|
+
* simply return the original object, and `isDirty()` is always `false`.
|
|
59
|
+
*
|
|
60
|
+
* Use this when no preprocessors or validators are defined — there is no
|
|
61
|
+
* risk of mutation, so the full transaction machinery can be skipped.
|
|
62
|
+
*/
|
|
63
|
+
export declare const noopTransaction: <T extends {}>(initial: T) => Transaction<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Checks if `obj` is an instance of a transaction.
|
|
43
66
|
* @param obj object to check if it's a transaction
|
|
44
|
-
* @returns
|
|
67
|
+
* @returns `true` if `obj` is a transaction, `false` otherwise
|
|
45
68
|
*/
|
|
46
69
|
export declare const isTransaction: (obj: any) => any;
|
package/package.json
CHANGED
|
@@ -10,13 +10,77 @@
|
|
|
10
10
|
],
|
|
11
11
|
"homepage": "https://docs.cleverbrush.com/modules/_cleverbrush_schema.html",
|
|
12
12
|
"keywords": [
|
|
13
|
-
"object schema validator",
|
|
14
13
|
"schema",
|
|
15
|
-
"
|
|
14
|
+
"validation",
|
|
15
|
+
"typescript",
|
|
16
|
+
"type-safe",
|
|
17
|
+
"runtime-validation",
|
|
18
|
+
"zod-alternative",
|
|
19
|
+
"schema-validation",
|
|
20
|
+
"standard-schema",
|
|
16
21
|
"cleverbrush"
|
|
17
22
|
],
|
|
18
23
|
"license": "BSD 3-Clause",
|
|
19
24
|
"main": "./dist/index.js",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./core": {
|
|
31
|
+
"types": "./dist/core.d.ts",
|
|
32
|
+
"import": "./dist/core.js"
|
|
33
|
+
},
|
|
34
|
+
"./string": {
|
|
35
|
+
"types": "./dist/builders/StringSchemaBuilder.d.ts",
|
|
36
|
+
"import": "./dist/builders/StringSchemaBuilder.js"
|
|
37
|
+
},
|
|
38
|
+
"./number": {
|
|
39
|
+
"types": "./dist/builders/NumberSchemaBuilder.d.ts",
|
|
40
|
+
"import": "./dist/builders/NumberSchemaBuilder.js"
|
|
41
|
+
},
|
|
42
|
+
"./boolean": {
|
|
43
|
+
"types": "./dist/builders/BooleanSchemaBuilder.d.ts",
|
|
44
|
+
"import": "./dist/builders/BooleanSchemaBuilder.js"
|
|
45
|
+
},
|
|
46
|
+
"./object": {
|
|
47
|
+
"types": "./dist/builders/ObjectSchemaBuilder.d.ts",
|
|
48
|
+
"import": "./dist/builders/ObjectSchemaBuilder.js"
|
|
49
|
+
},
|
|
50
|
+
"./array": {
|
|
51
|
+
"types": "./dist/builders/ArraySchemaBuilder.d.ts",
|
|
52
|
+
"import": "./dist/builders/ArraySchemaBuilder.js"
|
|
53
|
+
},
|
|
54
|
+
"./date": {
|
|
55
|
+
"types": "./dist/builders/DateSchemaBuilder.d.ts",
|
|
56
|
+
"import": "./dist/builders/DateSchemaBuilder.js"
|
|
57
|
+
},
|
|
58
|
+
"./any": {
|
|
59
|
+
"types": "./dist/builders/AnySchemaBuilder.d.ts",
|
|
60
|
+
"import": "./dist/builders/AnySchemaBuilder.js"
|
|
61
|
+
},
|
|
62
|
+
"./union": {
|
|
63
|
+
"types": "./dist/builders/UnionSchemaBuilder.d.ts",
|
|
64
|
+
"import": "./dist/builders/UnionSchemaBuilder.js"
|
|
65
|
+
},
|
|
66
|
+
"./tuple": {
|
|
67
|
+
"types": "./dist/builders/TupleSchemaBuilder.d.ts",
|
|
68
|
+
"import": "./dist/builders/TupleSchemaBuilder.js"
|
|
69
|
+
},
|
|
70
|
+
"./record": {
|
|
71
|
+
"types": "./dist/builders/RecordSchemaBuilder.d.ts",
|
|
72
|
+
"import": "./dist/builders/RecordSchemaBuilder.js"
|
|
73
|
+
},
|
|
74
|
+
"./function": {
|
|
75
|
+
"types": "./dist/builders/FunctionSchemaBuilder.d.ts",
|
|
76
|
+
"import": "./dist/builders/FunctionSchemaBuilder.js"
|
|
77
|
+
},
|
|
78
|
+
"./extern": {
|
|
79
|
+
"types": "./dist/builders/ExternSchemaBuilder.d.ts",
|
|
80
|
+
"import": "./dist/builders/ExternSchemaBuilder.js"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"sideEffects": false,
|
|
20
84
|
"name": "@cleverbrush/schema",
|
|
21
85
|
"readme": "https://github.com/cleverbrush/framework/tree/master/libs/schema#readme",
|
|
22
86
|
"repository": {
|
|
@@ -24,13 +88,17 @@
|
|
|
24
88
|
"url": "github:cleverbrush/framework"
|
|
25
89
|
},
|
|
26
90
|
"scripts": {
|
|
27
|
-
"watch": "tsc --watch",
|
|
28
|
-
"build": "tsc"
|
|
91
|
+
"watch": "tsc --build --watch",
|
|
92
|
+
"build": "tsup && tsc --project tsconfig.build.json --emitDeclarationOnly",
|
|
93
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
29
94
|
},
|
|
30
95
|
"type": "module",
|
|
31
96
|
"types": "./dist/index.d.ts",
|
|
32
|
-
"version": "
|
|
97
|
+
"version": "2.0.0",
|
|
33
98
|
"devDependencies": {
|
|
34
|
-
"@cleverbrush/deep": "
|
|
99
|
+
"@cleverbrush/deep": "^2.0.0"
|
|
100
|
+
},
|
|
101
|
+
"peerDependencies": {
|
|
102
|
+
"@standard-schema/spec": "^1.1.0"
|
|
35
103
|
}
|
|
36
104
|
}
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import { transaction } from '../utils/transaction.js';
|
|
2
|
-
/**
|
|
3
|
-
* Base class for all schema builders. Provides basic functionality for schema building.
|
|
4
|
-
*
|
|
5
|
-
* **Note:** this class is not intended to be used directly, use one of the subclasses instead.
|
|
6
|
-
* @typeparam TResult Type of the object that will be returned by `validate()` method.
|
|
7
|
-
* @typeparam TRequired If `true`, object will be required. If `false`, object will be optional.
|
|
8
|
-
*/
|
|
9
|
-
export class SchemaBuilder {
|
|
10
|
-
#isRequired = true;
|
|
11
|
-
#preprocessors = [];
|
|
12
|
-
#validators = [];
|
|
13
|
-
#type = 'base';
|
|
14
|
-
get type() {
|
|
15
|
-
return this.#type;
|
|
16
|
-
}
|
|
17
|
-
set type(value) {
|
|
18
|
-
if (typeof value !== 'string' || !value)
|
|
19
|
-
throw new Error('value should be non empty string');
|
|
20
|
-
this.#type = value;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* A list of preprocessors associated with
|
|
24
|
-
* the Builder
|
|
25
|
-
*/
|
|
26
|
-
get preprocessors() {
|
|
27
|
-
return this.#preprocessors;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* A list of validators associated with
|
|
31
|
-
* the Builder
|
|
32
|
-
*/
|
|
33
|
-
get validators() {
|
|
34
|
-
return this.#validators;
|
|
35
|
-
}
|
|
36
|
-
get isRequired() {
|
|
37
|
-
return this.#isRequired;
|
|
38
|
-
}
|
|
39
|
-
set isRequired(value) {
|
|
40
|
-
if (typeof value !== 'boolean')
|
|
41
|
-
throw new Error('should be a boolean value');
|
|
42
|
-
this.#isRequired = value;
|
|
43
|
-
}
|
|
44
|
-
async preValidate(
|
|
45
|
-
/**
|
|
46
|
-
* Object to validate
|
|
47
|
-
*/
|
|
48
|
-
object, context) {
|
|
49
|
-
const { doNotStopOnFirstError } = context || {
|
|
50
|
-
doNotStopOnFirstError: false
|
|
51
|
-
};
|
|
52
|
-
let path = '$';
|
|
53
|
-
if (typeof context?.path === 'string' && context.path)
|
|
54
|
-
path = context.path;
|
|
55
|
-
const resultingContext = {
|
|
56
|
-
path,
|
|
57
|
-
doNotStopOnFirstError
|
|
58
|
-
};
|
|
59
|
-
let preprocessingTransaction = transaction({
|
|
60
|
-
validatedObject: object
|
|
61
|
-
});
|
|
62
|
-
let preprocessedObject = preprocessingTransaction.object.validatedObject;
|
|
63
|
-
let errors = [];
|
|
64
|
-
if (Array.isArray(this.preprocessors)) {
|
|
65
|
-
let currentPrepropIndex = 0;
|
|
66
|
-
for (const preprocessor of this.preprocessors) {
|
|
67
|
-
try {
|
|
68
|
-
preprocessingTransaction = transaction({
|
|
69
|
-
validatedObject: await Promise.resolve(preprocessor(preprocessedObject))
|
|
70
|
-
});
|
|
71
|
-
preprocessedObject =
|
|
72
|
-
preprocessingTransaction.object.validatedObject;
|
|
73
|
-
}
|
|
74
|
-
catch (err) {
|
|
75
|
-
errors.push({
|
|
76
|
-
message: `Preprocessor #${currentPrepropIndex}${preprocessor.name ? ` (${preprocessor.name})` : ''} thrown an error: ${err.message}`,
|
|
77
|
-
path: `${path}($preprocessors[${currentPrepropIndex}])`
|
|
78
|
-
});
|
|
79
|
-
if (!doNotStopOnFirstError) {
|
|
80
|
-
preprocessingTransaction.rollback();
|
|
81
|
-
return {
|
|
82
|
-
valid: false,
|
|
83
|
-
errors: [errors[0]].filter((e) => e),
|
|
84
|
-
context: resultingContext
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
finally {
|
|
89
|
-
currentPrepropIndex++;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (Array.isArray(this.validators)) {
|
|
94
|
-
let currentValidatorIndex = 0;
|
|
95
|
-
for (const validator of this.validators) {
|
|
96
|
-
try {
|
|
97
|
-
const { valid, errors: validatorErrors } = await Promise.resolve(validator(preprocessedObject));
|
|
98
|
-
if (!valid) {
|
|
99
|
-
errors = [
|
|
100
|
-
...errors,
|
|
101
|
-
...(Array.isArray(validatorErrors) &&
|
|
102
|
-
validatorErrors.length
|
|
103
|
-
? validatorErrors.map((err) => ({
|
|
104
|
-
message: err.message,
|
|
105
|
-
path: `${path}($validators[${currentValidatorIndex}])`
|
|
106
|
-
}))
|
|
107
|
-
: [
|
|
108
|
-
{
|
|
109
|
-
message: `Validator #${currentValidatorIndex}${validator.name
|
|
110
|
-
? ` (${validator.name})`
|
|
111
|
-
: ''} didn't pass.`,
|
|
112
|
-
path: `${path}($validators[${currentValidatorIndex}])`
|
|
113
|
-
}
|
|
114
|
-
])
|
|
115
|
-
];
|
|
116
|
-
if (!doNotStopOnFirstError) {
|
|
117
|
-
preprocessingTransaction.rollback();
|
|
118
|
-
return {
|
|
119
|
-
valid: false,
|
|
120
|
-
errors: [errors[0]].filter((e) => e),
|
|
121
|
-
context: resultingContext
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
catch (err) {
|
|
127
|
-
errors.push({
|
|
128
|
-
message: `Validator #${currentValidatorIndex}${validator.name ? ` (${validator.name})` : ''} thrown an error: ${err.message}`,
|
|
129
|
-
path: `${path}($validators[${currentValidatorIndex}])`
|
|
130
|
-
});
|
|
131
|
-
if (!doNotStopOnFirstError) {
|
|
132
|
-
preprocessingTransaction.rollback();
|
|
133
|
-
return {
|
|
134
|
-
valid: false,
|
|
135
|
-
errors: [errors[0]].filter((e) => e),
|
|
136
|
-
context: resultingContext
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
finally {
|
|
141
|
-
currentValidatorIndex++;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (this.isRequired &&
|
|
146
|
-
(typeof preprocessedObject === 'undefined' ||
|
|
147
|
-
preprocessedObject === null)) {
|
|
148
|
-
errors.push({
|
|
149
|
-
message: 'is required',
|
|
150
|
-
path
|
|
151
|
-
});
|
|
152
|
-
if (!doNotStopOnFirstError) {
|
|
153
|
-
preprocessingTransaction.rollback();
|
|
154
|
-
return {
|
|
155
|
-
valid: false,
|
|
156
|
-
errors: [errors[0]],
|
|
157
|
-
context: resultingContext
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if (errors.length > 0) {
|
|
162
|
-
return {
|
|
163
|
-
valid: false,
|
|
164
|
-
errors: errors
|
|
165
|
-
.filter((e) => e)
|
|
166
|
-
.filter((e, i) => (doNotStopOnFirstError ? true : i === 0)),
|
|
167
|
-
context: resultingContext,
|
|
168
|
-
transaction: preprocessingTransaction
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
return {
|
|
172
|
-
valid: true,
|
|
173
|
-
context: resultingContext,
|
|
174
|
-
transaction: preprocessingTransaction
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Generates a serializable object describing the defined schema
|
|
179
|
-
*/
|
|
180
|
-
introspect() {
|
|
181
|
-
return {
|
|
182
|
-
/**
|
|
183
|
-
* String `id` of schema type, e.g. `string', `number` or `object`.
|
|
184
|
-
*/
|
|
185
|
-
type: this.type,
|
|
186
|
-
/**
|
|
187
|
-
* If set to `false`, schema will be optional (`null` or `undefined` values
|
|
188
|
-
* will be considered as valid).
|
|
189
|
-
*/
|
|
190
|
-
isRequired: this.#isRequired,
|
|
191
|
-
/**
|
|
192
|
-
* Array of preprocessor functions
|
|
193
|
-
*/
|
|
194
|
-
preprocessors: [
|
|
195
|
-
...this.preprocessors
|
|
196
|
-
],
|
|
197
|
-
/**
|
|
198
|
-
* Array of validator functions
|
|
199
|
-
*/
|
|
200
|
-
validators: [...this.validators]
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Makes schema optional (consider `null` and `undefined` as valid objects for this schema)
|
|
205
|
-
*/
|
|
206
|
-
optional() {
|
|
207
|
-
return this.createFromProps({
|
|
208
|
-
...this.introspect(),
|
|
209
|
-
isRequired: false
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Makes schema required (consider `null` and `undefined` as invalid objects for this schema)
|
|
214
|
-
*/
|
|
215
|
-
required() {
|
|
216
|
-
return this.createFromProps({
|
|
217
|
-
...this.introspect(),
|
|
218
|
-
isRequired: true
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Adds a `preprocessor` to a preprocessors list
|
|
223
|
-
*/
|
|
224
|
-
addPreprocessor(preprocessor) {
|
|
225
|
-
if (typeof preprocessor !== 'function') {
|
|
226
|
-
throw new Error('preprocessor must be a function');
|
|
227
|
-
}
|
|
228
|
-
return this.createFromProps({
|
|
229
|
-
...this.introspect(),
|
|
230
|
-
preprocessors: [...this.preprocessors, preprocessor]
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Remove all preprocessors for this schema.
|
|
235
|
-
*/
|
|
236
|
-
clearPreprocessors() {
|
|
237
|
-
return this.createFromProps({
|
|
238
|
-
...this.introspect(),
|
|
239
|
-
preprocessors: []
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Adds a `validator` to validators list.
|
|
244
|
-
*/
|
|
245
|
-
addValidator(validator) {
|
|
246
|
-
if (typeof validator !== 'function') {
|
|
247
|
-
throw new Error('validator must be a function');
|
|
248
|
-
}
|
|
249
|
-
return this.createFromProps({
|
|
250
|
-
...this.introspect(),
|
|
251
|
-
validators: [...this.validators, validator]
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Remove all validators for this schema.
|
|
256
|
-
*/
|
|
257
|
-
clearValidators() {
|
|
258
|
-
return this.createFromProps({
|
|
259
|
-
...this.introspect(),
|
|
260
|
-
validators: []
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
constructor(props) {
|
|
264
|
-
const { type, preprocessors, validators, isRequired } = props;
|
|
265
|
-
this.type = type;
|
|
266
|
-
if (typeof isRequired === 'boolean')
|
|
267
|
-
this.isRequired = isRequired;
|
|
268
|
-
if (Array.isArray(preprocessors)) {
|
|
269
|
-
this.#preprocessors = [...preprocessors];
|
|
270
|
-
}
|
|
271
|
-
if (Array.isArray(validators)) {
|
|
272
|
-
this.#validators = [...validators];
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
const TRANSACTION_SYMBOL = Symbol('transaction');
|
|
2
|
-
const defaultNonTransactionalTypes = [Error, RegExp, Date];
|
|
3
|
-
const defaultTransactionOptions = {
|
|
4
|
-
shouldNotWrapWithTransaction: (child) => !!defaultNonTransactionalTypes.find((t) => child instanceof t)
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Starts transaction over the `initial` object.
|
|
8
|
-
*/
|
|
9
|
-
export const transaction = (initial, options) => {
|
|
10
|
-
let newProperties = {};
|
|
11
|
-
let deletedProperties = new Map();
|
|
12
|
-
options = Object.assign({}, defaultTransactionOptions, options || {});
|
|
13
|
-
const { shouldNotWrapWithTransaction } = options;
|
|
14
|
-
const isDirty = () => !!Object.keys(newProperties).find((key) => {
|
|
15
|
-
if (newProperties[key] && newProperties[key][TRANSACTION_SYMBOL]) {
|
|
16
|
-
return newProperties[key][TRANSACTION_SYMBOL].isDirty();
|
|
17
|
-
}
|
|
18
|
-
return true;
|
|
19
|
-
}) || (deletedProperties.size > 0);
|
|
20
|
-
const commit = () => {
|
|
21
|
-
const result = {};
|
|
22
|
-
Object.keys(initial).forEach((key) => {
|
|
23
|
-
result[key] = initial[key];
|
|
24
|
-
});
|
|
25
|
-
Object.keys(newProperties).forEach((key) => {
|
|
26
|
-
const value = newProperties[key];
|
|
27
|
-
if (value[TRANSACTION_SYMBOL]) {
|
|
28
|
-
const { commit: childCommit } = value[TRANSACTION_SYMBOL];
|
|
29
|
-
result[key] = childCommit();
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
result[key] = newProperties[key];
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
for (const key of deletedProperties.keys()) {
|
|
36
|
-
delete result[key];
|
|
37
|
-
}
|
|
38
|
-
newProperties = {};
|
|
39
|
-
deletedProperties = new Map();
|
|
40
|
-
return result;
|
|
41
|
-
};
|
|
42
|
-
const rollback = () => {
|
|
43
|
-
for (const key in newProperties) {
|
|
44
|
-
const value = newProperties[key];
|
|
45
|
-
if (value &&
|
|
46
|
-
typeof value === 'object' &&
|
|
47
|
-
value[TRANSACTION_SYMBOL]) {
|
|
48
|
-
// this is nested transaction, let's rollback it as well
|
|
49
|
-
value[TRANSACTION_SYMBOL].rollback();
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
newProperties = {};
|
|
53
|
-
deletedProperties = new Map();
|
|
54
|
-
return initial;
|
|
55
|
-
};
|
|
56
|
-
if (Array.isArray(initial)) {
|
|
57
|
-
const result = initial.map((el) => typeof el === 'object' && el && !shouldNotWrapWithTransaction(el)
|
|
58
|
-
? transaction(el).object
|
|
59
|
-
: el);
|
|
60
|
-
const commitArray = () => result.map((el) => el && typeof el[TRANSACTION_SYMBOL] === 'object'
|
|
61
|
-
? el[TRANSACTION_SYMBOL].commit()
|
|
62
|
-
: el);
|
|
63
|
-
const isDirtyArray = () => {
|
|
64
|
-
return !!result.find((val, index) => {
|
|
65
|
-
if (val && typeof val[TRANSACTION_SYMBOL] === 'object') {
|
|
66
|
-
return val[TRANSACTION_SYMBOL].isDirty();
|
|
67
|
-
}
|
|
68
|
-
return result[index] !== initial[index];
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
Object.defineProperty(result, TRANSACTION_SYMBOL, {
|
|
72
|
-
writable: false,
|
|
73
|
-
configurable: false,
|
|
74
|
-
value: {
|
|
75
|
-
initial,
|
|
76
|
-
object: result,
|
|
77
|
-
commit: commitArray,
|
|
78
|
-
rollback: () => initial,
|
|
79
|
-
isDirty: isDirtyArray
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
return {
|
|
83
|
-
object: result,
|
|
84
|
-
commit: commitArray,
|
|
85
|
-
rollback: () => initial,
|
|
86
|
-
isDirty: isDirtyArray
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
const proxy = new Proxy(initial, {
|
|
90
|
-
set: (target, property, value) => {
|
|
91
|
-
if (target && target[property] === value) {
|
|
92
|
-
delete newProperties[property];
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
newProperties[property] = value;
|
|
96
|
-
deletedProperties.delete(property);
|
|
97
|
-
return true;
|
|
98
|
-
},
|
|
99
|
-
ownKeys: (target) => {
|
|
100
|
-
return [
|
|
101
|
-
...Object.keys(target).filter((k) => !deletedProperties.has(k)),
|
|
102
|
-
...Object.keys(newProperties).filter((k) => !(k in target))
|
|
103
|
-
];
|
|
104
|
-
},
|
|
105
|
-
getOwnPropertyDescriptor: (target, prop) => {
|
|
106
|
-
if (deletedProperties.has(prop)) {
|
|
107
|
-
return undefined;
|
|
108
|
-
}
|
|
109
|
-
if (prop in newProperties) {
|
|
110
|
-
return Object.getOwnPropertyDescriptor(newProperties, prop);
|
|
111
|
-
}
|
|
112
|
-
return Object.getOwnPropertyDescriptor(target, prop);
|
|
113
|
-
},
|
|
114
|
-
has: (target, prop) => {
|
|
115
|
-
if (deletedProperties.has(prop)) {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
if (prop in newProperties) {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
return prop in target;
|
|
122
|
-
},
|
|
123
|
-
get: (target, prop) => {
|
|
124
|
-
if (typeof prop === 'symbol') {
|
|
125
|
-
if (prop === TRANSACTION_SYMBOL) {
|
|
126
|
-
return {
|
|
127
|
-
initial,
|
|
128
|
-
object: proxy,
|
|
129
|
-
commit,
|
|
130
|
-
rollback,
|
|
131
|
-
isDirty
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
return target[prop];
|
|
135
|
-
}
|
|
136
|
-
if (prop in newProperties) {
|
|
137
|
-
return newProperties[prop];
|
|
138
|
-
}
|
|
139
|
-
if (deletedProperties.has(prop)) {
|
|
140
|
-
return undefined;
|
|
141
|
-
}
|
|
142
|
-
if (!isTransaction(target[prop]) &&
|
|
143
|
-
typeof target[prop] === 'object' &&
|
|
144
|
-
target[prop] &&
|
|
145
|
-
!shouldNotWrapWithTransaction(target[prop])) {
|
|
146
|
-
const { object } = transaction(target[prop], options);
|
|
147
|
-
newProperties[prop] = object;
|
|
148
|
-
return object;
|
|
149
|
-
}
|
|
150
|
-
return target[prop];
|
|
151
|
-
},
|
|
152
|
-
deleteProperty: (target, p) => {
|
|
153
|
-
if (p in newProperties) {
|
|
154
|
-
if (newProperties[p] &&
|
|
155
|
-
typeof newProperties[p][TRANSACTION_SYMBOL] === 'object') {
|
|
156
|
-
newProperties[p][TRANSACTION_SYMBOL].rollback();
|
|
157
|
-
}
|
|
158
|
-
delete newProperties[p];
|
|
159
|
-
}
|
|
160
|
-
if (p in target) {
|
|
161
|
-
deletedProperties.set(p, true);
|
|
162
|
-
}
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
return {
|
|
167
|
-
object: proxy,
|
|
168
|
-
commit,
|
|
169
|
-
rollback,
|
|
170
|
-
isDirty
|
|
171
|
-
};
|
|
172
|
-
};
|
|
173
|
-
/**
|
|
174
|
-
* Checks if `obj` is instance of transaction
|
|
175
|
-
* @param obj object to check if it's a transaction
|
|
176
|
-
* @returns
|
|
177
|
-
*/
|
|
178
|
-
export const isTransaction = (obj) => obj && typeof obj === 'object' && Object.hasOwn(obj, TRANSACTION_SYMBOL);
|