@ls-stack/utils 3.47.0 → 3.49.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,8 +1,10 @@
1
1
  import {
2
- deepEqual
3
- } from "./chunk-JQFUKJU5.js";
2
+ exhaustiveCheck
3
+ } from "./chunk-C2SVCIWE.js";
4
+ import "./chunk-JF2MDHOJ.js";
4
5
 
5
6
  // src/partialEqual.ts
7
+ import { err, ok } from "t-result";
6
8
  var has = Object.prototype.hasOwnProperty;
7
9
  function createComparison(type) {
8
10
  return { "~sc": type };
@@ -41,8 +43,24 @@ var match = {
41
43
  partialEqual: (value) => createComparison(["partialEqual", value]),
42
44
  custom: (isEqual) => createComparison(["custom", isEqual]),
43
45
  keyNotBePresent: createComparison(["keyNotBePresent", null]),
44
- any: (...comparisons) => createComparison(["any", comparisons.map((c) => c["~sc"])]),
45
- all: (...comparisons) => createComparison(["all", comparisons.map((c) => c["~sc"])]),
46
+ any: (...values) => createComparison([
47
+ "any",
48
+ values.map((v) => {
49
+ if (isComparison(v)) return v["~sc"];
50
+ if (typeof v === "object" && v !== null)
51
+ return ["partialEqual", v];
52
+ return ["deepEqual", v];
53
+ })
54
+ ]),
55
+ all: (...values) => createComparison([
56
+ "all",
57
+ values.map((v) => {
58
+ if (isComparison(v)) return v["~sc"];
59
+ if (typeof v === "object" && v !== null)
60
+ return ["partialEqual", v];
61
+ return ["deepEqual", v];
62
+ })
63
+ ]),
46
64
  not: {
47
65
  hasType: {
48
66
  string: createComparison(["not", ["hasType", "string"]]),
@@ -73,311 +91,640 @@ var match = {
73
91
  equal: (value) => createComparison(["not", ["deepEqual", value]]),
74
92
  partialEqual: (value) => createComparison(["not", ["partialEqual", value]]),
75
93
  custom: (value) => createComparison(["not", ["custom", value]]),
76
- any: (...comparisons) => createComparison(["not", ["any", comparisons.map((c) => c["~sc"])]]),
77
- all: (...comparisons) => createComparison(["not", ["all", comparisons.map((c) => c["~sc"])]]),
94
+ any: (...values) => createComparison([
95
+ "not",
96
+ [
97
+ "any",
98
+ values.map((v) => {
99
+ if (isComparison(v)) return v["~sc"];
100
+ if (typeof v === "object" && v !== null)
101
+ return ["partialEqual", v];
102
+ return ["deepEqual", v];
103
+ })
104
+ ]
105
+ ]),
106
+ all: (...values) => createComparison([
107
+ "not",
108
+ [
109
+ "all",
110
+ values.map((v) => {
111
+ if (isComparison(v)) return v["~sc"];
112
+ if (typeof v === "object" && v !== null)
113
+ return ["partialEqual", v];
114
+ return ["deepEqual", v];
115
+ })
116
+ ]
117
+ ]),
78
118
  noExtraKeys: (partialShape) => createComparison(["not", ["withNoExtraKeys", partialShape]]),
79
119
  deepNoExtraKeys: (partialShape) => createComparison(["not", ["withDeepNoExtraKeys", partialShape]]),
80
120
  noExtraDefinedKeys: (partialShape) => createComparison(["not", ["noExtraDefinedKeys", partialShape]]),
81
121
  deepNoExtraDefinedKeys: (partialShape) => createComparison(["not", ["deepNoExtraDefinedKeys", partialShape]])
82
122
  }
83
123
  };
84
- function find(iter, tar) {
85
- for (const key of iter.keys()) {
86
- if (partialEqual(key, tar)) return key;
87
- }
88
- }
89
- function executeComparisonWithKeyContext(target, comp, keyExists) {
90
- const [type, value] = comp;
91
- if (type === "keyNotBePresent") {
92
- return !keyExists;
93
- }
94
- if (type === "any") {
95
- for (const childComp of value) {
96
- if (executeComparisonWithKeyContext(target, childComp, keyExists)) {
97
- return true;
98
- }
99
- }
100
- return false;
101
- }
102
- if (type === "not") {
103
- return !executeComparisonWithKeyContext(target, value, keyExists);
104
- }
105
- return executeComparison(target, comp);
124
+ function isComparison(value) {
125
+ return value && typeof value === "object" && "~sc" in value;
106
126
  }
107
- function executeComparison(target, comparison) {
127
+ function executeComparison(target, comparison, context) {
108
128
  const [type, value] = comparison;
109
129
  switch (type) {
110
- case "hasType":
111
- switch (value) {
112
- case "string":
113
- return typeof target === "string";
114
- case "number":
115
- return typeof target === "number";
116
- case "boolean":
117
- return typeof target === "boolean";
118
- case "function":
119
- return typeof target === "function";
120
- case "array":
121
- return Array.isArray(target);
122
- case "object":
123
- return typeof target === "object" && target !== null && !Array.isArray(target);
124
- default:
125
- return false;
126
- }
127
- case "isInstanceOf":
128
- return target instanceof value;
129
130
  case "strStartsWith":
130
- return typeof target === "string" && target.startsWith(value);
131
+ if (typeof target !== "string") {
132
+ addError(context, {
133
+ message: `Expected string starting with "${value}"`,
134
+ received: target
135
+ });
136
+ return false;
137
+ }
138
+ if (!target.startsWith(value)) {
139
+ addError(context, {
140
+ message: `Expected string starting with "${value}"`,
141
+ received: target
142
+ });
143
+ return false;
144
+ }
145
+ return true;
131
146
  case "strEndsWith":
132
- return typeof target === "string" && target.endsWith(value);
133
- case "strContains":
134
- return typeof target === "string" && target.includes(value);
135
- case "strMatchesRegex":
136
- return typeof target === "string" && value.test(target);
137
- case "numIsGreaterThan":
138
- return typeof target === "number" && target > value;
139
- case "numIsGreaterThanOrEqual":
140
- return typeof target === "number" && target >= value;
141
- case "numIsLessThan":
142
- return typeof target === "number" && target < value;
143
- case "numIsLessThanOrEqual":
144
- return typeof target === "number" && target <= value;
145
- case "numIsInRange":
146
- return typeof target === "number" && target >= value[0] && target <= value[1];
147
- case "jsonStringHasPartial":
148
- if (typeof target !== "string") return false;
149
- try {
150
- const parsed = JSON.parse(target);
151
- return partialEqual(parsed, value);
152
- } catch {
147
+ if (typeof target !== "string") {
148
+ addError(context, {
149
+ message: `Expected string ending with "${value}"`,
150
+ received: target
151
+ });
153
152
  return false;
154
153
  }
155
- case "deepEqual":
156
- return deepEqual(target, value);
157
- case "partialEqual":
158
- return partialEqual(target, value);
159
- case "custom":
160
- return value(target);
161
- case "keyNotBePresent":
162
- return false;
163
- case "any":
164
- for (const comp of value) {
165
- if (executeComparison(target, comp)) {
166
- return true;
167
- }
154
+ if (!target.endsWith(value)) {
155
+ addError(context, {
156
+ message: `Expected string ending with "${value}"`,
157
+ received: target
158
+ });
159
+ return false;
168
160
  }
169
- return false;
170
- case "all":
171
- for (const comp of value) {
172
- if (!executeComparison(target, comp)) {
173
- return false;
174
- }
161
+ return true;
162
+ case "strContains":
163
+ if (typeof target !== "string") {
164
+ addError(context, {
165
+ message: `Expected string containing "${value}"`,
166
+ received: target
167
+ });
168
+ return false;
169
+ }
170
+ if (!target.includes(value)) {
171
+ addError(context, {
172
+ message: `Expected string containing "${value}"`,
173
+ received: target
174
+ });
175
+ return false;
175
176
  }
176
177
  return true;
177
- case "not":
178
- return !executeComparison(target, value);
179
- case "withNoExtraKeys":
180
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
178
+ case "strMatchesRegex":
179
+ if (typeof target !== "string") {
180
+ addError(context, {
181
+ message: `Expected string matching regex ${value}`,
182
+ received: target
183
+ });
181
184
  return false;
182
185
  }
183
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
186
+ if (!value.test(target)) {
187
+ addError(context, {
188
+ message: `Expected string matching regex ${value}`,
189
+ received: target
190
+ });
184
191
  return false;
185
192
  }
186
- for (const key in target) {
187
- if (has.call(target, key) && !has.call(value, key)) {
188
- return false;
193
+ return true;
194
+ case "hasType": {
195
+ let actualType;
196
+ if (value === "array") {
197
+ actualType = Array.isArray(target) ? "array" : typeof target;
198
+ } else if (value === "object") {
199
+ if (target === null || Array.isArray(target)) {
200
+ actualType = "not-object";
201
+ } else {
202
+ actualType = typeof target;
189
203
  }
204
+ } else {
205
+ actualType = typeof target;
190
206
  }
191
- for (const key in value) {
192
- if (has.call(value, key)) {
193
- if (!has.call(target, key)) {
194
- return false;
195
- }
196
- if (!partialEqual(target[key], value[key])) {
197
- return false;
198
- }
199
- }
207
+ if (actualType !== value) {
208
+ addError(context, {
209
+ message: `Expected type ${value}`,
210
+ received: target
211
+ });
212
+ return false;
200
213
  }
201
214
  return true;
202
- case "withDeepNoExtraKeys":
203
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
215
+ }
216
+ case "deepEqual":
217
+ if (!deepEqual(target, value)) {
218
+ addError(context, {
219
+ message: "Values are not deeply equal",
220
+ received: target,
221
+ expected: value
222
+ });
204
223
  return false;
205
224
  }
206
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
225
+ return true;
226
+ case "numIsGreaterThan":
227
+ if (typeof target !== "number" || target <= value) {
228
+ addError(context, {
229
+ message: `Expected number greater than ${value}`,
230
+ received: target
231
+ });
207
232
  return false;
208
233
  }
209
- for (const key in target) {
210
- if (has.call(target, key) && !has.call(value, key)) {
211
- return false;
212
- }
234
+ return true;
235
+ case "numIsGreaterThanOrEqual":
236
+ if (typeof target !== "number" || target < value) {
237
+ addError(context, {
238
+ message: `Expected number greater than or equal to ${value}`,
239
+ received: target
240
+ });
241
+ return false;
213
242
  }
214
- for (const key in value) {
215
- if (has.call(value, key)) {
216
- if (!has.call(target, key)) {
217
- return false;
218
- }
219
- const targetValue = target[key];
220
- const partialValue = value[key];
221
- if (partialValue && typeof partialValue === "object" && "~sc" in partialValue && partialValue["~sc"][0] === "withDeepNoExtraKeys") {
222
- if (!executeComparison(targetValue, partialValue["~sc"])) {
223
- return false;
224
- }
225
- } else if (partialValue && typeof partialValue === "object" && !Array.isArray(partialValue) && partialValue.constructor === Object && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) && targetValue.constructor === Object) {
226
- if (!executeComparison(targetValue, [
227
- "withDeepNoExtraKeys",
228
- partialValue
229
- ])) {
230
- return false;
231
- }
232
- } else {
233
- if (!partialEqual(targetValue, partialValue)) {
234
- return false;
235
- }
236
- }
237
- }
243
+ return true;
244
+ case "numIsLessThan":
245
+ if (typeof target !== "number" || target >= value) {
246
+ addError(context, {
247
+ message: `Expected number less than ${value}`,
248
+ received: target
249
+ });
250
+ return false;
238
251
  }
239
252
  return true;
240
- case "noExtraDefinedKeys":
241
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
253
+ case "numIsLessThanOrEqual":
254
+ if (typeof target !== "number" || target > value) {
255
+ addError(context, {
256
+ message: `Expected number less than or equal to ${value}`,
257
+ received: target
258
+ });
242
259
  return false;
243
260
  }
244
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
261
+ return true;
262
+ case "numIsInRange":
263
+ if (typeof target !== "number" || target < value[0] || target > value[1]) {
264
+ addError(context, {
265
+ message: `Expected number in range [${value[0]}, ${value[1]}]`,
266
+ received: target
267
+ });
268
+ return false;
269
+ }
270
+ return true;
271
+ case "jsonStringHasPartial":
272
+ if (typeof target !== "string") {
273
+ addError(context, {
274
+ message: "Expected JSON string",
275
+ received: target
276
+ });
245
277
  return false;
246
278
  }
247
- for (const key in target) {
248
- if (has.call(target, key) && target[key] !== void 0 && !has.call(value, key)) {
279
+ try {
280
+ const parsed = JSON.parse(target);
281
+ if (!partialEqualInternal(parsed, value, context)) {
249
282
  return false;
250
283
  }
284
+ } catch {
285
+ addError(context, {
286
+ message: "Expected valid JSON string",
287
+ received: target
288
+ });
289
+ return false;
251
290
  }
252
- for (const key in value) {
253
- if (has.call(value, key)) {
254
- if (!has.call(target, key)) {
255
- return false;
256
- }
257
- if (!partialEqual(target[key], value[key])) {
258
- return false;
259
- }
260
- }
291
+ return true;
292
+ case "partialEqual":
293
+ return partialEqualInternal(target, value, context);
294
+ case "custom": {
295
+ const result = value(target);
296
+ if (result !== true) {
297
+ addError(context, {
298
+ message: `Custom validation failed ${typeof result === "object" ? `: ${result.error}` : ""}`,
299
+ received: target
300
+ });
301
+ return false;
261
302
  }
262
303
  return true;
263
- case "deepNoExtraDefinedKeys":
264
- if (typeof target !== "object" || target === null || Array.isArray(target)) {
304
+ }
305
+ case "isInstanceOf":
306
+ if (!(target instanceof value)) {
307
+ addError(context, {
308
+ message: `Expected instance of ${value.name}`,
309
+ received: target
310
+ });
265
311
  return false;
266
312
  }
267
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
313
+ return true;
314
+ case "keyNotBePresent":
315
+ addError(context, {
316
+ message: "This property should not be present",
317
+ received: target
318
+ });
319
+ return false;
320
+ case "not": {
321
+ const tempContext = {
322
+ errors: [],
323
+ path: context.path
324
+ };
325
+ const result = executeComparison(target, value, tempContext);
326
+ if (result) {
327
+ addError(context, {
328
+ message: "Expected negated condition to fail",
329
+ received: target,
330
+ expected: { "not match": value }
331
+ });
268
332
  return false;
269
333
  }
270
- for (const key in target) {
271
- if (has.call(target, key) && target[key] !== void 0 && !has.call(value, key)) {
272
- return false;
334
+ return true;
335
+ }
336
+ case "any": {
337
+ for (const subComparison of value) {
338
+ const anyTempContext = {
339
+ errors: [],
340
+ path: context.path
341
+ };
342
+ if (executeComparison(target, subComparison, anyTempContext)) {
343
+ return true;
273
344
  }
274
345
  }
275
- for (const key in value) {
276
- if (has.call(value, key)) {
277
- if (!has.call(target, key)) {
278
- return false;
279
- }
280
- const targetValue = target[key];
281
- const partialValue = value[key];
282
- if (partialValue && typeof partialValue === "object" && "~sc" in partialValue && partialValue["~sc"][0] === "deepNoExtraDefinedKeys") {
283
- if (!executeComparison(targetValue, partialValue["~sc"])) {
284
- return false;
285
- }
286
- } else if (partialValue && typeof partialValue === "object" && !Array.isArray(partialValue) && partialValue.constructor === Object && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) && targetValue.constructor === Object) {
287
- if (!executeComparison(targetValue, [
288
- "deepNoExtraDefinedKeys",
289
- partialValue
290
- ])) {
291
- return false;
292
- }
293
- } else {
294
- if (!partialEqual(targetValue, partialValue)) {
295
- return false;
296
- }
297
- }
346
+ addError(context, {
347
+ message: "None of the alternative comparisons matched",
348
+ received: target,
349
+ expected: {
350
+ matchAny: value
351
+ }
352
+ });
353
+ return false;
354
+ }
355
+ case "all": {
356
+ let allMatch = true;
357
+ for (const subComparison of value) {
358
+ if (!executeComparison(target, subComparison, context)) {
359
+ allMatch = false;
298
360
  }
299
361
  }
300
- return true;
362
+ return allMatch;
363
+ }
364
+ case "withNoExtraKeys":
365
+ return checkNoExtraKeys(target, value, context, false);
366
+ case "withDeepNoExtraKeys":
367
+ return checkNoExtraKeys(target, value, context, true);
368
+ case "noExtraDefinedKeys":
369
+ return checkNoExtraDefinedKeys(target, value, context, false);
370
+ case "deepNoExtraDefinedKeys":
371
+ return checkNoExtraDefinedKeys(target, value, context, true);
301
372
  default:
302
- return false;
373
+ throw exhaustiveCheck(type);
303
374
  }
304
375
  }
305
- function partialEqual(target, sub) {
306
- if (sub === target) return true;
307
- if (sub && typeof sub === "object" && "~sc" in sub) {
308
- return executeComparison(target, sub["~sc"]);
376
+ function formatPath(path) {
377
+ if (path.length === 0) return "";
378
+ let result = path[0] || "";
379
+ for (let i = 1; i < path.length; i++) {
380
+ const segment = path[i];
381
+ if (segment && segment.startsWith("[") && segment.endsWith("]")) {
382
+ result += segment;
383
+ } else if (segment) {
384
+ if (result) {
385
+ result += `.${segment}`;
386
+ } else {
387
+ result += segment;
388
+ }
389
+ }
309
390
  }
310
- if (sub && target && sub.constructor === target.constructor) {
311
- const ctor = sub.constructor;
312
- if (ctor === Date) {
313
- return sub.getTime() === target.getTime();
391
+ return result;
392
+ }
393
+ function addError(context, error) {
394
+ context.errors.push({
395
+ path: formatPath(context.path),
396
+ ...error
397
+ });
398
+ }
399
+ function deepEqual(a, b) {
400
+ if (a === b) return true;
401
+ if (Number.isNaN(a) && Number.isNaN(b)) return true;
402
+ if (a === null || b === null) return false;
403
+ if (typeof a !== typeof b) return false;
404
+ if (Array.isArray(a) && Array.isArray(b)) {
405
+ if (a.length !== b.length) return false;
406
+ for (let i = 0; i < a.length; i++) {
407
+ if (!deepEqual(a[i], b[i])) return false;
314
408
  }
315
- if (ctor === RegExp) {
316
- return sub.toString() === target.toString();
409
+ return true;
410
+ }
411
+ if (typeof a === "object") {
412
+ const keysA = Object.keys(a);
413
+ const keysB = Object.keys(b);
414
+ if (keysA.length !== keysB.length) return false;
415
+ for (const key of keysA) {
416
+ if (!has.call(b, key) || !deepEqual(a[key], b[key])) return false;
317
417
  }
318
- if (ctor === Array) {
319
- if (sub.length > target.length) return false;
320
- for (let i = 0; i < sub.length; i++) {
321
- if (!partialEqual(target[i], sub[i])) return false;
322
- }
323
- return true;
418
+ return true;
419
+ }
420
+ return false;
421
+ }
422
+ function partialEqualInternal(target, sub, context) {
423
+ if (isComparison(sub)) {
424
+ return executeComparison(target, sub["~sc"], context);
425
+ }
426
+ if (isComparison(sub) && sub["~sc"][0] === "keyNotBePresent") {
427
+ addError(context, {
428
+ message: "Key should not be present",
429
+ received: target,
430
+ expected: "key not present"
431
+ });
432
+ return false;
433
+ }
434
+ if (target === sub) return true;
435
+ if (Number.isNaN(target) && Number.isNaN(sub)) return true;
436
+ if (target === null || sub === null || target === void 0 || sub === void 0) {
437
+ if (target !== sub) {
438
+ addError(context, {
439
+ message: "Value mismatch",
440
+ received: target,
441
+ expected: sub
442
+ });
443
+ return false;
324
444
  }
325
- if (ctor === Set) {
326
- if (sub.size > target.size) return false;
327
- for (const value of sub) {
328
- let found = false;
329
- if (value && typeof value === "object") {
330
- found = !!find(target, value);
331
- } else {
332
- found = target.has(value);
445
+ return true;
446
+ }
447
+ if (target instanceof Date && sub instanceof Date) {
448
+ if (target.getTime() !== sub.getTime()) {
449
+ addError(context, {
450
+ message: "Date mismatch",
451
+ received: target,
452
+ expected: sub
453
+ });
454
+ return false;
455
+ }
456
+ return true;
457
+ }
458
+ if (target instanceof RegExp && sub instanceof RegExp) {
459
+ if (target.source !== sub.source || target.flags !== sub.flags) {
460
+ addError(context, {
461
+ message: "RegExp mismatch",
462
+ received: target,
463
+ expected: sub
464
+ });
465
+ return false;
466
+ }
467
+ return true;
468
+ }
469
+ if (target instanceof Set && sub instanceof Set) {
470
+ if (sub.size > target.size) {
471
+ addError(context, {
472
+ message: "Set too small",
473
+ received: target,
474
+ expected: sub
475
+ });
476
+ return false;
477
+ }
478
+ for (const subValue of sub) {
479
+ let found = false;
480
+ for (const targetValue of target) {
481
+ const tempContext = {
482
+ errors: [],
483
+ path: context.path
484
+ };
485
+ if (partialEqualInternal(targetValue, subValue, tempContext)) {
486
+ found = true;
487
+ break;
333
488
  }
334
- if (!found) return false;
335
489
  }
336
- return true;
490
+ if (!found) {
491
+ addError(context, {
492
+ message: "Set element not found",
493
+ received: target,
494
+ expected: sub
495
+ });
496
+ return false;
497
+ }
337
498
  }
338
- if (ctor === Map) {
339
- if (sub.size > target.size) return false;
340
- for (const [key, value] of sub) {
341
- let targetKey = key;
342
- if (key && typeof key === "object") {
343
- targetKey = find(target, key);
344
- if (!targetKey) return false;
345
- }
346
- if (!target.has(targetKey) || !partialEqual(target.get(targetKey), value)) {
347
- return false;
499
+ return true;
500
+ }
501
+ if (target instanceof Map && sub instanceof Map) {
502
+ if (sub.size > target.size) {
503
+ addError(context, {
504
+ message: "Map too small",
505
+ received: target,
506
+ expected: sub
507
+ });
508
+ return false;
509
+ }
510
+ for (const [subKey, subValue] of sub) {
511
+ let found = false;
512
+ for (const [targetKey, targetValue] of target) {
513
+ const tempContextKey = {
514
+ errors: [],
515
+ path: context.path
516
+ };
517
+ const tempContextValue = {
518
+ errors: [],
519
+ path: context.path
520
+ };
521
+ if (partialEqualInternal(targetKey, subKey, tempContextKey) && partialEqualInternal(targetValue, subValue, tempContextValue)) {
522
+ found = true;
523
+ break;
348
524
  }
349
525
  }
350
- return true;
526
+ if (!found) {
527
+ addError(context, {
528
+ message: "Map entry not found",
529
+ received: target,
530
+ expected: sub
531
+ });
532
+ return false;
533
+ }
351
534
  }
352
- if (!ctor || typeof sub === "object") {
353
- for (const key in sub) {
354
- if (has.call(sub, key)) {
355
- const subValue = sub[key];
356
- if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "keyNotBePresent") {
357
- if (has.call(target, key)) {
358
- return false;
359
- }
360
- } else if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "any") {
361
- const targetHasKey = has.call(target, key);
362
- const targetValue = targetHasKey ? target[key] : void 0;
363
- if (!executeComparisonWithKeyContext(
364
- targetValue,
365
- subValue["~sc"],
366
- targetHasKey
367
- )) {
368
- return false;
369
- }
370
- } else {
371
- if (!has.call(target, key) || !partialEqual(target[key], subValue)) {
372
- return false;
535
+ return true;
536
+ }
537
+ if (typeof target !== typeof sub) {
538
+ addError(context, {
539
+ message: "Value mismatch",
540
+ received: target,
541
+ expected: sub
542
+ });
543
+ return false;
544
+ }
545
+ if (Array.isArray(sub)) {
546
+ if (!Array.isArray(target)) {
547
+ addError(context, {
548
+ message: "Expected array",
549
+ received: target,
550
+ expected: sub
551
+ });
552
+ return false;
553
+ }
554
+ if (target.length < sub.length) {
555
+ addError(context, {
556
+ message: `Array too short: expected at least ${sub.length} elements, got ${target.length}`,
557
+ received: target,
558
+ expected: sub
559
+ });
560
+ return false;
561
+ }
562
+ let allMatch = true;
563
+ for (let i = 0; i < sub.length; i++) {
564
+ const oldPath = context.path;
565
+ context.path = [...oldPath, `[${i}]`];
566
+ const result = partialEqualInternal(target[i], sub[i], context);
567
+ context.path = oldPath;
568
+ if (!result) allMatch = false;
569
+ }
570
+ return allMatch;
571
+ }
572
+ if (typeof sub === "object") {
573
+ if (typeof target !== "object" || Array.isArray(target)) {
574
+ addError(context, {
575
+ message: "Expected object",
576
+ received: target,
577
+ expected: sub
578
+ });
579
+ return false;
580
+ }
581
+ let allMatch = true;
582
+ for (const key of Object.keys(sub)) {
583
+ if (isComparison(sub[key]) && sub[key]["~sc"][0] === "keyNotBePresent") {
584
+ if (has.call(target, key)) {
585
+ const oldPath2 = context.path;
586
+ context.path = [...oldPath2, key];
587
+ addError(context, {
588
+ message: "Key should not be present",
589
+ received: target[key],
590
+ expected: "key not present"
591
+ });
592
+ context.path = oldPath2;
593
+ allMatch = false;
594
+ }
595
+ continue;
596
+ }
597
+ if (!has.call(target, key)) {
598
+ if (isComparison(sub[key])) {
599
+ const comparison = sub[key]["~sc"];
600
+ if (comparison[0] === "any") {
601
+ const anyComparisons = comparison[1];
602
+ const hasKeyNotBePresent = anyComparisons.some(
603
+ (comp) => comp[0] === "keyNotBePresent"
604
+ );
605
+ if (hasKeyNotBePresent) {
606
+ continue;
373
607
  }
374
608
  }
375
609
  }
610
+ const oldPath2 = context.path;
611
+ context.path = [...oldPath2, key];
612
+ addError(context, {
613
+ message: "Missing property",
614
+ received: void 0,
615
+ expected: sub[key]
616
+ });
617
+ context.path = oldPath2;
618
+ allMatch = false;
619
+ continue;
376
620
  }
377
- return true;
621
+ const oldPath = context.path;
622
+ context.path = [...oldPath, key];
623
+ const result = partialEqualInternal(target[key], sub[key], context);
624
+ context.path = oldPath;
625
+ if (!result) allMatch = false;
378
626
  }
627
+ return allMatch;
628
+ }
629
+ if (target !== sub) {
630
+ addError(context, {
631
+ message: "Value mismatch",
632
+ received: target,
633
+ expected: sub
634
+ });
635
+ return false;
636
+ }
637
+ return true;
638
+ }
639
+ function checkNoExtraKeys(target, partialShape, context, deep) {
640
+ if (typeof target !== "object" || target === null || Array.isArray(target)) {
641
+ addError(context, {
642
+ message: "Expected object for key validation",
643
+ received: target
644
+ });
645
+ return false;
646
+ }
647
+ if (!partialEqualInternal(target, partialShape, context)) {
648
+ return false;
649
+ }
650
+ const allowedKeys = new Set(Object.keys(partialShape));
651
+ for (const key of Object.keys(target)) {
652
+ if (!allowedKeys.has(key)) {
653
+ const oldPath = context.path;
654
+ context.path = [...oldPath, key];
655
+ addError(context, {
656
+ message: `Extra key "${key}" not expected`
657
+ });
658
+ context.path = oldPath;
659
+ return false;
660
+ }
661
+ }
662
+ if (deep) {
663
+ for (const key of Object.keys(partialShape)) {
664
+ if (typeof partialShape[key] === "object" && partialShape[key] !== null && !Array.isArray(partialShape[key]) && !isComparison(partialShape[key])) {
665
+ const oldPath = context.path;
666
+ context.path = [...oldPath, key];
667
+ const result = checkNoExtraKeys(
668
+ target[key],
669
+ partialShape[key],
670
+ context,
671
+ true
672
+ );
673
+ context.path = oldPath;
674
+ if (!result) return false;
675
+ }
676
+ }
677
+ }
678
+ return true;
679
+ }
680
+ function checkNoExtraDefinedKeys(target, partialShape, context, deep) {
681
+ if (typeof target !== "object" || target === null || Array.isArray(target)) {
682
+ addError(context, {
683
+ message: "Expected object for key validation",
684
+ received: target
685
+ });
686
+ return false;
687
+ }
688
+ if (!partialEqualInternal(target, partialShape, context)) {
689
+ return false;
690
+ }
691
+ const allowedKeys = new Set(Object.keys(partialShape));
692
+ for (const key of Object.keys(target)) {
693
+ if (!allowedKeys.has(key) && target[key] !== void 0) {
694
+ const oldPath = context.path;
695
+ context.path = [...oldPath, key];
696
+ addError(context, {
697
+ message: `Extra defined key "${key}" not expected`
698
+ });
699
+ context.path = oldPath;
700
+ return false;
701
+ }
702
+ }
703
+ if (deep) {
704
+ for (const key of Object.keys(partialShape)) {
705
+ if (typeof partialShape[key] === "object" && partialShape[key] !== null && !Array.isArray(partialShape[key]) && !isComparison(partialShape[key])) {
706
+ const oldPath = context.path;
707
+ context.path = [...oldPath, key];
708
+ const result = checkNoExtraDefinedKeys(
709
+ target[key],
710
+ partialShape[key],
711
+ context,
712
+ true
713
+ );
714
+ context.path = oldPath;
715
+ if (!result) return false;
716
+ }
717
+ }
718
+ }
719
+ return true;
720
+ }
721
+ function partialEqual(target, sub, returnErrors) {
722
+ const context = { errors: [], path: [] };
723
+ const result = partialEqualInternal(target, sub, context);
724
+ if (returnErrors) {
725
+ return result ? ok(void 0) : err(context.errors);
379
726
  }
380
- return sub !== sub && target !== target;
727
+ return result;
381
728
  }
382
729
  export {
383
730
  match,