bupkis 0.1.2 → 0.3.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/CHANGELOG.md +28 -0
- package/README.md +16 -16
- package/dist/commonjs/assertion/assertion-async.d.ts +2 -1
- package/dist/commonjs/assertion/assertion-async.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion-async.js +84 -2
- package/dist/commonjs/assertion/assertion-async.js.map +1 -1
- package/dist/commonjs/assertion/assertion-sync.d.ts +1 -1
- package/dist/commonjs/assertion/assertion-sync.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion-sync.js +5 -1
- package/dist/commonjs/assertion/assertion-sync.js.map +1 -1
- package/dist/commonjs/assertion/assertion-types.d.ts +39 -84
- package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion.d.ts +1 -1
- package/dist/commonjs/assertion/assertion.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion.js +1 -14
- package/dist/commonjs/assertion/assertion.js.map +1 -1
- package/dist/commonjs/assertion/create.d.ts +5 -33
- package/dist/commonjs/assertion/create.d.ts.map +1 -1
- package/dist/commonjs/assertion/create.js +17 -6
- package/dist/commonjs/assertion/create.js.map +1 -1
- package/dist/commonjs/assertion/impl/async.d.ts +122 -21
- package/dist/commonjs/assertion/impl/async.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/async.js +114 -90
- package/dist/commonjs/assertion/impl/async.js.map +1 -1
- package/dist/commonjs/assertion/impl/callback.d.ts +104 -0
- package/dist/commonjs/assertion/impl/callback.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/callback.js +694 -0
- package/dist/commonjs/assertion/impl/callback.js.map +1 -0
- package/dist/commonjs/assertion/impl/index.d.ts +1 -1
- package/dist/commonjs/assertion/impl/index.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/index.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync-esoteric.js +1 -1
- package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts +37 -34
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.js +32 -47
- package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync.d.ts +105 -58
- package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync.js +4 -1
- package/dist/commonjs/assertion/impl/sync.js.map +1 -1
- package/dist/commonjs/bootstrap.d.ts +199 -85
- package/dist/commonjs/bootstrap.d.ts.map +1 -1
- package/dist/commonjs/bootstrap.js +19 -10
- package/dist/commonjs/bootstrap.js.map +1 -1
- package/dist/commonjs/constant.js +7 -1
- package/dist/commonjs/constant.js.map +1 -1
- package/dist/commonjs/error.d.ts +32 -5
- package/dist/commonjs/error.d.ts.map +1 -1
- package/dist/commonjs/error.js +60 -5
- package/dist/commonjs/error.js.map +1 -1
- package/dist/commonjs/expect.d.ts +130 -3
- package/dist/commonjs/expect.d.ts.map +1 -1
- package/dist/commonjs/expect.js +116 -1
- package/dist/commonjs/expect.js.map +1 -1
- package/dist/commonjs/guards.d.ts +45 -20
- package/dist/commonjs/guards.d.ts.map +1 -1
- package/dist/commonjs/guards.js +56 -40
- package/dist/commonjs/guards.js.map +1 -1
- package/dist/commonjs/index.d.ts +241 -86
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +44 -42
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/metadata.d.ts +1 -27
- package/dist/commonjs/metadata.d.ts.map +1 -1
- package/dist/commonjs/metadata.js +16 -15
- package/dist/commonjs/metadata.js.map +1 -1
- package/dist/commonjs/schema.d.ts +76 -33
- package/dist/commonjs/schema.d.ts.map +1 -1
- package/dist/commonjs/schema.js +77 -34
- package/dist/commonjs/schema.js.map +1 -1
- package/dist/commonjs/types.d.ts +480 -39
- package/dist/commonjs/types.d.ts.map +1 -1
- package/dist/commonjs/types.js +12 -2
- package/dist/commonjs/types.js.map +1 -1
- package/dist/commonjs/util.d.ts +72 -49
- package/dist/commonjs/util.d.ts.map +1 -1
- package/dist/commonjs/util.js +175 -155
- package/dist/commonjs/util.js.map +1 -1
- package/dist/commonjs/value-to-schema.d.ts +122 -0
- package/dist/commonjs/value-to-schema.d.ts.map +1 -0
- package/dist/commonjs/value-to-schema.js +309 -0
- package/dist/commonjs/value-to-schema.js.map +1 -0
- package/dist/esm/assertion/assertion-async.d.ts +2 -1
- package/dist/esm/assertion/assertion-async.d.ts.map +1 -1
- package/dist/esm/assertion/assertion-async.js +85 -3
- package/dist/esm/assertion/assertion-async.js.map +1 -1
- package/dist/esm/assertion/assertion-sync.d.ts +1 -1
- package/dist/esm/assertion/assertion-sync.d.ts.map +1 -1
- package/dist/esm/assertion/assertion-sync.js +6 -2
- package/dist/esm/assertion/assertion-sync.js.map +1 -1
- package/dist/esm/assertion/assertion-types.d.ts +39 -84
- package/dist/esm/assertion/assertion-types.d.ts.map +1 -1
- package/dist/esm/assertion/assertion.d.ts +1 -1
- package/dist/esm/assertion/assertion.d.ts.map +1 -1
- package/dist/esm/assertion/assertion.js +1 -14
- package/dist/esm/assertion/assertion.js.map +1 -1
- package/dist/esm/assertion/create.d.ts +5 -33
- package/dist/esm/assertion/create.d.ts.map +1 -1
- package/dist/esm/assertion/create.js +14 -4
- package/dist/esm/assertion/create.js.map +1 -1
- package/dist/esm/assertion/impl/async.d.ts +122 -21
- package/dist/esm/assertion/impl/async.d.ts.map +1 -1
- package/dist/esm/assertion/impl/async.js +113 -89
- package/dist/esm/assertion/impl/async.js.map +1 -1
- package/dist/esm/assertion/impl/callback.d.ts +104 -0
- package/dist/esm/assertion/impl/callback.d.ts.map +1 -0
- package/dist/esm/assertion/impl/callback.js +691 -0
- package/dist/esm/assertion/impl/callback.js.map +1 -0
- package/dist/esm/assertion/impl/index.d.ts +1 -1
- package/dist/esm/assertion/impl/index.d.ts.map +1 -1
- package/dist/esm/assertion/impl/index.js +1 -1
- package/dist/esm/assertion/impl/index.js.map +1 -1
- package/dist/esm/assertion/impl/sync-esoteric.js +2 -2
- package/dist/esm/assertion/impl/sync-esoteric.js.map +1 -1
- package/dist/esm/assertion/impl/sync-parametric.d.ts +37 -34
- package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync-parametric.js +32 -47
- package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/esm/assertion/impl/sync.d.ts +105 -58
- package/dist/esm/assertion/impl/sync.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync.js +3 -1
- package/dist/esm/assertion/impl/sync.js.map +1 -1
- package/dist/esm/bootstrap.d.ts +199 -85
- package/dist/esm/bootstrap.d.ts.map +1 -1
- package/dist/esm/bootstrap.js +19 -10
- package/dist/esm/bootstrap.js.map +1 -1
- package/dist/esm/constant.js +6 -0
- package/dist/esm/constant.js.map +1 -1
- package/dist/esm/error.d.ts +32 -5
- package/dist/esm/error.d.ts.map +1 -1
- package/dist/esm/error.js +59 -5
- package/dist/esm/error.js.map +1 -1
- package/dist/esm/expect.d.ts +130 -3
- package/dist/esm/expect.d.ts.map +1 -1
- package/dist/esm/expect.js +117 -2
- package/dist/esm/expect.js.map +1 -1
- package/dist/esm/guards.d.ts +45 -20
- package/dist/esm/guards.d.ts.map +1 -1
- package/dist/esm/guards.js +48 -31
- package/dist/esm/guards.js.map +1 -1
- package/dist/esm/index.d.ts +241 -86
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +46 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/metadata.d.ts +1 -27
- package/dist/esm/metadata.d.ts.map +1 -1
- package/dist/esm/metadata.js +2 -1
- package/dist/esm/metadata.js.map +1 -1
- package/dist/esm/schema.d.ts +76 -33
- package/dist/esm/schema.d.ts.map +1 -1
- package/dist/esm/schema.js +77 -34
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/types.d.ts +480 -39
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +12 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/util.d.ts +72 -49
- package/dist/esm/util.d.ts.map +1 -1
- package/dist/esm/util.js +159 -153
- package/dist/esm/util.js.map +1 -1
- package/dist/esm/value-to-schema.d.ts +122 -0
- package/dist/esm/value-to-schema.d.ts.map +1 -0
- package/dist/esm/value-to-schema.js +305 -0
- package/dist/esm/value-to-schema.js.map +1 -0
- package/package.json +94 -17
- package/src/assertion/assertion-async.ts +113 -3
- package/src/assertion/assertion-sync.ts +5 -2
- package/src/assertion/assertion-types.ts +52 -45
- package/src/assertion/assertion.ts +2 -17
- package/src/assertion/create.ts +16 -65
- package/src/assertion/impl/async.ts +132 -92
- package/src/assertion/impl/callback.ts +882 -0
- package/src/assertion/impl/index.ts +1 -1
- package/src/assertion/impl/sync-esoteric.ts +2 -2
- package/src/assertion/impl/sync-parametric.ts +41 -49
- package/src/assertion/impl/sync.ts +3 -0
- package/src/bootstrap.ts +21 -11
- package/src/constant.ts +8 -0
- package/src/error.ts +75 -4
- package/src/expect.ts +275 -20
- package/src/guards.ts +74 -69
- package/src/index.ts +72 -11
- package/src/metadata.ts +3 -4
- package/src/schema.ts +80 -36
- package/src/types.ts +625 -72
- package/src/util.ts +174 -222
- package/src/value-to-schema.ts +464 -0
- package/dist/commonjs/api.d.ts +0 -93
- package/dist/commonjs/api.d.ts.map +0 -1
- package/dist/commonjs/api.js +0 -8
- package/dist/commonjs/api.js.map +0 -1
- package/dist/esm/api.d.ts +0 -93
- package/dist/esm/api.d.ts.map +0 -1
- package/dist/esm/api.js +0 -7
- package/dist/esm/api.js.map +0 -1
- package/src/api.ts +0 -149
- package/src/schema.md +0 -15
package/src/util.ts
CHANGED
|
@@ -1,264 +1,216 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Utility functions
|
|
2
|
+
* Utility functions.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* @category API
|
|
5
|
+
* @example
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import * as util from 'bupkis/util';
|
|
9
|
+
* ```
|
|
9
10
|
*
|
|
10
11
|
* @packageDocumentation
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import { type StringKeyOf } from 'type-fest';
|
|
14
|
-
import { z } from 'zod/v4';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
FunctionSchema,
|
|
19
|
-
RegExpSchema,
|
|
20
|
-
StrongMapSchema,
|
|
21
|
-
StrongSetSchema,
|
|
22
|
-
WrappedPromiseLikeSchema,
|
|
23
|
-
} from './schema.js';
|
|
24
|
-
|
|
25
|
-
export function keyBy<
|
|
26
|
-
const T extends readonly Record<PropertyKey, any>[],
|
|
27
|
-
K extends StringKeyOf<T[number]>,
|
|
28
|
-
>(collection: T, key: K): Record<string, T[number]> {
|
|
29
|
-
const result = {} as Record<string, T[number]>;
|
|
30
|
-
|
|
31
|
-
for (const item of collection) {
|
|
32
|
-
const keyValue = item[key];
|
|
33
|
-
if (
|
|
34
|
-
typeof keyValue === 'string' ||
|
|
35
|
-
typeof keyValue === 'number' ||
|
|
36
|
-
typeof keyValue === 'symbol'
|
|
37
|
-
) {
|
|
38
|
-
result[String(keyValue)] = item;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
16
|
+
export * from './value-to-schema.js';
|
|
44
17
|
|
|
45
18
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
19
|
+
* _Recursively_ searches within an object, array, or any nested structure to
|
|
20
|
+
* find whether a specific key exists.
|
|
48
21
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* primitives, objects, arrays, functions, and various built-in types, with
|
|
52
|
-
* support for circular reference detection.
|
|
22
|
+
* Handles circular references by tracking visited objects to prevent infinite
|
|
23
|
+
* recursion.
|
|
53
24
|
*
|
|
54
25
|
* @example
|
|
55
26
|
*
|
|
56
|
-
* ```
|
|
57
|
-
*
|
|
58
|
-
* valueToSchema('hello'); // z.string()
|
|
59
|
-
* valueToSchema(42); // z.number()
|
|
60
|
-
* valueToSchema(true); // z.boolean()
|
|
61
|
-
*
|
|
62
|
-
* // Objects
|
|
63
|
-
* valueToSchema({ name: 'John', age: 30 });
|
|
64
|
-
* // z.object({ name: z.string(), age: z.number() })
|
|
65
|
-
*
|
|
66
|
-
* // Arrays
|
|
67
|
-
* valueToSchema(['a', 'b', 'c']); // z.array(z.string())
|
|
68
|
-
* valueToSchema([1, 'mixed']); // z.array(z.union([z.number(), z.string()]))
|
|
27
|
+
* ```ts
|
|
28
|
+
* const obj = { a: 1, b: { c: 2, d: [{ e: 3 }] } };
|
|
69
29
|
*
|
|
70
|
-
* //
|
|
71
|
-
*
|
|
72
|
-
*
|
|
30
|
+
* hasKey(obj, 'c'); // true
|
|
31
|
+
* hasKey(obj, 'e'); // true
|
|
32
|
+
* hasKey(obj, 'x'); // false (key not found)
|
|
73
33
|
* ```
|
|
74
34
|
*
|
|
75
|
-
* @param
|
|
76
|
-
* @param
|
|
77
|
-
* @param visited
|
|
78
|
-
* @returns
|
|
35
|
+
* @param obj The object, array, or value to search within
|
|
36
|
+
* @param key The key to search for
|
|
37
|
+
* @param visited Internal set for circular reference detection
|
|
38
|
+
* @returns True if the key is found anywhere in the structure, false otherwise
|
|
79
39
|
*/
|
|
80
|
-
export
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
/** Current depth (internal) */
|
|
84
|
-
_currentDepth?: number;
|
|
85
|
-
/** Whether to allow mixed types in arrays (default: true) */
|
|
86
|
-
allowMixedArrays?: boolean;
|
|
87
|
-
/** If `true`, use `z.literal()` for primitive values instead of type schemas */
|
|
88
|
-
literalPrimitives?: boolean;
|
|
89
|
-
/**
|
|
90
|
-
* If `true`, treat `RegExp` literals as `RegExp` literals; otherwise treat
|
|
91
|
-
* as strings and attempt match
|
|
92
|
-
*/
|
|
93
|
-
literalRegExp?: boolean;
|
|
94
|
-
/** Maximum nesting depth to prevent stack overflow (default: 10) */
|
|
95
|
-
maxDepth?: number;
|
|
96
|
-
/** If `true`, will disallow unknown properties in objects */
|
|
97
|
-
strict?: boolean;
|
|
98
|
-
} = {},
|
|
40
|
+
export function hasKey(
|
|
41
|
+
obj: unknown,
|
|
42
|
+
key: PropertyKey,
|
|
99
43
|
visited = new WeakSet<object>(),
|
|
100
|
-
):
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
literalPrimitives = false,
|
|
105
|
-
literalRegExp = false,
|
|
106
|
-
maxDepth = 10,
|
|
107
|
-
strict = false,
|
|
108
|
-
} = options;
|
|
109
|
-
|
|
110
|
-
// Prevent infinite recursion
|
|
111
|
-
if (_currentDepth >= maxDepth) {
|
|
112
|
-
return z.unknown();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Handle primitives
|
|
116
|
-
if (value === null) {
|
|
117
|
-
return z.null();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (value === undefined) {
|
|
121
|
-
return z.undefined();
|
|
44
|
+
): boolean {
|
|
45
|
+
// Handle primitives that can't contain keys
|
|
46
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
47
|
+
return false;
|
|
122
48
|
}
|
|
123
|
-
if (Number.isNaN(value as number)) {
|
|
124
|
-
return z.nan();
|
|
125
|
-
}
|
|
126
|
-
if (value === Infinity || value === -Infinity) {
|
|
127
|
-
return z.literal(value as any);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const valueType = typeof value;
|
|
131
49
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
case 'boolean':
|
|
136
|
-
return literalPrimitives ? z.literal(value as boolean) : z.boolean();
|
|
137
|
-
case 'function':
|
|
138
|
-
return FunctionSchema;
|
|
139
|
-
case 'number':
|
|
140
|
-
return literalPrimitives ? z.literal(value as number) : z.number();
|
|
141
|
-
case 'string':
|
|
142
|
-
return literalPrimitives ? z.literal(value as string) : z.string();
|
|
143
|
-
case 'symbol':
|
|
144
|
-
return z.symbol();
|
|
50
|
+
// Prevent infinite recursion with circular references
|
|
51
|
+
if (visited.has(obj)) {
|
|
52
|
+
return false;
|
|
145
53
|
}
|
|
54
|
+
visited.add(obj);
|
|
146
55
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// Return a recursive schema reference or unknown for circular refs
|
|
152
|
-
return z.unknown();
|
|
56
|
+
try {
|
|
57
|
+
// Check if this object has the key
|
|
58
|
+
if (Object.hasOwn(obj, key)) {
|
|
59
|
+
return true;
|
|
153
60
|
}
|
|
154
61
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (value instanceof RegExp) {
|
|
164
|
-
if (literalRegExp) {
|
|
165
|
-
return RegExpSchema;
|
|
62
|
+
// Recursively search in object/array values
|
|
63
|
+
if (Array.isArray(obj)) {
|
|
64
|
+
// For arrays, search in each element
|
|
65
|
+
for (const item of obj) {
|
|
66
|
+
if (hasKey(item, key, visited)) {
|
|
67
|
+
return true;
|
|
166
68
|
}
|
|
167
|
-
return z.coerce.string().regex(value);
|
|
168
69
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return StrongSetSchema;
|
|
70
|
+
} else {
|
|
71
|
+
// For objects, search in each property value
|
|
72
|
+
for (const propValue of Object.values(obj)) {
|
|
73
|
+
if (hasKey(propValue, key, visited)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
176
76
|
}
|
|
77
|
+
}
|
|
177
78
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
79
|
+
return false;
|
|
80
|
+
} finally {
|
|
81
|
+
visited.delete(obj);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
181
84
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
85
|
+
/**
|
|
86
|
+
* _Recursively_ searches within an object, array, or any nested structure to
|
|
87
|
+
* find whether a specific value exists.
|
|
88
|
+
*
|
|
89
|
+
* Uses strict equality (===) to compare values, with special handling for empty
|
|
90
|
+
* objects. Handles circular references by tracking visited objects to prevent
|
|
91
|
+
* infinite recursion.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* const obj = { a: 1, b: { c: 2, d: [{ e: 3 }] }, empty: {} };
|
|
97
|
+
*
|
|
98
|
+
* hasValue(obj, 2); // true (found in obj.b.c)
|
|
99
|
+
* hasValue(obj, 3); // true (found in obj.b.d[0].e)
|
|
100
|
+
* hasValue(obj, {}); // true (found in obj.empty, matches empty objects)
|
|
101
|
+
* hasValue(obj, '1'); // false (strict equality, 1 !== '1')
|
|
102
|
+
* hasValue(obj, 999); // false (value not found)
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @param obj The object, array, or value to search within
|
|
106
|
+
* @param value The value to search for (using strict equality, with special
|
|
107
|
+
* empty object handling)
|
|
108
|
+
* @param visited Internal set for circular reference detection
|
|
109
|
+
* @returns True if the value is found anywhere in the structure, false
|
|
110
|
+
* otherwise
|
|
111
|
+
*/
|
|
112
|
+
export function hasValue(
|
|
113
|
+
obj: unknown,
|
|
114
|
+
value: unknown,
|
|
115
|
+
visited = new WeakSet<object>(),
|
|
116
|
+
): boolean {
|
|
117
|
+
// Direct value comparison
|
|
118
|
+
if (obj === value) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
185
121
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
122
|
+
// Special case: Check for empty objects
|
|
123
|
+
if (
|
|
124
|
+
typeof obj === 'object' &&
|
|
125
|
+
obj !== null &&
|
|
126
|
+
!Array.isArray(obj) &&
|
|
127
|
+
typeof value === 'object' &&
|
|
128
|
+
value !== null &&
|
|
129
|
+
!Array.isArray(value)
|
|
130
|
+
) {
|
|
131
|
+
const objKeys = Object.keys(obj);
|
|
132
|
+
const valueKeys = Object.keys(value);
|
|
133
|
+
|
|
134
|
+
// Both are empty objects
|
|
135
|
+
if (objKeys.length === 0 && valueKeys.length === 0) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
189
139
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
140
|
+
// Handle primitives that can't contain nested values
|
|
141
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
193
144
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
145
|
+
// Prevent infinite recursion with circular references
|
|
146
|
+
if (visited.has(obj)) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
visited.add(obj);
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Recursively search in object/array values
|
|
153
|
+
if (Array.isArray(obj)) {
|
|
154
|
+
// For arrays, search in each element
|
|
155
|
+
for (const item of obj) {
|
|
156
|
+
if (hasValue(item, value, visited)) {
|
|
157
|
+
return true;
|
|
198
158
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
_currentDepth: _currentDepth + 1,
|
|
206
|
-
},
|
|
207
|
-
visited,
|
|
208
|
-
),
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
if (allowMixedArrays) {
|
|
212
|
-
// Create a union of all unique element types
|
|
213
|
-
const uniqueSchemas = Array.from(
|
|
214
|
-
new Set(elementSchemas.map((schema) => schema.constructor.name)),
|
|
215
|
-
).map((_, index) => elementSchemas[index]);
|
|
216
|
-
|
|
217
|
-
if (uniqueSchemas.length === 1) {
|
|
218
|
-
return z.array(uniqueSchemas[0]!);
|
|
219
|
-
} else {
|
|
220
|
-
return z.array(
|
|
221
|
-
z.union(uniqueSchemas as [z.ZodType, z.ZodType, ...z.ZodType[]]),
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
} else {
|
|
225
|
-
// Use the first element's schema for all elements
|
|
226
|
-
return z.array(elementSchemas[0]!);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
// For objects, search in each property value
|
|
162
|
+
for (const propValue of Object.values(obj)) {
|
|
163
|
+
if (hasValue(propValue, value, visited)) {
|
|
164
|
+
return true;
|
|
227
165
|
}
|
|
228
166
|
}
|
|
167
|
+
}
|
|
229
168
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (isString(key)) {
|
|
236
|
-
schemaShape[key] = valueToSchema(
|
|
237
|
-
val,
|
|
238
|
-
{
|
|
239
|
-
...options,
|
|
240
|
-
_currentDepth: _currentDepth + 1,
|
|
241
|
-
},
|
|
242
|
-
visited,
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
169
|
+
return false;
|
|
170
|
+
} finally {
|
|
171
|
+
visited.delete(obj);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
246
174
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
175
|
+
/**
|
|
176
|
+
* _Recursively_ searches for a key-value pair within an object or array.
|
|
177
|
+
*
|
|
178
|
+
* Uses strict equality (===) to compare values. Handles circular references by
|
|
179
|
+
* tracking visited objects to prevent infinite recursion.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
*
|
|
183
|
+
* ```ts
|
|
184
|
+
* const obj = { a: 1, b: { c: 2, d: [{ e: 3 }] } };
|
|
185
|
+
*
|
|
186
|
+
* hasKeyValue(obj, 'c', 2); // true
|
|
187
|
+
* hasKeyValue(obj, 'e', 3); // true
|
|
188
|
+
* hasKeyValue(obj, 'a', '1'); // false (strict equality)
|
|
189
|
+
* hasKeyValue(obj, 'x', 1); // false (key not found)
|
|
190
|
+
* /**
|
|
191
|
+
* Maps an array of objects to an object keyed by the specified key.
|
|
192
|
+
*
|
|
193
|
+
* @param collection Array of objects
|
|
194
|
+
* @param key Name of key
|
|
195
|
+
* @returns Object mapping key values to objects
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export function keyBy<
|
|
199
|
+
const T extends readonly Record<PropertyKey, any>[],
|
|
200
|
+
K extends StringKeyOf<T[number]>,
|
|
201
|
+
>(collection: T, key: K): Record<string, T[number]> {
|
|
202
|
+
const result = {} as Record<string, T[number]>;
|
|
251
203
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
204
|
+
for (const item of collection) {
|
|
205
|
+
const keyValue = item[key];
|
|
206
|
+
if (
|
|
207
|
+
typeof keyValue === 'string' ||
|
|
208
|
+
typeof keyValue === 'number' ||
|
|
209
|
+
typeof keyValue === 'symbol'
|
|
210
|
+
) {
|
|
211
|
+
result[String(keyValue)] = item;
|
|
259
212
|
}
|
|
260
213
|
}
|
|
261
214
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
};
|
|
215
|
+
return result;
|
|
216
|
+
}
|