@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.
@@ -1,77 +1,30 @@
1
- const EXPRESSION_HAS_NUMBER = /\d+/;
2
- const EXPRESSION_INDEX = /\.\d+$/;
3
- const EXPRESSION_PROPERTY = /\.\$(required|type)(\.|$)/;
4
- const PROPERTY_REQUIRED = "$required";
5
- const PROPERTY_TYPE = "$type";
6
- const SCHEMATIC_NAME = "$schematic";
7
- const TYPE_OBJECT = "object";
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
- function calculateSum(current, value, notNumber) {
63
- return notNumber ? value : current + value;
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
- function getAggregated(type, array, key) {
66
- const aggregated = aggregate(type, array, key);
67
- return aggregated.count > 0 ? aggregated.value : NaN;
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
- * Split a string into words _(and other readable parts)_
110
- * @param value Original string
111
- * @returns Array of words found in the string
112
- */
113
- function words(value) {
114
- return typeof value === "string" ? value.match(EXPRESSION_WORDS) ?? [] : [];
115
- }
116
- var EXPRESSION_WORDS = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
117
- /**
118
- * Is the value an array or a record?
119
- * @param value Value to check
120
- * @returns `true` if the value is an array or a record, otherwise `false`
121
- */
122
- function isArrayOrPlainObject(value) {
123
- return Array.isArray(value) || isPlainObject(value);
124
- }
125
- /**
126
- * Is the value a constructor?
127
- * @param value Value to check
128
- * @returns `true` if the value is a constructor, otherwise `false`
129
- */
130
- function isConstructor(value) {
131
- return typeof value === "function" && EXPRESSION_CONSTRUCTOR.test(value.name);
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
- * Merge multiple arrays or objects into a single one
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$1(type) ? isInstance(type) : type);
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
- if (typeof value === "object" && value !== null && PROPERTY_REQUIRED in value) required = typeof value[PROPERTY_REQUIRED] === "boolean" ? value[PROPERTY_REQUIRED] : true;
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 validateType(type, value) {
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
  }