@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.
Files changed (87) hide show
  1. package/README.md +1314 -101
  2. package/dist/builders/AnySchemaBuilder.d.ts +54 -14
  3. package/dist/builders/AnySchemaBuilder.js +2 -112
  4. package/dist/builders/AnySchemaBuilder.js.map +1 -0
  5. package/dist/builders/ArraySchemaBuilder.d.ts +124 -23
  6. package/dist/builders/ArraySchemaBuilder.js +2 -284
  7. package/dist/builders/ArraySchemaBuilder.js.map +1 -0
  8. package/dist/builders/BooleanSchemaBuilder.d.ts +94 -19
  9. package/dist/builders/BooleanSchemaBuilder.js +2 -150
  10. package/dist/builders/BooleanSchemaBuilder.js.map +1 -0
  11. package/dist/builders/DateSchemaBuilder.d.ts +134 -34
  12. package/dist/builders/DateSchemaBuilder.js +2 -433
  13. package/dist/builders/DateSchemaBuilder.js.map +1 -0
  14. package/dist/builders/ExternSchemaBuilder.d.ts +200 -0
  15. package/dist/builders/ExternSchemaBuilder.js +2 -0
  16. package/dist/builders/ExternSchemaBuilder.js.map +1 -0
  17. package/dist/builders/FunctionSchemaBuilder.d.ts +57 -14
  18. package/dist/builders/FunctionSchemaBuilder.js +2 -113
  19. package/dist/builders/FunctionSchemaBuilder.js.map +1 -0
  20. package/dist/builders/LazySchemaBuilder.d.ts +167 -0
  21. package/dist/builders/NullSchemaBuilder.d.ts +162 -0
  22. package/dist/builders/NumberSchemaBuilder.d.ts +143 -31
  23. package/dist/builders/NumberSchemaBuilder.js +2 -386
  24. package/dist/builders/NumberSchemaBuilder.js.map +1 -0
  25. package/dist/builders/ObjectSchemaBuilder.d.ts +266 -61
  26. package/dist/builders/ObjectSchemaBuilder.js +2 -589
  27. package/dist/builders/ObjectSchemaBuilder.js.map +1 -0
  28. package/dist/builders/PropertyValidationResult.d.ts +68 -0
  29. package/dist/builders/RecordSchemaBuilder.d.ts +341 -0
  30. package/dist/builders/RecordSchemaBuilder.js +2 -0
  31. package/dist/builders/RecordSchemaBuilder.js.map +1 -0
  32. package/dist/builders/SchemaBuilder.d.ts +818 -30
  33. package/dist/builders/StringSchemaBuilder.d.ts +152 -37
  34. package/dist/builders/StringSchemaBuilder.js +2 -414
  35. package/dist/builders/StringSchemaBuilder.js.map +1 -0
  36. package/dist/builders/TupleSchemaBuilder.d.ts +248 -0
  37. package/dist/builders/TupleSchemaBuilder.js +2 -0
  38. package/dist/builders/TupleSchemaBuilder.js.map +1 -0
  39. package/dist/builders/UnionSchemaBuilder.d.ts +133 -39
  40. package/dist/builders/UnionSchemaBuilder.js +2 -216
  41. package/dist/builders/UnionSchemaBuilder.js.map +1 -0
  42. package/dist/chunk-BFCMDDTX.js +2 -0
  43. package/dist/chunk-BFCMDDTX.js.map +1 -0
  44. package/dist/chunk-CTP4RHDG.js +2 -0
  45. package/dist/chunk-CTP4RHDG.js.map +1 -0
  46. package/dist/chunk-EUQ5AE3H.js +2 -0
  47. package/dist/chunk-EUQ5AE3H.js.map +1 -0
  48. package/dist/chunk-FWUEUW2N.js +2 -0
  49. package/dist/chunk-FWUEUW2N.js.map +1 -0
  50. package/dist/chunk-IJ6FO37G.js +2 -0
  51. package/dist/chunk-IJ6FO37G.js.map +1 -0
  52. package/dist/chunk-KESLT5EE.js +2 -0
  53. package/dist/chunk-KESLT5EE.js.map +1 -0
  54. package/dist/chunk-KFTO7TMB.js +2 -0
  55. package/dist/chunk-KFTO7TMB.js.map +1 -0
  56. package/dist/chunk-MKAYFPAR.js +2 -0
  57. package/dist/chunk-MKAYFPAR.js.map +1 -0
  58. package/dist/chunk-NXPH3ZUW.js +2 -0
  59. package/dist/chunk-NXPH3ZUW.js.map +1 -0
  60. package/dist/chunk-S5TC6NSU.js +2 -0
  61. package/dist/chunk-S5TC6NSU.js.map +1 -0
  62. package/dist/chunk-YWBNVHR6.js +2 -0
  63. package/dist/chunk-YWBNVHR6.js.map +1 -0
  64. package/dist/chunk-Z7QZ7IXT.js +2 -0
  65. package/dist/chunk-Z7QZ7IXT.js.map +1 -0
  66. package/dist/chunk-ZAN4ZHCJ.js +2 -0
  67. package/dist/chunk-ZAN4ZHCJ.js.map +1 -0
  68. package/dist/chunk-ZUPYV5TI.js +2 -0
  69. package/dist/chunk-ZUPYV5TI.js.map +1 -0
  70. package/dist/core.d.ts +23 -0
  71. package/dist/core.js +2 -0
  72. package/dist/core.js.map +1 -0
  73. package/dist/extension.d.ts +413 -0
  74. package/dist/extensions/array.d.ts +112 -0
  75. package/dist/extensions/enum.d.ts +190 -0
  76. package/dist/extensions/index.d.ts +108 -0
  77. package/dist/extensions/nullable.d.ts +26 -0
  78. package/dist/extensions/number.d.ts +228 -0
  79. package/dist/extensions/string.d.ts +332 -0
  80. package/dist/extensions/util.d.ts +45 -0
  81. package/dist/index.d.ts +7 -20
  82. package/dist/index.js +2 -19
  83. package/dist/index.js.map +1 -0
  84. package/dist/utils/transaction.d.ts +27 -4
  85. package/package.json +74 -6
  86. package/dist/builders/SchemaBuilder.js +0 -275
  87. 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 makes is different from `initial`
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
- * Checks if `obj` is instance of transaction
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
- "validate object",
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": "1.1.11",
97
+ "version": "2.0.0",
33
98
  "devDependencies": {
34
- "@cleverbrush/deep": "1.1.11"
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);