@ls-stack/utils 3.24.0 → 3.25.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.
Files changed (56) hide show
  1. package/docs/_media/modules.md +1 -0
  2. package/docs/arrayUtils/-internal-.md +1 -1
  3. package/docs/arrayUtils/README.md +12 -12
  4. package/docs/consoleFmt.md +2 -2
  5. package/docs/exhaustiveMatch/-internal-.md +1 -1
  6. package/docs/exhaustiveMatch/README.md +1 -1
  7. package/docs/filterObjectOrArrayKeys.md +80 -0
  8. package/docs/modules.md +1 -0
  9. package/docs/objUtils.md +7 -7
  10. package/docs/parallelAsyncCalls/-internal-.md +3 -3
  11. package/docs/parallelAsyncCalls/README.md +1 -1
  12. package/docs/retryOnError/README.md +1 -1
  13. package/docs/runShellCmd/README.md +9 -9
  14. package/docs/safeJson.md +2 -2
  15. package/docs/saferTyping.md +7 -7
  16. package/docs/stringUtils/README.md +6 -6
  17. package/docs/testUtils.md +40 -6
  18. package/docs/time.md +1 -1
  19. package/docs/tsResult/README.md +17 -11
  20. package/docs/typingFnUtils/-internal-.md +1 -1
  21. package/docs/typingFnUtils/README.md +8 -10
  22. package/lib/arrayUtils.d.cts +6 -1
  23. package/lib/arrayUtils.d.ts +6 -1
  24. package/lib/{chunk-JAPKLFIK.js → chunk-QLD7KG5I.js} +34 -20
  25. package/lib/chunk-XXYTMSFH.js +240 -0
  26. package/lib/filterObjectOrArrayKeys.cjs +275 -0
  27. package/lib/filterObjectOrArrayKeys.d.cts +42 -0
  28. package/lib/filterObjectOrArrayKeys.d.ts +42 -0
  29. package/lib/filterObjectOrArrayKeys.js +7 -0
  30. package/lib/objUtils.d.cts +4 -1
  31. package/lib/objUtils.d.ts +4 -1
  32. package/lib/parallelAsyncCalls.cjs +4 -1
  33. package/lib/parallelAsyncCalls.d.cts +4 -1
  34. package/lib/parallelAsyncCalls.d.ts +4 -1
  35. package/lib/parallelAsyncCalls.js +4 -1
  36. package/lib/retryOnError.d.cts +2 -0
  37. package/lib/retryOnError.d.ts +2 -0
  38. package/lib/runShellCmd.d.cts +17 -0
  39. package/lib/runShellCmd.d.ts +17 -0
  40. package/lib/safeJson.d.cts +8 -2
  41. package/lib/safeJson.d.ts +8 -2
  42. package/lib/saferTyping.d.cts +3 -0
  43. package/lib/saferTyping.d.ts +3 -0
  44. package/lib/stringUtils.d.cts +1 -0
  45. package/lib/stringUtils.d.ts +1 -0
  46. package/lib/testUtils.cjs +273 -106
  47. package/lib/testUtils.d.cts +81 -2
  48. package/lib/testUtils.d.ts +81 -2
  49. package/lib/testUtils.js +10 -87
  50. package/lib/tsResult.d.cts +31 -7
  51. package/lib/tsResult.d.ts +31 -7
  52. package/lib/typingFnUtils.d.cts +20 -6
  53. package/lib/typingFnUtils.d.ts +20 -6
  54. package/lib/yamlStringify.cjs +34 -20
  55. package/lib/yamlStringify.js +1 -1
  56. package/package.json +5 -1
@@ -18,6 +18,8 @@ type RetryOptions = {
18
18
  * @param fn - Function to retry that receives context with retry count
19
19
  * @param maxRetries - Maximum number of retries
20
20
  * @param options - Configuration options
21
+ * @param retry
22
+ * @param originalMaxRetries
21
23
  * @returns Promise resolving to the function result or rejecting with the final error
22
24
  *
23
25
  * @example
@@ -13,6 +13,14 @@ type RunCmdOptions = {
13
13
  noCiColorForce?: boolean;
14
14
  };
15
15
  /**
16
+ * @param label
17
+ * @param command
18
+ * @param root0
19
+ * @param root0.mock
20
+ * @param root0.silent
21
+ * @param root0.throwOnError
22
+ * @param root0.cwd
23
+ * @param root0.noCiColorForce
16
24
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
17
25
  * ```
18
26
  * // Old (deprecated)
@@ -24,6 +32,9 @@ type RunCmdOptions = {
24
32
  */
25
33
  declare function runCmd(label: string | null, command: string | string[], { mock, silent, throwOnError, cwd, noCiColorForce, }?: RunCmdOptions): Promise<CmdResult>;
26
34
  /**
35
+ * @param label
36
+ * @param cmd
37
+ * @param onResult
27
38
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
28
39
  * ```
29
40
  * // Old (deprecated)
@@ -35,6 +46,10 @@ declare function runCmd(label: string | null, command: string | string[], { mock
35
46
  */
36
47
  declare function concurrentCmd(label: string, cmd: string | string[], onResult: (result: CmdResult) => void): Promise<() => void>;
37
48
  /**
49
+ * @param label
50
+ * @param command
51
+ * @param root0
52
+ * @param root0.silent
38
53
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
39
54
  * ```
40
55
  * // Old (deprecated)
@@ -48,6 +63,7 @@ declare function runCmdUnwrap(label: string | null, command: string | string[],
48
63
  silent?: boolean | 'timeOnly';
49
64
  }): Promise<string>;
50
65
  /**
66
+ * @param command
51
67
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
52
68
  * ```
53
69
  * // Old (deprecated)
@@ -59,6 +75,7 @@ declare function runCmdUnwrap(label: string | null, command: string | string[],
59
75
  */
60
76
  declare function runCmdSilent(command: string | string[]): Promise<CmdResult>;
61
77
  /**
78
+ * @param command
62
79
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
63
80
  * ```
64
81
  * // Old (deprecated)
@@ -13,6 +13,14 @@ type RunCmdOptions = {
13
13
  noCiColorForce?: boolean;
14
14
  };
15
15
  /**
16
+ * @param label
17
+ * @param command
18
+ * @param root0
19
+ * @param root0.mock
20
+ * @param root0.silent
21
+ * @param root0.throwOnError
22
+ * @param root0.cwd
23
+ * @param root0.noCiColorForce
16
24
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
17
25
  * ```
18
26
  * // Old (deprecated)
@@ -24,6 +32,9 @@ type RunCmdOptions = {
24
32
  */
25
33
  declare function runCmd(label: string | null, command: string | string[], { mock, silent, throwOnError, cwd, noCiColorForce, }?: RunCmdOptions): Promise<CmdResult>;
26
34
  /**
35
+ * @param label
36
+ * @param cmd
37
+ * @param onResult
27
38
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
28
39
  * ```
29
40
  * // Old (deprecated)
@@ -35,6 +46,10 @@ declare function runCmd(label: string | null, command: string | string[], { mock
35
46
  */
36
47
  declare function concurrentCmd(label: string, cmd: string | string[], onResult: (result: CmdResult) => void): Promise<() => void>;
37
48
  /**
49
+ * @param label
50
+ * @param command
51
+ * @param root0
52
+ * @param root0.silent
38
53
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
39
54
  * ```
40
55
  * // Old (deprecated)
@@ -48,6 +63,7 @@ declare function runCmdUnwrap(label: string | null, command: string | string[],
48
63
  silent?: boolean | 'timeOnly';
49
64
  }): Promise<string>;
50
65
  /**
66
+ * @param command
51
67
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
52
68
  * ```
53
69
  * // Old (deprecated)
@@ -59,6 +75,7 @@ declare function runCmdUnwrap(label: string | null, command: string | string[],
59
75
  */
60
76
  declare function runCmdSilent(command: string | string[]): Promise<CmdResult>;
61
77
  /**
78
+ * @param command
62
79
  * @deprecated This utility has been moved to @ls-stack/node-utils. Please update your imports:
63
80
  * ```
64
81
  * // Old (deprecated)
@@ -1,6 +1,12 @@
1
- /** JSON.stringify can throw if the value is circular or contains functions, this function catches those errors and returns undefined */
1
+ /**
2
+ * JSON.stringify can throw if the value is circular or contains functions, this function catches those errors and returns undefined
3
+ * @param value
4
+ */
2
5
  declare function safeJsonStringify(value: unknown): string | undefined;
3
- /** JSON.parse can throw if the value is not valid JSON, this function catches those errors and returns undefined */
6
+ /**
7
+ * JSON.parse can throw if the value is not valid JSON, this function catches those errors and returns undefined
8
+ * @param value
9
+ */
4
10
  declare function safeJsonParse(value: string): unknown;
5
11
 
6
12
  export { safeJsonParse, safeJsonStringify };
package/lib/safeJson.d.ts CHANGED
@@ -1,6 +1,12 @@
1
- /** JSON.stringify can throw if the value is circular or contains functions, this function catches those errors and returns undefined */
1
+ /**
2
+ * JSON.stringify can throw if the value is circular or contains functions, this function catches those errors and returns undefined
3
+ * @param value
4
+ */
2
5
  declare function safeJsonStringify(value: unknown): string | undefined;
3
- /** JSON.parse can throw if the value is not valid JSON, this function catches those errors and returns undefined */
6
+ /**
7
+ * JSON.parse can throw if the value is not valid JSON, this function catches those errors and returns undefined
8
+ * @param value
9
+ */
4
10
  declare function safeJsonParse(value: string): unknown;
5
11
 
6
12
  export { safeJsonParse, safeJsonStringify };
@@ -8,12 +8,14 @@ type EmptyObject = Record<string, never>;
8
8
  /**
9
9
  * Cast a value to `any` type. Use this when you have legit usage of `any` casting.
10
10
  *
11
+ * @param value
11
12
  * @template V (optional) - When used enforces that the casted value is assignable to the type V, use it for safer casts
12
13
  */
13
14
  declare function __LEGIT_ANY_CAST__<V = unknown>(value: V): __LEGIT_ANY__;
14
15
  /**
15
16
  * Cast a value to a specific type T. Use this when you have legit usage of type assertion.
16
17
  *
18
+ * @param value
17
19
  * @template T - The type to cast to
18
20
  * @template V (optional) - When used enforces that the casted value is assignable to the type V, use it for safer casts
19
21
  */
@@ -21,6 +23,7 @@ declare function __LEGIT_CAST__<T, V = unknown>(value: V): T;
21
23
  /**
22
24
  * Refine a value to a specific type T. Use this when you have legit usage of type assertion.
23
25
  *
26
+ * @param value
24
27
  * @template T - The type to cast to
25
28
  * @template V (optional) - When used enforces that the casted value is assignable to the type V, use it for safer casts
26
29
  */
@@ -8,12 +8,14 @@ type EmptyObject = Record<string, never>;
8
8
  /**
9
9
  * Cast a value to `any` type. Use this when you have legit usage of `any` casting.
10
10
  *
11
+ * @param value
11
12
  * @template V (optional) - When used enforces that the casted value is assignable to the type V, use it for safer casts
12
13
  */
13
14
  declare function __LEGIT_ANY_CAST__<V = unknown>(value: V): __LEGIT_ANY__;
14
15
  /**
15
16
  * Cast a value to a specific type T. Use this when you have legit usage of type assertion.
16
17
  *
18
+ * @param value
17
19
  * @template T - The type to cast to
18
20
  * @template V (optional) - When used enforces that the casted value is assignable to the type V, use it for safer casts
19
21
  */
@@ -21,6 +23,7 @@ declare function __LEGIT_CAST__<T, V = unknown>(value: V): T;
21
23
  /**
22
24
  * Refine a value to a specific type T. Use this when you have legit usage of type assertion.
23
25
  *
26
+ * @param value
24
27
  * @template T - The type to cast to
25
28
  * @template V (optional) - When used enforces that the casted value is assignable to the type V, use it for safer casts
26
29
  */
@@ -2,6 +2,7 @@ type Arg = string | false | undefined | null;
2
2
  /**
3
3
  * A util to create more legible conditional concatenated strings
4
4
  *
5
+ * @param args
5
6
  * @example
6
7
  * joinStrings('a', 'b', 'c') // 'abc'
7
8
  * joinStrings('a', false, 'c') // 'ac'
@@ -2,6 +2,7 @@ type Arg = string | false | undefined | null;
2
2
  /**
3
3
  * A util to create more legible conditional concatenated strings
4
4
  *
5
+ * @param args
5
6
  * @example
6
7
  * joinStrings('a', 'b', 'c') // 'abc'
7
8
  * joinStrings('a', false, 'c') // 'ac'
package/lib/testUtils.cjs CHANGED
@@ -131,6 +131,239 @@ function deepEqual(foo, bar, maxDepth = 20) {
131
131
  return foo !== foo && bar !== bar;
132
132
  }
133
133
 
134
+ // src/filterObjectOrArrayKeys.ts
135
+ function filterObjectOrArrayKeys(objOrArray, {
136
+ filterKeys,
137
+ rejectKeys,
138
+ rejectEmptyObjectsInArray = true
139
+ }) {
140
+ const toArray = (v) => v === void 0 ? [] : Array.isArray(v) ? v : [v];
141
+ const filterPatternsRaw = toArray(filterKeys);
142
+ const rejectPatternsRaw = toArray(rejectKeys);
143
+ const hasFilters = filterPatternsRaw.length > 0;
144
+ const hasRejects = rejectPatternsRaw.length > 0;
145
+ function parsePattern(pattern) {
146
+ const tokens = [];
147
+ let i = 0;
148
+ const n = pattern.length;
149
+ const pushKey = (name) => {
150
+ if (name.length === 0) return;
151
+ tokens.push({ type: "KEY", name });
152
+ };
153
+ while (i < n) {
154
+ const ch = pattern[i];
155
+ if (ch === ".") {
156
+ i += 1;
157
+ continue;
158
+ }
159
+ if (ch === "[") {
160
+ const end = pattern.indexOf("]", i + 1);
161
+ const inside = end === -1 ? pattern.slice(i + 1) : pattern.slice(i + 1, end);
162
+ if (inside === "*") {
163
+ tokens.push({ type: "INDEX_ANY" });
164
+ } else if (inside.includes("-")) {
165
+ const parts = inside.split("-");
166
+ const startStr = parts[0] ?? "";
167
+ const endStr = parts[1] ?? "";
168
+ const start = parseInt(startStr, 10);
169
+ const endNum = endStr === "*" ? null : parseInt(endStr, 10);
170
+ tokens.push({
171
+ type: "INDEX_RANGE",
172
+ start,
173
+ end: endNum === null || Number.isFinite(endNum) ? endNum : null
174
+ });
175
+ } else if (inside.length > 0) {
176
+ const idx = parseInt(inside, 10);
177
+ tokens.push({ type: "INDEX", index: idx });
178
+ }
179
+ i = end === -1 ? n : end + 1;
180
+ continue;
181
+ }
182
+ if (ch === "*") {
183
+ if (pattern[i + 1] === "*") {
184
+ tokens.push({ type: "WILDCARD_ANY" });
185
+ i += 2;
186
+ let j2 = i;
187
+ while (j2 < n) {
188
+ const c = pattern[j2];
189
+ if (c === "." || c === "[") break;
190
+ j2 += 1;
191
+ }
192
+ if (j2 > i) {
193
+ pushKey(pattern.slice(i, j2));
194
+ i = j2;
195
+ }
196
+ continue;
197
+ } else {
198
+ tokens.push({ type: "WILDCARD_ONE" });
199
+ i += 1;
200
+ continue;
201
+ }
202
+ }
203
+ let j = i;
204
+ while (j < n) {
205
+ const c = pattern[j];
206
+ if (c === "." || c === "[") break;
207
+ j += 1;
208
+ }
209
+ pushKey(pattern.slice(i, j));
210
+ i = j;
211
+ }
212
+ return tokens;
213
+ }
214
+ const filterPatterns = filterPatternsRaw.map(parsePattern);
215
+ const rejectPatterns = rejectPatternsRaw.map(parsePattern);
216
+ function matchPath(path, pattern) {
217
+ function rec(pi, pti) {
218
+ if (pti >= pattern.length) return pi === path.length;
219
+ const pt = pattern[pti];
220
+ if (pt.type === "WILDCARD_ANY") {
221
+ if (rec(pi, pti + 1)) return true;
222
+ if (pi < path.length) return rec(pi + 1, pti);
223
+ return false;
224
+ }
225
+ if (pt.type === "WILDCARD_ONE") {
226
+ let j = pi;
227
+ let sawKey = false;
228
+ while (j < path.length) {
229
+ if (path[j].type === "KEY") sawKey = true;
230
+ if (sawKey && rec(j, pti + 1)) return true;
231
+ j += 1;
232
+ }
233
+ return false;
234
+ }
235
+ if (pi >= path.length) return false;
236
+ const ct = path[pi];
237
+ switch (pt.type) {
238
+ case "KEY":
239
+ if (ct.type === "KEY" && ct.name === pt.name)
240
+ return rec(pi + 1, pti + 1);
241
+ if (ct.type === "INDEX") return rec(pi + 1, pti);
242
+ return false;
243
+ case "INDEX":
244
+ if (ct.type === "INDEX" && ct.index === pt.index)
245
+ return rec(pi + 1, pti + 1);
246
+ return false;
247
+ case "INDEX_ANY":
248
+ if (ct.type === "INDEX") return rec(pi + 1, pti + 1);
249
+ return false;
250
+ case "INDEX_RANGE":
251
+ if (ct.type === "INDEX") {
252
+ const okLower = ct.index >= pt.start;
253
+ const okUpper = pt.end === null ? true : ct.index <= pt.end;
254
+ if (okLower && okUpper) return rec(pi + 1, pti + 1);
255
+ }
256
+ return false;
257
+ }
258
+ }
259
+ return rec(0, 0);
260
+ }
261
+ const matchesAnyFilter = (path) => filterPatterns.some((p) => matchPath(path, p));
262
+ const matchesAnyReject = (path) => rejectPatterns.some((p) => matchPath(path, p));
263
+ const build = (value, path, allowedByFilter, stack2, isRoot, parentIsArray) => {
264
+ if (Array.isArray(value)) {
265
+ if (stack2.has(value)) {
266
+ throw new TypeError("Circular references are not supported");
267
+ }
268
+ stack2.add(value);
269
+ const out = [];
270
+ const includeAllChildren = allowedByFilter || !hasFilters;
271
+ for (let index = 0; index < value.length; index += 1) {
272
+ const childPath = path.concat({ type: "INDEX", index });
273
+ if (hasRejects && matchesAnyReject(childPath)) continue;
274
+ const child = value[index];
275
+ const directInclude = hasFilters ? matchesAnyFilter(childPath) : true;
276
+ const childAllowed = includeAllChildren || directInclude;
277
+ if (isPlainObject(child) || Array.isArray(child)) {
278
+ const builtChild = build(
279
+ child,
280
+ childPath,
281
+ childAllowed,
282
+ stack2,
283
+ false,
284
+ true
285
+ );
286
+ if (builtChild !== void 0) {
287
+ out.push(builtChild);
288
+ }
289
+ } else {
290
+ if (childAllowed) {
291
+ out.push(child);
292
+ }
293
+ }
294
+ }
295
+ stack2.delete(value);
296
+ const filteredOut = rejectEmptyObjectsInArray ? out.filter(
297
+ (item) => !(isPlainObject(item) && Object.keys(item).length === 0)
298
+ ) : out;
299
+ if (filteredOut.length === 0 && !allowedByFilter && !isRoot)
300
+ return void 0;
301
+ return filteredOut;
302
+ }
303
+ if (isPlainObject(value)) {
304
+ if (stack2.has(value)) {
305
+ throw new TypeError("Circular references are not supported");
306
+ }
307
+ stack2.add(value);
308
+ const result = {};
309
+ const includeAllChildren = allowedByFilter || !hasFilters;
310
+ for (const key of Object.keys(value)) {
311
+ const childPath = path.concat({ type: "KEY", name: key });
312
+ if (hasRejects && matchesAnyReject(childPath)) continue;
313
+ const val = value[key];
314
+ const directInclude = hasFilters ? matchesAnyFilter(childPath) : true;
315
+ const childAllowed = includeAllChildren || directInclude;
316
+ if (isPlainObject(val) || Array.isArray(val)) {
317
+ const builtChild = build(
318
+ val,
319
+ childPath,
320
+ childAllowed,
321
+ stack2,
322
+ false,
323
+ false
324
+ );
325
+ if (builtChild === void 0) {
326
+ continue;
327
+ }
328
+ if (Array.isArray(builtChild) && builtChild.length === 0 && !childAllowed) {
329
+ continue;
330
+ }
331
+ if (isPlainObject(builtChild) && Object.keys(builtChild).length === 0 && !childAllowed) {
332
+ continue;
333
+ }
334
+ result[key] = builtChild;
335
+ } else {
336
+ if (childAllowed) {
337
+ result[key] = val;
338
+ }
339
+ }
340
+ }
341
+ stack2.delete(value);
342
+ if (Object.keys(result).length === 0 && !allowedByFilter && !isRoot) {
343
+ if (parentIsArray && !rejectEmptyObjectsInArray) {
344
+ return {};
345
+ }
346
+ return void 0;
347
+ }
348
+ return result;
349
+ }
350
+ return allowedByFilter || !hasFilters ? value : void 0;
351
+ };
352
+ const startPath = [];
353
+ const initialAllowed = !hasFilters;
354
+ const stack = /* @__PURE__ */ new WeakSet();
355
+ const built = build(
356
+ objOrArray,
357
+ startPath,
358
+ initialAllowed,
359
+ stack,
360
+ true,
361
+ false
362
+ );
363
+ if (built === void 0) return Array.isArray(objOrArray) ? [] : {};
364
+ return built;
365
+ }
366
+
134
367
  // src/mathUtils.ts
135
368
  function clampMin(value, min) {
136
369
  return value < min ? min : value;
@@ -197,12 +430,12 @@ function yamlStringify(obj, {
197
430
  addRootObjSpaces = "beforeAndAfter"
198
431
  } = {}) {
199
432
  if (isObject(obj) || Array.isArray(obj) || typeof obj === "function") {
200
- return `${stringifyValue(obj, "", maxLineLength, !!showUndefined, maxDepth, 0, collapseObjects, addRootObjSpaces)}
433
+ return `${stringifyValue(obj, "", maxLineLength, !!showUndefined, maxDepth, 0, collapseObjects, addRootObjSpaces, false)}
201
434
  `;
202
435
  }
203
436
  return JSON.stringify(obj) || "undefined";
204
437
  }
205
- function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, depth, collapseObjects, addObjSpaces) {
438
+ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, depth, collapseObjects, addObjSpaces, isArrayItem) {
206
439
  let result = "";
207
440
  const childIndent = `${indent} `;
208
441
  if (isPlainObject(value)) {
@@ -221,7 +454,8 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
221
454
  return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
222
455
  }
223
456
  );
224
- if (isSimpleObject && entries.length > 0) {
457
+ const shouldCollapse = isArrayItem ? entries.length > 1 : entries.length > 0;
458
+ if (isSimpleObject && shouldCollapse) {
225
459
  let line = "{ ";
226
460
  line += entries.map(([key, val]) => {
227
461
  let valueStr;
@@ -268,20 +502,29 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
268
502
  maxDepth,
269
503
  depth + 1,
270
504
  collapseObjects,
271
- addObjSpaces
505
+ addObjSpaces,
506
+ false
272
507
  );
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
- }));
508
+ const willBeCollapsed = isObject(objVal) && (Object.keys(objVal).length === 0 || collapseObjects && depth + 1 > 0 && (() => {
509
+ const filteredEntries = Object.entries(objVal).filter(([, val]) => val !== void 0 || showUndefined);
510
+ const shouldCollapseThis = isArrayItem ? filteredEntries.length > 1 : filteredEntries.length > 0;
511
+ return shouldCollapseThis && filteredEntries.every(([, val]) => {
512
+ if (typeof val === "string") {
513
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
514
+ }
515
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
516
+ });
517
+ })());
518
+ const prevWasCollapsed = prevValue && isObject(prevValue) && (Object.keys(prevValue).length === 0 || collapseObjects && depth + 1 > 0 && (() => {
519
+ const filteredEntries = Object.entries(prevValue).filter(([, val]) => val !== void 0 || showUndefined);
520
+ const shouldCollapseThis = isArrayItem ? filteredEntries.length > 1 : filteredEntries.length > 0;
521
+ return shouldCollapseThis && filteredEntries.every(([, val]) => {
522
+ if (typeof val === "string") {
523
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
524
+ }
525
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
526
+ });
527
+ })());
285
528
  if (!afterSpaceWasAdded && indent === "" && isObject(objVal) && !willBeCollapsed && prevValue && !prevWasCollapsed && (addObjSpaces === "before" || addObjSpaces === "beforeAndAfter")) {
286
529
  result += "\n";
287
530
  }
@@ -344,7 +587,8 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
344
587
  maxDepth,
345
588
  depth + 1,
346
589
  collapseObjects,
347
- addObjSpaces
590
+ addObjSpaces,
591
+ true
348
592
  );
349
593
  }).join(", ");
350
594
  line += "]";
@@ -368,7 +612,8 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
368
612
  maxDepth,
369
613
  depth + 1,
370
614
  collapseObjects,
371
- addObjSpaces
615
+ addObjSpaces,
616
+ true
372
617
  );
373
618
  arrayString = arrayString.trimStart();
374
619
  result += arrayString;
@@ -381,7 +626,8 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
381
626
  maxDepth,
382
627
  depth + 1,
383
628
  collapseObjects,
384
- addObjSpaces
629
+ addObjSpaces,
630
+ true
385
631
  );
386
632
  }
387
633
  result += "\n";
@@ -435,7 +681,8 @@ ${indent}${line}
435
681
  maxDepth,
436
682
  depth + 1,
437
683
  collapseObjects,
438
- addObjSpaces
684
+ addObjSpaces,
685
+ false
439
686
  );
440
687
  }
441
688
  return JSON.stringify(value);
@@ -753,91 +1000,6 @@ function waitController() {
753
1000
  }
754
1001
  };
755
1002
  }
756
- function matchesKeyPattern(path, pattern) {
757
- if (path === pattern) {
758
- return true;
759
- }
760
- if (pattern.includes("*")) {
761
- const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, "[^.]*");
762
- const regex = new RegExp(`^${regexPattern}$`);
763
- return regex.test(path);
764
- }
765
- return false;
766
- }
767
- function isParentOfPattern(path, pattern) {
768
- if (pattern.includes("*")) {
769
- const patternParts = pattern.split(".");
770
- const pathParts = path.split(".");
771
- if (pathParts.length >= patternParts.length) {
772
- return false;
773
- }
774
- for (let i = 0; i < pathParts.length; i++) {
775
- const pathPart = pathParts[i];
776
- const patternPart = patternParts[i];
777
- if (patternPart === "*") {
778
- continue;
779
- }
780
- if (pathPart !== patternPart) {
781
- return false;
782
- }
783
- }
784
- return true;
785
- } else {
786
- return pattern.startsWith(`${path}.`);
787
- }
788
- }
789
- function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "", visited = /* @__PURE__ */ new Set()) {
790
- if (!isPlainObject(value) && !Array.isArray(value)) {
791
- return value;
792
- }
793
- if (Array.isArray(value)) {
794
- if (visited.has(value)) {
795
- throw new Error("Circular reference detected in array during key filtering");
796
- }
797
- visited.add(value);
798
- try {
799
- return value.map(
800
- (item, index) => applyKeyFiltering(item, { rejectKeys, filterKeys }, currentPath ? `${currentPath}[${index}]` : `[${index}]`, visited)
801
- );
802
- } finally {
803
- visited.delete(value);
804
- }
805
- }
806
- if (isPlainObject(value)) {
807
- if (visited.has(value)) {
808
- throw new Error("Circular reference detected in object during key filtering");
809
- }
810
- visited.add(value);
811
- try {
812
- const result = {};
813
- for (const [key, itemValue] of Object.entries(value)) {
814
- const fullPath = currentPath ? `${currentPath}.${key}` : key;
815
- if (rejectKeys?.some(
816
- (rejectPath) => matchesKeyPattern(fullPath, rejectPath) || matchesKeyPattern(key, rejectPath)
817
- )) {
818
- continue;
819
- }
820
- if (filterKeys) {
821
- const shouldInclude = filterKeys.some(
822
- (filterPath) => (
823
- // Exact match
824
- matchesKeyPattern(fullPath, filterPath) || matchesKeyPattern(key, filterPath) || // This path is a parent of a filter pattern (so we include it to allow children)
825
- isParentOfPattern(fullPath, filterPath)
826
- )
827
- );
828
- if (!shouldInclude) {
829
- continue;
830
- }
831
- }
832
- result[key] = applyKeyFiltering(itemValue, { rejectKeys, filterKeys }, fullPath, visited);
833
- }
834
- return result;
835
- } finally {
836
- visited.delete(value);
837
- }
838
- }
839
- return value;
840
- }
841
1003
  function compactSnapshot(value, {
842
1004
  collapseObjects = true,
843
1005
  maxLineLength = 100,
@@ -849,7 +1011,12 @@ function compactSnapshot(value, {
849
1011
  } = {}) {
850
1012
  let processedValue = value;
851
1013
  if (rejectKeys || filterKeys) {
852
- processedValue = applyKeyFiltering(processedValue, { rejectKeys, filterKeys });
1014
+ if (isPlainObject(processedValue) || Array.isArray(processedValue)) {
1015
+ processedValue = filterObjectOrArrayKeys(processedValue, {
1016
+ rejectKeys,
1017
+ filterKeys
1018
+ });
1019
+ }
853
1020
  }
854
1021
  processedValue = showBooleansAs ? replaceBooleansWithEmoji(processedValue, showBooleansAs) : processedValue;
855
1022
  return `