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.
Files changed (198) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +16 -16
  3. package/dist/commonjs/assertion/assertion-async.d.ts +2 -1
  4. package/dist/commonjs/assertion/assertion-async.d.ts.map +1 -1
  5. package/dist/commonjs/assertion/assertion-async.js +84 -2
  6. package/dist/commonjs/assertion/assertion-async.js.map +1 -1
  7. package/dist/commonjs/assertion/assertion-sync.d.ts +1 -1
  8. package/dist/commonjs/assertion/assertion-sync.d.ts.map +1 -1
  9. package/dist/commonjs/assertion/assertion-sync.js +5 -1
  10. package/dist/commonjs/assertion/assertion-sync.js.map +1 -1
  11. package/dist/commonjs/assertion/assertion-types.d.ts +39 -84
  12. package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -1
  13. package/dist/commonjs/assertion/assertion.d.ts +1 -1
  14. package/dist/commonjs/assertion/assertion.d.ts.map +1 -1
  15. package/dist/commonjs/assertion/assertion.js +1 -14
  16. package/dist/commonjs/assertion/assertion.js.map +1 -1
  17. package/dist/commonjs/assertion/create.d.ts +5 -33
  18. package/dist/commonjs/assertion/create.d.ts.map +1 -1
  19. package/dist/commonjs/assertion/create.js +17 -6
  20. package/dist/commonjs/assertion/create.js.map +1 -1
  21. package/dist/commonjs/assertion/impl/async.d.ts +122 -21
  22. package/dist/commonjs/assertion/impl/async.d.ts.map +1 -1
  23. package/dist/commonjs/assertion/impl/async.js +114 -90
  24. package/dist/commonjs/assertion/impl/async.js.map +1 -1
  25. package/dist/commonjs/assertion/impl/callback.d.ts +104 -0
  26. package/dist/commonjs/assertion/impl/callback.d.ts.map +1 -0
  27. package/dist/commonjs/assertion/impl/callback.js +694 -0
  28. package/dist/commonjs/assertion/impl/callback.js.map +1 -0
  29. package/dist/commonjs/assertion/impl/index.d.ts +1 -1
  30. package/dist/commonjs/assertion/impl/index.d.ts.map +1 -1
  31. package/dist/commonjs/assertion/impl/index.js.map +1 -1
  32. package/dist/commonjs/assertion/impl/sync-esoteric.js +1 -1
  33. package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -1
  34. package/dist/commonjs/assertion/impl/sync-parametric.d.ts +37 -34
  35. package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
  36. package/dist/commonjs/assertion/impl/sync-parametric.js +32 -47
  37. package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
  38. package/dist/commonjs/assertion/impl/sync.d.ts +105 -58
  39. package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -1
  40. package/dist/commonjs/assertion/impl/sync.js +4 -1
  41. package/dist/commonjs/assertion/impl/sync.js.map +1 -1
  42. package/dist/commonjs/bootstrap.d.ts +199 -85
  43. package/dist/commonjs/bootstrap.d.ts.map +1 -1
  44. package/dist/commonjs/bootstrap.js +19 -10
  45. package/dist/commonjs/bootstrap.js.map +1 -1
  46. package/dist/commonjs/constant.js +7 -1
  47. package/dist/commonjs/constant.js.map +1 -1
  48. package/dist/commonjs/error.d.ts +32 -5
  49. package/dist/commonjs/error.d.ts.map +1 -1
  50. package/dist/commonjs/error.js +60 -5
  51. package/dist/commonjs/error.js.map +1 -1
  52. package/dist/commonjs/expect.d.ts +130 -3
  53. package/dist/commonjs/expect.d.ts.map +1 -1
  54. package/dist/commonjs/expect.js +116 -1
  55. package/dist/commonjs/expect.js.map +1 -1
  56. package/dist/commonjs/guards.d.ts +45 -20
  57. package/dist/commonjs/guards.d.ts.map +1 -1
  58. package/dist/commonjs/guards.js +56 -40
  59. package/dist/commonjs/guards.js.map +1 -1
  60. package/dist/commonjs/index.d.ts +241 -86
  61. package/dist/commonjs/index.d.ts.map +1 -1
  62. package/dist/commonjs/index.js +44 -42
  63. package/dist/commonjs/index.js.map +1 -1
  64. package/dist/commonjs/metadata.d.ts +1 -27
  65. package/dist/commonjs/metadata.d.ts.map +1 -1
  66. package/dist/commonjs/metadata.js +16 -15
  67. package/dist/commonjs/metadata.js.map +1 -1
  68. package/dist/commonjs/schema.d.ts +76 -33
  69. package/dist/commonjs/schema.d.ts.map +1 -1
  70. package/dist/commonjs/schema.js +77 -34
  71. package/dist/commonjs/schema.js.map +1 -1
  72. package/dist/commonjs/types.d.ts +480 -39
  73. package/dist/commonjs/types.d.ts.map +1 -1
  74. package/dist/commonjs/types.js +12 -2
  75. package/dist/commonjs/types.js.map +1 -1
  76. package/dist/commonjs/util.d.ts +72 -49
  77. package/dist/commonjs/util.d.ts.map +1 -1
  78. package/dist/commonjs/util.js +175 -155
  79. package/dist/commonjs/util.js.map +1 -1
  80. package/dist/commonjs/value-to-schema.d.ts +122 -0
  81. package/dist/commonjs/value-to-schema.d.ts.map +1 -0
  82. package/dist/commonjs/value-to-schema.js +309 -0
  83. package/dist/commonjs/value-to-schema.js.map +1 -0
  84. package/dist/esm/assertion/assertion-async.d.ts +2 -1
  85. package/dist/esm/assertion/assertion-async.d.ts.map +1 -1
  86. package/dist/esm/assertion/assertion-async.js +85 -3
  87. package/dist/esm/assertion/assertion-async.js.map +1 -1
  88. package/dist/esm/assertion/assertion-sync.d.ts +1 -1
  89. package/dist/esm/assertion/assertion-sync.d.ts.map +1 -1
  90. package/dist/esm/assertion/assertion-sync.js +6 -2
  91. package/dist/esm/assertion/assertion-sync.js.map +1 -1
  92. package/dist/esm/assertion/assertion-types.d.ts +39 -84
  93. package/dist/esm/assertion/assertion-types.d.ts.map +1 -1
  94. package/dist/esm/assertion/assertion.d.ts +1 -1
  95. package/dist/esm/assertion/assertion.d.ts.map +1 -1
  96. package/dist/esm/assertion/assertion.js +1 -14
  97. package/dist/esm/assertion/assertion.js.map +1 -1
  98. package/dist/esm/assertion/create.d.ts +5 -33
  99. package/dist/esm/assertion/create.d.ts.map +1 -1
  100. package/dist/esm/assertion/create.js +14 -4
  101. package/dist/esm/assertion/create.js.map +1 -1
  102. package/dist/esm/assertion/impl/async.d.ts +122 -21
  103. package/dist/esm/assertion/impl/async.d.ts.map +1 -1
  104. package/dist/esm/assertion/impl/async.js +113 -89
  105. package/dist/esm/assertion/impl/async.js.map +1 -1
  106. package/dist/esm/assertion/impl/callback.d.ts +104 -0
  107. package/dist/esm/assertion/impl/callback.d.ts.map +1 -0
  108. package/dist/esm/assertion/impl/callback.js +691 -0
  109. package/dist/esm/assertion/impl/callback.js.map +1 -0
  110. package/dist/esm/assertion/impl/index.d.ts +1 -1
  111. package/dist/esm/assertion/impl/index.d.ts.map +1 -1
  112. package/dist/esm/assertion/impl/index.js +1 -1
  113. package/dist/esm/assertion/impl/index.js.map +1 -1
  114. package/dist/esm/assertion/impl/sync-esoteric.js +2 -2
  115. package/dist/esm/assertion/impl/sync-esoteric.js.map +1 -1
  116. package/dist/esm/assertion/impl/sync-parametric.d.ts +37 -34
  117. package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
  118. package/dist/esm/assertion/impl/sync-parametric.js +32 -47
  119. package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
  120. package/dist/esm/assertion/impl/sync.d.ts +105 -58
  121. package/dist/esm/assertion/impl/sync.d.ts.map +1 -1
  122. package/dist/esm/assertion/impl/sync.js +3 -1
  123. package/dist/esm/assertion/impl/sync.js.map +1 -1
  124. package/dist/esm/bootstrap.d.ts +199 -85
  125. package/dist/esm/bootstrap.d.ts.map +1 -1
  126. package/dist/esm/bootstrap.js +19 -10
  127. package/dist/esm/bootstrap.js.map +1 -1
  128. package/dist/esm/constant.js +6 -0
  129. package/dist/esm/constant.js.map +1 -1
  130. package/dist/esm/error.d.ts +32 -5
  131. package/dist/esm/error.d.ts.map +1 -1
  132. package/dist/esm/error.js +59 -5
  133. package/dist/esm/error.js.map +1 -1
  134. package/dist/esm/expect.d.ts +130 -3
  135. package/dist/esm/expect.d.ts.map +1 -1
  136. package/dist/esm/expect.js +117 -2
  137. package/dist/esm/expect.js.map +1 -1
  138. package/dist/esm/guards.d.ts +45 -20
  139. package/dist/esm/guards.d.ts.map +1 -1
  140. package/dist/esm/guards.js +48 -31
  141. package/dist/esm/guards.js.map +1 -1
  142. package/dist/esm/index.d.ts +241 -86
  143. package/dist/esm/index.d.ts.map +1 -1
  144. package/dist/esm/index.js +46 -7
  145. package/dist/esm/index.js.map +1 -1
  146. package/dist/esm/metadata.d.ts +1 -27
  147. package/dist/esm/metadata.d.ts.map +1 -1
  148. package/dist/esm/metadata.js +2 -1
  149. package/dist/esm/metadata.js.map +1 -1
  150. package/dist/esm/schema.d.ts +76 -33
  151. package/dist/esm/schema.d.ts.map +1 -1
  152. package/dist/esm/schema.js +77 -34
  153. package/dist/esm/schema.js.map +1 -1
  154. package/dist/esm/types.d.ts +480 -39
  155. package/dist/esm/types.d.ts.map +1 -1
  156. package/dist/esm/types.js +12 -2
  157. package/dist/esm/types.js.map +1 -1
  158. package/dist/esm/util.d.ts +72 -49
  159. package/dist/esm/util.d.ts.map +1 -1
  160. package/dist/esm/util.js +159 -153
  161. package/dist/esm/util.js.map +1 -1
  162. package/dist/esm/value-to-schema.d.ts +122 -0
  163. package/dist/esm/value-to-schema.d.ts.map +1 -0
  164. package/dist/esm/value-to-schema.js +305 -0
  165. package/dist/esm/value-to-schema.js.map +1 -0
  166. package/package.json +94 -17
  167. package/src/assertion/assertion-async.ts +113 -3
  168. package/src/assertion/assertion-sync.ts +5 -2
  169. package/src/assertion/assertion-types.ts +52 -45
  170. package/src/assertion/assertion.ts +2 -17
  171. package/src/assertion/create.ts +16 -65
  172. package/src/assertion/impl/async.ts +132 -92
  173. package/src/assertion/impl/callback.ts +882 -0
  174. package/src/assertion/impl/index.ts +1 -1
  175. package/src/assertion/impl/sync-esoteric.ts +2 -2
  176. package/src/assertion/impl/sync-parametric.ts +41 -49
  177. package/src/assertion/impl/sync.ts +3 -0
  178. package/src/bootstrap.ts +21 -11
  179. package/src/constant.ts +8 -0
  180. package/src/error.ts +75 -4
  181. package/src/expect.ts +275 -20
  182. package/src/guards.ts +74 -69
  183. package/src/index.ts +72 -11
  184. package/src/metadata.ts +3 -4
  185. package/src/schema.ts +80 -36
  186. package/src/types.ts +625 -72
  187. package/src/util.ts +174 -222
  188. package/src/value-to-schema.ts +464 -0
  189. package/dist/commonjs/api.d.ts +0 -93
  190. package/dist/commonjs/api.d.ts.map +0 -1
  191. package/dist/commonjs/api.js +0 -8
  192. package/dist/commonjs/api.js.map +0 -1
  193. package/dist/esm/api.d.ts +0 -93
  194. package/dist/esm/api.d.ts.map +0 -1
  195. package/dist/esm/api.js +0 -7
  196. package/dist/esm/api.js.map +0 -1
  197. package/src/api.ts +0 -149
  198. package/src/schema.md +0 -15
package/src/types.ts CHANGED
@@ -1,14 +1,25 @@
1
1
  /**
2
- * Types used throughout _BUPKIS_, mainly related to the
3
- * `expect()`/`expectAsync()` API.
2
+ * Types used throughout _BUPKIS_.
3
+ *
4
+ * May be useful for those building on top of _BUPKIS_.
5
+ *
6
+ * @example
7
+ *
8
+ * ```ts
9
+ * // namespace
10
+ * import { types } from 'bupkis';
11
+ * // subpath import
12
+ * import type * as alsoTypes from 'bupkis/types';
13
+ * ```
4
14
  *
5
15
  * @packageDocumentation
6
16
  */
7
17
 
8
18
  import type {
9
19
  ArrayValues,
10
- NonEmptyTuple,
20
+ TupleToUnion,
11
21
  Constructor as TypeFestConstructor,
22
+ UnionToIntersection,
12
23
  } from 'type-fest';
13
24
  import type { z } from 'zod/v4';
14
25
 
@@ -18,99 +29,494 @@ import type {
18
29
  AnyAsyncAssertions,
19
30
  AnySyncAssertion,
20
31
  AnySyncAssertions,
32
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
+ Assertion,
34
+ AssertionFunctionAsync,
35
+ AssertionFunctionSync,
36
+ AssertionImplFnAsync,
37
+ AssertionImplFnSync,
38
+ AssertionImplSchemaAsync,
39
+ AssertionImplSchemaSync,
21
40
  AssertionPart,
22
41
  AssertionParts,
42
+ AssertionSchemaAsync,
43
+ AssertionSchemaSync,
23
44
  AssertionSlot,
45
+ AssertionSlots,
46
+ BuiltinAsyncAssertions,
47
+ BuiltinSyncAssertions,
24
48
  NoNeverTuple,
25
49
  PhraseLiteral,
26
50
  PhraseLiteralChoice,
27
51
  PhraseLiteralChoiceSlot,
28
52
  PhraseLiteralSlot,
53
+ RawAssertionImplSchemaSync,
29
54
  } from './assertion/assertion-types.js';
30
55
 
31
- import { type Expect, type ExpectAsync } from './api.js';
32
-
33
56
  /**
34
- * Helper type to create negated version of assertion parts. For phrase
35
- * literals, creates "not phrase" version. For phrase arrays, creates negated
36
- * versions of each phrase in the array.
57
+ * Creates a negated version of a tuple of
58
+ * {@link AssertionPart | AssertionParts}.
59
+ *
60
+ * For {@link PhraseLiteral | PhraseLiterals}, creates a
61
+ * {@link Negation | "not" variant}. For
62
+ * {@link PhraseLiteralChoice | PhraseLiteralChoices}, creates negated versions
63
+ * of each `Phrase` in the array.
64
+ *
65
+ * Does not affect Zod schemas.
66
+ *
67
+ * @template Parts Parts containing `PhraseLiterals` or `PhraseLiteralChoices`
68
+ * to negate.
37
69
  */
38
- export type AddNegation<T extends readonly AssertionPart[]> =
39
- T extends readonly [
70
+ export type AddNegation<Parts extends readonly AssertionPart[]> =
71
+ Parts extends readonly [
40
72
  infer First extends AssertionPart,
41
73
  ...infer Rest extends readonly AssertionPart[],
42
74
  ]
43
- ? First extends NonEmptyTuple<string>
75
+ ? First extends PhraseLiteralChoice
44
76
  ? readonly [
45
77
  {
46
- [K in keyof First]: First[K] extends string
78
+ [K in keyof First]: First[K] extends PhraseLiteral
47
79
  ? Negation<First[K]>
48
80
  : never;
49
81
  },
50
82
  ...AddNegation<Rest>,
51
83
  ]
52
- : First extends string
84
+ : First extends PhraseLiteral
53
85
  ? readonly [Negation<First>, ...AddNegation<Rest>]
54
86
  : readonly [First, ...AddNegation<Rest>]
55
87
  : readonly [];
56
88
 
89
+ /**
90
+ * Base set of properties included in both {@link Expect} and {@link ExpectAsync}.
91
+ *
92
+ * @preventExpand
93
+ */
94
+ export interface BaseExpect {
95
+ /**
96
+ * Creates a new synchronous assertion.
97
+ */
98
+ createAssertion: CreateAssertionFn;
99
+
100
+ /**
101
+ * Creates a new asynchronous assertion.
102
+ */
103
+ createAsyncAssertion: CreateAsyncAssertionFn;
104
+ /**
105
+ * Fails immediately with optional `reason`.
106
+ *
107
+ * @param reason Reason for failure
108
+ * @throws {AssertionError}
109
+ */
110
+ fail: FailFn;
111
+ }
112
+
113
+ export type * from './assertion/assertion-types.js';
114
+
115
+ /**
116
+ * The main API as returned by a {@link UseFn}.
117
+ *
118
+ * @template BaseSyncAssertions Base set of synchronous
119
+ * {@link Assertion Assertions}; will be the builtin sync assertions, at
120
+ * minimum)
121
+ * @template BaseAsyncAssertions Base set of asynchronous
122
+ * {@link Assertion Assertions}; will be the builtin async assertions, at
123
+ * minimum)
124
+ * @template ExtendedSyncAssertions Synchronous assertions extracted from
125
+ * `MixedAssertions`
126
+ * @template ExtendedAsyncAssertions Asynchronous assertions extracted from
127
+ * `MixedAssertions`
128
+ */
129
+ export interface Bupkis<
130
+ BaseSyncAssertions extends AnySyncAssertions,
131
+ BaseAsyncAssertions extends AnyAsyncAssertions,
132
+ ExtendedSyncAssertions extends readonly AnySyncAssertion[] = readonly [],
133
+ ExtendedAsyncAssertions extends readonly AnyAsyncAssertion[] = readonly [],
134
+ > {
135
+ /**
136
+ * A new {@link Expect} function which handles {@link ExtendedSyncAssertions}
137
+ * and {@link BaseSyncAssertions}
138
+ */
139
+ expect: Expect<
140
+ Concat<BaseSyncAssertions, ExtendedSyncAssertions>,
141
+ Concat<BaseAsyncAssertions, ExtendedAsyncAssertions>
142
+ >;
143
+ /**
144
+ * A new {@link ExpectAsync} function which handles
145
+ * {@link ExtendedAsyncAssertions} and {@link BaseAsyncAssertions}
146
+ */
147
+ expectAsync: ExpectAsync<
148
+ Concat<BaseAsyncAssertions, ExtendedAsyncAssertions>,
149
+ Concat<BaseSyncAssertions, ExtendedSyncAssertions>
150
+ >;
151
+ /**
152
+ * For composing arrays of assertions, one after another.
153
+ *
154
+ * The _only_ chainable API in <span class="bupkis">Bupkis</span>.
155
+ *
156
+ * @since 0.1.0
157
+ * @example
158
+ *
159
+ * ```ts
160
+ * const { expect } = use([...someAssertions]).use([...otherAssertions]);
161
+ * ```
162
+ */
163
+ use: UseFn<
164
+ Concat<BaseSyncAssertions, ExtendedSyncAssertions>,
165
+ Concat<BaseAsyncAssertions, ExtendedAsyncAssertions>
166
+ >;
167
+ }
168
+
57
169
  /**
58
170
  * Helper type to concatenate two tuples
59
171
  */
60
172
  export type Concat<
61
- A extends readonly unknown[],
62
- B extends readonly unknown[],
63
- > = readonly [...A, ...B];
173
+ TupleA extends readonly unknown[],
174
+ TupleB extends readonly unknown[],
175
+ > = readonly [...TupleA, ...TupleB];
64
176
 
65
177
  /**
66
178
  * A constructor based on {@link TypeFestConstructor type-fest's Constructor}
67
179
  * with a default instance type argument.
68
180
  */
69
181
  export type Constructor<
70
- T = any,
71
- Arguments extends unknown[] = any[],
72
- > = TypeFestConstructor<T, Arguments>;
73
-
74
- export type FilterAsyncAssertions<T extends readonly AnyAssertion[]> =
75
- T extends readonly [
76
- infer S extends AnyAssertion,
77
- ...infer Rest extends readonly AnyAssertion[],
78
- ]
79
- ? S extends AnyAsyncAssertion
80
- ? readonly [S, ...FilterAsyncAssertions<Rest>]
81
- : FilterAsyncAssertions<Rest>
82
- : readonly [];
182
+ Instance = any,
183
+ Args extends unknown[] = any[],
184
+ > = TypeFestConstructor<Instance, Args>;
83
185
 
84
- export type FilterSyncAssertions<T extends readonly AnyAssertion[]> =
85
- T extends readonly [
86
- infer S extends AnyAssertion,
87
- ...infer Rest extends readonly AnyAssertion[],
88
- ]
89
- ? S extends AnySyncAssertion
90
- ? readonly [S, ...FilterSyncAssertions<Rest>]
91
- : FilterSyncAssertions<Rest>
92
- : readonly [];
186
+ /**
187
+ * The main factory function for creating synchronous assertions.
188
+ */
189
+ export interface CreateAssertionFn {
190
+ /**
191
+ * Create a synchronous `Assertion` from {@link AssertionParts parts} and a
192
+ * {@link z.ZodType Zod schema}.
193
+ *
194
+ * @template Parts Parts defining the shape of the assertion, including
195
+ * Phrases and Zod schemas
196
+ * @template Impl Assertion implementation as a Zod schema
197
+ * @template Slots Inferred slots based on the provided `Parts`
198
+ * @returns New `AssertionSchemaSync` object
199
+ */
200
+ <
201
+ const Parts extends AssertionParts,
202
+ Impl extends RawAssertionImplSchemaSync<Parts>,
203
+ Slots extends AssertionSlots<Parts>,
204
+ >(
205
+ parts: Parts,
206
+ impl: Impl,
207
+ ): AssertionSchemaSync<Parts, AssertionImplSchemaSync<Parts>, Slots>;
208
+
209
+ /**
210
+ * Create a synchronous `Assertion` from {@link AssertionParts parts} and an
211
+ * implementation function.
212
+ *
213
+ * @template Parts Parts defining the shape of the assertion, including
214
+ * Phrases and Zod schemas
215
+ * @template Impl Assertion implementation as a function
216
+ * @template Slots Inferred slots based on the provided `Parts`
217
+ * @returns New `AssertionFunctionSync` object
218
+ */
219
+ <
220
+ const Parts extends AssertionParts,
221
+ Impl extends AssertionImplFnSync<Parts>,
222
+ Slots extends AssertionSlots<Parts>,
223
+ >(
224
+ parts: Parts,
225
+ impl: Impl,
226
+ ): AssertionFunctionSync<Parts, Impl, Slots>;
227
+ }
93
228
 
94
229
  /**
95
- * Prepares {@link MapExpectSlots} by injecting `unknown` if the `AssertionParts`
96
- * have no `z.ZodType` in the head position.
230
+ * The main factory function for creating asynchronous assertions.
231
+ */
232
+ export interface CreateAsyncAssertionFn {
233
+ /**
234
+ * Create an async `Assertion` from {@link AssertionParts parts} and an
235
+ * {@link z.ZodType Zod schema}.
236
+ *
237
+ * The Zod schema need not be async itself.
238
+ *
239
+ * @template Parts Parts defining the shape of the assertion, including
240
+ * Phrases and Zod schemas
241
+ * @template Impl Assertion implementation as a Zod schema
242
+ * @template Slots Inferred slots based on the provided `Parts`
243
+ * @returns New `AssertionSchemaAsync` object
244
+ */
245
+ <
246
+ const Parts extends AssertionParts,
247
+ Impl extends AssertionImplSchemaAsync<Parts>,
248
+ Slots extends AssertionSlots<Parts>,
249
+ >(
250
+ parts: Parts,
251
+ impl: Impl,
252
+ ): AssertionSchemaAsync<Parts, Impl, Slots>;
253
+
254
+ /**
255
+ * Create an async `Assertion` from {@link AssertionParts parts} and an
256
+ * implementation function.
257
+ *
258
+ * @template Parts Parts defining the shape of the assertion, including
259
+ * Phrases and Zod schemas
260
+ * @template Impl Assertion implementation as a function
261
+ * @template Slots Inferred slots based on the provided `Parts`
262
+ * @returns New `AssertionFunctionAsync` object
263
+ */
264
+ <
265
+ const Parts extends AssertionParts,
266
+ Impl extends AssertionImplFnAsync<Parts>,
267
+ Slots extends AssertionSlots<Parts>,
268
+ >(
269
+ parts: Parts,
270
+ impl: Impl,
271
+ ): AssertionFunctionAsync<Parts, Impl, Slots>;
272
+ }
273
+
274
+ /**
275
+ * The main synchronous assertion function.
97
276
  *
98
- * Also filters out `never` from the resulting tuple to guarantee tupleness.
277
+ * Contains properties in {@link ExpectSyncProps}.
99
278
  *
100
- * @remarks
101
- * This is a convenience and I hope it's not too confusing.
279
+ * @template SyncAssertions All synchronous assertions available
280
+ * @template AsyncAssertions All asynchronous assertions available; for use in
281
+ * {@link ExpectSyncProps.use}
282
+ * @expandType ExpectSyncProps
283
+ * @see {@link expect}
102
284
  */
103
- export type InferredExpectSlots<Parts extends AssertionParts> = NoNeverTuple<
104
- Parts extends readonly [infer First extends AssertionPart, ...infer _]
105
- ? First extends PhraseLiteral | PhraseLiteralChoice
106
- ? [unknown, ...MapExpectSlots<Parts>]
107
- : MapExpectSlots<Parts>
108
- : never
109
- >; /**
110
- * Maps `AssertionParts` to the corresponding argument types for `expect` and
111
- * `expectAsync`, as provided by the user.
285
+ export type Expect<
286
+ SyncAssertions extends AnySyncAssertions = BuiltinSyncAssertions,
287
+ AsyncAssertions extends AnyAsyncAssertions = BuiltinAsyncAssertions,
288
+ > = ExpectFunction<SyncAssertions> &
289
+ ExpectSyncProps<SyncAssertions, AsyncAssertions>;
290
+
291
+ /**
292
+ * The main asynchronous assertion function.
293
+ *
294
+ * Contains properties in {@link ExpectAsyncProps}.
295
+ *
296
+ * @template AsyncAssertions All asynchronous assertions available
297
+ * @template SyncAssertions All synchronous assertions available; for use in
298
+ * {@link ExpectAsyncProps.use}
299
+ * @expandType ExpectAsyncProps
300
+ * @see {@link expectAsync}
301
+ */
302
+ export type ExpectAsync<
303
+ AsyncAssertions extends AnyAsyncAssertions = BuiltinAsyncAssertions,
304
+ SyncAssertions extends AnySyncAssertions = BuiltinSyncAssertions,
305
+ > = ExpectAsyncFunction<AsyncAssertions> &
306
+ ExpectAsyncProps<AsyncAssertions, SyncAssertions>;
307
+
308
+ /**
309
+ * The callable function type for asynchronous assertions.
310
+ *
311
+ * This type represents the actual function signature of an async expect
312
+ * function, created by mapping all available assertions to their respective
313
+ * function signatures and combining them using intersection types. Each
314
+ * assertion contributes its own overload to the final function type.
315
+ *
316
+ * The function signatures are derived from the {@link AssertionParts} of each
317
+ * assertion, with parameters that match the expected slots for natural language
318
+ * assertion calls.
112
319
  *
113
- * Overloads each phrase literal slot with a negated version using a union.
320
+ * @example
321
+ *
322
+ * ```typescript
323
+ * // Example function type derived from async assertions
324
+ * const expectAsync: ExpectAsyncFunction<MyAsyncAssertions> = ...;
325
+ * await expectAsync(promise, 'to resolve');
326
+ * await expectAsync(promise, 'to resolve with value satisfying', expectedValue);
327
+ * ```
328
+ *
329
+ * @template T - Array of async assertion objects that define available
330
+ * assertion logic
331
+ * @see {@link ExpectFunction} for the synchronous equivalent
332
+ * @see {@link SlotsFromParts} for how assertion parts are converted to function parameters
333
+ */
334
+ export type ExpectAsyncFunction<
335
+ T extends AnyAsyncAssertions = BuiltinAsyncAssertions,
336
+ > = UnionToIntersection<
337
+ TupleToUnion<{
338
+ [K in keyof T]: T[K] extends AnyAsyncAssertion
339
+ ? (
340
+ ...args: MutableOrReadonly<SlotsFromParts<T[K]['parts']>>
341
+ ) => Promise<void>
342
+ : never;
343
+ }>
344
+ >;
345
+
346
+ /**
347
+ * Properties available on asynchronous expect functions.
348
+ *
349
+ * This interface defines the additional properties and methods that are
350
+ * attached to async expect functions, extending the base expect functionality
351
+ * with async-specific features. These properties provide access to the
352
+ * underlying assertions and enable function composition through the
353
+ * {@link UseFn | use} method.
354
+ *
355
+ * @example
356
+ *
357
+ * ```typescript
358
+ * const expectAsync: ExpectAsync<MyAsyncAssertions> =
359
+ * createExpectAsyncFunction(assertions);
360
+ *
361
+ * // Access the underlying assertions
362
+ * console.log(expectAsync.assertions.length);
363
+ *
364
+ * // Compose with additional assertions
365
+ * const { expectAsync: enhanced } = expectAsync.use(moreAssertions);
366
+ * ```
367
+ *
368
+ * @template AsyncAssertions - Array of async assertion objects available to
369
+ * this expect function
370
+ * @template SyncAssertions - Array of sync assertion objects available for
371
+ * composition via {@link UseFn | use}
372
+ */
373
+ export interface ExpectAsyncProps<
374
+ AsyncAssertions extends AnyAsyncAssertions,
375
+ SyncAssertions extends AnySyncAssertions,
376
+ > extends BaseExpect {
377
+ /**
378
+ * Tuple of all assertions available in this `expect()`.
379
+ *
380
+ * @preventExpand
381
+ */
382
+ assertions: AsyncAssertions;
383
+ /**
384
+ * {@inheritDoc UseFn}
385
+ */
386
+ use: UseFn<SyncAssertions, AsyncAssertions>;
387
+ }
388
+
389
+ export type ExpectFunction<
390
+ SyncAssertions extends AnySyncAssertions = BuiltinSyncAssertions,
391
+ > = UnionToIntersection<
392
+ TupleToUnion<{
393
+ [K in keyof SyncAssertions]: SyncAssertions[K] extends AnySyncAssertion
394
+ ? (
395
+ ...args: MutableOrReadonly<SlotsFromParts<SyncAssertions[K]['parts']>>
396
+ ) => void
397
+ : never;
398
+ }>
399
+ >;
400
+
401
+ /**
402
+ * Properties of {@link expect}.
403
+ */
404
+ export interface ExpectSyncProps<
405
+ SyncAssertions extends AnySyncAssertions,
406
+ AsyncAssertions extends AnyAsyncAssertions,
407
+ > extends BaseExpect {
408
+ /**
409
+ * Tuple of all assertions available in this `expect()`.
410
+ *
411
+ * @preventExpand
412
+ */
413
+ assertions: SyncAssertions;
414
+
415
+ /**
416
+ * Function to add more assertions to this `expect()`, returning a new
417
+ * `expect()` and `expectAsync()` pair with the combined assertions.
418
+ */
419
+ use: UseFn<SyncAssertions, AsyncAssertions>;
420
+ }
421
+
422
+ /**
423
+ * A function which immediately throws an {@link AssertionError}.
424
+ *
425
+ * Member of {@link BaseExpect}.
426
+ *
427
+ * @param reason Optional reason for failure
428
+ * @see {@link fail}
429
+ */
430
+ export type FailFn = (reason?: string) => never;
431
+
432
+ /**
433
+ * Given a mixed array of assertions, filters out only the async assertions.
434
+ */
435
+ /**
436
+ * Given a mixed array of assertions, filters out only the async assertions.
437
+ *
438
+ * This utility type recursively examines each assertion in the input array and
439
+ * constructs a new tuple containing only the asynchronous assertions. It uses
440
+ * conditional types to test whether each assertion extends
441
+ * {@link AnyAsyncAssertion} and includes it in the result if so.
442
+ *
443
+ * Used primarily by {@link UseFn} to separate async assertions from mixed
444
+ * assertion arrays when composing expect functions.
445
+ *
446
+ * @example
447
+ *
448
+ * ```typescript
449
+ * type Mixed = [
450
+ * SyncAssertion1,
451
+ * AsyncAssertion1,
452
+ * SyncAssertion2,
453
+ * AsyncAssertion2,
454
+ * ];
455
+ * type AsyncOnly = FilterAsyncAssertions<Mixed>; // [AsyncAssertion1, AsyncAssertion2]
456
+ * ```
457
+ *
458
+ * @template MixedAssertions - Array that may contain both sync and async
459
+ * assertions
460
+ * @see {@link FilterSyncAssertions} for extracting synchronous assertions
461
+ * @see {@link UseFn} for the primary use case of this type
462
+ */
463
+ export type FilterAsyncAssertions<
464
+ MixedAssertions extends readonly AnyAssertion[],
465
+ > = MixedAssertions extends readonly [
466
+ infer MixedAssertion extends AnyAssertion,
467
+ ...infer Rest extends readonly AnyAssertion[],
468
+ ]
469
+ ? MixedAssertion extends AnyAsyncAssertion
470
+ ? readonly [MixedAssertion, ...FilterAsyncAssertions<Rest>]
471
+ : FilterAsyncAssertions<Rest>
472
+ : readonly [];
473
+
474
+ /**
475
+ * Given a mixed array of assertions, extracts only the synchronous assertions.
476
+ */
477
+ export type FilterSyncAssertions<
478
+ MixedAssertions extends readonly AnyAssertion[],
479
+ > = MixedAssertions extends readonly [
480
+ infer MixedAssertion extends AnyAssertion,
481
+ ...infer Rest extends readonly AnyAssertion[],
482
+ ]
483
+ ? MixedAssertion extends AnySyncAssertion
484
+ ? readonly [MixedAssertion, ...FilterSyncAssertions<Rest>]
485
+ : FilterSyncAssertions<Rest>
486
+ : readonly [];
487
+
488
+ /**
489
+ * Maps AssertionParts to the corresponding argument types for expect and
490
+ * expectAsync functions.
491
+ *
492
+ * This utility type transforms assertion parts into the actual parameter types
493
+ * that users provide when calling expect functions. It handles both phrase
494
+ * literals and Zod schemas, creating appropriate TypeScript types for each
495
+ * slot.
496
+ *
497
+ * For phrase literals, it creates union types that include both the original
498
+ * phrase and its negated version (with "not " prefix). For Zod schemas, it
499
+ * extracts the inferred type. This enables natural language assertions with
500
+ * optional negation support.
501
+ *
502
+ * @remarks
503
+ * This type works recursively through the parts tuple, transforming each part
504
+ * according to its type. The resulting tuple maintains the same structure as
505
+ * the input but with user-facing TypeScript types instead of internal assertion
506
+ * part types.
507
+ * @example
508
+ *
509
+ * ```typescript
510
+ * // Given parts: ['to be a', z.string()]
511
+ * // Results in: ['to be a' | 'not to be a', string]
512
+ * type Slots = MapExpectSlots<['to be a', z.string()]>;
513
+ * // Usage: expect(value, 'to be a', 'hello') or expect(value, 'not to be a', 'hello')
514
+ * ```
515
+ *
516
+ * @template Parts - Tuple of assertion parts to be converted to function
517
+ * parameter types
518
+ * @see {@link SlotsFromParts} for the complete slot transformation including subject injection
519
+ * @see {@link Negation} for how phrase negation is implemented
114
520
  */
115
521
  export type MapExpectSlots<Parts extends readonly AssertionPart[]> =
116
522
  Parts extends readonly [
@@ -134,32 +540,179 @@ export type MapExpectSlots<Parts extends readonly AssertionPart[]> =
134
540
  : readonly [];
135
541
 
136
542
  /**
137
- * Makes tuple types accept both mutable and readonly variants
543
+ * Makes tuple types accept both mutable and readonly variants.
544
+ *
545
+ * This utility type creates a union of both mutable and readonly versions of a
546
+ * tuple type, providing flexibility for function parameters that should accept
547
+ * either variant. This is particularly useful for assertion function parameters
548
+ * where users may pass either `const` arrays (readonly) or regular arrays.
549
+ *
550
+ * The type handles both array types and specific tuple types, creating
551
+ * appropriate unions for each case to maintain type safety while maximizing
552
+ * usability.
553
+ *
554
+ * @example
555
+ *
556
+ * ```typescript
557
+ * type FlexibleArgs = MutableOrReadonly<readonly [string, number]>;
558
+ * // Results in: [string, number] | readonly [string, number]
559
+ *
560
+ * function acceptArgs(args: FlexibleArgs) { ... }
561
+ * acceptArgs(['hello', 42]); // ✓ mutable array
562
+ * acceptArgs(['hello', 42] as const); // ✓ readonly array
563
+ * ```
564
+ *
565
+ * @template Tuple - The readonly tuple type to make flexible
566
+ * @see {@link ExpectFunction} and {@link ExpectAsyncFunction} which use this for parameter flexibility
567
+ */
568
+ export type MutableOrReadonly<Tuple extends readonly unknown[]> =
569
+ Tuple extends readonly (infer Item)[]
570
+ ? Item[] | readonly Item[]
571
+ : Tuple extends readonly [infer First, ...infer Rest]
572
+ ? [First, ...Rest] | readonly [First, ...Rest]
573
+ : Tuple;
574
+
575
+ /**
576
+ * Creates a negated version of a phrase literal by prefixing "not ".
577
+ *
578
+ * This utility type transforms assertion phrases into their negated
579
+ * equivalents, enabling the natural language negation feature in Bupkis
580
+ * assertions. When users provide phrases like "not to be a string", this type
581
+ * helps the system understand and process the negation.
582
+ *
583
+ * The negation is applied at the type level during assertion matching and
584
+ * affects how the assertion logic is executed - negated assertions expect the
585
+ * opposite result.
586
+ *
587
+ * @example
588
+ *
589
+ * ```typescript
590
+ * type Negated = Negation<'to be a string'>; // "not to be a string"
591
+ * type AlsoNegated = Negation<'to equal'>; // "not to equal"
592
+ *
593
+ * // Usage in assertions:
594
+ * expect(42, 'not to be a string'); // Uses negated assertion logic
595
+ * ```
596
+ *
597
+ * @template S - The string literal phrase to be negated
598
+ * @see {@link AddNegation} for applying negation to entire AssertionParts tuples
599
+ * @see {@link MapExpectSlots} for how negation is incorporated into function signatures
138
600
  */
139
- export type MutableOrReadonly<T> = T extends readonly (infer U)[]
140
- ? readonly U[] | U[]
141
- : T extends readonly [infer First, ...infer Rest]
142
- ? [First, ...Rest] | readonly [First, ...Rest]
143
- : T;
601
+ export type Negation<S extends string> = `not ${S}`;
144
602
 
145
603
  /**
146
- * The type of a `PhraesLiteral` which is negated, e.g. "not to be"
604
+ * Converts AssertionParts to complete function parameter types for expect
605
+ * functions.
606
+ *
607
+ * This utility type prepares assertion parts for use as function parameters by
608
+ * applying several transformations:
609
+ *
610
+ * 1. Injects an `unknown` type for the subject parameter if the first part is a
611
+ * phrase literal
612
+ * 2. Maps the remaining parts to their corresponding TypeScript types via
613
+ * {@link MapExpectSlots}
614
+ * 3. Filters out `never` types to ensure a clean tuple structure
615
+ *
616
+ * The subject injection is a key feature - when assertions start with phrases
617
+ * like "to be a string", users still need to provide the subject being tested
618
+ * as the first argument to expect functions.
619
+ *
620
+ * @remarks
621
+ * This type is essential for bridging the gap between assertion definitions and
622
+ * user-facing function signatures. The subject injection ensures that all
623
+ * assertions have a consistent calling pattern regardless of whether they
624
+ * explicitly define a subject parameter.
625
+ * @example
626
+ *
627
+ * ```typescript
628
+ * // Assertion parts: ['to equal', z.string()]
629
+ * // Results in: [unknown, 'to equal' | 'not to equal', string]
630
+ * type Slots = SlotsFromParts<['to equal', z.string()]>;
631
+ *
632
+ * // Usage: expect(subject, 'to equal', 'expected')
633
+ * // expect(subject, 'not to equal', 'unexpected')
634
+ * ```
635
+ *
636
+ * @template Parts - Tuple of assertion parts that define the assertion
637
+ * structure
638
+ * @see {@link MapExpectSlots} for the core slot mapping logic
639
+ * @see {@link NoNeverTuple} for never-type filtering
147
640
  */
148
- export type Negation<T extends string> = `not ${T}`;
641
+ export type SlotsFromParts<Parts extends AssertionParts> = NoNeverTuple<
642
+ Parts extends readonly [infer First extends AssertionPart, ...infer _]
643
+ ? First extends PhraseLiteral | PhraseLiteralChoice
644
+ ? [unknown, ...MapExpectSlots<Parts>]
645
+ : MapExpectSlots<Parts>
646
+ : never
647
+ >;
149
648
 
649
+ /**
650
+ * The type of a `use()` function.
651
+ */
150
652
  export interface UseFn<
151
- T extends AnySyncAssertions,
152
- U extends AnyAsyncAssertions,
653
+ BaseSyncAssertions extends AnySyncAssertions,
654
+ BaseAsyncAssertions extends AnyAsyncAssertions,
153
655
  > {
656
+ /**
657
+ * @template MixedAssertions Mixed set of assertions to add; may include both
658
+ * sync and async assertions
659
+ * @template ExtendedSyncAssertions Synchronous assertions extracted from
660
+ * `MixedAssertions`
661
+ * @template ExtendedAsyncAssertions Asynchronous assertions extracted from
662
+ * `MixedAssertions`
663
+ * @param assertions Array of assertion classes to add
664
+ * @returns New {@link expect} and {@link expectAsync} functions with the
665
+ * combined assertions
666
+ */
154
667
  <
155
- V extends readonly AnyAssertion[],
156
- W extends FilterSyncAssertions<V>,
157
- X extends FilterAsyncAssertions<V>,
668
+ MixedAssertions extends readonly AnyAssertion[],
669
+ ExtendedSyncAssertions extends FilterSyncAssertions<MixedAssertions>,
670
+ ExtendedAsyncAssertions extends FilterAsyncAssertions<MixedAssertions>,
158
671
  >(
159
- assertions: V,
160
- ): {
161
- expect: Expect<Concat<T, W>, Concat<U, X>>;
162
- expectAsync: ExpectAsync<Concat<U, X>, Concat<T, W>>;
163
- use: UseFn<Concat<T, W>, Concat<U, X>>;
164
- };
672
+ assertions: MixedAssertions,
673
+ ): Bupkis<
674
+ BaseSyncAssertions,
675
+ BaseAsyncAssertions,
676
+ ExtendedSyncAssertions,
677
+ ExtendedAsyncAssertions
678
+ >;
679
+ }
680
+
681
+ /**
682
+ * Maps Zod `def.type` strings to their corresponding ZodType classes.
683
+ *
684
+ * This allows for type-safe discrimination of ZodTypes based on their internal
685
+ * `def.type` property in Zod v4.
686
+ */
687
+ export interface ZodTypeMap {
688
+ any: z.ZodAny;
689
+ array: z.ZodArray<any>;
690
+ bigint: z.ZodBigInt;
691
+ boolean: z.ZodBoolean;
692
+ catch: z.ZodCatch<any>;
693
+ date: z.ZodDate;
694
+ default: z.ZodDefault<any>;
695
+ enum: z.ZodEnum<any>;
696
+ function: z.ZodFunction<any, any>;
697
+ lazy: z.ZodLazy<any>;
698
+ literal: z.ZodLiteral<any>;
699
+ map: z.ZodMap<any, any>;
700
+ never: z.ZodNever;
701
+ null: z.ZodNull;
702
+ nullable: z.ZodNullable<any>;
703
+ number: z.ZodNumber;
704
+ object: z.ZodObject<any>;
705
+ optional: z.ZodOptional<any>;
706
+ pipe: z.ZodPipe<any, any>;
707
+ promise: z.ZodPromise<any>;
708
+ readonly: z.ZodReadonly<any>;
709
+ record: z.ZodRecord<any, any>;
710
+ set: z.ZodSet<any>;
711
+ string: z.ZodString;
712
+ symbol: z.ZodSymbol;
713
+ tuple: z.ZodTuple<any>;
714
+ undefined: z.ZodUndefined;
715
+ union: z.ZodUnion<any>;
716
+ unknown: z.ZodUnknown;
717
+ void: z.ZodVoid;
165
718
  }