@ls-stack/utils 3.22.0 → 3.24.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.
package/lib/testUtils.js CHANGED
@@ -1,7 +1,11 @@
1
+ import {
2
+ yamlStringify
3
+ } from "./chunk-JAPKLFIK.js";
1
4
  import {
2
5
  omit,
3
6
  pick
4
7
  } from "./chunk-GHAQOUA6.js";
8
+ import "./chunk-IATIXMCE.js";
5
9
  import {
6
10
  deepEqual
7
11
  } from "./chunk-JQFUKJU5.js";
@@ -11,6 +15,7 @@ import {
11
15
  import {
12
16
  clampMin
13
17
  } from "./chunk-HTCYUMDR.js";
18
+ import "./chunk-4REIIZQY.js";
14
19
  import {
15
20
  arrayWithPrevAndIndex,
16
21
  filterAndMap
@@ -18,7 +23,9 @@ import {
18
23
  import {
19
24
  isObject
20
25
  } from "./chunk-C2SVCIWE.js";
21
- import "./chunk-JF2MDHOJ.js";
26
+ import {
27
+ isPlainObject
28
+ } from "./chunk-JF2MDHOJ.js";
22
29
 
23
30
  // src/testUtils.ts
24
31
  function createLoggerStore({
@@ -246,7 +253,171 @@ function waitController() {
246
253
  }
247
254
  };
248
255
  }
256
+ function matchesKeyPattern(path, pattern) {
257
+ if (path === pattern) {
258
+ return true;
259
+ }
260
+ if (pattern.includes("*")) {
261
+ const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, "[^.]*");
262
+ const regex = new RegExp(`^${regexPattern}$`);
263
+ return regex.test(path);
264
+ }
265
+ return false;
266
+ }
267
+ function isParentOfPattern(path, pattern) {
268
+ if (pattern.includes("*")) {
269
+ const patternParts = pattern.split(".");
270
+ const pathParts = path.split(".");
271
+ if (pathParts.length >= patternParts.length) {
272
+ return false;
273
+ }
274
+ for (let i = 0; i < pathParts.length; i++) {
275
+ const pathPart = pathParts[i];
276
+ const patternPart = patternParts[i];
277
+ if (patternPart === "*") {
278
+ continue;
279
+ }
280
+ if (pathPart !== patternPart) {
281
+ return false;
282
+ }
283
+ }
284
+ return true;
285
+ } else {
286
+ return pattern.startsWith(`${path}.`);
287
+ }
288
+ }
289
+ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "", visited = /* @__PURE__ */ new Set()) {
290
+ if (!isPlainObject(value) && !Array.isArray(value)) {
291
+ return value;
292
+ }
293
+ if (Array.isArray(value)) {
294
+ if (visited.has(value)) {
295
+ throw new Error("Circular reference detected in array during key filtering");
296
+ }
297
+ visited.add(value);
298
+ try {
299
+ return value.map(
300
+ (item, index) => applyKeyFiltering(item, { rejectKeys, filterKeys }, currentPath ? `${currentPath}[${index}]` : `[${index}]`, visited)
301
+ );
302
+ } finally {
303
+ visited.delete(value);
304
+ }
305
+ }
306
+ if (isPlainObject(value)) {
307
+ if (visited.has(value)) {
308
+ throw new Error("Circular reference detected in object during key filtering");
309
+ }
310
+ visited.add(value);
311
+ try {
312
+ const result = {};
313
+ for (const [key, itemValue] of Object.entries(value)) {
314
+ const fullPath = currentPath ? `${currentPath}.${key}` : key;
315
+ if (rejectKeys?.some(
316
+ (rejectPath) => matchesKeyPattern(fullPath, rejectPath) || matchesKeyPattern(key, rejectPath)
317
+ )) {
318
+ continue;
319
+ }
320
+ if (filterKeys) {
321
+ const shouldInclude = filterKeys.some(
322
+ (filterPath) => (
323
+ // Exact match
324
+ matchesKeyPattern(fullPath, filterPath) || matchesKeyPattern(key, filterPath) || // This path is a parent of a filter pattern (so we include it to allow children)
325
+ isParentOfPattern(fullPath, filterPath)
326
+ )
327
+ );
328
+ if (!shouldInclude) {
329
+ continue;
330
+ }
331
+ }
332
+ result[key] = applyKeyFiltering(itemValue, { rejectKeys, filterKeys }, fullPath, visited);
333
+ }
334
+ return result;
335
+ } finally {
336
+ visited.delete(value);
337
+ }
338
+ }
339
+ return value;
340
+ }
341
+ function compactSnapshot(value, {
342
+ collapseObjects = true,
343
+ maxLineLength = 100,
344
+ showUndefined = false,
345
+ showBooleansAs = true,
346
+ rejectKeys,
347
+ filterKeys,
348
+ ...options
349
+ } = {}) {
350
+ let processedValue = value;
351
+ if (rejectKeys || filterKeys) {
352
+ processedValue = applyKeyFiltering(processedValue, { rejectKeys, filterKeys });
353
+ }
354
+ processedValue = showBooleansAs ? replaceBooleansWithEmoji(processedValue, showBooleansAs) : processedValue;
355
+ return `
356
+ ${yamlStringify(processedValue, {
357
+ collapseObjects,
358
+ maxLineLength,
359
+ showUndefined,
360
+ ...options
361
+ })}`;
362
+ }
363
+ function replaceBooleansWithEmoji(value, showBooleansAs, visited = /* @__PURE__ */ new Set()) {
364
+ if (showBooleansAs === false) {
365
+ return value;
366
+ }
367
+ const defaultTrueText = "\u2705";
368
+ const defaultFalseText = "\u274C";
369
+ const config = typeof showBooleansAs === "boolean" ? { trueText: defaultTrueText, falseText: defaultFalseText } : {
370
+ trueText: showBooleansAs.trueText ?? defaultTrueText,
371
+ falseText: showBooleansAs.falseText ?? defaultFalseText,
372
+ props: showBooleansAs.props ?? {},
373
+ ignoreProps: showBooleansAs.ignoreProps ?? []
374
+ };
375
+ function processValue(val, propName) {
376
+ if (typeof val === "boolean") {
377
+ if (propName && config.ignoreProps?.includes(propName)) {
378
+ return val;
379
+ }
380
+ if (propName && config.props?.[propName]) {
381
+ const propConfig = config.props[propName];
382
+ if (propConfig === true) {
383
+ return val ? config.trueText : config.falseText;
384
+ }
385
+ return val ? propConfig.trueText ?? config.trueText : propConfig.falseText ?? config.falseText;
386
+ }
387
+ return val ? config.trueText : config.falseText;
388
+ }
389
+ if (Array.isArray(val)) {
390
+ if (visited.has(val)) {
391
+ throw new Error("Circular reference detected in array");
392
+ }
393
+ visited.add(val);
394
+ try {
395
+ return val.map((item) => processValue(item));
396
+ } finally {
397
+ visited.delete(val);
398
+ }
399
+ }
400
+ if (isPlainObject(val)) {
401
+ if (visited.has(val)) {
402
+ throw new Error("Circular reference detected in object");
403
+ }
404
+ visited.add(val);
405
+ try {
406
+ const result = {};
407
+ for (const [key, itemValue] of Object.entries(val)) {
408
+ result[key] = processValue(itemValue, key);
409
+ }
410
+ return result;
411
+ } finally {
412
+ visited.delete(val);
413
+ }
414
+ }
415
+ return val;
416
+ }
417
+ return processValue(value);
418
+ }
249
419
  export {
420
+ compactSnapshot,
250
421
  createLoggerStore,
251
422
  getResultFn,
252
423
  waitController
@@ -24,26 +24,6 @@ __export(yamlStringify_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(yamlStringify_exports);
26
26
 
27
- // src/typeGuards.ts
28
- function isObject(value) {
29
- return typeof value === "object" && value !== null && !Array.isArray(value);
30
- }
31
- function isPlainObject(value) {
32
- if (!value || typeof value !== "object") return false;
33
- const proto = Object.getPrototypeOf(value);
34
- if (proto === null) {
35
- return true;
36
- }
37
- const Ctor = Object.hasOwnProperty.call(proto, "constructor") && proto.constructor;
38
- if (Ctor === Object) return true;
39
- const objectCtorString = Object.prototype.constructor.toString();
40
- return typeof Ctor == "function" && Function.toString.call(Ctor) === objectCtorString;
41
- }
42
-
43
- // src/assertions.ts
44
- var isObject2 = isObject;
45
- var isPlainObject2 = isPlainObject;
46
-
47
27
  // src/conversions.ts
48
28
  function bytesToHumanReadable(bytes) {
49
29
  if (bytes < 1024) {
@@ -67,26 +47,80 @@ function truncateString(str, length, ellipsis = "\u2026") {
67
47
  return str.slice(0, length - 1) + ellipsis;
68
48
  }
69
49
 
50
+ // src/typeGuards.ts
51
+ function isObject(value) {
52
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53
+ }
54
+ function isPlainObject(value) {
55
+ if (!value || typeof value !== "object") return false;
56
+ const proto = Object.getPrototypeOf(value);
57
+ if (proto === null) {
58
+ return true;
59
+ }
60
+ const Ctor = Object.hasOwnProperty.call(proto, "constructor") && proto.constructor;
61
+ if (Ctor === Object) return true;
62
+ const objectCtorString = Object.prototype.constructor.toString();
63
+ return typeof Ctor == "function" && Function.toString.call(Ctor) === objectCtorString;
64
+ }
65
+
70
66
  // src/yamlStringify.ts
71
67
  function yamlStringify(obj, {
72
68
  maxLineLength = 100,
73
69
  showUndefined,
74
70
  maxDepth = 50,
71
+ collapseObjects = false,
75
72
  addRootObjSpaces = "beforeAndAfter"
76
73
  } = {}) {
77
- if (isObject2(obj) || Array.isArray(obj) || typeof obj === "function") {
78
- return `${stringifyValue(obj, "", maxLineLength, !!showUndefined, maxDepth, 0, addRootObjSpaces)}
74
+ if (isObject(obj) || Array.isArray(obj) || typeof obj === "function") {
75
+ return `${stringifyValue(obj, "", maxLineLength, !!showUndefined, maxDepth, 0, collapseObjects, addRootObjSpaces)}
79
76
  `;
80
77
  }
81
78
  return JSON.stringify(obj) || "undefined";
82
79
  }
83
- function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, depth, addObjSpaces) {
80
+ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, depth, collapseObjects, addObjSpaces) {
84
81
  let result = "";
85
82
  const childIndent = `${indent} `;
86
- if (isPlainObject2(value)) {
83
+ if (isPlainObject(value)) {
87
84
  if (Object.keys(value).length === 0) {
88
85
  return "{}";
89
86
  }
87
+ if (collapseObjects && depth > 0) {
88
+ const entries = Object.entries(value).filter(
89
+ ([, val]) => val !== void 0 || showUndefined
90
+ );
91
+ const isSimpleObject = entries.every(
92
+ ([, val]) => {
93
+ if (typeof val === "string") {
94
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
95
+ }
96
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
97
+ }
98
+ );
99
+ if (isSimpleObject && entries.length > 0) {
100
+ let line = "{ ";
101
+ line += entries.map(([key, val]) => {
102
+ let valueStr;
103
+ if (typeof val === "string") {
104
+ if (val.includes("'") && !val.includes('"')) {
105
+ valueStr = `"${val}"`;
106
+ } else if (val.includes('"') && !val.includes("'")) {
107
+ valueStr = `'${val}'`;
108
+ } else if (val.includes("'") && val.includes('"')) {
109
+ valueStr = `"${val.replace(/"/g, '\\"')}"`;
110
+ } else {
111
+ valueStr = `'${val}'`;
112
+ }
113
+ } else {
114
+ valueStr = String(val);
115
+ }
116
+ return `${key}: ${valueStr}`;
117
+ }).join(", ");
118
+ line += " }";
119
+ if (line.length <= maxLineLength) {
120
+ return line;
121
+ }
122
+ }
123
+ }
90
124
  let prevValue;
91
125
  let afterSpaceWasAdded = false;
92
126
  for (let [key, objVal] of Object.entries(value)) {
@@ -108,9 +142,22 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
108
142
  showUndefined,
109
143
  maxDepth,
110
144
  depth + 1,
145
+ collapseObjects,
111
146
  addObjSpaces
112
147
  );
113
- if (!afterSpaceWasAdded && indent === "" && isObject2(objVal) && prevValue && (addObjSpaces === "before" || addObjSpaces === "beforeAndAfter")) {
148
+ const willBeCollapsed = isObject(objVal) && (Object.keys(objVal).length === 0 || collapseObjects && depth + 1 > 0 && Object.entries(objVal).filter(([, val]) => val !== void 0 || showUndefined).every(([, val]) => {
149
+ if (typeof val === "string") {
150
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
151
+ }
152
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
153
+ }));
154
+ 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]) => {
155
+ if (typeof val === "string") {
156
+ return !val.includes("'") && !val.includes('"') && !val.includes("\\");
157
+ }
158
+ return typeof val === "number" || typeof val === "boolean" || val === null || val === void 0;
159
+ }));
160
+ if (!afterSpaceWasAdded && indent === "" && isObject(objVal) && !willBeCollapsed && prevValue && !prevWasCollapsed && (addObjSpaces === "before" || addObjSpaces === "beforeAndAfter")) {
114
161
  result += "\n";
115
162
  }
116
163
  if (Array.isArray(objVal)) {
@@ -121,8 +168,9 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
121
168
  result += `${indent}${key}:
122
169
  `;
123
170
  }
124
- } else if (isObject2(objVal)) {
125
- if (Object.keys(objVal).length === 0) {
171
+ } else if (isObject(objVal)) {
172
+ const isCollapsedObject = valueString.startsWith("{") && !valueString.includes("\n");
173
+ if (Object.keys(objVal).length === 0 || isCollapsedObject) {
126
174
  result += `${indent}${key}: `;
127
175
  } else {
128
176
  result += `${indent}${key}:
@@ -134,7 +182,8 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
134
182
  result += valueString;
135
183
  result += "\n";
136
184
  if (indent === "") {
137
- if (isObject2(objVal)) {
185
+ const isCollapsedObject = valueString.startsWith("{") && !valueString.includes("\n") && valueString.length > 2;
186
+ if (isObject(objVal) && !isCollapsedObject) {
138
187
  if (addObjSpaces === "after" || addObjSpaces === "beforeAndAfter") {
139
188
  result += "\n";
140
189
  afterSpaceWasAdded = true;
@@ -169,6 +218,7 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
169
218
  showUndefined,
170
219
  maxDepth,
171
220
  depth + 1,
221
+ collapseObjects,
172
222
  addObjSpaces
173
223
  );
174
224
  }).join(", ");
@@ -184,7 +234,7 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
184
234
  item = `{max depth reached}`;
185
235
  }
186
236
  result += `${indent}- `;
187
- if (Array.isArray(item) || isObject2(item)) {
237
+ if (Array.isArray(item) || isObject(item)) {
188
238
  let arrayString = stringifyValue(
189
239
  item,
190
240
  childIndent,
@@ -192,6 +242,7 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
192
242
  showUndefined,
193
243
  maxDepth,
194
244
  depth + 1,
245
+ collapseObjects,
195
246
  addObjSpaces
196
247
  );
197
248
  arrayString = arrayString.trimStart();
@@ -204,6 +255,7 @@ function stringifyValue(value, indent, maxLineLength, showUndefined, maxDepth, d
204
255
  showUndefined,
205
256
  maxDepth,
206
257
  depth + 1,
258
+ collapseObjects,
207
259
  addObjSpaces
208
260
  );
209
261
  }
@@ -257,13 +309,14 @@ ${indent}${line}
257
309
  showUndefined,
258
310
  maxDepth,
259
311
  depth + 1,
312
+ collapseObjects,
260
313
  addObjSpaces
261
314
  );
262
315
  }
263
316
  return JSON.stringify(value);
264
317
  }
265
318
  function normalizeValue(value) {
266
- if (value === null || isPlainObject2(value) || Array.isArray(value)) {
319
+ if (value === null || isPlainObject(value) || Array.isArray(value)) {
267
320
  return null;
268
321
  }
269
322
  if (value instanceof Map) {
@@ -1,8 +1,10 @@
1
- declare function yamlStringify(obj: unknown, { maxLineLength, showUndefined, maxDepth, addRootObjSpaces, }?: {
1
+ type YamlStringifyOptions = {
2
2
  maxLineLength?: number;
3
3
  showUndefined?: boolean;
4
4
  maxDepth?: number;
5
+ collapseObjects?: boolean;
5
6
  addRootObjSpaces?: 'before' | 'after' | 'beforeAndAfter' | false;
6
- }): string;
7
+ };
8
+ declare function yamlStringify(obj: unknown, { maxLineLength, showUndefined, maxDepth, collapseObjects, addRootObjSpaces, }?: YamlStringifyOptions): string;
7
9
 
8
- export { yamlStringify };
10
+ export { type YamlStringifyOptions, yamlStringify };
@@ -1,8 +1,10 @@
1
- declare function yamlStringify(obj: unknown, { maxLineLength, showUndefined, maxDepth, addRootObjSpaces, }?: {
1
+ type YamlStringifyOptions = {
2
2
  maxLineLength?: number;
3
3
  showUndefined?: boolean;
4
4
  maxDepth?: number;
5
+ collapseObjects?: boolean;
5
6
  addRootObjSpaces?: 'before' | 'after' | 'beforeAndAfter' | false;
6
- }): string;
7
+ };
8
+ declare function yamlStringify(obj: unknown, { maxLineLength, showUndefined, maxDepth, collapseObjects, addRootObjSpaces, }?: YamlStringifyOptions): string;
7
9
 
8
- export { yamlStringify };
10
+ export { type YamlStringifyOptions, yamlStringify };