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