@oscarpalmer/jhunal 0.8.0 → 0.10.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/dist/constants.js +3 -2
- package/dist/is.js +2 -4
- package/dist/jhunal.full.js +76 -609
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/array/compact.js +12 -0
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/is.js +20 -0
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/string.js +24 -0
- package/dist/node_modules/@oscarpalmer/atoms/dist/value/smush.js +36 -0
- package/dist/validation/schema.validation.js +23 -8
- package/dist/validation/value.validation.js +5 -5
- package/package.json +6 -6
- package/src/constants.ts +3 -1
- package/src/is.ts +1 -4
- package/src/models.ts +40 -0
- package/src/validation/schema.validation.ts +42 -6
- package/src/validation/value.validation.ts +13 -6
- package/types/constants.d.ts +1 -0
- package/types/is.d.ts +0 -1
- package/types/models.d.ts +29 -0
- package/types/validation/value.validation.d.ts +2 -2
package/dist/jhunal.full.js
CHANGED
|
@@ -1,77 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const TYPE_UNDEFINED = "undefined";
|
|
9
|
-
const TYPE_ALL = new Set([
|
|
10
|
-
"array",
|
|
11
|
-
"bigint",
|
|
12
|
-
"boolean",
|
|
13
|
-
"date",
|
|
14
|
-
"function",
|
|
15
|
-
"null",
|
|
16
|
-
"number",
|
|
17
|
-
"string",
|
|
18
|
-
"symbol",
|
|
19
|
-
TYPE_OBJECT,
|
|
20
|
-
TYPE_UNDEFINED
|
|
21
|
-
]);
|
|
22
|
-
function isConstructor$1(value) {
|
|
23
|
-
return typeof value === "function" && value.prototype !== void 0;
|
|
24
|
-
}
|
|
25
|
-
function isInstance(constructor) {
|
|
26
|
-
if (!isConstructor$1(constructor)) throw new TypeError("Expected a constructor function");
|
|
27
|
-
return (value) => {
|
|
28
|
-
return value instanceof constructor;
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function isSchematic(value) {
|
|
32
|
-
return typeof value === "object" && value !== null && SCHEMATIC_NAME in value && value[SCHEMATIC_NAME] === true;
|
|
33
|
-
}
|
|
34
|
-
function aggregate(type, array, key) {
|
|
35
|
-
const length = Array.isArray(array) ? array.length : 0;
|
|
36
|
-
if (length === 0) return {
|
|
37
|
-
count: 0,
|
|
38
|
-
value: NaN
|
|
39
|
-
};
|
|
40
|
-
const aggregator = aggregators[type];
|
|
41
|
-
const isCallback = typeof key === "function";
|
|
42
|
-
let counted = 0;
|
|
43
|
-
let aggregated = NaN;
|
|
44
|
-
let notNumber = true;
|
|
45
|
-
for (let index = 0; index < length; index += 1) {
|
|
46
|
-
const item = array[index];
|
|
47
|
-
const value = isCallback ? key(item, index, array) : item[key] ?? item;
|
|
48
|
-
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
49
|
-
aggregated = aggregator(aggregated, value, notNumber);
|
|
50
|
-
counted += 1;
|
|
51
|
-
notNumber = false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
count: counted,
|
|
56
|
-
value: aggregated
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function max(array, key) {
|
|
60
|
-
return getAggregated("max", array, key);
|
|
1
|
+
/**
|
|
2
|
+
* Is the value an array or a record?
|
|
3
|
+
* @param value Value to check
|
|
4
|
+
* @returns `true` if the value is an array or a record, otherwise `false`
|
|
5
|
+
*/
|
|
6
|
+
function isArrayOrPlainObject(value) {
|
|
7
|
+
return Array.isArray(value) || isPlainObject(value);
|
|
61
8
|
}
|
|
62
|
-
|
|
63
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Is the value a constructor function?
|
|
11
|
+
* @param value Value to check
|
|
12
|
+
* @returns `true` if the value is a constructor function, otherwise `false`
|
|
13
|
+
*/
|
|
14
|
+
function isConstructor(value) {
|
|
15
|
+
return typeof value === "function" && value.prototype?.constructor === value;
|
|
64
16
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Is the value a plain object?
|
|
19
|
+
* @param value Value to check
|
|
20
|
+
* @returns `true` if the value is a plain object, otherwise `false`
|
|
21
|
+
*/
|
|
22
|
+
function isPlainObject(value) {
|
|
23
|
+
if (value === null || typeof value !== "object") return false;
|
|
24
|
+
if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
|
|
25
|
+
const prototype = Object.getPrototypeOf(value);
|
|
26
|
+
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
68
27
|
}
|
|
69
|
-
var aggregators = {
|
|
70
|
-
average: calculateSum,
|
|
71
|
-
max: (current, value, notNumber) => notNumber || value > current ? value : current,
|
|
72
|
-
min: (current, value, notNumber) => notNumber || value < current ? value : current,
|
|
73
|
-
sum: calculateSum
|
|
74
|
-
};
|
|
75
28
|
function compact(array, strict) {
|
|
76
29
|
if (!Array.isArray(array)) return [];
|
|
77
30
|
if (strict === true) return array.filter(Boolean);
|
|
@@ -105,536 +58,36 @@ function getString(value) {
|
|
|
105
58
|
function join(value, delimiter) {
|
|
106
59
|
return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
|
|
107
60
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
* Is the value a plain object?
|
|
135
|
-
* @param value Value to check
|
|
136
|
-
* @returns `true` if the value is a plain object, otherwise `false`
|
|
137
|
-
*/
|
|
138
|
-
function isPlainObject(value) {
|
|
139
|
-
if (value === null || typeof value !== "object") return false;
|
|
140
|
-
if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
|
|
141
|
-
const prototype = Object.getPrototypeOf(value);
|
|
142
|
-
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Is the value a typed array?
|
|
146
|
-
* @param value Value to check
|
|
147
|
-
* @returns `true` if the value is a typed array, otherwise `false`
|
|
148
|
-
*/
|
|
149
|
-
function isTypedArray(value) {
|
|
150
|
-
TYPED_ARRAYS ??= new Set([
|
|
151
|
-
Int8Array,
|
|
152
|
-
Uint8Array,
|
|
153
|
-
Uint8ClampedArray,
|
|
154
|
-
Int16Array,
|
|
155
|
-
Uint16Array,
|
|
156
|
-
Int32Array,
|
|
157
|
-
Uint32Array,
|
|
158
|
-
Float32Array,
|
|
159
|
-
Float64Array,
|
|
160
|
-
BigInt64Array,
|
|
161
|
-
BigUint64Array
|
|
162
|
-
]);
|
|
163
|
-
return TYPED_ARRAYS.has(value?.constructor);
|
|
164
|
-
}
|
|
165
|
-
var EXPRESSION_CONSTRUCTOR = /^[A-Z][A-Za-z0-9]*$/;
|
|
166
|
-
var TYPED_ARRAYS;
|
|
167
|
-
function getCompareHandlers(owner, options) {
|
|
168
|
-
const { get, register, unregister } = getHandlers(owner, options);
|
|
169
|
-
return {
|
|
170
|
-
register,
|
|
171
|
-
unregister,
|
|
172
|
-
handle(first, second, ...parameters) {
|
|
173
|
-
const handler = get(first, second);
|
|
174
|
-
if (handler == null) return options.callback(first, second, ...parameters);
|
|
175
|
-
return typeof handler === "function" ? handler(first, second) : first[handler](second);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function getHandlers(owner, options) {
|
|
180
|
-
const handlers = /* @__PURE__ */ new WeakMap();
|
|
181
|
-
return {
|
|
182
|
-
get(first, second) {
|
|
183
|
-
if (isConstructable(first) && isConstructable(second) && first.constructor === second.constructor) return handlers.get(first.constructor);
|
|
184
|
-
},
|
|
185
|
-
register(constructor, handler) {
|
|
186
|
-
if (!isConstructor(constructor) || handler === owner) return;
|
|
187
|
-
let actual = handler ?? options.method;
|
|
188
|
-
if (typeof actual !== "function" && typeof actual !== "string") return;
|
|
189
|
-
if (typeof actual === "string") actual = typeof constructor.prototype[actual] === "function" ? actual : void 0;
|
|
190
|
-
if (actual != null) handlers.set(constructor, actual);
|
|
191
|
-
},
|
|
192
|
-
unregister(constructor) {
|
|
193
|
-
handlers.delete(constructor);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
function getSelfHandlers(owner, options) {
|
|
198
|
-
const { get, register, unregister } = getHandlers(owner, options);
|
|
199
|
-
return {
|
|
200
|
-
register,
|
|
201
|
-
unregister,
|
|
202
|
-
handle(value, ...parameters) {
|
|
203
|
-
const handler = get(value, value);
|
|
204
|
-
if (handler == null) return options.callback(value, ...parameters);
|
|
205
|
-
return typeof handler === "function" ? handler(value) : value[handler]();
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function isConstructable(value) {
|
|
210
|
-
return typeof value === "object" && value !== null;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Compare two values _(for sorting purposes)_
|
|
214
|
-
* @param first First value
|
|
215
|
-
* @param second Second value
|
|
216
|
-
* @returns `0` if equal; `-1` first comes before second; `1` first comes after second
|
|
217
|
-
*/
|
|
218
|
-
function compare(first, second) {
|
|
219
|
-
if (Object.is(first, second)) return 0;
|
|
220
|
-
if (first == null) return -1;
|
|
221
|
-
if (second == null) return 1;
|
|
222
|
-
let comparison = compareValue(first, second, false);
|
|
223
|
-
if (comparison != null) return comparison;
|
|
224
|
-
const firstParts = getComparisonParts(first);
|
|
225
|
-
const secondParts = getComparisonParts(second);
|
|
226
|
-
const length = max([firstParts.length, secondParts.length]);
|
|
227
|
-
const lastIndex = length - 1;
|
|
228
|
-
for (let index = 0; index < length; index += 1) {
|
|
229
|
-
const firstPart = firstParts[index];
|
|
230
|
-
const secondPart = secondParts[index];
|
|
231
|
-
comparison = compareValue(firstPart, secondPart, true);
|
|
232
|
-
if (comparison === 0) {
|
|
233
|
-
if (index === lastIndex) break;
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
return comparison;
|
|
237
|
-
}
|
|
238
|
-
return 0;
|
|
239
|
-
}
|
|
240
|
-
compare.handlers = getCompareHandlers(compare, {
|
|
241
|
-
callback: (first, second, compareStrings) => {
|
|
242
|
-
if (compareStrings) return getString(first).localeCompare(getString(second));
|
|
243
|
-
},
|
|
244
|
-
method: "compare"
|
|
245
|
-
});
|
|
246
|
-
/**
|
|
247
|
-
* Register a custom comparison handler for a class
|
|
248
|
-
* @param constructor Class constructor
|
|
249
|
-
* @param handler Method name or comparison function _(defaults to `compare`)_
|
|
250
|
-
*/
|
|
251
|
-
compare.register = function(constructor, handler) {
|
|
252
|
-
compare.handlers.register(constructor, handler);
|
|
253
|
-
};
|
|
254
|
-
/**
|
|
255
|
-
* Unregister a custom comparison handler for a class
|
|
256
|
-
* @param constructor Class constructor
|
|
257
|
-
*/
|
|
258
|
-
compare.unregister = compare.handlers.unregister;
|
|
259
|
-
function compareNumbers(first, second) {
|
|
260
|
-
const firstNumber = Number(first);
|
|
261
|
-
const secondNumber = Number(second);
|
|
262
|
-
if (firstNumber === secondNumber) return 0;
|
|
263
|
-
return firstNumber > secondNumber ? 1 : -1;
|
|
264
|
-
}
|
|
265
|
-
function compareSymbols(first, second) {
|
|
266
|
-
return getString(first.description ?? first).localeCompare(getString(second.description ?? second));
|
|
267
|
-
}
|
|
268
|
-
function compareValue(first, second, compareStrings) {
|
|
269
|
-
const firstType = typeof first;
|
|
270
|
-
if (firstType === typeof second && firstType in comparators) return comparators[firstType](first, second);
|
|
271
|
-
if (first instanceof Date && second instanceof Date) return compareNumbers(first.getTime(), second.getTime());
|
|
272
|
-
return compare.handlers.handle(first, second, compareStrings);
|
|
273
|
-
}
|
|
274
|
-
function getComparisonParts(value) {
|
|
275
|
-
if (Array.isArray(value)) return value;
|
|
276
|
-
return typeof value === "object" ? [value] : words(getString(value));
|
|
277
|
-
}
|
|
278
|
-
var comparators = {
|
|
279
|
-
bigint: compareNumbers,
|
|
280
|
-
boolean: compareNumbers,
|
|
281
|
-
number: compareNumbers,
|
|
282
|
-
symbol: compareSymbols
|
|
283
|
-
};
|
|
284
|
-
/**
|
|
285
|
-
* Chunk an array into smaller arrays
|
|
286
|
-
* @param array Array to chunk
|
|
287
|
-
* @param size Size of each chunk _(minimum is `1`, maximum is `5000`; defaults to `5000`)_
|
|
288
|
-
* @returns Array of arrays
|
|
289
|
-
*/
|
|
290
|
-
function chunk(array, size) {
|
|
291
|
-
if (!Array.isArray(array)) return [];
|
|
292
|
-
if (array.length === 0) return [];
|
|
293
|
-
const { length } = array;
|
|
294
|
-
const actualSize = typeof size === "number" && size > 0 && size <= MAX_SIZE ? size : MAX_SIZE;
|
|
295
|
-
if (length <= actualSize) return [array];
|
|
296
|
-
const chunks = [];
|
|
297
|
-
let index = 0;
|
|
298
|
-
while (index < length) {
|
|
299
|
-
chunks.push(array.slice(index, index + actualSize));
|
|
300
|
-
index += actualSize;
|
|
301
|
-
}
|
|
302
|
-
return chunks;
|
|
303
|
-
}
|
|
304
|
-
var MAX_SIZE = 5e3;
|
|
305
|
-
function equal(first, second, options) {
|
|
306
|
-
return equalValue(first, second, getEqualOptions(options));
|
|
307
|
-
}
|
|
308
|
-
function equalArray(first, second, options) {
|
|
309
|
-
const { length } = first;
|
|
310
|
-
if (length !== second.length) return false;
|
|
311
|
-
let offset = 0;
|
|
312
|
-
if (length >= ARRAY_THRESHOLD) {
|
|
313
|
-
offset = Math.round(length / ARRAY_PEEK_PERCENTAGE);
|
|
314
|
-
offset = offset > ARRAY_THRESHOLD ? ARRAY_THRESHOLD : offset;
|
|
315
|
-
for (let index = 0; index < offset; index += 1) if (!(equalValue(first[index], second[index], options) && equalValue(first[length - index - 1], second[length - index - 1], options))) return false;
|
|
316
|
-
}
|
|
317
|
-
const firstChunks = chunk(first.slice(offset, length - offset), ARRAY_THRESHOLD);
|
|
318
|
-
const secondChunks = chunk(second.slice(offset, length - offset), ARRAY_THRESHOLD);
|
|
319
|
-
const chunksLength = firstChunks.length;
|
|
320
|
-
for (let chunkIndex = 0; chunkIndex < chunksLength; chunkIndex += 1) {
|
|
321
|
-
const firstChunk = firstChunks[chunkIndex];
|
|
322
|
-
const secondChunk = secondChunks[chunkIndex];
|
|
323
|
-
const chunkLength = firstChunk.length;
|
|
324
|
-
for (let index = 0; index < chunkLength; index += 1) if (!equalValue(firstChunk[index], secondChunk[index], options)) return false;
|
|
325
|
-
}
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
function equalArrayBuffer(first, second, options) {
|
|
329
|
-
return first.byteLength === second.byteLength ? equalArray(new Uint8Array(first), new Uint8Array(second), options) : false;
|
|
330
|
-
}
|
|
331
|
-
function equalDataView(first, second, options) {
|
|
332
|
-
return first.byteOffset === second.byteOffset ? equalArrayBuffer(first.buffer, second.buffer, options) : false;
|
|
333
|
-
}
|
|
334
|
-
function equalMap(first, second, options) {
|
|
335
|
-
const { size } = first;
|
|
336
|
-
if (size !== second.size) return false;
|
|
337
|
-
const firstKeys = [...first.keys()];
|
|
338
|
-
const secondKeys = [...second.keys()];
|
|
339
|
-
if (firstKeys.some((key) => !secondKeys.includes(key))) return false;
|
|
340
|
-
for (let index = 0; index < size; index += 1) {
|
|
341
|
-
const key = firstKeys[index];
|
|
342
|
-
if (!equalValue(first.get(key), second.get(key), options)) return false;
|
|
343
|
-
}
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
function equalPlainObject(first, second, options) {
|
|
347
|
-
let firstKeys = [...Object.keys(first), ...Object.getOwnPropertySymbols(first)];
|
|
348
|
-
let secondKeys = [...Object.keys(second), ...Object.getOwnPropertySymbols(second)];
|
|
349
|
-
if (options.ignoreKeys.enabled || options.ignoreExpressions.enabled) {
|
|
350
|
-
firstKeys = firstKeys.filter((key) => filterKey(key, options));
|
|
351
|
-
secondKeys = secondKeys.filter((key) => filterKey(key, options));
|
|
352
|
-
}
|
|
353
|
-
const { length } = firstKeys;
|
|
354
|
-
if (length !== secondKeys.length || firstKeys.some((key) => !secondKeys.includes(key))) return false;
|
|
355
|
-
for (let index = 0; index < length; index += 1) {
|
|
356
|
-
const key = firstKeys[index];
|
|
357
|
-
if (!equalValue(first[key], second[key], options)) return false;
|
|
358
|
-
}
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
361
|
-
function equalProperties(first, second, properties, options) {
|
|
362
|
-
const { length } = properties;
|
|
363
|
-
for (let index = 0; index < length; index += 1) {
|
|
364
|
-
const property = properties[index];
|
|
365
|
-
if (!equalValue(first[property], second[property], options)) return false;
|
|
366
|
-
}
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
function equalSet(first, second, options) {
|
|
370
|
-
const { size } = first;
|
|
371
|
-
if (size !== second.size) return false;
|
|
372
|
-
const firstValues = [...first];
|
|
373
|
-
const secondValues = [...second];
|
|
374
|
-
for (let index = 0; index < size; index += 1) {
|
|
375
|
-
const firstValue = firstValues[index];
|
|
376
|
-
if (!secondValues.some((secondValue) => equalValue(firstValue, secondValue, options))) return false;
|
|
377
|
-
}
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
|
-
function equalTypedArray(first, second) {
|
|
381
|
-
if (first.constructor !== second.constructor) return false;
|
|
382
|
-
if (first.byteLength !== second.byteLength) return false;
|
|
383
|
-
const { length } = first;
|
|
384
|
-
for (let index = 0; index < length; index += 1) if (first[index] !== second[index]) return false;
|
|
385
|
-
return true;
|
|
386
|
-
}
|
|
387
|
-
function equalValue(first, second, options) {
|
|
388
|
-
if (options.relaxedNullish && first == null && second == null) return true;
|
|
389
|
-
switch (true) {
|
|
390
|
-
case Object.is(first, second): return true;
|
|
391
|
-
case first == null || second == null: return first === second;
|
|
392
|
-
case typeof first !== typeof second: return false;
|
|
393
|
-
case typeof first === "string" && options.ignoreCase === true: return Object.is(first.toLocaleLowerCase(), second.toLocaleLowerCase());
|
|
394
|
-
case first instanceof ArrayBuffer && second instanceof ArrayBuffer: return equalArrayBuffer(first, second, options);
|
|
395
|
-
case first instanceof Date && second instanceof Date: return Object.is(Number(first), Number(second));
|
|
396
|
-
case first instanceof DataView && second instanceof DataView: return equalDataView(first, second, options);
|
|
397
|
-
case first instanceof Error && second instanceof Error: return equalProperties(first, second, ["name", "message"], options);
|
|
398
|
-
case first instanceof Map && second instanceof Map: return equalMap(first, second, options);
|
|
399
|
-
case first instanceof RegExp && second instanceof RegExp: return equalProperties(first, second, ["source", "flags"], options);
|
|
400
|
-
case first instanceof Set && second instanceof Set: return equalSet(first, second, options);
|
|
401
|
-
case Array.isArray(first) && Array.isArray(second): return equalArray(first, second, options);
|
|
402
|
-
case isPlainObject(first) && isPlainObject(second): return equalPlainObject(first, second, options);
|
|
403
|
-
case isTypedArray(first) && isTypedArray(second): return equalTypedArray(first, second);
|
|
404
|
-
default: return equal.handlers.handle(first, second, options);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
equal.handlers = getCompareHandlers(equal, { callback: Object.is });
|
|
408
|
-
/**
|
|
409
|
-
* Create an equalizer with predefined options
|
|
410
|
-
* @param options Comparison options
|
|
411
|
-
* @returns Equalizer function
|
|
412
|
-
*/
|
|
413
|
-
equal.initialize = function(options) {
|
|
414
|
-
const actual = getEqualOptions(options);
|
|
415
|
-
const equalizer = (first, second) => equalValue(first, second, actual);
|
|
416
|
-
equalizer.register = equal.register;
|
|
417
|
-
equalizer.unregister = equal.unregister;
|
|
418
|
-
return equalizer;
|
|
419
|
-
};
|
|
420
|
-
/**
|
|
421
|
-
* Register a equality comparison function for a specific class
|
|
422
|
-
* @param constructor Class constructor
|
|
423
|
-
* @param fn Comparison function
|
|
424
|
-
*/
|
|
425
|
-
equal.register = function(constructor, fn) {
|
|
426
|
-
equal.handlers.register(constructor, fn);
|
|
427
|
-
};
|
|
428
|
-
/**
|
|
429
|
-
* Unregister a equality comparison handler for a specific class
|
|
430
|
-
* @param constructor Class constructor
|
|
431
|
-
*/
|
|
432
|
-
equal.unregister = equal.handlers.unregister;
|
|
433
|
-
function filterKey(key, options) {
|
|
434
|
-
if (typeof key !== "string") return true;
|
|
435
|
-
if (options.ignoreExpressions.enabled && options.ignoreExpressions.values.some((expression) => expression.test(key))) return false;
|
|
436
|
-
if (options.ignoreKeys.enabled && options.ignoreKeys.values.has(key)) return false;
|
|
437
|
-
return true;
|
|
438
|
-
}
|
|
439
|
-
function getEqualOptions(input) {
|
|
440
|
-
const options = {
|
|
441
|
-
ignoreCase: false,
|
|
442
|
-
ignoreExpressions: {
|
|
443
|
-
enabled: false,
|
|
444
|
-
values: []
|
|
445
|
-
},
|
|
446
|
-
ignoreKeys: {
|
|
447
|
-
enabled: false,
|
|
448
|
-
values: /* @__PURE__ */ new Set()
|
|
449
|
-
},
|
|
450
|
-
relaxedNullish: false
|
|
451
|
-
};
|
|
452
|
-
if (typeof input === "boolean") {
|
|
453
|
-
options.ignoreCase = input;
|
|
454
|
-
return options;
|
|
455
|
-
}
|
|
456
|
-
if (!isPlainObject(input)) return options;
|
|
457
|
-
options.ignoreCase = typeof input.ignoreCase === "boolean" ? input.ignoreCase : false;
|
|
458
|
-
options.ignoreExpressions.values = (Array.isArray(input.ignoreKeys) ? input.ignoreKeys : [input.ignoreKeys]).filter((key) => key instanceof RegExp);
|
|
459
|
-
options.ignoreKeys.values = new Set((Array.isArray(input.ignoreKeys) ? input.ignoreKeys : [input.ignoreKeys]).filter((key) => typeof key === "string"));
|
|
460
|
-
options.ignoreExpressions.enabled = options.ignoreExpressions.values.length > 0;
|
|
461
|
-
options.ignoreKeys.enabled = options.ignoreKeys.values.size > 0;
|
|
462
|
-
options.relaxedNullish = input.relaxedNullish === true;
|
|
463
|
-
return options;
|
|
464
|
-
}
|
|
465
|
-
var ARRAY_PEEK_PERCENTAGE = 10;
|
|
466
|
-
var ARRAY_THRESHOLD = 100;
|
|
467
|
-
function clone(value) {
|
|
468
|
-
return cloneValue(value, 0, /* @__PURE__ */ new WeakMap());
|
|
469
|
-
}
|
|
470
|
-
clone.handlers = getSelfHandlers(clone, {
|
|
471
|
-
callback: tryStructuredClone,
|
|
472
|
-
method: "clone"
|
|
473
|
-
});
|
|
474
|
-
/**
|
|
475
|
-
* Register a clone handler for a specific class
|
|
476
|
-
* @param constructor Class constructor
|
|
477
|
-
* @param handler Method name or clone function _(defaults to `clone`)_
|
|
478
|
-
*/
|
|
479
|
-
clone.register = function(constructor, handler) {
|
|
480
|
-
clone.handlers.register(constructor, handler);
|
|
481
|
-
};
|
|
482
|
-
/**
|
|
483
|
-
* Unregister a clone handler for a specific class
|
|
484
|
-
* @param constructor Class constructor
|
|
485
|
-
*/
|
|
486
|
-
clone.unregister = clone.handlers.unregister;
|
|
487
|
-
function cloneArrayBuffer(value, depth, references) {
|
|
488
|
-
if (typeof depth === "number" && depth >= MAX_CLONE_DEPTH) return value;
|
|
489
|
-
const cloned = new ArrayBuffer(value.byteLength);
|
|
490
|
-
new Uint8Array(cloned).set(new Uint8Array(value));
|
|
491
|
-
references?.set(value, cloned);
|
|
492
|
-
return cloned;
|
|
493
|
-
}
|
|
494
|
-
function cloneDataView(value, depth, references) {
|
|
495
|
-
if (depth >= MAX_CLONE_DEPTH) return value;
|
|
496
|
-
const buffer = cloneArrayBuffer(value.buffer);
|
|
497
|
-
const cloned = new DataView(buffer, value.byteOffset, value.byteLength);
|
|
498
|
-
references.set(value, cloned);
|
|
499
|
-
return cloned;
|
|
500
|
-
}
|
|
501
|
-
function cloneMapOrSet(value, depth, references) {
|
|
502
|
-
if (depth >= MAX_CLONE_DEPTH) return value;
|
|
503
|
-
const isMap = value instanceof Map;
|
|
504
|
-
const cloned = isMap ? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Set();
|
|
505
|
-
const entries = [...value.entries()];
|
|
506
|
-
const { length } = entries;
|
|
507
|
-
for (let index = 0; index < length; index += 1) {
|
|
508
|
-
const entry = entries[index];
|
|
509
|
-
if (isMap) cloned.set(cloneValue(entry[0], depth + 1, references), cloneValue(entry[1], depth + 1, references));
|
|
510
|
-
else cloned.add(cloneValue(entry[0], depth + 1, references));
|
|
511
|
-
}
|
|
512
|
-
references.set(value, cloned);
|
|
513
|
-
return cloned;
|
|
514
|
-
}
|
|
515
|
-
function cloneNode(node, depth, references) {
|
|
516
|
-
if (depth >= MAX_CLONE_DEPTH) return node;
|
|
517
|
-
const cloned = node.cloneNode(true);
|
|
518
|
-
references.set(node, cloned);
|
|
519
|
-
return cloned;
|
|
520
|
-
}
|
|
521
|
-
function clonePlainObject(value, depth, references) {
|
|
522
|
-
if (depth >= MAX_CLONE_DEPTH) return Array.isArray(value) ? [...value] : { ...value };
|
|
523
|
-
const cloned = Array.isArray(value) ? [] : {};
|
|
524
|
-
const keys = [...Object.keys(value), ...Object.getOwnPropertySymbols(value)];
|
|
525
|
-
const { length } = keys;
|
|
526
|
-
for (let index = 0; index < length; index += 1) {
|
|
527
|
-
const key = keys[index];
|
|
528
|
-
cloned[key] = cloneValue(value[key], depth + 1, references);
|
|
529
|
-
}
|
|
530
|
-
references.set(value, cloned);
|
|
531
|
-
return cloned;
|
|
532
|
-
}
|
|
533
|
-
function cloneRegularExpression(value, depth, references) {
|
|
534
|
-
if (depth >= MAX_CLONE_DEPTH) return value;
|
|
535
|
-
const cloned = new RegExp(value.source, value.flags);
|
|
536
|
-
cloned.lastIndex = value.lastIndex;
|
|
537
|
-
references.set(value, cloned);
|
|
538
|
-
return cloned;
|
|
539
|
-
}
|
|
540
|
-
function cloneTypedArray(value, depth, references) {
|
|
541
|
-
if (depth >= MAX_CLONE_DEPTH) return value;
|
|
542
|
-
const cloned = new value.constructor(value);
|
|
543
|
-
references.set(value, cloned);
|
|
544
|
-
return cloned;
|
|
545
|
-
}
|
|
546
|
-
function cloneValue(value, depth, references) {
|
|
547
|
-
switch (true) {
|
|
548
|
-
case value == null: return value;
|
|
549
|
-
case typeof value === "bigint": return BigInt(value);
|
|
550
|
-
case typeof value === "boolean": return Boolean(value);
|
|
551
|
-
case typeof value === "function": return;
|
|
552
|
-
case typeof value === "number": return Number(value);
|
|
553
|
-
case typeof value === "string": return String(value);
|
|
554
|
-
case typeof value === "symbol": return Symbol(value.description);
|
|
555
|
-
case references.has(value): return references.get(value);
|
|
556
|
-
case value instanceof ArrayBuffer: return cloneArrayBuffer(value, depth, references);
|
|
557
|
-
case value instanceof DataView: return cloneDataView(value, depth, references);
|
|
558
|
-
case value instanceof Date: return new Date(value.getTime());
|
|
559
|
-
case value instanceof RegExp: return cloneRegularExpression(value, depth, references);
|
|
560
|
-
case value instanceof Map:
|
|
561
|
-
case value instanceof Set: return cloneMapOrSet(value, depth, references);
|
|
562
|
-
case value instanceof Node: return cloneNode(value, depth, references);
|
|
563
|
-
case isArrayOrPlainObject(value): return clonePlainObject(value, depth, references);
|
|
564
|
-
case isTypedArray(value): return cloneTypedArray(value, depth, references);
|
|
565
|
-
default: return clone.handlers.handle(value, depth, references);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
function tryStructuredClone(value, depth, references) {
|
|
569
|
-
if (depth >= MAX_CLONE_DEPTH) return value;
|
|
570
|
-
try {
|
|
571
|
-
const cloned = structuredClone(value);
|
|
572
|
-
references.set(value, cloned);
|
|
573
|
-
return cloned;
|
|
574
|
-
} catch {
|
|
575
|
-
references.set(value, value);
|
|
576
|
-
return value;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
var MAX_CLONE_DEPTH = 100;
|
|
580
|
-
function getMergeOptions(options) {
|
|
581
|
-
const actual = {
|
|
582
|
-
replaceableObjects: void 0,
|
|
583
|
-
skipNullableInArrays: false
|
|
61
|
+
const EXPRESSION_HAS_NUMBER = /\d+/;
|
|
62
|
+
const EXPRESSION_INDEX = /\.\d+$/;
|
|
63
|
+
const EXPRESSION_PROPERTY = /\.\$(required|type|validators)(\.|$)/;
|
|
64
|
+
const PROPERTY_REQUIRED = "$required";
|
|
65
|
+
const PROPERTY_TYPE = "$type";
|
|
66
|
+
const PROPERTY_VALIDATORS = "$validators";
|
|
67
|
+
const SCHEMATIC_NAME = "$schematic";
|
|
68
|
+
const TYPE_OBJECT = "object";
|
|
69
|
+
const TYPE_UNDEFINED = "undefined";
|
|
70
|
+
const TYPE_ALL = new Set([
|
|
71
|
+
"array",
|
|
72
|
+
"bigint",
|
|
73
|
+
"boolean",
|
|
74
|
+
"date",
|
|
75
|
+
"function",
|
|
76
|
+
"null",
|
|
77
|
+
"number",
|
|
78
|
+
"string",
|
|
79
|
+
"symbol",
|
|
80
|
+
TYPE_OBJECT,
|
|
81
|
+
TYPE_UNDEFINED
|
|
82
|
+
]);
|
|
83
|
+
function isInstance(constructor) {
|
|
84
|
+
if (!isConstructor(constructor)) throw new TypeError("Expected a constructor function");
|
|
85
|
+
return (value) => {
|
|
86
|
+
return value instanceof constructor;
|
|
584
87
|
};
|
|
585
|
-
if (typeof options !== "object" || options == null) return actual;
|
|
586
|
-
actual.replaceableObjects = getReplaceableObjects(options.replaceableObjects);
|
|
587
|
-
actual.skipNullableInArrays = options.skipNullableInArrays === true;
|
|
588
|
-
return actual;
|
|
589
|
-
}
|
|
590
|
-
function getReplaceableObjects(value) {
|
|
591
|
-
const items = (Array.isArray(value) ? value : [value]).filter((item) => typeof item === "string" || item instanceof RegExp);
|
|
592
|
-
if (items.length > 0) return (name) => items.some((item) => typeof item === "string" ? item === name : item.test(name));
|
|
593
|
-
}
|
|
594
|
-
function handleMerge(values, options) {
|
|
595
|
-
return !Array.isArray(values) || values.length === 0 ? {} : mergeValues(values, options, true);
|
|
596
88
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
* @param values Values to merge
|
|
600
|
-
* @param options Merging options
|
|
601
|
-
* @returns Merged value
|
|
602
|
-
*/
|
|
603
|
-
function merge(values, options) {
|
|
604
|
-
return handleMerge(values, getMergeOptions(options));
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Create a merger with predefined options
|
|
608
|
-
* @param options Merging options
|
|
609
|
-
* @returns Merger function
|
|
610
|
-
*/
|
|
611
|
-
merge.initialize = function(options) {
|
|
612
|
-
const actual = getMergeOptions(options);
|
|
613
|
-
return (values) => handleMerge(values, actual);
|
|
614
|
-
};
|
|
615
|
-
function mergeObjects(values, options, prefix) {
|
|
616
|
-
const { length } = values;
|
|
617
|
-
const isArray = values.every(Array.isArray);
|
|
618
|
-
const merged = isArray ? [] : {};
|
|
619
|
-
for (let outerIndex = 0; outerIndex < length; outerIndex += 1) {
|
|
620
|
-
const item = values[outerIndex];
|
|
621
|
-
const keys = Object.keys(item);
|
|
622
|
-
const size = keys.length;
|
|
623
|
-
for (let innerIndex = 0; innerIndex < size; innerIndex += 1) {
|
|
624
|
-
const key = keys[innerIndex];
|
|
625
|
-
const full = join([prefix, key], ".");
|
|
626
|
-
const next = item[key];
|
|
627
|
-
const previous = merged[key];
|
|
628
|
-
if (isArray && options.skipNullableInArrays && next == null) continue;
|
|
629
|
-
if (isArrayOrPlainObject(next) && isArrayOrPlainObject(previous) && !(options.replaceableObjects?.(full) ?? false)) merged[key] = mergeValues([previous, next], options, false, full);
|
|
630
|
-
else merged[key] = next;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
return merged;
|
|
634
|
-
}
|
|
635
|
-
function mergeValues(values, options, validate, prefix) {
|
|
636
|
-
const actual = validate ? values.filter(isArrayOrPlainObject) : values;
|
|
637
|
-
return actual.length > 1 ? mergeObjects(actual, options, prefix) : actual[0] ?? {};
|
|
89
|
+
function isSchematic(value) {
|
|
90
|
+
return typeof value === "object" && value !== null && SCHEMATIC_NAME in value && value[SCHEMATIC_NAME] === true;
|
|
638
91
|
}
|
|
639
92
|
function flattenObject(value, depth, smushed, prefix) {
|
|
640
93
|
if (depth >= MAX_DEPTH) return {};
|
|
@@ -669,7 +122,7 @@ function smush(value) {
|
|
|
669
122
|
return typeof value === "object" && value !== null ? flattenObject(value, 0, /* @__PURE__ */ new WeakMap()) : {};
|
|
670
123
|
}
|
|
671
124
|
var MAX_DEPTH = 100;
|
|
672
|
-
function addPropertyType(to, key, values, required) {
|
|
125
|
+
function addPropertyType(to, key, values, validators, required) {
|
|
673
126
|
if (to.keys.set.has(key)) {
|
|
674
127
|
const property = to.properties[key];
|
|
675
128
|
for (const type of values) if (!property.types.includes(type)) property.types.push(type);
|
|
@@ -678,10 +131,12 @@ function addPropertyType(to, key, values, required) {
|
|
|
678
131
|
to.keys.set.add(key);
|
|
679
132
|
to.properties[key] = {
|
|
680
133
|
required,
|
|
681
|
-
types: values
|
|
134
|
+
types: values,
|
|
135
|
+
validators: {}
|
|
682
136
|
};
|
|
683
137
|
}
|
|
684
138
|
if (!required && !to.properties[key].types.includes(TYPE_UNDEFINED)) to.properties[key].types.push(TYPE_UNDEFINED);
|
|
139
|
+
to.properties[key].validators = validators;
|
|
685
140
|
}
|
|
686
141
|
function getSchema(schema) {
|
|
687
142
|
const validated = {
|
|
@@ -706,7 +161,7 @@ function getTypes(value, validated, prefix) {
|
|
|
706
161
|
continue;
|
|
707
162
|
}
|
|
708
163
|
if (typeOfType === "function") {
|
|
709
|
-
propertyTypes.push(isConstructor
|
|
164
|
+
propertyTypes.push(isConstructor(type) ? isInstance(type) : type);
|
|
710
165
|
continue;
|
|
711
166
|
}
|
|
712
167
|
if (typeOfType !== "object" || type === null) continue;
|
|
@@ -714,7 +169,7 @@ function getTypes(value, validated, prefix) {
|
|
|
714
169
|
propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
|
|
715
170
|
continue;
|
|
716
171
|
}
|
|
717
|
-
addPropertyType(validated, prefix, [TYPE_OBJECT], type[PROPERTY_REQUIRED] !== false);
|
|
172
|
+
addPropertyType(validated, prefix, [TYPE_OBJECT], {}, type[PROPERTY_REQUIRED] !== false);
|
|
718
173
|
propertyTypes.push(TYPE_OBJECT);
|
|
719
174
|
getValidatedSchema(type, validated, prefix);
|
|
720
175
|
}
|
|
@@ -734,18 +189,30 @@ function getValidatedSchema(schema, validated, prefix) {
|
|
|
734
189
|
if (EXPRESSION_PROPERTY.test(key)) continue;
|
|
735
190
|
if (EXPRESSION_HAS_NUMBER.test(key) && arrayKeys.has(key.replace(EXPRESSION_INDEX, ""))) continue;
|
|
736
191
|
let required = true;
|
|
737
|
-
|
|
192
|
+
let validators = {};
|
|
193
|
+
const isObject = typeof value === "object" && value !== null;
|
|
194
|
+
if (isObject && PROPERTY_REQUIRED in value) required = typeof value[PROPERTY_REQUIRED] === "boolean" ? value[PROPERTY_REQUIRED] : true;
|
|
195
|
+
if (isObject && PROPERTY_VALIDATORS in value) validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
738
196
|
const prefixedKey = `${prefix}${key}`;
|
|
739
197
|
const types = getTypes(value, validated, prefixedKey);
|
|
740
|
-
if (types.length > 0) addPropertyType(validated, prefixedKey, types, required);
|
|
198
|
+
if (types.length > 0) addPropertyType(validated, prefixedKey, types, validators, required);
|
|
741
199
|
}
|
|
742
200
|
if (noPrefix) validated.keys.array.sort();
|
|
743
201
|
return validated;
|
|
744
202
|
}
|
|
745
|
-
function
|
|
203
|
+
function getValidators(original) {
|
|
204
|
+
const validators = {};
|
|
205
|
+
if (typeof original !== "object" || original === null) return validators;
|
|
206
|
+
for (const type of TYPE_ALL) {
|
|
207
|
+
const value = original[type];
|
|
208
|
+
validators[type] = (Array.isArray(value) ? value : [value]).filter((validator) => typeof validator === "function");
|
|
209
|
+
}
|
|
210
|
+
return validators;
|
|
211
|
+
}
|
|
212
|
+
function validateType(type, property, value) {
|
|
746
213
|
switch (true) {
|
|
747
214
|
case typeof type === "function": return type(value);
|
|
748
|
-
case typeof type === "string": return validators[type](value);
|
|
215
|
+
case typeof type === "string": return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
|
|
749
216
|
default: return type.is(value);
|
|
750
217
|
}
|
|
751
218
|
}
|
|
@@ -764,12 +231,12 @@ function validateValue(validated, obj) {
|
|
|
764
231
|
if (value === void 0 && property.required && !property.types.includes(TYPE_UNDEFINED)) return false;
|
|
765
232
|
const typesLength = property.types.length;
|
|
766
233
|
if (typesLength === 1) {
|
|
767
|
-
if (!validateType(property.types[0], value)) return false;
|
|
234
|
+
if (!validateType(property.types[0], property, value)) return false;
|
|
768
235
|
continue;
|
|
769
236
|
}
|
|
770
237
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
771
238
|
const type = property.types[typeIndex];
|
|
772
|
-
if (validateType(type, value)) {
|
|
239
|
+
if (validateType(type, property, value)) {
|
|
773
240
|
if (type !== "object") ignore.add(key);
|
|
774
241
|
continue outer;
|
|
775
242
|
}
|