xypriss 9.12.11 → 9.12.13

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.
@@ -2,10 +2,456 @@
2
2
 
3
3
  var xyprissSecurity = require('xypriss-security');
4
4
 
5
+ class ObjectWrapper {
6
+ constructor(obj) {
7
+ this.current = obj;
8
+ }
9
+ /**
10
+ * **value**
11
+ *
12
+ * Unwraps and returns the underlying plain object held by this wrapper.
13
+ * Call this at the end of a chain to get back a normal object.
14
+ *
15
+ * @returns The current wrapped object.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const obj = __sys__.utils.obj.of({ a: 1 });
20
+ * obj.value(); // { a: 1 }
21
+ * ```
22
+ */
23
+ value() {
24
+ return this.current;
25
+ }
26
+ /**
27
+ * **raw**
28
+ *
29
+ * Alias for {@link value}. Useful when `value` reads awkwardly in context.
30
+ *
31
+ * @returns The current wrapped object.
32
+ */
33
+ raw() {
34
+ return this.current;
35
+ }
36
+ /**
37
+ * **clone**
38
+ *
39
+ * Deep-clones the wrapped object (via `XStringify`) and continues
40
+ * the chain on the cloned copy, leaving the original untouched.
41
+ *
42
+ * @returns `this`, now wrapping a deep copy of the previous value.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const source = { nested: { count: 1 } };
47
+ * const obj = __sys__.utils.obj.of(source).clone();
48
+ * obj.value().nested.count = 99;
49
+ * // source.nested.count is still 1
50
+ * ```
51
+ */
52
+ clone() {
53
+ this.current = JSON.parse(xyprissSecurity.XStringify(this.current));
54
+ return this;
55
+ }
56
+ /**
57
+ * **pick**
58
+ *
59
+ * Narrows the wrapped object down to only the given keys.
60
+ *
61
+ * @param keys - The keys to keep.
62
+ * @returns `this`, now wrapping only the picked keys.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * __sys__.utils.obj.of({ a: 1, b: 2, c: 3 }).pick(["a", "c"]).value();
67
+ * // { a: 1, c: 3 }
68
+ * ```
69
+ */
70
+ pick(keys) {
71
+ const result = keys.reduce((acc, key) => {
72
+ if (key in this.current)
73
+ acc[key] = this.current[key];
74
+ return acc;
75
+ }, {});
76
+ return new ObjectWrapper(result);
77
+ }
78
+ /**
79
+ * **omit**
80
+ *
81
+ * Removes the given keys from the wrapped object.
82
+ *
83
+ * @param keys - The keys to remove.
84
+ * @returns `this`, now wrapping the object without those keys.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * __sys__.utils.obj.of({ a: 1, b: 2, c: 3 }).omit(["b"]).value();
89
+ * // { a: 1, c: 3 }
90
+ * ```
91
+ */
92
+ omit(keys) {
93
+ const result = { ...this.current };
94
+ keys.forEach((key) => delete result[key]);
95
+ return new ObjectWrapper(result);
96
+ }
97
+ /**
98
+ * **isEmpty**
99
+ *
100
+ * Checks whether the wrapped object has no own enumerable keys.
101
+ * This is a terminal read (does not return the wrapper), since it
102
+ * yields a boolean rather than an object.
103
+ *
104
+ * @returns `true` if the object has no own keys.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * __sys__.utils.obj.of({}).isEmpty(); // true
109
+ * ```
110
+ */
111
+ isEmpty() {
112
+ return Object.keys(this.current).length === 0;
113
+ }
114
+ /**
115
+ * **flatten**
116
+ *
117
+ * Collapses the wrapped object's nested structure into flat
118
+ * dot-notation (or custom separator) keys.
119
+ *
120
+ * @param separator - Path separator (default: `"."`).
121
+ * @returns A new wrapper around the flattened object.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * __sys__.utils.obj.of({ a: { b: 1 } }).flatten().value();
126
+ * // { "a.b": 1 }
127
+ * ```
128
+ */
129
+ flatten(separator = ".") {
130
+ const result = {};
131
+ const recurse = (current, path = "") => {
132
+ for (const [key, val] of Object.entries(current)) {
133
+ const newPath = path ? `${path}${separator}${key}` : key;
134
+ if (val &&
135
+ typeof val === "object" &&
136
+ !Array.isArray(val) &&
137
+ Object.keys(val).length > 0) {
138
+ recurse(val, newPath);
139
+ }
140
+ else {
141
+ result[newPath] = val;
142
+ }
143
+ }
144
+ };
145
+ recurse(this.current);
146
+ return new ObjectWrapper(result);
147
+ }
148
+ /**
149
+ * **unflatten**
150
+ *
151
+ * Reverses {@link flatten}, expanding dot-notation (or custom
152
+ * separator) keys back into a nested object.
153
+ *
154
+ * @param separator - Path separator used in the flat keys (default: `"."`).
155
+ * @returns A new wrapper around the nested object.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * __sys__.utils.obj.of({ "a.b": 1, "a.c": 2 }).unflatten().value();
160
+ * // { a: { b: 1, c: 2 } }
161
+ * ```
162
+ */
163
+ unflatten(separator = ".") {
164
+ const result = {};
165
+ for (const [flatKey, val] of Object.entries(this.current)) {
166
+ const parts = flatKey.split(separator);
167
+ let cursor = result;
168
+ parts.forEach((part, i) => {
169
+ if (i === parts.length - 1) {
170
+ cursor[part] = val;
171
+ }
172
+ else {
173
+ if (typeof cursor[part] !== "object" ||
174
+ cursor[part] === null) {
175
+ cursor[part] = {};
176
+ }
177
+ cursor = cursor[part];
178
+ }
179
+ });
180
+ }
181
+ return new ObjectWrapper(result);
182
+ }
183
+ /**
184
+ * **merge**
185
+ *
186
+ * Deep-merges one or more source objects into the wrapped object.
187
+ * Plain object values are merged recursively; arrays and primitives
188
+ * are overwritten by the last source that defines them.
189
+ *
190
+ * @param sources - One or more partial objects to merge in.
191
+ * @returns `this`, now wrapping the merged result.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * __sys__.utils.obj.of({ a: 1, nested: { x: 1 } })
196
+ * .merge({ nested: { y: 2 } })
197
+ * .value();
198
+ * // { a: 1, nested: { x: 1, y: 2 } }
199
+ * ```
200
+ */
201
+ merge(...sources) {
202
+ const isPlainObject = (val) => !!val && typeof val === "object" && !Array.isArray(val);
203
+ const deepMerge = (target, source) => {
204
+ for (const key of Object.keys(source)) {
205
+ if (isPlainObject(source[key]) && isPlainObject(target[key])) {
206
+ deepMerge(target[key], source[key]);
207
+ }
208
+ else {
209
+ target[key] = source[key];
210
+ }
211
+ }
212
+ return target;
213
+ };
214
+ this.current = sources.reduce((acc, src) => deepMerge(acc, src), this.current);
215
+ return this;
216
+ }
217
+ /**
218
+ * **mapValues**
219
+ *
220
+ * Transforms every value of the wrapped object using `fn`, keeping
221
+ * the same keys.
222
+ *
223
+ * @param fn - Mapping function receiving `(value, key)`.
224
+ * @returns A new wrapper around the transformed object.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * __sys__.utils.obj.of({ a: 1, b: 2 }).mapValues((v) => v * 10).value();
229
+ * // { a: 10, b: 20 }
230
+ * ```
231
+ */
232
+ mapValues(fn) {
233
+ const result = {};
234
+ for (const key of Object.keys(this.current)) {
235
+ result[key] = fn(this.current[key], key);
236
+ }
237
+ return new ObjectWrapper(result);
238
+ }
239
+ /**
240
+ * **mapKeys**
241
+ *
242
+ * Transforms every key of the wrapped object using `fn`, keeping
243
+ * the same values.
244
+ *
245
+ * @param fn - Mapping function receiving `(key, value)`. Must return a string.
246
+ * @returns A new wrapper around the object with renamed keys.
247
+ *
248
+ * @example
249
+ * ```ts
250
+ * __sys__.utils.obj.of({ a: 1, b: 2 })
251
+ * .mapKeys((k) => k.toUpperCase())
252
+ * .value();
253
+ * // { A: 1, B: 2 }
254
+ * ```
255
+ */
256
+ mapKeys(fn) {
257
+ const result = {};
258
+ for (const key of Object.keys(this.current)) {
259
+ result[fn(key, this.current[key])] = this.current[key];
260
+ }
261
+ return new ObjectWrapper(result);
262
+ }
263
+ /**
264
+ * **filter**
265
+ *
266
+ * Keeps only the key-value pairs for which `predicate` returns `true`.
267
+ *
268
+ * @param predicate - Function receiving `(value, key)`.
269
+ * @returns A new wrapper around the filtered object.
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * __sys__.utils.obj.of({ a: 1, b: 2, c: 3 })
274
+ * .filter((v) => v > 1)
275
+ * .value();
276
+ * // { b: 2, c: 3 }
277
+ * ```
278
+ */
279
+ filter(predicate) {
280
+ const result = {};
281
+ for (const key of Object.keys(this.current)) {
282
+ if (predicate(this.current[key], key)) {
283
+ result[key] = this.current[key];
284
+ }
285
+ }
286
+ return new ObjectWrapper(result);
287
+ }
288
+ /**
289
+ * **keys**
290
+ *
291
+ * Returns the own enumerable keys of the wrapped object.
292
+ * This is a terminal read.
293
+ *
294
+ * @returns Array of keys.
295
+ */
296
+ keys() {
297
+ return Object.keys(this.current);
298
+ }
299
+ /**
300
+ * **values**
301
+ *
302
+ * Returns the own enumerable values of the wrapped object.
303
+ * This is a terminal read.
304
+ *
305
+ * @returns Array of values.
306
+ */
307
+ values() {
308
+ return Object.values(this.current);
309
+ }
310
+ /**
311
+ * **entries**
312
+ *
313
+ * Returns the own enumerable `[key, value]` pairs of the wrapped object.
314
+ * This is a terminal read.
315
+ *
316
+ * @returns Array of `[key, value]` tuples.
317
+ */
318
+ entries() {
319
+ return Object.entries(this.current);
320
+ }
321
+ /**
322
+ * **has**
323
+ *
324
+ * Checks whether the wrapped object has the given own key.
325
+ * This is a terminal read.
326
+ *
327
+ * @param key - The key to check.
328
+ * @returns `true` if the key exists on the object.
329
+ */
330
+ has(key) {
331
+ return Object.prototype.hasOwnProperty.call(this.current, key);
332
+ }
333
+ /**
334
+ * **get**
335
+ *
336
+ * Safely reads a possibly-nested value using dot-notation path,
337
+ * returning `fallback` if any part of the path is missing.
338
+ * This is a terminal read.
339
+ *
340
+ * @param path - Dot-notation path (e.g. `"a.b.c"`).
341
+ * @param fallback - Value returned if the path can't be resolved.
342
+ * @returns The resolved value or the fallback.
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * __sys__.utils.obj.of({ a: { b: { c: 42 } } }).get("a.b.c"); // 42
347
+ * __sys__.utils.obj.of({ a: {} }).get("a.b.c", "missing"); // "missing"
348
+ * ```
349
+ */
350
+ get(path, fallback = undefined) {
351
+ const parts = path.split(".");
352
+ let cursor = this.current;
353
+ for (const part of parts) {
354
+ if (cursor === null || cursor === undefined)
355
+ return fallback;
356
+ cursor = cursor[part];
357
+ }
358
+ return cursor === undefined ? fallback : cursor;
359
+ }
360
+ /**
361
+ * **set**
362
+ *
363
+ * Sets a possibly-nested value using dot-notation path, creating
364
+ * intermediate objects as needed.
365
+ *
366
+ * @param path - Dot-notation path (e.g. `"a.b.c"`).
367
+ * @param value - The value to set.
368
+ * @returns `this`, with the value set at the given path.
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * __sys__.utils.obj.of<any>({}).set("a.b.c", 42).value();
373
+ * // { a: { b: { c: 42 } } }
374
+ * ```
375
+ */
376
+ set(path, value) {
377
+ const parts = path.split(".");
378
+ let cursor = this.current;
379
+ parts.forEach((part, i) => {
380
+ if (i === parts.length - 1) {
381
+ cursor[part] = value;
382
+ }
383
+ else {
384
+ if (typeof cursor[part] !== "object" || cursor[part] === null) {
385
+ cursor[part] = {};
386
+ }
387
+ cursor = cursor[part];
388
+ }
389
+ });
390
+ return this;
391
+ }
392
+ /**
393
+ * **equals**
394
+ *
395
+ * Performs a deep structural equality check between the wrapped
396
+ * object and `other`. This is a terminal read.
397
+ *
398
+ * @param other - The object to compare against.
399
+ * @returns `true` if both objects are deeply equal.
400
+ *
401
+ * @example
402
+ * ```ts
403
+ * __sys__.utils.obj.of({ a: { b: 1 } }).equals({ a: { b: 1 } }); // true
404
+ * ```
405
+ */
406
+ equals(other) {
407
+ return __sys__.utils.obj.deepEqual(this.current, other);
408
+ }
409
+ }
5
410
  /**
6
411
  * **ObjectUtils — XyPriss Object Utilities**
412
+ *
413
+ * All original methods remain fully supported and are **not deprecated**;
414
+ * they are the right choice for one-off, single-call operations. For
415
+ * chaining several operations on the same object without repeating it as
416
+ * an argument each time, use {@link __sys__.utils.obj.of} to get a fluent
417
+ * {@link ObjectWrapper} instance instead:
418
+ *
419
+ * @example
420
+ * ```ts
421
+ * // Classic style (still fully supported):
422
+ * const utils = new ObjectUtils();
423
+ * utils.omit(utils.pick(obj, ["a", "b", "c"]), ["c"]);
424
+ *
425
+ * // Fluent style (new, optional):
426
+ * const result = __sys__.utils.obj.of(obj)
427
+ * .pick(["a", "b", "c"])
428
+ * .omit(["c"])
429
+ * .value();
430
+ * ```
7
431
  */
8
432
  class ObjectUtils {
433
+ /**
434
+ * **of**
435
+ *
436
+ * Wraps `obj` in a chainable {@link ObjectWrapper}, so multiple
437
+ * operations can be applied in sequence without re-declaring the
438
+ * object as an argument each time.
439
+ *
440
+ * This does not mutate or replace the classic API — it's an
441
+ * additive convenience entry point.
442
+ *
443
+ * @param obj - The object to wrap.
444
+ * @returns An {@link ObjectWrapper} bound to `obj`.
445
+ *
446
+ * @example
447
+ * ```ts
448
+ * const obj = __sys__.utils.obj.of({ a: 1, b: 2, c: 3 });
449
+ * const picked = obj.pick(["a", "b"]).value(); // { a: 1, b: 2 }
450
+ * ```
451
+ */
452
+ static of(obj) {
453
+ return new ObjectWrapper(obj);
454
+ }
9
455
  /**
10
456
  * **Deep Clone an Object**
11
457
  *
@@ -97,6 +543,12 @@ class ObjectUtils {
97
543
  * @param obj - The object to flatten.
98
544
  * @param separator - Optional path separator (default: `"."`).
99
545
  * @returns A shallow object with path-based keys.
546
+ *
547
+ * @example
548
+ * ```ts
549
+ * utils.flattenObject({ a: { b: 1, c: { d: 2 } } });
550
+ * // { "a.b": 1, "a.c.d": 2 }
551
+ * ```
100
552
  */
101
553
  flattenObject(obj, separator = ".") {
102
554
  const result = {};
@@ -117,6 +569,42 @@ class ObjectUtils {
117
569
  recurse(obj);
118
570
  return result;
119
571
  }
572
+ /**
573
+ * **unflattenObject**
574
+ *
575
+ * Reverses {@link flattenObject}, expanding dot-notation (or custom
576
+ * separator) keys back into a nested object structure.
577
+ *
578
+ * @param obj - The flat object to expand.
579
+ * @param separator - Path separator used in the flat keys (default: `"."`).
580
+ * @returns A nested object reconstructed from the flat keys.
581
+ *
582
+ * @example
583
+ * ```ts
584
+ * utils.unflattenObject({ "a.b": 1, "a.c.d": 2 });
585
+ * // { a: { b: 1, c: { d: 2 } } }
586
+ * ```
587
+ */
588
+ unflattenObject(obj, separator = ".") {
589
+ const result = {};
590
+ for (const [flatKey, val] of Object.entries(obj)) {
591
+ const parts = flatKey.split(separator);
592
+ let cursor = result;
593
+ parts.forEach((part, i) => {
594
+ if (i === parts.length - 1) {
595
+ cursor[part] = val;
596
+ }
597
+ else {
598
+ if (typeof cursor[part] !== "object" ||
599
+ cursor[part] === null) {
600
+ cursor[part] = {};
601
+ }
602
+ cursor = cursor[part];
603
+ }
604
+ });
605
+ }
606
+ return result;
607
+ }
120
608
  /**
121
609
  * **parse**
122
610
  *
@@ -126,6 +614,12 @@ class ObjectUtils {
126
614
  * @param json The JSON string to parse.
127
615
  * @param fallback The fallback value to return on failure.
128
616
  * @returns The parsed object or the fallback value.
617
+ *
618
+ * @example
619
+ * ```ts
620
+ * utils.parse('{"a":1}'); // { a: 1 }
621
+ * utils.parse("not json", {}); // {}
622
+ * ```
129
623
  */
130
624
  parse(json, fallback = null) {
131
625
  try {
@@ -135,7 +629,345 @@ class ObjectUtils {
135
629
  return fallback;
136
630
  }
137
631
  }
632
+ /**
633
+ * **merge**
634
+ *
635
+ * Deep-merges one or more source objects into a copy of `target`.
636
+ * Plain object values are merged recursively; arrays and primitive
637
+ * values are overwritten by the last source that defines them.
638
+ * The original `target` and `sources` are never mutated.
639
+ *
640
+ * @param target - The base object.
641
+ * @param sources - One or more objects to merge into the base.
642
+ * @returns A new object containing the deep-merged result.
643
+ *
644
+ * @example
645
+ * ```ts
646
+ * utils.merge({ a: 1, nested: { x: 1 } }, { nested: { y: 2 } });
647
+ * // { a: 1, nested: { x: 1, y: 2 } }
648
+ * ```
649
+ */
650
+ merge(target, ...sources) {
651
+ const isPlainObject = (val) => !!val && typeof val === "object" && !Array.isArray(val);
652
+ const deepMerge = (t, s) => {
653
+ for (const key of Object.keys(s)) {
654
+ if (isPlainObject(s[key]) && isPlainObject(t[key])) {
655
+ deepMerge(t[key], s[key]);
656
+ }
657
+ else {
658
+ t[key] = s[key];
659
+ }
660
+ }
661
+ return t;
662
+ };
663
+ const base = JSON.parse(xyprissSecurity.XStringify(target));
664
+ return sources.reduce((acc, src) => deepMerge(acc, src), base);
665
+ }
666
+ /**
667
+ * **mapValues**
668
+ *
669
+ * Builds a new object by transforming every value of `obj` with `fn`,
670
+ * keeping the same keys.
671
+ *
672
+ * @param obj - The source object.
673
+ * @param fn - Mapping function receiving `(value, key)`.
674
+ * @returns A new object with transformed values.
675
+ *
676
+ * @example
677
+ * ```ts
678
+ * utils.mapValues({ a: 1, b: 2 }, (v) => v * 10);
679
+ * // { a: 10, b: 20 }
680
+ * ```
681
+ */
682
+ mapValues(obj, fn) {
683
+ const result = {};
684
+ for (const key of Object.keys(obj)) {
685
+ result[key] = fn(obj[key], key);
686
+ }
687
+ return result;
688
+ }
689
+ /**
690
+ * **mapKeys**
691
+ *
692
+ * Builds a new object by transforming every key of `obj` with `fn`,
693
+ * keeping the same values.
694
+ *
695
+ * @param obj - The source object.
696
+ * @param fn - Mapping function receiving `(key, value)`. Must return a string.
697
+ * @returns A new object with renamed keys.
698
+ *
699
+ * @example
700
+ * ```ts
701
+ * utils.mapKeys({ a: 1, b: 2 }, (k) => k.toUpperCase());
702
+ * // { A: 1, B: 2 }
703
+ * ```
704
+ */
705
+ mapKeys(obj, fn) {
706
+ const result = {};
707
+ for (const key of Object.keys(obj)) {
708
+ result[fn(key, obj[key])] = obj[key];
709
+ }
710
+ return result;
711
+ }
712
+ /**
713
+ * **filter**
714
+ *
715
+ * Builds a new object containing only the key-value pairs of `obj`
716
+ * for which `predicate` returns `true`.
717
+ *
718
+ * @param obj - The source object.
719
+ * @param predicate - Function receiving `(value, key)`.
720
+ * @returns A new, filtered object.
721
+ *
722
+ * @example
723
+ * ```ts
724
+ * utils.filter({ a: 1, b: 2, c: 3 }, (v) => v > 1);
725
+ * // { b: 2, c: 3 }
726
+ * ```
727
+ */
728
+ filter(obj, predicate) {
729
+ const result = {};
730
+ for (const key of Object.keys(obj)) {
731
+ if (predicate(obj[key], key)) {
732
+ result[key] = obj[key];
733
+ }
734
+ }
735
+ return result;
736
+ }
737
+ /**
738
+ * **get**
739
+ *
740
+ * Safely reads a possibly-nested value from `obj` using a dot-notation
741
+ * path, returning `fallback` if any part of the path is missing.
742
+ *
743
+ * @param obj - The source object.
744
+ * @param path - Dot-notation path (e.g. `"a.b.c"`).
745
+ * @param fallback - Value returned if the path can't be resolved.
746
+ * @returns The resolved value or the fallback.
747
+ *
748
+ * @example
749
+ * ```ts
750
+ * utils.get({ a: { b: { c: 42 } } }, "a.b.c"); // 42
751
+ * utils.get({ a: {} }, "a.b.c", "missing"); // "missing"
752
+ * ```
753
+ */
754
+ get(obj, path, fallback = undefined) {
755
+ const parts = path.split(".");
756
+ let cursor = obj;
757
+ for (const part of parts) {
758
+ if (cursor === null || cursor === undefined)
759
+ return fallback;
760
+ cursor = cursor[part];
761
+ }
762
+ return cursor === undefined ? fallback : cursor;
763
+ }
764
+ /**
765
+ * **set**
766
+ *
767
+ * Returns a new object equal to `obj` but with a possibly-nested value
768
+ * set at `path` (dot-notation), creating intermediate objects as needed.
769
+ * The original `obj` is not mutated.
770
+ *
771
+ * @param obj - The source object.
772
+ * @param path - Dot-notation path (e.g. `"a.b.c"`).
773
+ * @param value - The value to set.
774
+ * @returns A new object with the value set at the given path.
775
+ *
776
+ * @example
777
+ * ```ts
778
+ * utils.set({}, "a.b.c", 42);
779
+ * // { a: { b: { c: 42 } } }
780
+ * ```
781
+ */
782
+ set(obj, path, value) {
783
+ const clone = JSON.parse(xyprissSecurity.XStringify(obj));
784
+ const parts = path.split(".");
785
+ let cursor = clone;
786
+ parts.forEach((part, i) => {
787
+ if (i === parts.length - 1) {
788
+ cursor[part] = value;
789
+ }
790
+ else {
791
+ if (typeof cursor[part] !== "object" || cursor[part] === null) {
792
+ cursor[part] = {};
793
+ }
794
+ cursor = cursor[part];
795
+ }
796
+ });
797
+ return clone;
798
+ }
799
+ /**
800
+ * **has**
801
+ *
802
+ * Checks whether `obj` has the given own key (shortcut over
803
+ * `Object.prototype.hasOwnProperty`).
804
+ *
805
+ * @param obj - The object to inspect.
806
+ * @param key - The key to check.
807
+ * @returns `true` if the key exists as an own property.
808
+ *
809
+ * @example
810
+ * ```ts
811
+ * utils.has({ a: 1 }, "a"); // true
812
+ * utils.has({ a: 1 }, "b"); // false
813
+ * ```
814
+ */
815
+ has(obj, key) {
816
+ return Object.prototype.hasOwnProperty.call(obj, key);
817
+ }
818
+ /**
819
+ * **deepEqual**
820
+ *
821
+ * Performs a deep structural equality check between two values,
822
+ * comparing plain objects and arrays recursively by value rather
823
+ * than by reference.
824
+ *
825
+ * @param a - First value.
826
+ * @param b - Second value.
827
+ * @returns `true` if both values are deeply equal.
828
+ *
829
+ * @example
830
+ * ```ts
831
+ * utils.deepEqual({ a: { b: 1 } }, { a: { b: 1 } }); // true
832
+ * utils.deepEqual({ a: 1 }, { a: 2 }); // false
833
+ * ```
834
+ */
835
+ static deepEqual(a, b) {
836
+ if (a === b)
837
+ return true;
838
+ if (typeof a !== typeof b)
839
+ return false;
840
+ if (a === null || b === null)
841
+ return a === b;
842
+ if (typeof a !== "object")
843
+ return false;
844
+ if (Array.isArray(a) || Array.isArray(b)) {
845
+ if (!Array.isArray(a) || !Array.isArray(b))
846
+ return false;
847
+ if (a.length !== b.length)
848
+ return false;
849
+ return a.every((item, i) => __sys__.utils.obj.deepEqual(item, b[i]));
850
+ }
851
+ const aKeys = Object.keys(a);
852
+ const bKeys = Object.keys(b);
853
+ if (aKeys.length !== bKeys.length)
854
+ return false;
855
+ return aKeys.every((key) => __sys__.utils.obj.deepEqual(a[key], b[key]));
856
+ }
857
+ /**
858
+ * **deepEqual** (instance method)
859
+ *
860
+ * Instance-bound convenience wrapper around the static
861
+ * {@link __sys__.utils.obj.deepEqual}, for callers already holding an
862
+ * `ObjectUtils` instance.
863
+ *
864
+ * @param a - First value.
865
+ * @param b - Second value.
866
+ * @returns `true` if both values are deeply equal.
867
+ */
868
+ deepEqual(a, b) {
869
+ return __sys__.utils.obj.deepEqual(a, b);
870
+ }
871
+ /**
872
+ * **invert**
873
+ *
874
+ * Swaps keys and values: returns a new object where each original
875
+ * value (stringified) becomes a key, and each original key becomes
876
+ * its value. If multiple keys share a value, the last one wins.
877
+ *
878
+ * @param obj - The source object. Values are coerced to strings for keys.
879
+ * @returns A new object with keys and values swapped.
880
+ *
881
+ * @example
882
+ * ```ts
883
+ * utils.invert({ a: "x", b: "y" });
884
+ * // { x: "a", y: "b" }
885
+ * ```
886
+ */
887
+ invert(obj) {
888
+ const result = {};
889
+ for (const [key, value] of Object.entries(obj)) {
890
+ result[String(value)] = key;
891
+ }
892
+ return result;
893
+ }
894
+ /**
895
+ * **defaults**
896
+ *
897
+ * Returns a new object built from `obj`, filling in any keys that are
898
+ * `undefined` with the corresponding value from `defaultsObj`.
899
+ * Does not overwrite keys that already have a defined value.
900
+ *
901
+ * @param obj - The source object.
902
+ * @param defaultsObj - Object providing fallback values.
903
+ * @returns A new object with defaults applied.
904
+ *
905
+ * @example
906
+ * ```ts
907
+ * utils.defaults({ a: 1, b: undefined }, { b: 2, c: 3 });
908
+ * // { a: 1, b: 2, c: 3 }
909
+ * ```
910
+ */
911
+ defaults(obj, defaultsObj) {
912
+ const result = { ...defaultsObj, ...obj };
913
+ for (const key of Object.keys(result)) {
914
+ if (result[key] === undefined && key in defaultsObj) {
915
+ result[key] = defaultsObj[key];
916
+ }
917
+ }
918
+ return result;
919
+ }
920
+ /**
921
+ * **compact**
922
+ *
923
+ * Returns a new object with all keys whose value is `null` or
924
+ * `undefined` removed.
925
+ *
926
+ * @param obj - The source object.
927
+ * @returns A new object without null/undefined values.
928
+ *
929
+ * @example
930
+ * ```ts
931
+ * utils.compact({ a: 1, b: null, c: undefined, d: 0 });
932
+ * // { a: 1, d: 0 }
933
+ * ```
934
+ */
935
+ compact(obj) {
936
+ const result = {};
937
+ for (const key of Object.keys(obj)) {
938
+ const val = obj[key];
939
+ if (val !== null && val !== undefined) {
940
+ result[key] = val;
941
+ }
942
+ }
943
+ return result;
944
+ }
945
+ /**
946
+ * **isPlainObject**
947
+ *
948
+ * Checks whether `value` is a plain object (i.e. not `null`, not an
949
+ * array, not a class instance created from a custom prototype, and
950
+ * not a built-in like `Date`, `Map`, or `Set`).
951
+ *
952
+ * @param value - The value to check.
953
+ * @returns `true` if `value` is a plain object.
954
+ *
955
+ * @example
956
+ * ```ts
957
+ * utils.isPlainObject({}); // true
958
+ * utils.isPlainObject([]); // false
959
+ * utils.isPlainObject(new Date()); // false
960
+ * utils.isPlainObject(null); // false
961
+ * ```
962
+ */
963
+ isPlainObject(value) {
964
+ if (value === null || typeof value !== "object")
965
+ return false;
966
+ const proto = Object.getPrototypeOf(value);
967
+ return proto === Object.prototype || proto === null;
968
+ }
138
969
  }
139
970
 
140
971
  exports.ObjectUtils = ObjectUtils;
972
+ exports.ObjectWrapper = ObjectWrapper;
141
973
  //# sourceMappingURL=ObjectUtils.js.map