bupkis 0.7.2 → 0.9.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 (215) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +19 -2
  3. package/dist/commonjs/assertion/assertion-async.d.ts.map +1 -1
  4. package/dist/commonjs/assertion/assertion-async.js +37 -7
  5. package/dist/commonjs/assertion/assertion-async.js.map +1 -1
  6. package/dist/commonjs/assertion/assertion-sync.d.ts.map +1 -1
  7. package/dist/commonjs/assertion/assertion-sync.js +32 -8
  8. package/dist/commonjs/assertion/assertion-sync.js.map +1 -1
  9. package/dist/commonjs/assertion/assertion-types.d.ts +37 -31
  10. package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -1
  11. package/dist/commonjs/assertion/assertion-types.js +0 -32
  12. package/dist/commonjs/assertion/assertion-types.js.map +1 -1
  13. package/dist/commonjs/assertion/assertion.d.ts +3 -21
  14. package/dist/commonjs/assertion/assertion.d.ts.map +1 -1
  15. package/dist/commonjs/assertion/assertion.js +42 -27
  16. package/dist/commonjs/assertion/assertion.js.map +1 -1
  17. package/dist/commonjs/assertion/create.d.ts +2 -0
  18. package/dist/commonjs/assertion/create.d.ts.map +1 -1
  19. package/dist/commonjs/assertion/create.js +38 -42
  20. package/dist/commonjs/assertion/create.js.map +1 -1
  21. package/dist/commonjs/assertion/impl/assertion-util.d.ts +16 -4
  22. package/dist/commonjs/assertion/impl/assertion-util.d.ts.map +1 -1
  23. package/dist/commonjs/assertion/impl/assertion-util.js +20 -15
  24. package/dist/commonjs/assertion/impl/assertion-util.js.map +1 -1
  25. package/dist/commonjs/assertion/impl/async-parametric.d.ts +63 -11
  26. package/dist/commonjs/assertion/impl/async-parametric.d.ts.map +1 -1
  27. package/dist/commonjs/assertion/impl/async-parametric.js +89 -52
  28. package/dist/commonjs/assertion/impl/async-parametric.js.map +1 -1
  29. package/dist/commonjs/assertion/impl/async.d.ts +116 -12
  30. package/dist/commonjs/assertion/impl/async.d.ts.map +1 -1
  31. package/dist/commonjs/assertion/impl/async.js +1 -1
  32. package/dist/commonjs/assertion/impl/sync-basic.d.ts.map +1 -1
  33. package/dist/commonjs/assertion/impl/sync-basic.js +6 -4
  34. package/dist/commonjs/assertion/impl/sync-basic.js.map +1 -1
  35. package/dist/commonjs/assertion/impl/sync-collection.d.ts.map +1 -1
  36. package/dist/commonjs/assertion/impl/sync-collection.js +24 -14
  37. package/dist/commonjs/assertion/impl/sync-collection.js.map +1 -1
  38. package/dist/commonjs/assertion/impl/sync-esoteric.d.ts +1 -5
  39. package/dist/commonjs/assertion/impl/sync-esoteric.d.ts.map +1 -1
  40. package/dist/commonjs/assertion/impl/sync-esoteric.js +11 -13
  41. package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -1
  42. package/dist/commonjs/assertion/impl/sync-parametric.d.ts +27 -7
  43. package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
  44. package/dist/commonjs/assertion/impl/sync-parametric.js +75 -51
  45. package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
  46. package/dist/commonjs/assertion/impl/sync.d.ts +54 -22
  47. package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -1
  48. package/dist/commonjs/assertion/impl/sync.js +1 -1
  49. package/dist/commonjs/assertion/impl/sync.js.map +1 -1
  50. package/dist/commonjs/assertion/index.d.ts +1 -1
  51. package/dist/commonjs/assertion/index.d.ts.map +1 -1
  52. package/dist/commonjs/assertion/index.js +0 -1
  53. package/dist/commonjs/assertion/index.js.map +1 -1
  54. package/dist/commonjs/assertion/slotify.d.ts +1 -13
  55. package/dist/commonjs/assertion/slotify.d.ts.map +1 -1
  56. package/dist/commonjs/assertion/slotify.js +49 -16
  57. package/dist/commonjs/assertion/slotify.js.map +1 -1
  58. package/dist/commonjs/bootstrap.d.ts +85 -17
  59. package/dist/commonjs/bootstrap.d.ts.map +1 -1
  60. package/dist/commonjs/bootstrap.js +1 -0
  61. package/dist/commonjs/bootstrap.js.map +1 -1
  62. package/dist/commonjs/diff.d.ts +51 -0
  63. package/dist/commonjs/diff.d.ts.map +1 -0
  64. package/dist/commonjs/diff.js +279 -0
  65. package/dist/commonjs/diff.js.map +1 -0
  66. package/dist/commonjs/error.d.ts +37 -18
  67. package/dist/commonjs/error.d.ts.map +1 -1
  68. package/dist/commonjs/error.js +44 -30
  69. package/dist/commonjs/error.js.map +1 -1
  70. package/dist/commonjs/expect.d.ts.map +1 -1
  71. package/dist/commonjs/expect.js +131 -78
  72. package/dist/commonjs/expect.js.map +1 -1
  73. package/dist/commonjs/guards.d.ts +24 -10
  74. package/dist/commonjs/guards.d.ts.map +1 -1
  75. package/dist/commonjs/guards.js +56 -39
  76. package/dist/commonjs/guards.js.map +1 -1
  77. package/dist/commonjs/index.d.ts +85 -17
  78. package/dist/commonjs/index.d.ts.map +1 -1
  79. package/dist/commonjs/internal-schema.d.ts +25 -0
  80. package/dist/commonjs/internal-schema.d.ts.map +1 -0
  81. package/dist/commonjs/internal-schema.js +209 -0
  82. package/dist/commonjs/internal-schema.js.map +1 -0
  83. package/dist/commonjs/schema.d.ts.map +1 -1
  84. package/dist/commonjs/schema.js +3 -2
  85. package/dist/commonjs/schema.js.map +1 -1
  86. package/dist/commonjs/use.js +22 -8
  87. package/dist/commonjs/use.js.map +1 -1
  88. package/dist/commonjs/util.d.ts +1 -0
  89. package/dist/commonjs/util.d.ts.map +1 -1
  90. package/dist/commonjs/util.js +14 -10
  91. package/dist/commonjs/util.js.map +1 -1
  92. package/dist/commonjs/value-to-schema.d.ts +1 -0
  93. package/dist/commonjs/value-to-schema.d.ts.map +1 -1
  94. package/dist/commonjs/value-to-schema.js +19 -12
  95. package/dist/commonjs/value-to-schema.js.map +1 -1
  96. package/dist/esm/assertion/assertion-async.d.ts.map +1 -1
  97. package/dist/esm/assertion/assertion-async.js +37 -7
  98. package/dist/esm/assertion/assertion-async.js.map +1 -1
  99. package/dist/esm/assertion/assertion-sync.d.ts.map +1 -1
  100. package/dist/esm/assertion/assertion-sync.js +32 -8
  101. package/dist/esm/assertion/assertion-sync.js.map +1 -1
  102. package/dist/esm/assertion/assertion-types.d.ts +37 -31
  103. package/dist/esm/assertion/assertion-types.d.ts.map +1 -1
  104. package/dist/esm/assertion/assertion-types.js +1 -31
  105. package/dist/esm/assertion/assertion-types.js.map +1 -1
  106. package/dist/esm/assertion/assertion.d.ts +3 -21
  107. package/dist/esm/assertion/assertion.d.ts.map +1 -1
  108. package/dist/esm/assertion/assertion.js +42 -27
  109. package/dist/esm/assertion/assertion.js.map +1 -1
  110. package/dist/esm/assertion/create.d.ts +2 -0
  111. package/dist/esm/assertion/create.d.ts.map +1 -1
  112. package/dist/esm/assertion/create.js +37 -41
  113. package/dist/esm/assertion/create.js.map +1 -1
  114. package/dist/esm/assertion/impl/assertion-util.d.ts +16 -4
  115. package/dist/esm/assertion/impl/assertion-util.d.ts.map +1 -1
  116. package/dist/esm/assertion/impl/assertion-util.js +20 -15
  117. package/dist/esm/assertion/impl/assertion-util.js.map +1 -1
  118. package/dist/esm/assertion/impl/async-parametric.d.ts +63 -11
  119. package/dist/esm/assertion/impl/async-parametric.d.ts.map +1 -1
  120. package/dist/esm/assertion/impl/async-parametric.js +89 -52
  121. package/dist/esm/assertion/impl/async-parametric.js.map +1 -1
  122. package/dist/esm/assertion/impl/async.d.ts +116 -12
  123. package/dist/esm/assertion/impl/async.d.ts.map +1 -1
  124. package/dist/esm/assertion/impl/async.js +2 -2
  125. package/dist/esm/assertion/impl/async.js.map +1 -1
  126. package/dist/esm/assertion/impl/sync-basic.d.ts.map +1 -1
  127. package/dist/esm/assertion/impl/sync-basic.js +6 -4
  128. package/dist/esm/assertion/impl/sync-basic.js.map +1 -1
  129. package/dist/esm/assertion/impl/sync-collection.d.ts.map +1 -1
  130. package/dist/esm/assertion/impl/sync-collection.js +24 -14
  131. package/dist/esm/assertion/impl/sync-collection.js.map +1 -1
  132. package/dist/esm/assertion/impl/sync-esoteric.d.ts +1 -5
  133. package/dist/esm/assertion/impl/sync-esoteric.d.ts.map +1 -1
  134. package/dist/esm/assertion/impl/sync-esoteric.js +11 -13
  135. package/dist/esm/assertion/impl/sync-esoteric.js.map +1 -1
  136. package/dist/esm/assertion/impl/sync-parametric.d.ts +27 -7
  137. package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
  138. package/dist/esm/assertion/impl/sync-parametric.js +75 -51
  139. package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
  140. package/dist/esm/assertion/impl/sync.d.ts +54 -22
  141. package/dist/esm/assertion/impl/sync.d.ts.map +1 -1
  142. package/dist/esm/assertion/impl/sync.js +2 -2
  143. package/dist/esm/assertion/impl/sync.js.map +1 -1
  144. package/dist/esm/assertion/index.d.ts +1 -1
  145. package/dist/esm/assertion/index.d.ts.map +1 -1
  146. package/dist/esm/assertion/index.js +0 -1
  147. package/dist/esm/assertion/index.js.map +1 -1
  148. package/dist/esm/assertion/slotify.d.ts +1 -13
  149. package/dist/esm/assertion/slotify.d.ts.map +1 -1
  150. package/dist/esm/assertion/slotify.js +50 -17
  151. package/dist/esm/assertion/slotify.js.map +1 -1
  152. package/dist/esm/bootstrap.d.ts +85 -17
  153. package/dist/esm/bootstrap.d.ts.map +1 -1
  154. package/dist/esm/bootstrap.js +1 -0
  155. package/dist/esm/bootstrap.js.map +1 -1
  156. package/dist/esm/diff.d.ts +51 -0
  157. package/dist/esm/diff.d.ts.map +1 -0
  158. package/dist/esm/diff.js +273 -0
  159. package/dist/esm/diff.js.map +1 -0
  160. package/dist/esm/error.d.ts +37 -18
  161. package/dist/esm/error.d.ts.map +1 -1
  162. package/dist/esm/error.js +41 -27
  163. package/dist/esm/error.js.map +1 -1
  164. package/dist/esm/expect.d.ts.map +1 -1
  165. package/dist/esm/expect.js +133 -80
  166. package/dist/esm/expect.js.map +1 -1
  167. package/dist/esm/guards.d.ts +24 -10
  168. package/dist/esm/guards.d.ts.map +1 -1
  169. package/dist/esm/guards.js +52 -36
  170. package/dist/esm/guards.js.map +1 -1
  171. package/dist/esm/index.d.ts +85 -17
  172. package/dist/esm/index.d.ts.map +1 -1
  173. package/dist/esm/internal-schema.d.ts +25 -0
  174. package/dist/esm/internal-schema.d.ts.map +1 -0
  175. package/dist/esm/internal-schema.js +203 -0
  176. package/dist/esm/internal-schema.js.map +1 -0
  177. package/dist/esm/schema.d.ts.map +1 -1
  178. package/dist/esm/schema.js +3 -2
  179. package/dist/esm/schema.js.map +1 -1
  180. package/dist/esm/use.js +19 -6
  181. package/dist/esm/use.js.map +1 -1
  182. package/dist/esm/util.d.ts +1 -0
  183. package/dist/esm/util.d.ts.map +1 -1
  184. package/dist/esm/util.js +14 -10
  185. package/dist/esm/util.js.map +1 -1
  186. package/dist/esm/value-to-schema.d.ts +1 -0
  187. package/dist/esm/value-to-schema.d.ts.map +1 -1
  188. package/dist/esm/value-to-schema.js +20 -13
  189. package/dist/esm/value-to-schema.js.map +1 -1
  190. package/package.json +29 -11
  191. package/src/assertion/assertion-async.ts +42 -14
  192. package/src/assertion/assertion-sync.ts +40 -17
  193. package/src/assertion/assertion-types.ts +55 -45
  194. package/src/assertion/assertion.ts +49 -32
  195. package/src/assertion/create.ts +46 -65
  196. package/src/assertion/impl/assertion-util.ts +31 -18
  197. package/src/assertion/impl/async-parametric.ts +93 -52
  198. package/src/assertion/impl/async.ts +2 -2
  199. package/src/assertion/impl/sync-basic.ts +7 -4
  200. package/src/assertion/impl/sync-collection.ts +34 -14
  201. package/src/assertion/impl/sync-esoteric.ts +17 -13
  202. package/src/assertion/impl/sync-parametric.ts +79 -52
  203. package/src/assertion/impl/sync.ts +2 -2
  204. package/src/assertion/index.ts +1 -1
  205. package/src/assertion/slotify.ts +67 -21
  206. package/src/bootstrap.ts +1 -0
  207. package/src/diff.ts +343 -0
  208. package/src/error.ts +66 -31
  209. package/src/expect.ts +195 -129
  210. package/src/guards.ts +74 -48
  211. package/src/internal-schema.ts +246 -0
  212. package/src/schema.ts +4 -2
  213. package/src/use.ts +21 -7
  214. package/src/util.ts +15 -12
  215. package/src/value-to-schema.ts +21 -13
package/src/guards.ts CHANGED
@@ -16,18 +16,24 @@
16
16
  * @packageDocumentation
17
17
  */
18
18
 
19
- import { type Primitive } from 'type-fest';
20
- import { z } from 'zod/v4';
19
+ import { type z } from 'zod/v4';
21
20
 
22
21
  import type {
23
- AssertionFailure,
24
22
  AssertionPart,
25
23
  PhraseLiteralChoice,
26
24
  } from './assertion/assertion-types.js';
27
- import type { Constructor, ExpectItExecutor, ZodTypeMap } from './types.js';
25
+ import type {
26
+ AssertionParts,
27
+ Constructor,
28
+ ExpectItExecutor,
29
+ PhraseLiteral,
30
+ ZodTypeMap,
31
+ } from './types.js';
28
32
 
29
33
  import { kExpectIt } from './constant.js';
30
34
 
35
+ const { isArray } = Array;
36
+
31
37
  /**
32
38
  * Returns `true` if the given value looks like a Zod v4 schema, determined by
33
39
  * the presence of an internal {@link z.core.$ZodTypeDef} field.
@@ -43,6 +49,7 @@ export function isZodType<T extends keyof ZodTypeMap>(
43
49
  value: unknown,
44
50
  type: T,
45
51
  ): value is ZodTypeMap[T];
52
+
46
53
  /**
47
54
  * Returns `true` if the given value looks like a Zod v4 schema, determined by
48
55
  * the presence of an internal {@link z.core.$ZodTypeDef} field.
@@ -65,8 +72,12 @@ export function isZodType<T extends keyof ZodTypeMap>(
65
72
  typeof value.def === 'object' &&
66
73
  'type' in value.def;
67
74
 
68
- if (!isValid) return false;
69
- if (type === undefined) return true;
75
+ if (!isValid) {
76
+ return false;
77
+ }
78
+ if (type === undefined) {
79
+ return true;
80
+ }
70
81
 
71
82
  return (value as z.ZodType).def.type === type;
72
83
  }
@@ -74,16 +85,18 @@ export function isZodType<T extends keyof ZodTypeMap>(
74
85
  /**
75
86
  * Type guard for a plain object.
76
87
  *
88
+ * @function
77
89
  * @param value Value to test
78
90
  * @returns `true` if the value is a plain object, `false` otherwise
79
91
  */
80
92
  export const isObject = (value: unknown): value is NonNullable<object> => {
81
- return typeof value === 'object' && value !== null && !Array.isArray(value);
93
+ return typeof value === 'object' && value !== null && !isArray(value);
82
94
  };
83
95
 
84
96
  /**
85
97
  * Returns `true` if the given value is a {@link z.ZodPromise} schema.
86
98
  *
99
+ * @function
87
100
  * @param value - Value to test
88
101
  * @returns `true` if the value is a `ZodPromise` schema; `false` otherwise
89
102
  */
@@ -93,6 +106,7 @@ export const isZodPromise = (value: unknown): value is z.ZodPromise =>
93
106
  /**
94
107
  * Checks if a value is "promise-like", meaning it is a "thenable" object.
95
108
  *
109
+ * @function
96
110
  * @param value - Value to test
97
111
  * @returns `true` if the value is promise-like, `false` otherwise
98
112
  */
@@ -114,6 +128,7 @@ export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
114
128
  * This may be the only way we can determine, at runtime, if a function is a
115
129
  * constructor without actually calling it. I am unsure if this only works for
116
130
  * classes.
131
+ * @function
117
132
  * @param fn - Function to test
118
133
  * @returns Whether the function is constructable
119
134
  */
@@ -123,7 +138,7 @@ export const isConstructible = (fn: unknown): fn is Constructor => {
123
138
  }
124
139
  try {
125
140
  // this will throw if there is no `[[construct]]` slot.. or so I've heard.
126
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
141
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, custom/require-function-tag-in-arrow-functions
127
142
  new new Proxy(fn as any, { construct: () => ({}) })();
128
143
  return true;
129
144
  } catch {
@@ -134,6 +149,7 @@ export const isConstructible = (fn: unknown): fn is Constructor => {
134
149
  /**
135
150
  * Type guard for a boolean value
136
151
  *
152
+ * @function
137
153
  * @param value Value to check
138
154
  * @returns `true` if the value is a boolean, `false` otherwise
139
155
  */
@@ -143,42 +159,17 @@ export const isBoolean = (value: unknown): value is boolean =>
143
159
  /**
144
160
  * Type guard for a function value
145
161
  *
162
+ * @function
146
163
  * @param value Value to check
147
164
  * @returns `true` if the value is a function, `false` otherwise
148
165
  */
149
166
  export const isFunction = (value: unknown): value is (...args: any[]) => any =>
150
167
  typeof value === 'function';
151
168
 
152
- const AssertionFailureSchema: z.ZodType<AssertionFailure> = z.object({
153
- actual: z
154
- .unknown()
155
- .optional()
156
- .describe('The actual value or description of what actually occurred'),
157
- expected: z
158
- .unknown()
159
- .optional()
160
- .describe(
161
- 'The expected value or description of what was expected to occur',
162
- ),
163
- message: z
164
- .string()
165
- .optional()
166
- .describe('A human-readable message describing the failure'),
167
- });
168
-
169
- /**
170
- * Type guard for a {@link AssertionFailure} object
171
- *
172
- * @param value Value to check
173
- * @returns `true` if the value is an `AssertionFailure`, `false` otherwise
174
- * @internal
175
- */
176
- export const isAssertionFailure = (value: unknown): value is AssertionFailure =>
177
- AssertionFailureSchema.safeParse(value).success;
178
-
179
169
  /**
180
170
  * Type guard for a string value
181
171
  *
172
+ * @function
182
173
  * @param value Value to check
183
174
  * @returns `true` if the value is a string, `false` otherwise
184
175
  */
@@ -188,27 +179,20 @@ export const isString = (value: unknown): value is string =>
188
179
  /**
189
180
  * Type guard for a non-null object value
190
181
  *
182
+ * @function
191
183
  * @param value Value to check
192
184
  * @returns `true` if the value is an object and not null, `false` otherwise
193
185
  */
194
186
  export const isNonNullObject = (value: unknown): value is object =>
195
187
  typeof value === 'object' && value !== null;
196
188
 
197
- /**
198
- * Type guard for a null or non-object value
199
- *
200
- * @param value Value to check
201
- * @returns `true` if the value is null or not an object, `false` otherwise
202
- */
203
- export const isNullOrNonObject = (value: unknown): value is null | Primitive =>
204
- typeof value !== 'object' || value === null;
205
-
206
189
  /**
207
190
  * Type guard for a valid WeakKey (object, function, or symbol).
208
191
  *
209
192
  * WeakMaps and WeakSets can only use objects (including functions) or symbols
210
193
  * as keys, not primitives like strings, numbers, booleans, null, or undefined.
211
194
  *
195
+ * @function
212
196
  * @param value Value to check
213
197
  * @returns `true` if the value is a valid WeakKey (object, function, or
214
198
  * symbol), `false` otherwise
@@ -221,25 +205,40 @@ export const isWeakKey = (value: unknown): value is WeakKey =>
221
205
  /**
222
206
  * Type guard for a {@link PhraseLiteralChoice}, which is a tuple of strings.
223
207
  *
208
+ * @function
224
209
  * @param value Assertion part to check
225
210
  * @returns `true` if the part is a `PhraseLiteralChoice`, `false` otherwise
226
211
  * @internal
227
212
  */
228
213
  export const isPhraseLiteralChoice = (
229
- value: AssertionPart,
214
+ value: unknown,
230
215
  ): value is PhraseLiteralChoice =>
231
- Array.isArray(value) && value.every(isPhraseLiteral);
216
+ isArray(value) && value.every(isPhraseLiteral);
232
217
 
233
218
  /**
234
219
  * Type guard for a {@link PhraseLiteral}, which is just a string that does not
235
220
  * begin with `not `.
236
221
  *
222
+ * @function
237
223
  * @param value Assertion part to check
238
224
  * @returns `true` if the part is a `PhraseLiteral`, `false` otherwise
239
225
  * @internal
240
226
  */
241
- export const isPhraseLiteral = (value: AssertionPart): value is string =>
242
- isString(value) && !value.startsWith('not ');
227
+ export const isPhraseLiteral = (value: unknown): value is PhraseLiteral =>
228
+ isString(value) && !value.startsWith('not ') && value !== 'and';
229
+
230
+ /**
231
+ * Type guard for a {@link PhraseLiteral} or {@link PhraseLiteralChoice}.
232
+ *
233
+ * @function
234
+ * @param value Value to check
235
+ * @returns `true` if the value is a `PhraseLiteral` or `PhraseLiteralChoice`,
236
+ * `false` otherwise
237
+ */
238
+ export const isPhrase = (
239
+ value: unknown,
240
+ ): value is PhraseLiteral | PhraseLiteralChoice =>
241
+ isPhraseLiteral(value) || isPhraseLiteralChoice(value);
243
242
 
244
243
  /**
245
244
  * Generic type guard for instanceof checks.
@@ -260,6 +259,7 @@ export const isPhraseLiteral = (value: AssertionPart): value is string =>
260
259
  * ```
261
260
  *
262
261
  * @template T - The constructor type to check against
262
+ * @function
263
263
  * @param value - Value to test
264
264
  * @param ctor - Constructor function to check instanceof
265
265
  * @returns `true` if the value is an instance of the constructor, `false`
@@ -292,6 +292,7 @@ export const isA = <T extends Constructor>(
292
292
  * }
293
293
  * ```
294
294
  *
295
+ * @function
295
296
  * @param value - Value to test
296
297
  * @returns `true` if the value is an Error instance, `false` otherwise
297
298
  */
@@ -318,6 +319,7 @@ export const isError = (value: unknown): value is Error => isA(value, Error);
318
319
  * ```
319
320
  *
320
321
  * @template Subject - The subject type that the executor function operates on
322
+ * @function
321
323
  * @param value - Value to test
322
324
  * @returns `true` if the value is an ExpectItExecutor function, `false`
323
325
  * otherwise
@@ -327,3 +329,27 @@ export const isExpectItExecutor = <Subject extends z.ZodType = z.ZodUnknown>(
327
329
  ): value is ExpectItExecutor<Subject> => {
328
330
  return isFunction(value) && kExpectIt in value && value[kExpectIt] === true;
329
331
  };
332
+
333
+ /**
334
+ * Type guard for an {@link AssertionPart}, which can be a {@link PhraseLiteral},
335
+ * {@link PhraseLiteralChoice}, or a Zod schema.
336
+ *
337
+ * @function
338
+ * @param value Value to check
339
+ * @returns `true` if the value is an `AssertionPart`, `false` otherwise
340
+ * @internal
341
+ */
342
+ export const isAssertionPart = (value: unknown): value is AssertionPart =>
343
+ isPhraseLiteral(value) || isPhraseLiteralChoice(value) || isZodType(value);
344
+
345
+ /**
346
+ * Type guard for {@link AssertionParts}, which is an array of
347
+ * {@link AssertionPart}.
348
+ *
349
+ * @function
350
+ * @param value Value to check
351
+ * @returns `true` if the value is an `AssertionParts`, `false` otherwise
352
+ * @internal
353
+ */
354
+ export const isAssertionParts = (value: unknown): value is AssertionParts =>
355
+ isArray(value) && !!value.length && value.every(isAssertionPart);
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Internal schemas
3
+ *
4
+ * @internal
5
+ * @packageDocumentation
6
+ */
7
+
8
+ import { z } from 'zod/v4';
9
+
10
+ import type {
11
+ AssertionFailure,
12
+ AssertionMetadata,
13
+ AssertionParseRequest,
14
+ } from './types.js';
15
+
16
+ import { isZodType } from './guards.js';
17
+
18
+ /**
19
+ * Schema for internal assertion metadata.
20
+ *
21
+ * Used by documentation tooling.
22
+ */
23
+
24
+ export const AssertionMetadataSchema: z.ZodType<AssertionMetadata> = z
25
+ .looseObject({
26
+ anchor: z.string().describe('Anchor ID for linking to this assertion.'),
27
+ category: z
28
+ .enum([
29
+ 'collections',
30
+ 'date',
31
+ 'equality',
32
+ 'error',
33
+ 'function',
34
+ 'numeric',
35
+ 'object',
36
+ 'other',
37
+ 'primitives',
38
+ 'promise',
39
+ 'strings',
40
+ ])
41
+ .describe('Category to map to page of logically grouped assertions'),
42
+ redirect: z
43
+ .string()
44
+ .optional()
45
+ .describe(
46
+ 'Redirect for assertion to its documentation page, including anchor',
47
+ ),
48
+ })
49
+ .describe(
50
+ 'Metadata associated with an assertion, for internal use by documentation tooling.',
51
+ );
52
+
53
+ /**
54
+ * Schema for {@link AssertionFailure}.
55
+ *
56
+ * @internal
57
+ */
58
+
59
+ const AssertionFailureSchema: z.ZodType<AssertionFailure> = z
60
+ .object({
61
+ actual: z
62
+ .unknown()
63
+ .optional()
64
+ .describe('The actual value or description of what actually occurred'),
65
+ expected: z
66
+ .unknown()
67
+ .optional()
68
+ .describe(
69
+ 'The expected value or description of what was expected to occur',
70
+ ),
71
+ message: z
72
+ .string()
73
+ .optional()
74
+ .describe('A human-readable message describing the failure'),
75
+ })
76
+ .describe('Potential return type of an assertion implementation function');
77
+
78
+ /**
79
+ * @internal
80
+ */
81
+ const ZodTypeSchema = z
82
+ .custom<z.ZodType>(isZodType, {
83
+ error: 'Must be a Zod schema',
84
+ })
85
+ .describe('A Zod schema within AssertionParts');
86
+
87
+ /** @internal */
88
+ const BaseAssertionParseRequestSchema = z.object({
89
+ subject: z.unknown().describe('The subject value to be validated'),
90
+ });
91
+
92
+ /**
93
+ * @internal
94
+ */
95
+ const AssertionParseRequestSchema: z.ZodType<AssertionParseRequest> = z.union([
96
+ z.object({
97
+ ...BaseAssertionParseRequestSchema.shape,
98
+ schema: ZodTypeSchema.describe('The sync Zod schema to validate against'),
99
+ }),
100
+ z.object({
101
+ ...BaseAssertionParseRequestSchema.shape,
102
+ asyncSchema: ZodTypeSchema.describe(
103
+ 'The async Zod schema to validate against',
104
+ ),
105
+ }),
106
+ ]);
107
+
108
+ /**
109
+ * @internal
110
+ */
111
+ const PhraseLiteralSchema = z
112
+ .stringFormat('PhraseLiteral', (value) => !value.startsWith('not '), {
113
+ error: 'Phrase literals may not begin with "not "',
114
+ })
115
+ .min(1, { error: 'Phrase literals must be at least 1 character long' })
116
+ .describe('A phrase literal within AssertionParts');
117
+
118
+ /**
119
+ * @internal
120
+ */
121
+ const PhraseLiteralChoiceSchema = z
122
+ .array(PhraseLiteralSchema)
123
+ .min(1, { error: 'Phrase literal choices must have at least one option' })
124
+ .describe(
125
+ 'A choice of phrase literals, represented as an array of strings, within AssertionParts',
126
+ );
127
+ /**
128
+ * @internal
129
+ */
130
+ const AssertionImplSchemaSync = z
131
+ .union([
132
+ ZodTypeSchema,
133
+ z.function({
134
+ input: z.tuple([z.unknown()], z.unknown()),
135
+ output: z.union([
136
+ z.void(),
137
+ z.boolean(),
138
+ ZodTypeSchema,
139
+ AssertionFailureSchema,
140
+ AssertionParseRequestSchema,
141
+ ]),
142
+ }),
143
+ ])
144
+ .describe('A synchronous assertion implementation function');
145
+
146
+ const AssertionImplSchemaAsync = z
147
+ .union([
148
+ ZodTypeSchema,
149
+ z.function({
150
+ input: z.tuple([z.unknown()], z.unknown()),
151
+ output: z.union([
152
+ z.void(),
153
+ z.boolean(),
154
+ ZodTypeSchema,
155
+ AssertionFailureSchema,
156
+ AssertionParseRequestSchema,
157
+ z.promise(z.void()),
158
+ z.promise(z.boolean()),
159
+ z.promise(ZodTypeSchema),
160
+ z.promise(AssertionFailureSchema),
161
+ z.promise(AssertionParseRequestSchema),
162
+ ]),
163
+ }),
164
+ ])
165
+ .describe('An async assertion implementation function');
166
+
167
+ /**
168
+ * @internal
169
+ */
170
+ const AssertionPartsSchema = z
171
+ .array(
172
+ z.union([PhraseLiteralSchema, PhraseLiteralChoiceSchema, ZodTypeSchema]),
173
+ )
174
+ .min(1, { error: 'At least one part is required for an assertion' })
175
+ .refine(
176
+ (parts) => {
177
+ // Special validation for 'and': it can only appear if followed by a ZodType
178
+ for (let i = 0; i < parts.length; i++) {
179
+ if (parts[i] === 'and') {
180
+ // 'and' must be followed by another part, and that part must be a ZodType
181
+ if (i === parts.length - 1 || !isZodType(parts[i + 1])) {
182
+ return false;
183
+ }
184
+ }
185
+ }
186
+ return true;
187
+ },
188
+ { error: '"and" can only appear when followed by a Zod schema' },
189
+ )
190
+ .describe('Assertion "parts" which define the input of an assertion');
191
+
192
+ /**
193
+ * Type guard for a {@link AssertionFailure}.
194
+ *
195
+ * This cannot live in `guards.ts` because it would create a cycle.
196
+ *
197
+ * @function
198
+ * @param value Value to check
199
+ * @returns `true` if value is an AssertionFailure
200
+ * @internal
201
+ */
202
+ export const isAssertionFailure = (
203
+ value: unknown,
204
+ ): value is AssertionFailure => {
205
+ return AssertionFailureSchema.safeParse(value).success;
206
+ };
207
+
208
+ /**
209
+ * @function
210
+ */
211
+ export const isAssertionParseRequest = (
212
+ value: unknown,
213
+ ): value is AssertionParseRequest => {
214
+ return AssertionParseRequestSchema.safeParse(value).success;
215
+ };
216
+
217
+ /**
218
+ * @internal
219
+ */
220
+ export const CreateAssertionInputSchema = z
221
+ .tuple([
222
+ AssertionPartsSchema,
223
+ AssertionImplSchemaSync,
224
+ z.optional(AssertionMetadataSchema),
225
+ ])
226
+ .describe('Parameters for createAssertion()');
227
+
228
+ /**
229
+ * @internal
230
+ */
231
+ export const CreateAssertionInputSchemaAsync = z
232
+ .tuple([
233
+ AssertionPartsSchema,
234
+ AssertionImplSchemaAsync,
235
+ z.optional(AssertionMetadataSchema),
236
+ ])
237
+ .describe('Parameters for createAsyncAssertion()');
238
+
239
+ /**
240
+ * @function
241
+ * @param value
242
+ * @returns
243
+ * @knipignore
244
+ */
245
+ export const isMetadata = (value: unknown): value is AssertionMetadata =>
246
+ AssertionMetadataSchema.safeParse(value).success;
package/src/schema.ts CHANGED
@@ -50,6 +50,8 @@ import {
50
50
  type MutableOrReadonly,
51
51
  } from './types.js';
52
52
 
53
+ const { getPrototypeOf, prototype: objectPrototype } = Object;
54
+
53
55
  /**
54
56
  * A Zod schema that validates JavaScript constructible functions.
55
57
  *
@@ -386,7 +388,7 @@ export const WrappedPromiseLikeSchema = z
386
388
  */
387
389
  export const DictionarySchema = z
388
390
  .custom<Record<PropertyKey, unknown>>(
389
- (value) => isNonNullObject(value) && Object.getPrototypeOf(value) === null,
391
+ (value) => isNonNullObject(value) && getPrototypeOf(value) === null,
390
392
  )
391
393
  .describe('Object with null prototype')
392
394
  .register(BupkisRegistry, { name: 'dictionary' });
@@ -450,7 +452,7 @@ export const NullProtoObjectSchema = DictionarySchema;
450
452
  * @group Schema
451
453
  */
452
454
  export const AsyncFunctionSchema = FunctionSchema.refine(
453
- (value) => Object.prototype.toString.call(value) === '[object AsyncFunction]',
455
+ (value) => objectPrototype.toString.call(value) === '[object AsyncFunction]',
454
456
  )
455
457
  .describe('Function declared with the `async` keyword')
456
458
  .register(BupkisRegistry, { name: 'async-function' });
package/src/use.ts CHANGED
@@ -27,6 +27,8 @@ import {
27
27
  type UseFn,
28
28
  } from './types.js';
29
29
 
30
+ const { assign } = Object;
31
+
30
32
  /**
31
33
  * Creates an `expect.it()` factory function for generating embeddable assertion
32
34
  * executors.
@@ -60,6 +62,7 @@ import {
60
62
  *
61
63
  * @template SyncAssertions - Array of synchronous assertion objects that define
62
64
  * the available assertion logic for the embeddable functions
65
+ * @function
63
66
  * @param expect - The underlying expect function that will execute the
64
67
  * assertions when the returned executors are called
65
68
  * @returns A factory function that creates {@link ExpectItExecutor} instances
@@ -72,8 +75,11 @@ import {
72
75
  const createExpectIt = <SyncAssertions extends AnySyncAssertions>(
73
76
  expect: any,
74
77
  ): ExpectIt<SyncAssertions> => {
78
+ /**
79
+ * @function
80
+ */
75
81
  const expectIt = (...args: readonly unknown[]) => {
76
- const func = Object.assign(
82
+ const func = assign(
77
83
  (subject: unknown) => {
78
84
  const allArgs = [subject, ...args];
79
85
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
@@ -121,6 +127,7 @@ const createExpectIt = <SyncAssertions extends AnySyncAssertions>(
121
127
  *
122
128
  * @template AsyncAssertions - Array of asynchronous assertion objects that
123
129
  * define the available assertion logic for the embeddable async functions
130
+ * @function
124
131
  * @param expectAsync - The underlying expectAsync function that will execute
125
132
  * the assertions when the returned executors are called
126
133
  * @returns A factory function that creates {@link ExpectItExecutorAsync}
@@ -134,8 +141,11 @@ const createExpectIt = <SyncAssertions extends AnySyncAssertions>(
134
141
  const createExpectItAsync = <AsyncAssertions extends AnyAsyncAssertions>(
135
142
  expectAsync: any,
136
143
  ): ExpectItAsync<AsyncAssertions> => {
144
+ /**
145
+ * @function
146
+ */
137
147
  const expectItAsync = (...args: readonly unknown[]) => {
138
- const func = Object.assign(
148
+ const func = assign(
139
149
  async (subject: unknown) => {
140
150
  const allArgs = [subject, ...args];
141
151
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
@@ -210,6 +220,7 @@ const createExpectItAsync = <AsyncAssertions extends AnyAsyncAssertions>(
210
220
  * available in the composed functions
211
221
  * @template AsyncAssertions - Array of base asynchronous assertions that will
212
222
  * be available in the composed functions
223
+ * @function
213
224
  * @param syncAssertions - Base synchronous assertions to include in all
214
225
  * composed expect functions
215
226
  * @param asyncAssertions - Base asynchronous assertions to include in all
@@ -223,15 +234,18 @@ const createExpectItAsync = <AsyncAssertions extends AnyAsyncAssertions>(
223
234
  * @see {@link FilterAsyncAssertions} for how async assertions are extracted
224
235
  * @see {@link Concat} for how assertion arrays are combined
225
236
  */
226
- export function createUse<
237
+ export const createUse = <
227
238
  const SyncAssertions extends AnySyncAssertions,
228
239
  const AsyncAssertions extends AnyAsyncAssertions,
229
240
  >(
230
241
  syncAssertions: SyncAssertions,
231
242
  asyncAssertions: AsyncAssertions,
232
- ): UseFn<SyncAssertions, AsyncAssertions> {
243
+ ): UseFn<SyncAssertions, AsyncAssertions> => {
233
244
  const syncAssertionsIn = syncAssertions ?? [];
234
245
  const asyncAssertionsIn = asyncAssertions ?? [];
246
+ /**
247
+ * @function
248
+ */
235
249
  const use: UseFn<SyncAssertions, AsyncAssertions> = <
236
250
  AllAssertions extends readonly AnyAssertion[],
237
251
  FilteredSyncAssertions extends FilterSyncAssertions<AllAssertions>,
@@ -256,12 +270,12 @@ export function createUse<
256
270
  const expectFunction = createExpectSyncFunction(allSyncAssertions);
257
271
  const expectAsyncFunction = createExpectAsyncFunction(allAsyncAssertions);
258
272
 
259
- const expect = Object.assign(
273
+ const expect = assign(
260
274
  expectFunction,
261
275
  createBaseExpect(allSyncAssertions, allAsyncAssertions, 'sync'),
262
276
  { it: createExpectIt(expectFunction) },
263
277
  );
264
- const expectAsync = Object.assign(
278
+ const expectAsync = assign(
265
279
  expectAsyncFunction,
266
280
  createBaseExpect(allSyncAssertions, allAsyncAssertions, 'async'),
267
281
  { it: createExpectItAsync(expectAsyncFunction) },
@@ -276,4 +290,4 @@ export function createUse<
276
290
  };
277
291
  };
278
292
  return use;
279
- }
293
+ };