@ls-stack/utils 3.23.0 → 3.24.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.
package/lib/testUtils.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/testUtils.ts
21
21
  var testUtils_exports = {};
22
22
  __export(testUtils_exports, {
23
+ compactSnapshot: () => compactSnapshot,
23
24
  createLoggerStore: () => createLoggerStore,
24
25
  getResultFn: () => getResultFn,
25
26
  waitController: () => waitController
@@ -30,6 +31,17 @@ module.exports = __toCommonJS(testUtils_exports);
30
31
  function isObject(value) {
31
32
  return typeof value === "object" && value !== null && !Array.isArray(value);
32
33
  }
34
+ function isPlainObject(value) {
35
+ if (!value || typeof value !== "object") return false;
36
+ const proto = Object.getPrototypeOf(value);
37
+ if (proto === null) {
38
+ return true;
39
+ }
40
+ const Ctor = Object.hasOwnProperty.call(proto, "constructor") && proto.constructor;
41
+ if (Ctor === Object) return true;
42
+ const objectCtorString = Object.prototype.constructor.toString();
43
+ return typeof Ctor == "function" && Function.toString.call(Ctor) === objectCtorString;
44
+ }
33
45
 
34
46
  // src/assertions.ts
35
47
  var isObject2 = isObject;
@@ -153,6 +165,368 @@ function defer() {
153
165
  return { resolve, reject, promise };
154
166
  }
155
167
 
168
+ // src/conversions.ts
169
+ function bytesToHumanReadable(bytes) {
170
+ if (bytes < 1024) {
171
+ return `${bytes} B`;
172
+ }
173
+ const kb = bytes / 1024;
174
+ if (kb < 1024) {
175
+ return `${kb.toFixed(2)} KB`;
176
+ }
177
+ const mb = kb / 1024;
178
+ if (mb < 1024) {
179
+ return `${mb.toFixed(2)} MB`;
180
+ }
181
+ const gb = mb / 1024;
182
+ return `${gb.toFixed(2)} GB`;
183
+ }
184
+
185
+ // src/stringUtils.ts
186
+ function truncateString(str, length, ellipsis = "\u2026") {
187
+ if (str.length <= length) return str;
188
+ return str.slice(0, length - 1) + ellipsis;
189
+ }
190
+
191
+ // src/yamlStringify.ts
192
+ function yamlStringify(obj, {
193
+ maxLineLength = 100,
194
+ showUndefined,
195
+ maxDepth = 50,
196
+ collapseObjects = false,
197
+ addRootObjSpaces = "beforeAndAfter"
198
+ } = {}) {
199
+ if (isObject(obj) || Array.isArray(obj) || typeof obj === "function") {
200
+ return `${stringifyValue(obj, "", maxLineLength, !!showUndefined, maxDepth, 0, collapseObjects, addRootObjSpaces)}
201
+ `;
202
+ }
203
+ return JSON.stringify(obj) || "undefined";
204
+ }
205
+ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, depth, collapseObjects, addObjSpaces) {
206
+ let result = "";
207
+ const childIndent = `${indent} `;
208
+ if (isPlainObject(value)) {
209
+ if (Object.keys(value).length === 0) {
210
+ return "{}";
211
+ }
212
+ if (collapseObjects && depth > 0) {
213
+ const entries = Object.entries(value).filter(
214
+ ([, val]) => val !== void 0 || showUndefined
215
+ );
216
+ const isSimpleObject = entries.every(
217
+ ([, val]) => {
218
+ if (typeof val === "string") {
219
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
220
+ }
221
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
222
+ }
223
+ );
224
+ if (isSimpleObject && entries.length > 0) {
225
+ let line = "{ ";
226
+ line += entries.map(([key, val]) => {
227
+ let valueStr;
228
+ if (typeof val === "string") {
229
+ if (val.includes("'") && !val.includes('"')) {
230
+ valueStr = `"${val}"`;
231
+ } else if (val.includes('"') && !val.includes("'")) {
232
+ valueStr = `'${val}'`;
233
+ } else if (val.includes("'") && val.includes('"')) {
234
+ valueStr = `"${val.replace(/"/g, '\\"')}"`;
235
+ } else {
236
+ valueStr = `'${val}'`;
237
+ }
238
+ } else {
239
+ valueStr = String(val);
240
+ }
241
+ return `${key}: ${valueStr}`;
242
+ }).join(", ");
243
+ line += " }";
244
+ if (line.length <= maxLineLength) {
245
+ return line;
246
+ }
247
+ }
248
+ }
249
+ let prevValue;
250
+ let afterSpaceWasAdded = false;
251
+ for (let [key, objVal] of Object.entries(value)) {
252
+ if (objVal === void 0 && !showUndefined) {
253
+ continue;
254
+ }
255
+ if (depth > maxDepth) {
256
+ objVal = `{max depth reached}`;
257
+ }
258
+ const normalizedValue2 = normalizeValue(objVal);
259
+ if (normalizedValue2 !== null) {
260
+ objVal = normalizedValue2[1];
261
+ key = `${key}{${normalizedValue2[0]}}`;
262
+ }
263
+ const valueString = stringifyValue(
264
+ objVal,
265
+ childIndent,
266
+ maxLineLength,
267
+ showUndefined,
268
+ maxDepth,
269
+ depth + 1,
270
+ collapseObjects,
271
+ addObjSpaces
272
+ );
273
+ const willBeCollapsed = isObject(objVal) && (Object.keys(objVal).length === 0 || collapseObjects && depth + 1 > 0 && Object.entries(objVal).filter(([, val]) => val !== void 0 || showUndefined).every(([, val]) => {
274
+ if (typeof val === "string") {
275
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
276
+ }
277
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
278
+ }));
279
+ const prevWasCollapsed = prevValue && isObject(prevValue) && (Object.keys(prevValue).length === 0 || collapseObjects && depth + 1 > 0 && Object.entries(prevValue).filter(([, val]) => val !== void 0 || showUndefined).every(([, val]) => {
280
+ if (typeof val === "string") {
281
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
282
+ }
283
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
284
+ }));
285
+ if (!afterSpaceWasAdded && indent === "" && isObject(objVal) && !willBeCollapsed && prevValue && !prevWasCollapsed && (addObjSpaces === "before" || addObjSpaces === "beforeAndAfter")) {
286
+ result += "\n";
287
+ }
288
+ if (Array.isArray(objVal)) {
289
+ const arrayIsSingleLine = valueString.split("\n").length === 1;
290
+ if (arrayIsSingleLine && !valueString.trim().startsWith("-")) {
291
+ result += `${indent}${key}: `;
292
+ } else {
293
+ result += `${indent}${key}:
294
+ `;
295
+ }
296
+ } else if (isObject(objVal)) {
297
+ const isCollapsedObject = valueString.startsWith("{") && !valueString.includes("\n");
298
+ if (Object.keys(objVal).length === 0 || isCollapsedObject) {
299
+ result += `${indent}${key}: `;
300
+ } else {
301
+ result += `${indent}${key}:
302
+ `;
303
+ }
304
+ } else {
305
+ result += `${indent}${key}: `;
306
+ }
307
+ result += valueString;
308
+ result += "\n";
309
+ if (indent === "") {
310
+ const isCollapsedObject = valueString.startsWith("{") && !valueString.includes("\n") && valueString.length > 2;
311
+ if (isObject(objVal) && !isCollapsedObject) {
312
+ if (addObjSpaces === "after" || addObjSpaces === "beforeAndAfter") {
313
+ result += "\n";
314
+ afterSpaceWasAdded = true;
315
+ } else {
316
+ afterSpaceWasAdded = false;
317
+ }
318
+ }
319
+ }
320
+ prevValue = objVal;
321
+ }
322
+ return result.trimEnd();
323
+ }
324
+ if (Array.isArray(value)) {
325
+ let arrayWasAdded = false;
326
+ if (value.length === 0 || value.every(
327
+ (item) => typeof item === "string" || typeof item === "number" || typeof item === "boolean" || item === null || item === void 0
328
+ )) {
329
+ let line = "";
330
+ line += `[`;
331
+ line += value.map((item) => {
332
+ let valueToUse = item;
333
+ if (depth > maxDepth) {
334
+ valueToUse = `{max depth reached}`;
335
+ }
336
+ if (typeof valueToUse === "string" && valueToUse.includes("\n")) {
337
+ valueToUse = valueToUse.replace(/\n/g, "\\n");
338
+ }
339
+ return stringifyValue(
340
+ valueToUse,
341
+ "",
342
+ maxLineLength,
343
+ showUndefined,
344
+ maxDepth,
345
+ depth + 1,
346
+ collapseObjects,
347
+ addObjSpaces
348
+ );
349
+ }).join(", ");
350
+ line += "]";
351
+ if (line.length <= maxLineLength) {
352
+ result += line;
353
+ arrayWasAdded = true;
354
+ }
355
+ }
356
+ if (!arrayWasAdded) {
357
+ for (let item of value) {
358
+ if (depth > maxDepth) {
359
+ item = `{max depth reached}`;
360
+ }
361
+ result += `${indent}- `;
362
+ if (Array.isArray(item) || isObject(item)) {
363
+ let arrayString = stringifyValue(
364
+ item,
365
+ childIndent,
366
+ maxLineLength,
367
+ showUndefined,
368
+ maxDepth,
369
+ depth + 1,
370
+ collapseObjects,
371
+ addObjSpaces
372
+ );
373
+ arrayString = arrayString.trimStart();
374
+ result += arrayString;
375
+ } else {
376
+ result += stringifyValue(
377
+ item,
378
+ childIndent,
379
+ maxLineLength,
380
+ showUndefined,
381
+ maxDepth,
382
+ depth + 1,
383
+ collapseObjects,
384
+ addObjSpaces
385
+ );
386
+ }
387
+ result += "\n";
388
+ }
389
+ }
390
+ return result.trimEnd();
391
+ }
392
+ if (typeof value === "string") {
393
+ if (value.includes("\n")) {
394
+ const lines = value.split("\n");
395
+ for (const [i, line] of lines.entries()) {
396
+ if (i === 0) {
397
+ if (value.endsWith("\n")) {
398
+ result += `|`;
399
+ } else {
400
+ result += `|-`;
401
+ }
402
+ result += `
403
+ ${indent}${line}
404
+ `;
405
+ } else {
406
+ result += `${indent}${line}
407
+ `;
408
+ }
409
+ }
410
+ } else {
411
+ if (value.includes("'") && !value.includes('"')) {
412
+ result += `"${value}"`;
413
+ } else if (value.includes('"') && !value.includes("'")) {
414
+ result += `'${value}'`;
415
+ } else if (value.includes("'") && value.includes('"')) {
416
+ result += `"${value.replace(/"/g, '\\"')}"`;
417
+ } else {
418
+ result += `'${value}'`;
419
+ }
420
+ }
421
+ return result.trimEnd();
422
+ }
423
+ if (typeof value === "number" || typeof value === "boolean" || value === null || value === void 0) {
424
+ return String(value).trimEnd();
425
+ }
426
+ const normalizedValue = normalizeValue(value);
427
+ if (normalizedValue !== null) {
428
+ return stringifyValue(
429
+ {
430
+ [`${normalizedValue[0]}#`]: normalizedValue[1]
431
+ },
432
+ indent,
433
+ maxLineLength,
434
+ showUndefined,
435
+ maxDepth,
436
+ depth + 1,
437
+ collapseObjects,
438
+ addObjSpaces
439
+ );
440
+ }
441
+ return JSON.stringify(value);
442
+ }
443
+ function normalizeValue(value) {
444
+ if (value === null || isPlainObject(value) || Array.isArray(value)) {
445
+ return null;
446
+ }
447
+ if (value instanceof Map) {
448
+ const mapEntries = Array.from(value.entries());
449
+ let mapValue;
450
+ if (mapEntries.every(([key]) => typeof key === "string")) {
451
+ const mapObjValue = {};
452
+ for (const [key, val] of mapEntries) {
453
+ mapObjValue[key] = val;
454
+ }
455
+ mapValue = mapObjValue;
456
+ } else {
457
+ mapValue = mapEntries.map(([key, val]) => ({
458
+ key,
459
+ value: val
460
+ }));
461
+ }
462
+ return ["Map", mapValue];
463
+ }
464
+ if (value instanceof Set) {
465
+ const setValue = Array.from(value);
466
+ return ["Set", setValue];
467
+ }
468
+ if (value instanceof Date) {
469
+ return ["Date", value.toISOString()];
470
+ }
471
+ if (value instanceof RegExp) {
472
+ return ["RegExp", value.toString()];
473
+ }
474
+ if (value instanceof Error) {
475
+ return [
476
+ "Error",
477
+ {
478
+ message: value.message,
479
+ name: value.name,
480
+ stack: value.stack
481
+ }
482
+ ];
483
+ }
484
+ if (value instanceof File) {
485
+ return [
486
+ "File",
487
+ {
488
+ name: value.name,
489
+ type: value.type,
490
+ lastModified: new Date(value.lastModified).toISOString(),
491
+ size: bytesToHumanReadable(value.size)
492
+ }
493
+ ];
494
+ }
495
+ if (typeof value === "object") {
496
+ if ("toJSON" in value && typeof value.toJSON === "function") {
497
+ return [value.constructor.name, value.toJSON()];
498
+ }
499
+ if ("toString" in value && typeof value.toString === "function") {
500
+ const stringValue = value.toString();
501
+ if (stringValue.toString() !== "[object Object]") {
502
+ return [value.constructor.name, stringValue];
503
+ }
504
+ }
505
+ const objectValue = { ...value };
506
+ const displayValue = {};
507
+ let addedKeys = 0;
508
+ for (const [key, item] of Object.entries(objectValue)) {
509
+ if (addedKeys > 4) {
510
+ displayValue["...and more properties"] = Object.keys(objectValue).length - 4;
511
+ break;
512
+ }
513
+ if (typeof item === "string" || typeof item === "number" || typeof item === "boolean" || item === null || item === void 0) {
514
+ displayValue[key] = item;
515
+ addedKeys++;
516
+ }
517
+ }
518
+ return [String(value.constructor.name), displayValue];
519
+ }
520
+ if (typeof value === "function") {
521
+ const functionString = value.toString();
522
+ return [
523
+ `Function`,
524
+ functionString.includes("\n") ? truncateString(functionString.split("\n").join(""), 40) : functionString
525
+ ];
526
+ }
527
+ return null;
528
+ }
529
+
156
530
  // src/testUtils.ts
157
531
  function createLoggerStore({
158
532
  filterKeys: defaultFilterKeys,
@@ -379,8 +753,210 @@ function waitController() {
379
753
  }
380
754
  };
381
755
  }
756
+ function matchesKeyPattern(fullPath, key, pattern, currentPath) {
757
+ if (fullPath === pattern) {
758
+ return true;
759
+ }
760
+ if (pattern.startsWith("*.")) {
761
+ const propName = pattern.slice(2);
762
+ return currentPath !== "" && key === propName;
763
+ }
764
+ if (pattern.startsWith("*") && !pattern.startsWith("*.")) {
765
+ const propName = pattern.slice(1);
766
+ return key === propName;
767
+ }
768
+ if (!pattern.includes("*")) {
769
+ return currentPath === "" && key === pattern;
770
+ }
771
+ return false;
772
+ }
773
+ function isParentOfPattern(path, pattern) {
774
+ if (pattern.includes("*")) {
775
+ const patternParts = pattern.split(".");
776
+ const pathParts = path.split(".");
777
+ if (pathParts.length >= patternParts.length) {
778
+ return false;
779
+ }
780
+ for (let i = 0; i < pathParts.length; i++) {
781
+ const pathPart = pathParts[i];
782
+ const patternPart = patternParts[i];
783
+ if (patternPart === "*") {
784
+ continue;
785
+ }
786
+ if (pathPart !== patternPart) {
787
+ return false;
788
+ }
789
+ }
790
+ return true;
791
+ } else {
792
+ return pattern.startsWith(`${path}.`);
793
+ }
794
+ }
795
+ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "", visited = /* @__PURE__ */ new Set()) {
796
+ if (!isPlainObject(value) && !Array.isArray(value)) {
797
+ return value;
798
+ }
799
+ if (Array.isArray(value)) {
800
+ if (visited.has(value)) {
801
+ throw new Error(
802
+ "Circular reference detected in array during key filtering"
803
+ );
804
+ }
805
+ visited.add(value);
806
+ try {
807
+ return value.map(
808
+ (item, index) => applyKeyFiltering(
809
+ item,
810
+ { rejectKeys, filterKeys },
811
+ currentPath ? `${currentPath}[${index}]` : `[${index}]`,
812
+ visited
813
+ )
814
+ );
815
+ } finally {
816
+ visited.delete(value);
817
+ }
818
+ }
819
+ if (isPlainObject(value)) {
820
+ if (visited.has(value)) {
821
+ throw new Error(
822
+ "Circular reference detected in object during key filtering"
823
+ );
824
+ }
825
+ visited.add(value);
826
+ try {
827
+ const result = {};
828
+ for (const [key, itemValue] of Object.entries(value)) {
829
+ const fullPath = currentPath ? `${currentPath}.${key}` : key;
830
+ if (rejectKeys?.some(
831
+ (rejectPath) => matchesKeyPattern(fullPath, key, rejectPath, currentPath)
832
+ )) {
833
+ continue;
834
+ }
835
+ if (filterKeys) {
836
+ const exactMatch = filterKeys.some(
837
+ (filterPath) => matchesKeyPattern(fullPath, key, filterPath, currentPath)
838
+ );
839
+ const isParent = filterKeys.some(
840
+ (filterPath) => isParentOfPattern(fullPath, filterPath)
841
+ );
842
+ if (!exactMatch && !isParent) {
843
+ continue;
844
+ }
845
+ if (exactMatch) {
846
+ result[key] = applyKeyFiltering(
847
+ itemValue,
848
+ { rejectKeys },
849
+ fullPath,
850
+ visited
851
+ );
852
+ } else {
853
+ result[key] = applyKeyFiltering(
854
+ itemValue,
855
+ { rejectKeys, filterKeys },
856
+ fullPath,
857
+ visited
858
+ );
859
+ }
860
+ } else {
861
+ result[key] = applyKeyFiltering(
862
+ itemValue,
863
+ { rejectKeys, filterKeys },
864
+ fullPath,
865
+ visited
866
+ );
867
+ }
868
+ }
869
+ return result;
870
+ } finally {
871
+ visited.delete(value);
872
+ }
873
+ }
874
+ return value;
875
+ }
876
+ function compactSnapshot(value, {
877
+ collapseObjects = true,
878
+ maxLineLength = 100,
879
+ showUndefined = false,
880
+ showBooleansAs = true,
881
+ rejectKeys,
882
+ filterKeys,
883
+ ...options
884
+ } = {}) {
885
+ let processedValue = value;
886
+ if (rejectKeys || filterKeys) {
887
+ processedValue = applyKeyFiltering(processedValue, {
888
+ rejectKeys: Array.isArray(rejectKeys) ? rejectKeys : rejectKeys ? [rejectKeys] : void 0,
889
+ filterKeys: Array.isArray(filterKeys) ? filterKeys : filterKeys ? [filterKeys] : void 0
890
+ });
891
+ }
892
+ processedValue = showBooleansAs ? replaceBooleansWithEmoji(processedValue, showBooleansAs) : processedValue;
893
+ return `
894
+ ${yamlStringify(processedValue, {
895
+ collapseObjects,
896
+ maxLineLength,
897
+ showUndefined,
898
+ ...options
899
+ })}`;
900
+ }
901
+ function replaceBooleansWithEmoji(value, showBooleansAs, visited = /* @__PURE__ */ new Set()) {
902
+ if (showBooleansAs === false) {
903
+ return value;
904
+ }
905
+ const defaultTrueText = "\u2705";
906
+ const defaultFalseText = "\u274C";
907
+ const config = typeof showBooleansAs === "boolean" ? { trueText: defaultTrueText, falseText: defaultFalseText } : {
908
+ trueText: showBooleansAs.trueText ?? defaultTrueText,
909
+ falseText: showBooleansAs.falseText ?? defaultFalseText,
910
+ props: showBooleansAs.props ?? {},
911
+ ignoreProps: showBooleansAs.ignoreProps ?? []
912
+ };
913
+ function processValue(val, propName) {
914
+ if (typeof val === "boolean") {
915
+ if (propName && config.ignoreProps?.includes(propName)) {
916
+ return val;
917
+ }
918
+ if (propName && config.props?.[propName]) {
919
+ const propConfig = config.props[propName];
920
+ if (propConfig === true) {
921
+ return val ? config.trueText : config.falseText;
922
+ }
923
+ return val ? propConfig.trueText ?? config.trueText : propConfig.falseText ?? config.falseText;
924
+ }
925
+ return val ? config.trueText : config.falseText;
926
+ }
927
+ if (Array.isArray(val)) {
928
+ if (visited.has(val)) {
929
+ throw new Error("Circular reference detected in array");
930
+ }
931
+ visited.add(val);
932
+ try {
933
+ return val.map((item) => processValue(item));
934
+ } finally {
935
+ visited.delete(val);
936
+ }
937
+ }
938
+ if (isPlainObject(val)) {
939
+ if (visited.has(val)) {
940
+ throw new Error("Circular reference detected in object");
941
+ }
942
+ visited.add(val);
943
+ try {
944
+ const result = {};
945
+ for (const [key, itemValue] of Object.entries(val)) {
946
+ result[key] = processValue(itemValue, key);
947
+ }
948
+ return result;
949
+ } finally {
950
+ visited.delete(val);
951
+ }
952
+ }
953
+ return val;
954
+ }
955
+ return processValue(value);
956
+ }
382
957
  // Annotate the CommonJS export names for ESM import in node:
383
958
  0 && (module.exports = {
959
+ compactSnapshot,
384
960
  createLoggerStore,
385
961
  getResultFn,
386
962
  waitController
@@ -1,3 +1,5 @@
1
+ import { YamlStringifyOptions } from './yamlStringify.cjs';
2
+
1
3
  declare function createLoggerStore({ filterKeys: defaultFilterKeys, rejectKeys: defaultRejectKeys, splitLongLines: defaultSplitLongLines, maxLineLengthBeforeSplit: defaultMaxLineLengthBeforeSplit, fromLastSnapshot: defaultFromLastSnapshot, arrays: defaultArrays, changesOnly: defaultChangesOnly, useEmojiForBooleans: defaultUseEmojiForBooleans, }?: {
2
4
  filterKeys?: string[];
3
5
  rejectKeys?: string[];
@@ -39,5 +41,69 @@ declare function waitController(): {
39
41
  stopWaiting: () => void;
40
42
  stopWaitingAfter: (ms: number) => void;
41
43
  };
44
+ declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLength, showUndefined, showBooleansAs, rejectKeys, filterKeys, ...options }?: YamlStringifyOptions & {
45
+ showBooleansAs?: boolean | {
46
+ props?: Record<string, {
47
+ trueText?: string;
48
+ falseText?: string;
49
+ } | true>;
50
+ ignoreProps?: string[];
51
+ trueText?: string;
52
+ falseText?: string;
53
+ };
54
+ /**
55
+ * Reject (exclude) keys from the snapshot using pattern matching.
56
+ *
57
+ * **Pattern Syntax:**
58
+ * - `'prop'` - Only root-level properties named 'prop'
59
+ * - `'prop.nested'` - Exact nested property paths like `obj.prop.nested`
60
+ * - `'*prop'` - Any property named exactly 'prop' at any level (root or nested)
61
+ * - `'*.prop'` - Any nested property named 'prop' (excludes root-level matches)
62
+ *
63
+ * **Examples:**
64
+ * ```typescript
65
+ * // Reject root-level 'secret' only
66
+ * rejectKeys: ['secret']
67
+ *
68
+ * // Reject nested 'password' properties only
69
+ * rejectKeys: ['*.password']
70
+ *
71
+ * // Reject any 'apiKey' property at any level
72
+ * rejectKeys: ['*apiKey']
73
+ *
74
+ * // Reject specific nested path
75
+ * rejectKeys: ['user.settings.theme']
76
+ * ```
77
+ */
78
+ rejectKeys?: string[] | string;
79
+ /**
80
+ * Filter (include only) keys that match the specified patterns.
81
+ *
82
+ * **Pattern Syntax:** (same as rejectKeys)
83
+ * - `'prop'` - Only root-level properties named 'prop'
84
+ * - `'prop.nested'` - Exact nested property paths like `obj.prop.nested`
85
+ * - `'*prop'` - Any property named exactly 'prop' at any level (root or nested)
86
+ * - `'*.prop'` - Any nested property named 'prop' (excludes root-level matches)
87
+ *
88
+ * **Examples:**
89
+ * ```typescript
90
+ * // Include only root-level 'user'
91
+ * filterKeys: ['user']
92
+ *
93
+ * // Include all 'name' properties at any level
94
+ * filterKeys: ['*name']
95
+ *
96
+ * // Include only nested 'id' properties
97
+ * filterKeys: ['*.id']
98
+ *
99
+ * // Include specific nested paths
100
+ * filterKeys: ['user.profile.email', 'settings.theme']
101
+ * ```
102
+ *
103
+ * **Note:** When filtering, parent paths are automatically included if needed
104
+ * to preserve the structure for nested matches.
105
+ */
106
+ filterKeys?: string[] | string;
107
+ }): string;
42
108
 
43
- export { createLoggerStore, getResultFn, waitController };
109
+ export { compactSnapshot, createLoggerStore, getResultFn, waitController };