@ls-stack/utils 3.48.0 → 3.49.1

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.
@@ -26,72 +26,13 @@ __export(partialEqual_exports, {
26
26
  module.exports = __toCommonJS(partialEqual_exports);
27
27
  var import_t_result = require("t-result");
28
28
 
29
- // src/deepEqual.ts
30
- var has = Object.prototype.hasOwnProperty;
31
- function find(iter, tar, maxDepth) {
32
- for (const key of iter.keys()) {
33
- if (deepEqual(key, tar, maxDepth)) return key;
34
- }
35
- }
36
- function deepEqual(foo, bar, maxDepth = 20) {
37
- let ctor, len, tmp;
38
- if (foo === bar) return true;
39
- if (maxDepth && maxDepth <= 0) return false;
40
- if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
41
- if (ctor === Date)
42
- return deepEqual(foo.getTime(), bar.getTime(), maxDepth - 1);
43
- if (ctor === RegExp) return foo.toString() === bar.toString();
44
- if (ctor === Array) {
45
- if ((len = foo.length) === bar.length) {
46
- while (len-- && deepEqual(foo[len], bar[len], maxDepth - 1)) ;
47
- }
48
- return len === -1;
49
- }
50
- if (ctor === Set) {
51
- if (foo.size !== bar.size) {
52
- return false;
53
- }
54
- for (len of foo) {
55
- tmp = len;
56
- if (tmp && typeof tmp === "object") {
57
- tmp = find(bar, tmp, maxDepth - 1);
58
- if (!tmp) return false;
59
- }
60
- if (!bar.has(tmp)) return false;
61
- }
62
- return true;
63
- }
64
- if (ctor === Map) {
65
- if (foo.size !== bar.size) {
66
- return false;
67
- }
68
- for (len of foo) {
69
- tmp = len[0];
70
- if (tmp && typeof tmp === "object") {
71
- tmp = find(bar, tmp, maxDepth - 1);
72
- if (!tmp) return false;
73
- }
74
- if (!deepEqual(len[1], bar.get(tmp), maxDepth - 1)) {
75
- return false;
76
- }
77
- }
78
- return true;
79
- }
80
- if (!ctor || typeof foo === "object") {
81
- len = 0;
82
- for (ctor in foo) {
83
- if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
84
- if (!(ctor in bar) || !deepEqual(foo[ctor], bar[ctor], maxDepth - 1))
85
- return false;
86
- }
87
- return Object.keys(bar).length === len;
88
- }
89
- }
90
- return foo !== foo && bar !== bar;
29
+ // src/assertions.ts
30
+ function exhaustiveCheck(narrowedType) {
31
+ return new Error("This should never happen");
91
32
  }
92
33
 
93
34
  // src/partialEqual.ts
94
- var has2 = Object.prototype.hasOwnProperty;
35
+ var has = Object.prototype.hasOwnProperty;
95
36
  function createComparison(type) {
96
37
  return { "~sc": type };
97
38
  }
@@ -129,8 +70,24 @@ var match = {
129
70
  partialEqual: (value) => createComparison(["partialEqual", value]),
130
71
  custom: (isEqual) => createComparison(["custom", isEqual]),
131
72
  keyNotBePresent: createComparison(["keyNotBePresent", null]),
132
- any: (...comparisons) => createComparison(["any", comparisons.map((c) => c["~sc"])]),
133
- all: (...comparisons) => createComparison(["all", comparisons.map((c) => c["~sc"])]),
73
+ any: (...values) => createComparison([
74
+ "any",
75
+ values.map((v) => {
76
+ if (isComparison(v)) return v["~sc"];
77
+ if (typeof v === "object" && v !== null)
78
+ return ["partialEqual", v];
79
+ return ["deepEqual", v];
80
+ })
81
+ ]),
82
+ all: (...values) => createComparison([
83
+ "all",
84
+ values.map((v) => {
85
+ if (isComparison(v)) return v["~sc"];
86
+ if (typeof v === "object" && v !== null)
87
+ return ["partialEqual", v];
88
+ return ["deepEqual", v];
89
+ })
90
+ ]),
134
91
  not: {
135
92
  hasType: {
136
93
  string: createComparison(["not", ["hasType", "string"]]),
@@ -161,741 +118,640 @@ var match = {
161
118
  equal: (value) => createComparison(["not", ["deepEqual", value]]),
162
119
  partialEqual: (value) => createComparison(["not", ["partialEqual", value]]),
163
120
  custom: (value) => createComparison(["not", ["custom", value]]),
164
- any: (...comparisons) => createComparison(["not", ["any", comparisons.map((c) => c["~sc"])]]),
165
- all: (...comparisons) => createComparison(["not", ["all", comparisons.map((c) => c["~sc"])]]),
121
+ any: (...values) => createComparison([
122
+ "not",
123
+ [
124
+ "any",
125
+ values.map((v) => {
126
+ if (isComparison(v)) return v["~sc"];
127
+ if (typeof v === "object" && v !== null)
128
+ return ["partialEqual", v];
129
+ return ["deepEqual", v];
130
+ })
131
+ ]
132
+ ]),
133
+ all: (...values) => createComparison([
134
+ "not",
135
+ [
136
+ "all",
137
+ values.map((v) => {
138
+ if (isComparison(v)) return v["~sc"];
139
+ if (typeof v === "object" && v !== null)
140
+ return ["partialEqual", v];
141
+ return ["deepEqual", v];
142
+ })
143
+ ]
144
+ ]),
166
145
  noExtraKeys: (partialShape) => createComparison(["not", ["withNoExtraKeys", partialShape]]),
167
146
  deepNoExtraKeys: (partialShape) => createComparison(["not", ["withDeepNoExtraKeys", partialShape]]),
168
147
  noExtraDefinedKeys: (partialShape) => createComparison(["not", ["noExtraDefinedKeys", partialShape]]),
169
148
  deepNoExtraDefinedKeys: (partialShape) => createComparison(["not", ["deepNoExtraDefinedKeys", partialShape]])
170
149
  }
171
150
  };
172
- function find2(iter, tar) {
173
- for (const key of iter.keys()) {
174
- if (partialEqual(key, tar)) return key;
175
- }
151
+ function isComparison(value) {
152
+ return value && typeof value === "object" && "~sc" in value;
176
153
  }
177
- function executeComparisonWithKeyContext(target, comp, keyExists) {
178
- const [type, value] = comp;
179
- if (type === "keyNotBePresent") {
180
- return !keyExists;
181
- }
182
- if (type === "any") {
183
- for (const childComp of value) {
184
- if (executeComparisonWithKeyContext(target, childComp, keyExists)) {
185
- return true;
186
- }
187
- }
188
- return false;
189
- }
190
- if (type === "not") {
191
- return !executeComparisonWithKeyContext(target, value, keyExists);
192
- }
193
- return executeComparison(target, comp);
194
- }
195
- function executeComparison(target, comparison) {
154
+ function executeComparison(target, comparison, context) {
196
155
  const [type, value] = comparison;
197
156
  switch (type) {
198
- case "hasType":
199
- switch (value) {
200
- case "string":
201
- return typeof target === "string";
202
- case "number":
203
- return typeof target === "number";
204
- case "boolean":
205
- return typeof target === "boolean";
206
- case "function":
207
- return typeof target === "function";
208
- case "array":
209
- return Array.isArray(target);
210
- case "object":
211
- return typeof target === "object" && target !== null && !Array.isArray(target);
212
- default:
213
- return false;
214
- }
215
- case "isInstanceOf":
216
- return target instanceof value;
217
157
  case "strStartsWith":
218
- return typeof target === "string" && target.startsWith(value);
219
- case "strEndsWith":
220
- return typeof target === "string" && target.endsWith(value);
221
- case "strContains":
222
- return typeof target === "string" && target.includes(value);
223
- case "strMatchesRegex":
224
- return typeof target === "string" && value.test(target);
225
- case "numIsGreaterThan":
226
- return typeof target === "number" && target > value;
227
- case "numIsGreaterThanOrEqual":
228
- return typeof target === "number" && target >= value;
229
- case "numIsLessThan":
230
- return typeof target === "number" && target < value;
231
- case "numIsLessThanOrEqual":
232
- return typeof target === "number" && target <= value;
233
- case "numIsInRange":
234
- return typeof target === "number" && target >= value[0] && target <= value[1];
235
- case "jsonStringHasPartial":
236
- if (typeof target !== "string") return false;
237
- try {
238
- const parsed = JSON.parse(target);
239
- return partialEqual(parsed, value);
240
- } catch {
241
- return false;
242
- }
243
- case "deepEqual":
244
- return deepEqual(target, value);
245
- case "partialEqual":
246
- return partialEqual(target, value);
247
- case "custom":
248
- return value(target);
249
- case "keyNotBePresent":
250
- return false;
251
- case "any":
252
- for (const comp of value) {
253
- if (executeComparison(target, comp)) {
254
- return true;
255
- }
256
- }
257
- return false;
258
- case "all":
259
- for (const comp of value) {
260
- if (!executeComparison(target, comp)) {
261
- return false;
262
- }
263
- }
264
- return true;
265
- case "not":
266
- return !executeComparison(target, value);
267
- case "withNoExtraKeys":
268
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
158
+ if (typeof target !== "string") {
159
+ addError(context, {
160
+ message: `Expected string starting with "${value}"`,
161
+ received: target
162
+ });
269
163
  return false;
270
164
  }
271
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
165
+ if (!target.startsWith(value)) {
166
+ addError(context, {
167
+ message: `Expected string starting with "${value}"`,
168
+ received: target
169
+ });
272
170
  return false;
273
171
  }
274
- for (const key in target) {
275
- if (has2.call(target, key) && !has2.call(value, key)) {
276
- return false;
277
- }
278
- }
279
- for (const key in value) {
280
- if (has2.call(value, key)) {
281
- if (!has2.call(target, key)) {
282
- return false;
283
- }
284
- if (!partialEqual(target[key], value[key])) {
285
- return false;
286
- }
287
- }
288
- }
289
172
  return true;
290
- case "withDeepNoExtraKeys":
291
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
173
+ case "strEndsWith":
174
+ if (typeof target !== "string") {
175
+ addError(context, {
176
+ message: `Expected string ending with "${value}"`,
177
+ received: target
178
+ });
292
179
  return false;
293
180
  }
294
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
181
+ if (!target.endsWith(value)) {
182
+ addError(context, {
183
+ message: `Expected string ending with "${value}"`,
184
+ received: target
185
+ });
295
186
  return false;
296
187
  }
297
- for (const key in target) {
298
- if (has2.call(target, key) && !has2.call(value, key)) {
299
- return false;
300
- }
301
- }
302
- for (const key in value) {
303
- if (has2.call(value, key)) {
304
- if (!has2.call(target, key)) {
305
- return false;
306
- }
307
- const targetValue = target[key];
308
- const partialValue = value[key];
309
- if (partialValue && typeof partialValue === "object" && "~sc" in partialValue && partialValue["~sc"][0] === "withDeepNoExtraKeys") {
310
- if (!executeComparison(targetValue, partialValue["~sc"])) {
311
- return false;
312
- }
313
- } else if (partialValue && typeof partialValue === "object" && !Array.isArray(partialValue) && partialValue.constructor === Object && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) && targetValue.constructor === Object) {
314
- if (!executeComparison(targetValue, [
315
- "withDeepNoExtraKeys",
316
- partialValue
317
- ])) {
318
- return false;
319
- }
320
- } else {
321
- if (!partialEqual(targetValue, partialValue)) {
322
- return false;
323
- }
324
- }
325
- }
326
- }
327
188
  return true;
328
- case "noExtraDefinedKeys":
329
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
189
+ case "strContains":
190
+ if (typeof target !== "string") {
191
+ addError(context, {
192
+ message: `Expected string containing "${value}"`,
193
+ received: target
194
+ });
330
195
  return false;
331
196
  }
332
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
197
+ if (!target.includes(value)) {
198
+ addError(context, {
199
+ message: `Expected string containing "${value}"`,
200
+ received: target
201
+ });
333
202
  return false;
334
203
  }
335
- for (const key in target) {
336
- if (has2.call(target, key) && target[key] !== void 0 && !has2.call(value, key)) {
337
- return false;
338
- }
339
- }
340
- for (const key in value) {
341
- if (has2.call(value, key)) {
342
- if (!has2.call(target, key)) {
343
- return false;
344
- }
345
- if (!partialEqual(target[key], value[key])) {
346
- return false;
347
- }
348
- }
349
- }
350
204
  return true;
351
- case "deepNoExtraDefinedKeys":
352
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
205
+ case "strMatchesRegex":
206
+ if (typeof target !== "string") {
207
+ addError(context, {
208
+ message: `Expected string matching regex ${value}`,
209
+ received: target
210
+ });
353
211
  return false;
354
212
  }
355
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
213
+ if (!value.test(target)) {
214
+ addError(context, {
215
+ message: `Expected string matching regex ${value}`,
216
+ received: target
217
+ });
356
218
  return false;
357
219
  }
358
- for (const key in target) {
359
- if (has2.call(target, key) && target[key] !== void 0 && !has2.call(value, key)) {
360
- return false;
220
+ return true;
221
+ case "hasType": {
222
+ let actualType;
223
+ if (value === "array") {
224
+ actualType = Array.isArray(target) ? "array" : typeof target;
225
+ } else if (value === "object") {
226
+ if (target === null || Array.isArray(target)) {
227
+ actualType = "not-object";
228
+ } else {
229
+ actualType = typeof target;
361
230
  }
231
+ } else {
232
+ actualType = typeof target;
362
233
  }
363
- for (const key in value) {
364
- if (has2.call(value, key)) {
365
- if (!has2.call(target, key)) {
366
- return false;
367
- }
368
- const targetValue = target[key];
369
- const partialValue = value[key];
370
- if (partialValue && typeof partialValue === "object" && "~sc" in partialValue && partialValue["~sc"][0] === "deepNoExtraDefinedKeys") {
371
- if (!executeComparison(targetValue, partialValue["~sc"])) {
372
- return false;
373
- }
374
- } else if (partialValue && typeof partialValue === "object" && !Array.isArray(partialValue) && partialValue.constructor === Object && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) && targetValue.constructor === Object) {
375
- if (!executeComparison(targetValue, [
376
- "deepNoExtraDefinedKeys",
377
- partialValue
378
- ])) {
379
- return false;
380
- }
381
- } else {
382
- if (!partialEqual(targetValue, partialValue)) {
383
- return false;
384
- }
385
- }
386
- }
234
+ if (actualType !== value) {
235
+ addError(context, {
236
+ message: `Expected type ${value}`,
237
+ received: target
238
+ });
239
+ return false;
387
240
  }
388
241
  return true;
389
- default:
390
- return false;
391
- }
392
- }
393
- function formatPath(path) {
394
- if (path.length === 0) return "";
395
- let result = path[0] || "";
396
- for (let i = 1; i < path.length; i++) {
397
- const segment = path[i];
398
- if (segment && segment.startsWith("[") && segment.endsWith("]")) {
399
- result += segment;
400
- } else if (segment) {
401
- result += `.${segment}`;
402
242
  }
403
- }
404
- return result;
405
- }
406
- function addError(context, message, received, expected) {
407
- const error = {
408
- path: formatPath(context.path),
409
- message,
410
- received
411
- };
412
- if (expected !== void 0) {
413
- error.expected = expected;
414
- }
415
- context.errors.push(error);
416
- }
417
- function executeComparisonWithErrorCollection(target, comparison, context) {
418
- const [type, value] = comparison;
419
- switch (type) {
420
- case "hasType":
421
- switch (value) {
422
- case "string":
423
- if (typeof target !== "string") {
424
- addError(context, `Expected type string`, target);
425
- }
426
- break;
427
- case "number":
428
- if (typeof target !== "number") {
429
- addError(context, `Expected type number`, target);
430
- }
431
- break;
432
- case "boolean":
433
- if (typeof target !== "boolean") {
434
- addError(context, `Expected type boolean`, target);
435
- }
436
- break;
437
- case "function":
438
- if (typeof target !== "function") {
439
- addError(context, `Expected type function`, target);
440
- }
441
- break;
442
- case "array":
443
- if (!Array.isArray(target)) {
444
- addError(context, `Expected array`, target);
445
- }
446
- break;
447
- case "object":
448
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
449
- addError(context, `Expected object`, target);
450
- }
451
- break;
452
- }
453
- break;
454
- case "isInstanceOf":
455
- if (!(target instanceof value)) {
456
- addError(
457
- context,
458
- `Expected instance of ${value.name}`,
459
- target
460
- );
461
- }
462
- break;
463
- case "strStartsWith":
464
- if (typeof target !== "string" || !target.startsWith(value)) {
465
- addError(
466
- context,
467
- `Expected string starting with "${value}"`,
468
- target
469
- );
470
- }
471
- break;
472
- case "strEndsWith":
473
- if (typeof target !== "string" || !target.endsWith(value)) {
474
- addError(
475
- context,
476
- `Expected string ending with "${value}"`,
477
- target
478
- );
479
- }
480
- break;
481
- case "strContains":
482
- if (typeof target !== "string" || !target.includes(value)) {
483
- addError(
484
- context,
485
- `Expected string containing "${value}"`,
486
- target
487
- );
488
- }
489
- break;
490
- case "strMatchesRegex":
491
- if (typeof target !== "string" || !value.test(target)) {
492
- addError(
493
- context,
494
- `Expected string matching regex ${value}`,
495
- target
496
- );
243
+ case "deepEqual":
244
+ if (!deepEqual(target, value)) {
245
+ addError(context, {
246
+ message: "Values are not deeply equal",
247
+ received: target,
248
+ expected: value
249
+ });
250
+ return false;
497
251
  }
498
- break;
252
+ return true;
499
253
  case "numIsGreaterThan":
500
254
  if (typeof target !== "number" || target <= value) {
501
- addError(
502
- context,
503
- `Expected number greater than ${value}`,
504
- target
505
- );
255
+ addError(context, {
256
+ message: `Expected number greater than ${value}`,
257
+ received: target
258
+ });
259
+ return false;
506
260
  }
507
- break;
261
+ return true;
508
262
  case "numIsGreaterThanOrEqual":
509
263
  if (typeof target !== "number" || target < value) {
510
- addError(
511
- context,
512
- `Expected number greater than or equal to ${value}`,
513
- target
514
- );
264
+ addError(context, {
265
+ message: `Expected number greater than or equal to ${value}`,
266
+ received: target
267
+ });
268
+ return false;
515
269
  }
516
- break;
270
+ return true;
517
271
  case "numIsLessThan":
518
272
  if (typeof target !== "number" || target >= value) {
519
- addError(
520
- context,
521
- `Expected number less than ${value}`,
522
- target
523
- );
273
+ addError(context, {
274
+ message: `Expected number less than ${value}`,
275
+ received: target
276
+ });
277
+ return false;
524
278
  }
525
- break;
279
+ return true;
526
280
  case "numIsLessThanOrEqual":
527
281
  if (typeof target !== "number" || target > value) {
528
- addError(
529
- context,
530
- `Expected number less than or equal to ${value}`,
531
- target
532
- );
282
+ addError(context, {
283
+ message: `Expected number less than or equal to ${value}`,
284
+ received: target
285
+ });
286
+ return false;
533
287
  }
534
- break;
288
+ return true;
535
289
  case "numIsInRange":
536
290
  if (typeof target !== "number" || target < value[0] || target > value[1]) {
537
- addError(
538
- context,
539
- `Expected number in range [${value[0]}, ${value[1]}]`,
540
- target
541
- );
291
+ addError(context, {
292
+ message: `Expected number in range [${value[0]}, ${value[1]}]`,
293
+ received: target
294
+ });
295
+ return false;
542
296
  }
543
- break;
297
+ return true;
544
298
  case "jsonStringHasPartial":
545
299
  if (typeof target !== "string") {
546
- addError(context, `Expected JSON string`, target);
547
- } else {
548
- try {
549
- const parsed = JSON.parse(target);
550
- partialEqualWithErrorCollection(parsed, value, context);
551
- } catch {
552
- addError(
553
- context,
554
- `Expected valid JSON string`,
555
- target
556
- );
557
- }
300
+ addError(context, {
301
+ message: "Expected JSON string",
302
+ received: target
303
+ });
304
+ return false;
558
305
  }
559
- break;
560
- case "deepEqual":
561
- if (!deepEqual(target, value)) {
562
- addError(context, `Expected deep equal`, target, value);
306
+ try {
307
+ const parsed = JSON.parse(target);
308
+ if (!partialEqualInternal(parsed, value, context)) {
309
+ return false;
310
+ }
311
+ } catch {
312
+ addError(context, {
313
+ message: "Expected valid JSON string",
314
+ received: target
315
+ });
316
+ return false;
563
317
  }
564
- break;
318
+ return true;
565
319
  case "partialEqual":
566
- partialEqualWithErrorCollection(target, value, context);
567
- break;
568
- case "custom":
569
- if (!value(target)) {
570
- addError(context, `Custom validation failed`, target, "valid value");
320
+ return partialEqualInternal(target, value, context);
321
+ case "custom": {
322
+ const result = value(target);
323
+ if (result !== true) {
324
+ addError(context, {
325
+ message: `Custom validation failed ${typeof result === "object" ? `: ${result.error}` : ""}`,
326
+ received: target
327
+ });
328
+ return false;
571
329
  }
572
- break;
330
+ return true;
331
+ }
332
+ case "isInstanceOf":
333
+ if (!(target instanceof value)) {
334
+ addError(context, {
335
+ message: `Expected instance of ${value.name}`,
336
+ received: target
337
+ });
338
+ return false;
339
+ }
340
+ return true;
573
341
  case "keyNotBePresent":
574
- addError(context, `Key should not be present`, target);
575
- break;
342
+ addError(context, {
343
+ message: "This property should not be present",
344
+ received: target
345
+ });
346
+ return false;
347
+ case "not": {
348
+ const tempContext = {
349
+ errors: [],
350
+ path: context.path
351
+ };
352
+ const result = executeComparison(target, value, tempContext);
353
+ if (result) {
354
+ addError(context, {
355
+ message: "Expected negated condition to fail",
356
+ received: target,
357
+ expected: { "not match": value }
358
+ });
359
+ return false;
360
+ }
361
+ return true;
362
+ }
576
363
  case "any": {
577
- for (const comp of value) {
578
- const subContext = {
364
+ for (const subComparison of value) {
365
+ const anyTempContext = {
579
366
  errors: [],
580
- path: [...context.path]
367
+ path: context.path
581
368
  };
582
- executeComparisonWithErrorCollection(target, comp, subContext);
583
- if (subContext.errors.length === 0) {
584
- return;
369
+ if (executeComparison(target, subComparison, anyTempContext)) {
370
+ return true;
585
371
  }
586
372
  }
587
- addError(
588
- context,
589
- `None of the alternative comparisons matched`,
590
- target
591
- );
592
- break;
373
+ addError(context, {
374
+ message: "None of the alternative comparisons matched",
375
+ received: target,
376
+ expected: {
377
+ matchAny: value
378
+ }
379
+ });
380
+ return false;
593
381
  }
594
- case "all":
595
- for (const comp of value) {
596
- executeComparisonWithErrorCollection(target, comp, context);
597
- }
598
- break;
599
- case "not": {
600
- const subContext = {
601
- errors: [],
602
- path: [...context.path]
603
- };
604
- executeComparisonWithErrorCollection(target, value, subContext);
605
- if (subContext.errors.length === 0) {
606
- addError(
607
- context,
608
- `Expected negated condition to fail`,
609
- target
610
- );
382
+ case "all": {
383
+ let allMatch = true;
384
+ for (const subComparison of value) {
385
+ if (!executeComparison(target, subComparison, context)) {
386
+ allMatch = false;
387
+ }
611
388
  }
612
- break;
389
+ return allMatch;
613
390
  }
614
391
  case "withNoExtraKeys":
392
+ return checkNoExtraKeys(target, value, context, false);
615
393
  case "withDeepNoExtraKeys":
394
+ return checkNoExtraKeys(target, value, context, true);
616
395
  case "noExtraDefinedKeys":
396
+ return checkNoExtraDefinedKeys(target, value, context, false);
617
397
  case "deepNoExtraDefinedKeys":
618
- addError(
619
- context,
620
- `Complex validation not supported in error collection mode yet`,
621
- target
622
- );
623
- break;
398
+ return checkNoExtraDefinedKeys(target, value, context, true);
399
+ default:
400
+ throw exhaustiveCheck(type);
624
401
  }
625
402
  }
626
- function executeComparisonWithKeyContextAndErrorCollection(target, comp, keyExists, context) {
627
- const [type, value] = comp;
628
- if (type === "keyNotBePresent") {
629
- if (keyExists) {
630
- addError(context, `Key should not be present`, target);
403
+ function formatPath(path) {
404
+ if (path.length === 0) return "";
405
+ let result = path[0] || "";
406
+ for (let i = 1; i < path.length; i++) {
407
+ const segment = path[i];
408
+ if (segment && segment.startsWith("[") && segment.endsWith("]")) {
409
+ result += segment;
410
+ } else if (segment) {
411
+ if (result) {
412
+ result += `.${segment}`;
413
+ } else {
414
+ result += segment;
415
+ }
631
416
  }
632
- return;
633
417
  }
634
- if (type === "any") {
635
- for (const childComp of value) {
636
- const subContext = {
637
- errors: [],
638
- path: [...context.path]
639
- };
640
- executeComparisonWithKeyContextAndErrorCollection(
641
- target,
642
- childComp,
643
- keyExists,
644
- subContext
645
- );
646
- if (subContext.errors.length === 0) {
647
- return;
648
- }
418
+ return result;
419
+ }
420
+ function addError(context, error) {
421
+ context.errors.push({
422
+ path: formatPath(context.path),
423
+ ...error
424
+ });
425
+ }
426
+ function deepEqual(a, b) {
427
+ if (a === b) return true;
428
+ if (Number.isNaN(a) && Number.isNaN(b)) return true;
429
+ if (a === null || b === null) return false;
430
+ if (typeof a !== typeof b) return false;
431
+ if (Array.isArray(a) && Array.isArray(b)) {
432
+ if (a.length !== b.length) return false;
433
+ for (let i = 0; i < a.length; i++) {
434
+ if (!deepEqual(a[i], b[i])) return false;
649
435
  }
650
- addError(
651
- context,
652
- `None of the alternative comparisons matched`,
653
- target,
654
- "any alternative match"
655
- );
656
- return;
436
+ return true;
657
437
  }
658
- if (type === "not") {
659
- const subContext = {
660
- errors: [],
661
- path: [...context.path]
662
- };
663
- executeComparisonWithKeyContextAndErrorCollection(
664
- target,
665
- value,
666
- keyExists,
667
- subContext
668
- );
669
- if (subContext.errors.length === 0) {
670
- addError(
671
- context,
672
- `Expected negated condition to fail`,
673
- target,
674
- "negated condition"
675
- );
438
+ if (typeof a === "object") {
439
+ const keysA = Object.keys(a);
440
+ const keysB = Object.keys(b);
441
+ if (keysA.length !== keysB.length) return false;
442
+ for (const key of keysA) {
443
+ if (!has.call(b, key) || !deepEqual(a[key], b[key])) return false;
676
444
  }
677
- return;
445
+ return true;
678
446
  }
679
- executeComparisonWithErrorCollection(target, comp, context);
447
+ return false;
680
448
  }
681
- function partialEqualWithErrorCollection(target, sub, context) {
682
- if (sub === target) return;
683
- if (sub && typeof sub === "object" && "~sc" in sub) {
684
- executeComparisonWithErrorCollection(target, sub["~sc"], context);
685
- return;
449
+ function partialEqualInternal(target, sub, context) {
450
+ if (isComparison(sub)) {
451
+ return executeComparison(target, sub["~sc"], context);
686
452
  }
687
- if (sub && target && sub.constructor === target.constructor) {
688
- const ctor = sub.constructor;
689
- if (ctor === Date) {
690
- if (sub.getTime() !== target.getTime()) {
691
- addError(context, `Date mismatch`, target, sub);
692
- }
693
- return;
453
+ if (isComparison(sub) && sub["~sc"][0] === "keyNotBePresent") {
454
+ addError(context, {
455
+ message: "This property should not be present",
456
+ received: target
457
+ });
458
+ return false;
459
+ }
460
+ if (target === sub) return true;
461
+ if (Number.isNaN(target) && Number.isNaN(sub)) return true;
462
+ if (target === null || sub === null || target === void 0 || sub === void 0) {
463
+ if (target !== sub) {
464
+ addError(context, {
465
+ message: "Value mismatch",
466
+ received: target,
467
+ expected: sub
468
+ });
469
+ return false;
694
470
  }
695
- if (ctor === RegExp) {
696
- if (sub.toString() !== target.toString()) {
697
- addError(context, `RegExp mismatch`, target, sub);
698
- }
699
- return;
471
+ return true;
472
+ }
473
+ if (target instanceof Date && sub instanceof Date) {
474
+ if (target.getTime() !== sub.getTime()) {
475
+ addError(context, {
476
+ message: "Date mismatch",
477
+ received: target,
478
+ expected: sub
479
+ });
480
+ return false;
700
481
  }
701
- if (ctor === Array) {
702
- if (sub.length > target.length) {
703
- addError(
704
- context,
705
- `Array too short: expected at least ${sub.length} elements, got ${target.length}`,
706
- target,
707
- sub
708
- );
709
- return;
710
- }
711
- for (let i = 0; i < sub.length; i++) {
712
- context.path.push(`[${i}]`);
713
- partialEqualWithErrorCollection(target[i], sub[i], context);
714
- context.path.pop();
715
- }
716
- return;
482
+ return true;
483
+ }
484
+ if (target instanceof RegExp && sub instanceof RegExp) {
485
+ if (target.source !== sub.source || target.flags !== sub.flags) {
486
+ addError(context, {
487
+ message: "RegExp mismatch",
488
+ received: target,
489
+ expected: sub
490
+ });
491
+ return false;
717
492
  }
718
- if (ctor === Set) {
719
- if (sub.size > target.size) {
720
- addError(
721
- context,
722
- `Set too small: expected at least ${sub.size} elements, got ${target.size}`,
723
- target,
724
- sub
725
- );
726
- return;
727
- }
728
- for (const value of sub) {
729
- let found = false;
730
- if (value && typeof value === "object") {
731
- found = !!find2(target, value);
732
- } else {
733
- found = target.has(value);
734
- }
735
- if (!found) {
736
- addError(context, `Set missing value`, target, value);
493
+ return true;
494
+ }
495
+ if (target instanceof Set && sub instanceof Set) {
496
+ if (sub.size > target.size) {
497
+ addError(context, {
498
+ message: "Set too small",
499
+ received: target,
500
+ expected: sub
501
+ });
502
+ return false;
503
+ }
504
+ for (const subValue of sub) {
505
+ let found = false;
506
+ for (const targetValue of target) {
507
+ const tempContext = {
508
+ errors: [],
509
+ path: context.path
510
+ };
511
+ if (partialEqualInternal(targetValue, subValue, tempContext)) {
512
+ found = true;
513
+ break;
737
514
  }
738
515
  }
739
- return;
516
+ if (!found) {
517
+ addError(context, {
518
+ message: "Set element not found",
519
+ received: target,
520
+ expected: sub
521
+ });
522
+ return false;
523
+ }
740
524
  }
741
- if (ctor === Map) {
742
- if (sub.size > target.size) {
743
- addError(
744
- context,
745
- `Map too small: expected at least ${sub.size} entries, got ${target.size}`,
746
- target,
747
- sub
748
- );
749
- return;
750
- }
751
- for (const [key, value] of sub) {
752
- let targetKey = key;
753
- if (key && typeof key === "object") {
754
- targetKey = find2(target, key);
755
- if (!targetKey) {
756
- addError(context, `Map missing key`, target, key);
757
- continue;
758
- }
759
- }
760
- if (!target.has(targetKey)) {
761
- addError(context, `Map missing key`, target, key);
762
- continue;
525
+ return true;
526
+ }
527
+ if (target instanceof Map && sub instanceof Map) {
528
+ if (sub.size > target.size) {
529
+ addError(context, {
530
+ message: "Map has less entries than expected",
531
+ received: `${target.size} entries`,
532
+ expected: `${sub.size} entries`
533
+ });
534
+ return false;
535
+ }
536
+ for (const [subKey, subValue] of sub) {
537
+ let found = false;
538
+ for (const [targetKey, targetValue] of target) {
539
+ const tempContextKey = {
540
+ errors: [],
541
+ path: context.path
542
+ };
543
+ const tempContextValue = {
544
+ errors: [],
545
+ path: context.path
546
+ };
547
+ if (partialEqualInternal(targetKey, subKey, tempContextKey) && partialEqualInternal(targetValue, subValue, tempContextValue)) {
548
+ found = true;
549
+ break;
763
550
  }
764
- context.path.push(`[${key}]`);
765
- partialEqualWithErrorCollection(target.get(targetKey), value, context);
766
- context.path.pop();
767
551
  }
768
- return;
552
+ if (!found) {
553
+ addError(context, {
554
+ message: "Map entry not found",
555
+ received: target,
556
+ expected: sub
557
+ });
558
+ return false;
559
+ }
769
560
  }
770
- if (!ctor || typeof sub === "object") {
771
- for (const key in sub) {
772
- if (has2.call(sub, key)) {
773
- const subValue = sub[key];
774
- context.path.push(key);
775
- if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "keyNotBePresent") {
776
- if (has2.call(target, key)) {
777
- addError(
778
- context,
779
- `Key should not be present`,
780
- target[key],
781
- "key not present"
782
- );
783
- }
784
- } else if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "any") {
785
- const targetHasKey = has2.call(target, key);
786
- const targetValue = targetHasKey ? target[key] : void 0;
787
- executeComparisonWithKeyContextAndErrorCollection(
788
- targetValue,
789
- subValue["~sc"],
790
- targetHasKey,
791
- context
561
+ return true;
562
+ }
563
+ if (typeof target !== typeof sub) {
564
+ addError(context, {
565
+ message: "Value mismatch",
566
+ received: target,
567
+ expected: sub
568
+ });
569
+ return false;
570
+ }
571
+ if (Array.isArray(sub)) {
572
+ if (!Array.isArray(target)) {
573
+ addError(context, {
574
+ message: "Expected array",
575
+ received: target,
576
+ expected: sub
577
+ });
578
+ return false;
579
+ }
580
+ if (target.length < sub.length) {
581
+ addError(context, {
582
+ message: `Array too short: expected at least ${sub.length} elements, got ${target.length}`,
583
+ received: target,
584
+ expected: sub
585
+ });
586
+ return false;
587
+ }
588
+ let allMatch = true;
589
+ for (let i = 0; i < sub.length; i++) {
590
+ const oldPath = context.path;
591
+ context.path = [...oldPath, `[${i}]`];
592
+ const result = partialEqualInternal(target[i], sub[i], context);
593
+ context.path = oldPath;
594
+ if (!result) allMatch = false;
595
+ }
596
+ return allMatch;
597
+ }
598
+ if (typeof sub === "object") {
599
+ if (typeof target !== "object" || Array.isArray(target)) {
600
+ addError(context, {
601
+ message: "Expected object",
602
+ received: target,
603
+ expected: sub
604
+ });
605
+ return false;
606
+ }
607
+ let allMatch = true;
608
+ for (const key of Object.keys(sub)) {
609
+ if (isComparison(sub[key]) && sub[key]["~sc"][0] === "keyNotBePresent") {
610
+ if (has.call(target, key)) {
611
+ const oldPath2 = context.path;
612
+ context.path = [...oldPath2, key];
613
+ addError(context, {
614
+ message: "Key should not be present",
615
+ received: target[key]
616
+ });
617
+ context.path = oldPath2;
618
+ allMatch = false;
619
+ }
620
+ continue;
621
+ }
622
+ if (!has.call(target, key)) {
623
+ if (isComparison(sub[key])) {
624
+ const comparison = sub[key]["~sc"];
625
+ if (comparison[0] === "any") {
626
+ const anyComparisons = comparison[1];
627
+ const hasKeyNotBePresent = anyComparisons.some(
628
+ (comp) => comp[0] === "keyNotBePresent"
792
629
  );
793
- } else {
794
- if (!has2.call(target, key)) {
795
- addError(context, `Missing property`, void 0, subValue);
796
- } else {
797
- partialEqualWithErrorCollection(target[key], subValue, context);
630
+ if (hasKeyNotBePresent) {
631
+ continue;
798
632
  }
799
633
  }
800
- context.path.pop();
801
634
  }
802
- }
803
- return;
635
+ const oldPath2 = context.path;
636
+ context.path = [...oldPath2, key];
637
+ addError(context, {
638
+ message: "Missing property",
639
+ expected: sub[key],
640
+ received: { objectWithKeys: Object.keys(target) }
641
+ });
642
+ context.path = oldPath2;
643
+ allMatch = false;
644
+ continue;
645
+ }
646
+ const oldPath = context.path;
647
+ context.path = [...oldPath, key];
648
+ const result = partialEqualInternal(target[key], sub[key], context);
649
+ context.path = oldPath;
650
+ if (!result) allMatch = false;
804
651
  }
652
+ return allMatch;
805
653
  }
806
- if (sub !== sub && target !== target) {
807
- return;
654
+ if (target !== sub) {
655
+ addError(context, {
656
+ message: "Value mismatch",
657
+ received: target,
658
+ expected: sub
659
+ });
660
+ return false;
808
661
  }
809
- addError(context, `Value mismatch`, target, sub);
662
+ return true;
810
663
  }
811
- function partialEqual(target, sub, returnErrors) {
812
- if (returnErrors === true) {
813
- const context = {
814
- errors: [],
815
- path: []
816
- };
817
- partialEqualWithErrorCollection(target, sub, context);
818
- if (context.errors.length === 0) {
819
- return (0, import_t_result.ok)(void 0);
820
- } else {
821
- return (0, import_t_result.err)(context.errors);
822
- }
664
+ function checkNoExtraKeys(target, partialShape, context, deep) {
665
+ if (typeof target !== "object" || target === null || Array.isArray(target)) {
666
+ addError(context, {
667
+ message: "Expected object for key validation",
668
+ received: target
669
+ });
670
+ return false;
823
671
  }
824
- if (sub === target) return true;
825
- if (sub && typeof sub === "object" && "~sc" in sub) {
826
- return executeComparison(target, sub["~sc"]);
672
+ if (!partialEqualInternal(target, partialShape, context)) {
673
+ return false;
827
674
  }
828
- if (sub && target && sub.constructor === target.constructor) {
829
- const ctor = sub.constructor;
830
- if (ctor === Date) {
831
- return sub.getTime() === target.getTime();
832
- }
833
- if (ctor === RegExp) {
834
- return sub.toString() === target.toString();
835
- }
836
- if (ctor === Array) {
837
- if (sub.length > target.length) return false;
838
- for (let i = 0; i < sub.length; i++) {
839
- if (!partialEqual(target[i], sub[i])) return false;
840
- }
841
- return true;
675
+ const allowedKeys = new Set(Object.keys(partialShape));
676
+ for (const key of Object.keys(target)) {
677
+ if (!allowedKeys.has(key)) {
678
+ const oldPath = context.path;
679
+ context.path = [...oldPath, key];
680
+ addError(context, {
681
+ message: `Extra key "${key}" should not be present`,
682
+ received: target[key]
683
+ });
684
+ context.path = oldPath;
685
+ return false;
842
686
  }
843
- if (ctor === Set) {
844
- if (sub.size > target.size) return false;
845
- for (const value of sub) {
846
- let found = false;
847
- if (value && typeof value === "object") {
848
- found = !!find2(target, value);
849
- } else {
850
- found = target.has(value);
851
- }
852
- if (!found) return false;
687
+ }
688
+ if (deep) {
689
+ for (const key of Object.keys(partialShape)) {
690
+ if (typeof partialShape[key] === "object" && partialShape[key] !== null && !Array.isArray(partialShape[key]) && !isComparison(partialShape[key])) {
691
+ const oldPath = context.path;
692
+ context.path = [...oldPath, key];
693
+ const result = checkNoExtraKeys(
694
+ target[key],
695
+ partialShape[key],
696
+ context,
697
+ true
698
+ );
699
+ context.path = oldPath;
700
+ if (!result) return false;
853
701
  }
854
- return true;
855
702
  }
856
- if (ctor === Map) {
857
- if (sub.size > target.size) return false;
858
- for (const [key, value] of sub) {
859
- let targetKey = key;
860
- if (key && typeof key === "object") {
861
- targetKey = find2(target, key);
862
- if (!targetKey) return false;
863
- }
864
- if (!target.has(targetKey) || !partialEqual(target.get(targetKey), value)) {
865
- return false;
866
- }
867
- }
868
- return true;
703
+ }
704
+ return true;
705
+ }
706
+ function checkNoExtraDefinedKeys(target, partialShape, context, deep) {
707
+ if (typeof target !== "object" || target === null || Array.isArray(target)) {
708
+ addError(context, {
709
+ message: "Expected object for key validation",
710
+ received: target
711
+ });
712
+ return false;
713
+ }
714
+ if (!partialEqualInternal(target, partialShape, context)) {
715
+ return false;
716
+ }
717
+ const allowedKeys = new Set(Object.keys(partialShape));
718
+ for (const key of Object.keys(target)) {
719
+ if (!allowedKeys.has(key) && target[key] !== void 0) {
720
+ const oldPath = context.path;
721
+ context.path = [...oldPath, key];
722
+ addError(context, {
723
+ message: `Extra defined key "${key}" should not be present`,
724
+ received: target[key]
725
+ });
726
+ context.path = oldPath;
727
+ return false;
869
728
  }
870
- if (!ctor || typeof sub === "object") {
871
- for (const key in sub) {
872
- if (has2.call(sub, key)) {
873
- const subValue = sub[key];
874
- if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "keyNotBePresent") {
875
- if (has2.call(target, key)) {
876
- return false;
877
- }
878
- } else if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "any") {
879
- const targetHasKey = has2.call(target, key);
880
- const targetValue = targetHasKey ? target[key] : void 0;
881
- if (!executeComparisonWithKeyContext(
882
- targetValue,
883
- subValue["~sc"],
884
- targetHasKey
885
- )) {
886
- return false;
887
- }
888
- } else {
889
- if (!has2.call(target, key) || !partialEqual(target[key], subValue)) {
890
- return false;
891
- }
892
- }
893
- }
729
+ }
730
+ if (deep) {
731
+ for (const key of Object.keys(partialShape)) {
732
+ if (typeof partialShape[key] === "object" && partialShape[key] !== null && !Array.isArray(partialShape[key]) && !isComparison(partialShape[key])) {
733
+ const oldPath = context.path;
734
+ context.path = [...oldPath, key];
735
+ const result = checkNoExtraDefinedKeys(
736
+ target[key],
737
+ partialShape[key],
738
+ context,
739
+ true
740
+ );
741
+ context.path = oldPath;
742
+ if (!result) return false;
894
743
  }
895
- return true;
896
744
  }
897
745
  }
898
- return sub !== sub && target !== target;
746
+ return true;
747
+ }
748
+ function partialEqual(target, sub, returnErrors) {
749
+ const context = { errors: [], path: [] };
750
+ const result = partialEqualInternal(target, sub, context);
751
+ if (returnErrors) {
752
+ return result ? (0, import_t_result.ok)(void 0) : (0, import_t_result.err)(context.errors);
753
+ }
754
+ return result;
899
755
  }
900
756
  // Annotate the CommonJS export names for ESM import in node:
901
757
  0 && (module.exports = {