bupkis 0.1.2 → 0.2.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 (130) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +8 -8
  3. package/dist/commonjs/assertion/assertion-types.d.ts +33 -82
  4. package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -1
  5. package/dist/commonjs/assertion/create.d.ts +5 -33
  6. package/dist/commonjs/assertion/create.d.ts.map +1 -1
  7. package/dist/commonjs/assertion/create.js +17 -6
  8. package/dist/commonjs/assertion/create.js.map +1 -1
  9. package/dist/commonjs/assertion/impl/async.d.ts +5 -5
  10. package/dist/commonjs/assertion/impl/async.d.ts.map +1 -1
  11. package/dist/commonjs/assertion/impl/async.js +2 -2
  12. package/dist/commonjs/assertion/impl/async.js.map +1 -1
  13. package/dist/commonjs/assertion/impl/sync-parametric.d.ts +24 -24
  14. package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
  15. package/dist/commonjs/assertion/impl/sync-parametric.js +3 -3
  16. package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
  17. package/dist/commonjs/assertion/impl/sync.d.ts +50 -50
  18. package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -1
  19. package/dist/commonjs/bootstrap.d.ts +71 -61
  20. package/dist/commonjs/bootstrap.d.ts.map +1 -1
  21. package/dist/commonjs/bootstrap.js +20 -10
  22. package/dist/commonjs/bootstrap.js.map +1 -1
  23. package/dist/commonjs/error.d.ts +12 -5
  24. package/dist/commonjs/error.d.ts.map +1 -1
  25. package/dist/commonjs/error.js +19 -4
  26. package/dist/commonjs/error.js.map +1 -1
  27. package/dist/commonjs/expect.d.ts +130 -3
  28. package/dist/commonjs/expect.d.ts.map +1 -1
  29. package/dist/commonjs/expect.js +115 -0
  30. package/dist/commonjs/expect.js.map +1 -1
  31. package/dist/commonjs/guards.d.ts +22 -15
  32. package/dist/commonjs/guards.d.ts.map +1 -1
  33. package/dist/commonjs/guards.js +34 -20
  34. package/dist/commonjs/guards.js.map +1 -1
  35. package/dist/commonjs/index.d.ts +113 -62
  36. package/dist/commonjs/index.d.ts.map +1 -1
  37. package/dist/commonjs/index.js +44 -42
  38. package/dist/commonjs/index.js.map +1 -1
  39. package/dist/commonjs/metadata.d.ts +1 -27
  40. package/dist/commonjs/metadata.d.ts.map +1 -1
  41. package/dist/commonjs/metadata.js +16 -15
  42. package/dist/commonjs/metadata.js.map +1 -1
  43. package/dist/commonjs/schema.d.ts +59 -26
  44. package/dist/commonjs/schema.d.ts.map +1 -1
  45. package/dist/commonjs/schema.js +57 -24
  46. package/dist/commonjs/schema.js.map +1 -1
  47. package/dist/commonjs/types.d.ts +449 -39
  48. package/dist/commonjs/types.d.ts.map +1 -1
  49. package/dist/commonjs/types.js +12 -2
  50. package/dist/commonjs/types.js.map +1 -1
  51. package/dist/commonjs/util.d.ts +7 -0
  52. package/dist/commonjs/util.d.ts.map +1 -1
  53. package/dist/commonjs/util.js +7 -0
  54. package/dist/commonjs/util.js.map +1 -1
  55. package/dist/esm/assertion/assertion-types.d.ts +33 -82
  56. package/dist/esm/assertion/assertion-types.d.ts.map +1 -1
  57. package/dist/esm/assertion/create.d.ts +5 -33
  58. package/dist/esm/assertion/create.d.ts.map +1 -1
  59. package/dist/esm/assertion/create.js +14 -4
  60. package/dist/esm/assertion/create.js.map +1 -1
  61. package/dist/esm/assertion/impl/async.d.ts +5 -5
  62. package/dist/esm/assertion/impl/async.d.ts.map +1 -1
  63. package/dist/esm/assertion/impl/async.js +2 -2
  64. package/dist/esm/assertion/impl/async.js.map +1 -1
  65. package/dist/esm/assertion/impl/sync-parametric.d.ts +24 -24
  66. package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
  67. package/dist/esm/assertion/impl/sync-parametric.js +3 -3
  68. package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
  69. package/dist/esm/assertion/impl/sync.d.ts +50 -50
  70. package/dist/esm/assertion/impl/sync.d.ts.map +1 -1
  71. package/dist/esm/bootstrap.d.ts +71 -61
  72. package/dist/esm/bootstrap.d.ts.map +1 -1
  73. package/dist/esm/bootstrap.js +19 -9
  74. package/dist/esm/bootstrap.js.map +1 -1
  75. package/dist/esm/error.d.ts +12 -5
  76. package/dist/esm/error.d.ts.map +1 -1
  77. package/dist/esm/error.js +19 -4
  78. package/dist/esm/error.js.map +1 -1
  79. package/dist/esm/expect.d.ts +130 -3
  80. package/dist/esm/expect.d.ts.map +1 -1
  81. package/dist/esm/expect.js +115 -0
  82. package/dist/esm/expect.js.map +1 -1
  83. package/dist/esm/guards.d.ts +22 -15
  84. package/dist/esm/guards.d.ts.map +1 -1
  85. package/dist/esm/guards.js +29 -13
  86. package/dist/esm/guards.js.map +1 -1
  87. package/dist/esm/index.d.ts +113 -62
  88. package/dist/esm/index.d.ts.map +1 -1
  89. package/dist/esm/index.js +46 -7
  90. package/dist/esm/index.js.map +1 -1
  91. package/dist/esm/metadata.d.ts +1 -27
  92. package/dist/esm/metadata.d.ts.map +1 -1
  93. package/dist/esm/metadata.js +2 -1
  94. package/dist/esm/metadata.js.map +1 -1
  95. package/dist/esm/schema.d.ts +59 -26
  96. package/dist/esm/schema.d.ts.map +1 -1
  97. package/dist/esm/schema.js +57 -24
  98. package/dist/esm/schema.js.map +1 -1
  99. package/dist/esm/types.d.ts +449 -39
  100. package/dist/esm/types.d.ts.map +1 -1
  101. package/dist/esm/types.js +12 -2
  102. package/dist/esm/types.js.map +1 -1
  103. package/dist/esm/util.d.ts +7 -0
  104. package/dist/esm/util.d.ts.map +1 -1
  105. package/dist/esm/util.js +7 -0
  106. package/dist/esm/util.js.map +1 -1
  107. package/package.json +89 -15
  108. package/src/assertion/assertion-types.ts +38 -41
  109. package/src/assertion/create.ts +16 -65
  110. package/src/assertion/impl/async.ts +2 -2
  111. package/src/assertion/impl/sync-parametric.ts +3 -3
  112. package/src/bootstrap.ts +21 -10
  113. package/src/error.ts +21 -4
  114. package/src/expect.ts +269 -18
  115. package/src/guards.ts +29 -51
  116. package/src/index.ts +71 -11
  117. package/src/metadata.ts +3 -4
  118. package/src/schema.ts +59 -26
  119. package/src/types.ts +593 -72
  120. package/src/util.ts +7 -0
  121. package/dist/commonjs/api.d.ts +0 -93
  122. package/dist/commonjs/api.d.ts.map +0 -1
  123. package/dist/commonjs/api.js +0 -8
  124. package/dist/commonjs/api.js.map +0 -1
  125. package/dist/esm/api.d.ts +0 -93
  126. package/dist/esm/api.d.ts.map +0 -1
  127. package/dist/esm/api.js +0 -7
  128. package/dist/esm/api.js.map +0 -1
  129. package/src/api.ts +0 -149
  130. package/src/schema.md +0 -15
package/src/bootstrap.ts CHANGED
@@ -1,23 +1,22 @@
1
1
  /**
2
2
  * Factory function for creating the main assertion functions.
3
3
  *
4
- * This module provides the `bootstrap()` function that creates both synchronous
5
- * and asynchronous assertion engines. It contains the core implementation
6
- * previously split between `expect.ts` and `expect-async.ts`.
4
+ * This module provides the {@link bootstrap} function that creates both
5
+ * synchronous and asynchronous assertion engines.
7
6
  *
8
7
  * @packageDocumentation
9
8
  */
10
9
 
11
- import { type Expect, type ExpectAsync } from './api.js';
10
+ import { AsyncAssertions } from './assertion/impl/async.js';
12
11
  import { SyncAssertions } from './assertion/impl/sync.js';
13
- import { AsyncAssertions } from './assertion/index.js';
12
+ import { type Expect, type ExpectAsync } from './types.js';
14
13
  import { createUse } from './use.js';
15
14
 
16
15
  /**
17
16
  * Factory function that creates both synchronous and asynchronous assertion
18
17
  * engines.
19
18
  *
20
- * @returns Object containing `expect` and `expectAsync` functions
19
+ * @returns Object containing {@link expect} and {@link expectAsync} functions
21
20
  * @internal
22
21
  */
23
22
  const bootstrap = (): {
@@ -34,9 +33,21 @@ const bootstrap = (): {
34
33
 
35
34
  const api = bootstrap();
36
35
 
37
- /** {@inheritDoc Expect} */
38
- const { expect } = api;
39
- /** {@inheritDoc ExpectAsync} */
40
- const { expectAsync } = api;
36
+ const {
37
+ /**
38
+ * The main synchronous assertion function which can execute only built-in
39
+ * assertions.
40
+ *
41
+ * @function
42
+ */
43
+ expect,
44
+ /**
45
+ * The main asynchronous assertion function which can execute only built-in
46
+ * assertions.
47
+ *
48
+ * @function
49
+ */
50
+ expectAsync,
51
+ } = api;
41
52
 
42
53
  export { expect, expectAsync };
package/src/error.ts CHANGED
@@ -17,15 +17,32 @@ import { isA } from './guards.js';
17
17
  /**
18
18
  * _BUPKIS_' s custom `AssertionError` class, which is just a thin wrapper
19
19
  * around Node.js' {@link NodeAssertionError AssertionError}.
20
- *
21
- * @public
22
20
  */
23
21
  export class AssertionError extends NodeAssertionError {
22
+ /**
23
+ * @internal
24
+ */
24
25
  [kBupkisAssertionError] = true;
25
26
 
26
- static isAssertionError(err: unknown): err is AssertionError {
27
+ override name = 'AssertionError';
28
+
29
+ /**
30
+ * @param options Options passed to {@link NodeAssertionError}'s constructor
31
+ */
32
+ constructor(options?: ConstructorParameters<typeof NodeAssertionError>[0]) {
33
+ super(options);
34
+ }
35
+
36
+ /**
37
+ * Type guard for an instance of this error.
38
+ *
39
+ * @param value Some value
40
+ * @returns `true` if `value` is an instance of `AssertionError`
41
+ */
42
+ static isAssertionError(value: unknown): value is AssertionError {
27
43
  return (
28
- isA(err, NodeAssertionError) && Object.hasOwn(err, kBupkisAssertionError)
44
+ isA(value, NodeAssertionError) &&
45
+ Object.hasOwn(value, kBupkisAssertionError)
29
46
  );
30
47
  }
31
48
  }
package/src/expect.ts CHANGED
@@ -1,14 +1,6 @@
1
1
  import Debug from 'debug';
2
2
  import { inspect } from 'util';
3
3
 
4
- import {
5
- type Expect,
6
- type ExpectAsync,
7
- type ExpectAsyncFunction,
8
- type ExpectAsyncProps,
9
- type ExpectFunction,
10
- type ExpectSyncProps,
11
- } from './api.js';
12
4
  import {
13
5
  type AnyAsyncAssertion,
14
6
  type AnyAsyncAssertions,
@@ -26,17 +18,154 @@ import {
26
18
  import { createAssertion, createAsyncAssertion } from './assertion/create.js';
27
19
  import { AssertionError, NegatedAssertionError } from './error.js';
28
20
  import { isAssertionFailure, isString } from './guards.js';
21
+ import {
22
+ type Expect,
23
+ type ExpectAsync,
24
+ type ExpectAsyncFunction,
25
+ type ExpectAsyncProps,
26
+ type ExpectFunction,
27
+ type ExpectSyncProps,
28
+ type FailFn,
29
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30
+ type UseFn,
31
+ } from './types.js';
29
32
  import { createUse } from './use.js';
30
33
 
31
34
  const debug = Debug('bupkis:expect');
32
35
 
36
+ /**
37
+ * Creates an asynchronous expect function by extending a parent expectAsync
38
+ * function with additional assertions.
39
+ *
40
+ * This overload combines assertions from an existing parent expectAsync
41
+ * function with new assertions, creating a unified expectAsync function that
42
+ * supports both sets of assertions. The resulting function inherits all type
43
+ * information from both the parent and new assertions, providing complete
44
+ * TypeScript intellisense and type safety for Promise-based testing scenarios.
45
+ *
46
+ * @example
47
+ *
48
+ * ```typescript
49
+ * const baseExpectAsync = createExpectAsyncFunction(basicAsyncAssertions);
50
+ * const extendedExpectAsync = createExpectAsyncFunction(
51
+ * customAsyncAssertions,
52
+ * baseExpectAsync,
53
+ * );
54
+ *
55
+ * // Can use both basic and custom async assertions
56
+ * await extendedExpectAsync(promise, 'to resolve'); // From basic assertions
57
+ * await extendedExpectAsync(promise, 'to resolve with custom data'); // From custom assertions
58
+ * ```
59
+ *
60
+ * @param assertions - Array of new asynchronous assertion objects to add
61
+ * @param expect - Parent expectAsync function whose assertions will be
62
+ * inherited
63
+ * @returns ExpectAsync function with combined assertion types from both parent
64
+ * and new assertions
65
+ * @throws {@link AssertionError} When an assertion fails in normal
66
+ * (non-negated) mode
67
+ * @throws {@link NegatedAssertionError} When a negated assertion fails
68
+ * @throws {Error} When no matching assertion can be found for the provided
69
+ * arguments
70
+ */
33
71
  export function createExpectAsyncFunction<
34
72
  T extends AnyAsyncAssertions,
35
73
  U extends ExpectAsync<AnyAsyncAssertions>,
36
74
  >(assertions: T, expect: U): ExpectAsyncFunction<T & U['assertions']>;
75
+
76
+ /**
77
+ * Creates a new asynchronous expect function with the provided assertions.
78
+ *
79
+ * This overload creates a standalone expectAsync function from the provided
80
+ * assertions without inheriting from any parent function. This is typically
81
+ * used to create the initial expectAsync function or when you want a clean
82
+ * slate without any inherited assertions for Promise-based testing.
83
+ *
84
+ * @example
85
+ *
86
+ * ```typescript
87
+ * const expectAsync = createExpectAsyncFunction(asyncAssertions);
88
+ * await expectAsync(promise, 'to resolve');
89
+ * await expectAsync(rejectedPromise, 'to reject');
90
+ * await expectAsync(promise, 'to resolve to', expectedValue);
91
+ * ```
92
+ *
93
+ * @param assertions - Array of asynchronous assertion objects that define the
94
+ * available assertion phrases and Promise-based logic
95
+ * @returns An asynchronous expect function that can execute the provided
96
+ * assertions using natural language syntax
97
+ * @throws {@link AssertionError} When an assertion fails in normal
98
+ * (non-negated) mode
99
+ * @throws {@link NegatedAssertionError} When a negated assertion fails
100
+ * @throws {Error} When no matching assertion can be found for the provided
101
+ * arguments
102
+ */
37
103
  export function createExpectAsyncFunction<T extends AnyAsyncAssertions>(
38
104
  assertions: T,
39
105
  ): ExpectAsyncFunction<T>;
106
+
107
+ /**
108
+ * Implementation function that creates an asynchronous expect function with
109
+ * optional parent inheritance.
110
+ *
111
+ * This is the concrete implementation that handles both overload cases for
112
+ * Promise-based assertions. It creates an expectAsync function that uses a
113
+ * two-phase matching algorithm: first seeking exact phrase matches for optimal
114
+ * performance, then falling back to partial matches if needed. The function
115
+ * processes negation keywords, combines parent assertions with new ones, and
116
+ * ensures all operations are properly awaited.
117
+ *
118
+ * The matching algorithm prioritizes exact matches to minimize performance
119
+ * overhead, but provides flexibility through partial matching when exact
120
+ * phrases don't align. This enables natural language flexibility while
121
+ * maintaining execution speed for common async assertion patterns.
122
+ *
123
+ * @remarks
124
+ * The function performs async assertion matching in the following order:
125
+ *
126
+ * 1. Awaits `Promise.resolve()` to ensure the function is always asynchronous
127
+ * 2. Processes negation keywords ('not', 'to not') to determine assertion mode
128
+ * 3. Combines parent assertions (if provided) with new assertions in execution
129
+ * order
130
+ * 4. Attempts to parse arguments against each assertion's expected phrase pattern
131
+ * using `parseValuesAsync`
132
+ * 5. Prioritizes exact phrase matches over partial matches for performance
133
+ * 6. Executes the first successful match using {@link executeAsync} or throws an
134
+ * error if none found
135
+ *
136
+ * Performance considerations: The function loops through all available
137
+ * assertions for each call, but uses early termination when exact matches are
138
+ * found. For performance-critical code, consider using assertion functions with
139
+ * fewer total assertions or more specific phrase patterns to reduce matching
140
+ * overhead.
141
+ *
142
+ * All assertion execution is properly awaited to handle Promise-based
143
+ * validation logic, error handling, and negation scenarios in asynchronous
144
+ * contexts.
145
+ * @example
146
+ *
147
+ * ```typescript
148
+ * // Used internally by both public overloads
149
+ * const expectAsync1 = createExpectAsyncFunction(assertions); // No parent
150
+ * const expectAsync2 = createExpectAsyncFunction(assertions, parent); // With parent
151
+ * ```
152
+ *
153
+ * @param assertions - Array of asynchronous assertion objects to make available
154
+ * @param expect - Optional parent expectAsync function to inherit assertions
155
+ * from
156
+ * @returns Asynchronous expect function that processes natural language
157
+ * assertions with Promise support
158
+ * @throws {@link AssertionError} When an assertion fails in normal
159
+ * (non-negated) mode
160
+ * @throws {@link NegatedAssertionError} When a negated assertion fails (e.g.,
161
+ * `await expectAsync(promise, 'not to resolve')`)
162
+ * @throws {Error} When no matching assertion can be found for the provided
163
+ * arguments
164
+ * @internal This is the concrete implementation used by the public overloads
165
+ * @see {@link createExpectSyncFunction} for creating synchronous expect functions
166
+ * @see {@link createAsyncAssertion} for creating individual async assertion objects
167
+ * @see {@link ExpectAsync} for the main expectAsync interface
168
+ */
40
169
  export function createExpectAsyncFunction<
41
170
  T extends AnyAsyncAssertions,
42
171
  U extends ExpectAsync<AnyAsyncAssertions>,
@@ -87,18 +216,136 @@ export function createExpectAsyncFunction<
87
216
  return expectAsyncFunction;
88
217
  }
89
218
 
219
+ /**
220
+ * Creates a synchronous expect function by extending a parent expect function
221
+ * with additional assertions.
222
+ *
223
+ * This overload combines assertions from an existing parent expect function
224
+ * with new assertions, creating a unified expect function that supports both
225
+ * sets of assertions. The resulting function inherits all type information from
226
+ * both the parent and new assertions, providing complete TypeScript
227
+ * intellisense and type safety.
228
+ *
229
+ * @example
230
+ *
231
+ * ```typescript
232
+ * const baseExpect = createExpectSyncFunction(basicAssertions);
233
+ * const extendedExpect = createExpectSyncFunction(
234
+ * customAssertions,
235
+ * baseExpect,
236
+ * );
237
+ *
238
+ * // Can use both basic and custom assertions
239
+ * extendedExpect(42, 'to be a number'); // From basic assertions
240
+ * extendedExpect(obj, 'to have custom prop'); // From custom assertions
241
+ * ```
242
+ *
243
+ * @param assertions - Array of new synchronous assertion objects to add
244
+ * @param expect - Parent expect function whose assertions will be inherited
245
+ * @returns Expect function with combined assertion types from both parent and
246
+ * new assertions
247
+ * @throws {@link AssertionError} When an assertion fails in normal
248
+ * (non-negated) mode
249
+ * @throws {@link NegatedAssertionError} When a negated assertion fails
250
+ * @throws {Error} When no matching assertion can be found for the provided
251
+ * arguments
252
+ */
90
253
  export function createExpectSyncFunction<
91
- T extends AnySyncAssertions,
92
- U extends Expect<AnySyncAssertions>,
93
- >(assertions: T, expect: U): ExpectFunction<T & U['assertions']>;
254
+ Assertions extends AnySyncAssertions,
255
+ ParentExpect extends Expect<AnySyncAssertions>,
256
+ >(
257
+ assertions: Assertions,
258
+ expect: ParentExpect,
259
+ ): ExpectFunction<Assertions & ParentExpect['assertions']>;
94
260
 
95
- export function createExpectSyncFunction<T extends AnySyncAssertions>(
96
- assertions: T,
97
- ): ExpectFunction<T>;
261
+ /**
262
+ * Creates a new synchronous expect function with the provided assertions.
263
+ *
264
+ * This overload creates a standalone expect function from the provided
265
+ * assertions without inheriting from any parent function. This is typically
266
+ * used to create the initial expect function or when you want a clean slate
267
+ * without any inherited assertions.
268
+ *
269
+ * @example
270
+ *
271
+ * ```typescript
272
+ * const expect = createExpectSyncFunction(basicAssertions);
273
+ * expect(42, 'to be a number');
274
+ * expect('hello', 'to be a string');
275
+ * expect([], 'to be empty');
276
+ * ```
277
+ *
278
+ * @param assertions - Array of synchronous assertion objects that define the
279
+ * available assertion phrases and logic
280
+ * @returns A synchronous expect function that can execute the provided
281
+ * assertions using natural language syntax
282
+ * @throws {@link AssertionError} When an assertion fails in normal
283
+ * (non-negated) mode
284
+ * @throws {@link NegatedAssertionError} When a negated assertion fails
285
+ * @throws {Error} When no matching assertion can be found for the provided
286
+ * arguments
287
+ */
288
+ export function createExpectSyncFunction<Assertions extends AnySyncAssertions>(
289
+ assertions: Assertions,
290
+ ): ExpectFunction<Assertions>;
291
+
292
+ /**
293
+ * Implementation function that creates a synchronous expect function with
294
+ * optional parent inheritance.
295
+ *
296
+ * This is the concrete implementation that handles both overload cases. It
297
+ * creates an expect function that uses a two-phase matching algorithm: first
298
+ * seeking exact phrase matches for optimal performance, then falling back to
299
+ * partial matches if needed. The function processes negation keywords and
300
+ * combines parent assertions with new ones.
301
+ *
302
+ * The matching algorithm prioritizes exact matches to minimize performance
303
+ * overhead, but provides flexibility through partial matching when exact
304
+ * phrases don't align. This enables natural language flexibility while
305
+ * maintaining execution speed for common assertion patterns.
306
+ *
307
+ * @remarks
308
+ * The function performs assertion matching in the following order:
309
+ *
310
+ * 1. Processes negation keywords ('not', 'to not') to determine assertion mode
311
+ * 2. Combines parent assertions (if provided) with new assertions in execution
312
+ * order
313
+ * 3. Attempts to parse arguments against each assertion's expected phrase pattern
314
+ * 4. Prioritizes exact phrase matches over partial matches for performance
315
+ * 5. Executes the first successful match or throws an error if none found
316
+ *
317
+ * Performance considerations: The function loops through all available
318
+ * assertions for each call, but uses early termination when exact matches are
319
+ * found. For performance-critical code, consider using assertion functions with
320
+ * fewer total assertions or more specific phrase patterns to reduce matching
321
+ * overhead.
322
+ * @example
323
+ *
324
+ * ```typescript
325
+ * // Used internally by both public overloads
326
+ * const expect1 = createExpectSyncFunction(assertions); // No parent
327
+ * const expect2 = createExpectSyncFunction(assertions, parent); // With parent
328
+ * ```
329
+ *
330
+ * @param assertions - Array of synchronous assertion objects to make available
331
+ * @param expect - Optional parent expect function to inherit assertions from
332
+ * @returns Synchronous expect function that processes natural language
333
+ * assertions
334
+ * @throws {@link AssertionError} When an assertion fails in normal
335
+ * (non-negated) mode
336
+ * @throws {@link NegatedAssertionError} When a negated assertion fails (e.g.,
337
+ * `expect(42, 'not to be a number')`)
338
+ * @throws {Error} When no matching assertion can be found for the provided
339
+ * arguments
340
+ * @internal This is the concrete implementation used by the public overloads
341
+ * @see {@link createExpectAsyncFunction} for creating asynchronous expect functions
342
+ * @see {@link createAssertion} for creating individual assertion objects
343
+ * @see {@link Expect} for the main expect interface
344
+ */
98
345
  export function createExpectSyncFunction<
99
- T extends AnySyncAssertions,
100
- U extends Expect<AnySyncAssertions>,
101
- >(assertions: T, expect?: U) {
346
+ Assertions extends AnySyncAssertions,
347
+ ParentExpect extends Expect<AnySyncAssertions>,
348
+ >(assertions: Assertions, expect?: ParentExpect) {
102
349
  debug(
103
350
  'Creating expect function with %d assertions',
104
351
  assertions.length + (expect?.assertions.length ?? 0),
@@ -330,10 +577,14 @@ const detectNegation = (
330
577
  };
331
578
  };
332
579
 
333
- const fail = (reason?: string): never => {
580
+ const fail: FailFn = (reason?: string): never => {
334
581
  throw new AssertionError({ message: reason });
335
582
  };
336
583
 
584
+ /**
585
+ * Used by a {@link UseFn} to create base properties of the {@link Expect} and
586
+ * {@link ExpectAsync} functions.
587
+ */
337
588
  export function createBaseExpect<
338
589
  T extends AnySyncAssertions,
339
590
  U extends AnyAsyncAssertions,
package/src/guards.ts CHANGED
@@ -2,15 +2,22 @@
2
2
  * Type guard functions and runtime type checking utilities.
3
3
  *
4
4
  * This module provides various type guard functions for runtime type checking,
5
- * including guards for Zod schemas, constructors, Promise-like objects, and
6
- * assertion parts. These are used throughout the library for safe type
5
+ * including guards for Zod schemas, constructors, {@link PromiseLike} objects,
6
+ * and assertion parts. These are used throughout the library for safe type
7
7
  * narrowing and validation.
8
8
  *
9
+ * @category API
10
+ * @example
11
+ *
12
+ * ```ts
13
+ * import * as guards from 'bupkis/guards';
14
+ * ```
15
+ *
9
16
  * @packageDocumentation
10
17
  */
11
18
 
12
19
  import { type Primitive } from 'type-fest';
13
- import { z } from 'zod';
20
+ import { z } from 'zod/v4';
14
21
 
15
22
  import type {
16
23
  AssertionFailure,
@@ -20,15 +27,15 @@ import type {
20
27
  import type { Constructor } from './types.js';
21
28
 
22
29
  /**
23
- * Returns true if the given value looks like a Zod schema (v4), determined by
24
- * the presence of an internal `def.type` field.
30
+ * Returns `true` if the given value looks like a Zod v4 schema, determined by
31
+ * the presence of an internal {@link z.core.$ZodTypeDef} field.
25
32
  *
26
33
  * Note: This relies on Zod's internal shape and is intended for runtime
27
34
  * discrimination within this library.
28
35
  *
29
36
  * @template T
30
37
  * @param value - Value to test
31
- * @returns Whether the value is Zod-like
38
+ * @returns Whether the value is `ZodType`-like
32
39
  */
33
40
  export const isZodType = (value: unknown): value is z.ZodType =>
34
41
  !!(
@@ -64,11 +71,17 @@ export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
64
71
  );
65
72
 
66
73
  /**
67
- * Returns true if the given value is a constructable function (i.e., a class).
74
+ * Returns `true` if the given value is a constructable function (i.e., a
75
+ * class).
68
76
  *
69
- * This may be the only way we can determine, at runtime, if a function is a
70
- * constructor without actually calling it.
77
+ * This works by wrapping `fn` in a {@link Proxy}, attaching a no-op
78
+ * {@link ProxyHandler.construct} trap to it, then attempting to construct the
79
+ * proxy via `new`.
71
80
  *
81
+ * @privateRemarks
82
+ * This may be the only way we can determine, at runtime, if a function is a
83
+ * constructor without actually calling it. I am unsure if this only works for
84
+ * classes.
72
85
  * @param fn - Function to test
73
86
  * @returns Whether the function is constructable
74
87
  */
@@ -121,14 +134,16 @@ const AssertionFailureSchema: z.ZodType<AssertionFailure> = z.object({
121
134
  .describe('A human-readable message describing the failure'),
122
135
  });
123
136
 
137
+ /**
138
+ * Type guard for a {@link AssertionFailure} object
139
+ *
140
+ * @param value Value to check
141
+ * @returns `true` if the value is an `AssertionFailure`, `false` otherwise
142
+ * @internal
143
+ */
124
144
  export const isAssertionFailure = (value: unknown): value is AssertionFailure =>
125
145
  AssertionFailureSchema.safeParse(value).success;
126
146
 
127
- export const isAsyncFunction = (
128
- value: unknown,
129
- ): value is (...args: any[]) => Promise<any> =>
130
- isFunction(value) && value.constructor.name === 'AsyncFunction';
131
-
132
147
  /**
133
148
  * Type guard for a string value
134
149
  *
@@ -179,43 +194,6 @@ export const isPhraseLiteralChoice = (
179
194
  export const isPhraseLiteral = (value: AssertionPart): value is string =>
180
195
  isString(value) && !value.startsWith('not ');
181
196
 
182
- export type PrimitiveTypeName =
183
- | 'bigint'
184
- | 'boolean'
185
- | 'function'
186
- | 'null'
187
- | 'number'
188
- | 'object'
189
- | 'string'
190
- | 'symbol'
191
- | 'undefined';
192
-
193
- export type PrimitiveTypeNameToType<T extends PrimitiveTypeName> =
194
- T extends 'undefined'
195
- ? undefined
196
- : T extends 'object'
197
- ? null | object
198
- : T extends 'function'
199
- ? (...args: any[]) => any
200
- : T extends 'string'
201
- ? string
202
- : T extends 'number'
203
- ? number
204
- : T extends 'boolean'
205
- ? boolean
206
- : T extends 'bigint'
207
- ? bigint
208
- : T extends 'symbol'
209
- ? symbol
210
- : never;
211
-
212
- export const isType = <T extends PrimitiveTypeName>(
213
- a: unknown,
214
- b: T,
215
- ): a is PrimitiveTypeNameToType<T> => {
216
- return typeof a === b;
217
- };
218
-
219
197
  export const isA = <T extends Constructor>(
220
198
  value: unknown,
221
199
  ctor: T,
package/src/index.ts CHANGED
@@ -6,24 +6,84 @@
6
6
  * guards, schema definitions, utility functions, and error types.
7
7
  *
8
8
  * @module bupkis
9
+ * @category API
10
+ * @example
11
+ *
12
+ * ```ts
13
+ * import { expect, expectAsync, z, createAssertion } from 'bupkis';
14
+ * ```
15
+ *
16
+ * @showGroups
9
17
  */
10
18
 
11
- import { expect as sacrificialExpect } from './bootstrap.js';
12
- export type * from './api.js';
19
+ import { z } from 'zod/v4';
13
20
 
14
- export * as assertion from './assertion/index.js';
21
+ import { expect as sacrificialExpect } from './bootstrap.js';
15
22
  export { expect, expectAsync } from './bootstrap.js';
16
23
 
17
- export * as error from './error.js';
18
- export * as guards from './guards.js';
24
+ export { AssertionError } from './error.js';
19
25
 
20
- export type * as metadata from './metadata.js';
21
- export * as schema from './schema.js';
26
+ /**
27
+ * Re-export of most (all?) types defined within <span
28
+ * class="bupkis">Bupkis</span>.
29
+ *
30
+ * @example
31
+ *
32
+ * ```ts
33
+ * import { types } from 'bupkis';
34
+ * ```
35
+ */
22
36
  export type * as types from './types.js';
23
- export * as util from './util.js';
24
37
 
25
- export { z } from 'zod/v4';
38
+ /**
39
+ * Re-export of {@link https://zod.dev Zod v4} for use in custom assertion
40
+ * implementations.
41
+ */
42
+ export { z };
26
43
 
44
+ /**
45
+ * @primaryExport
46
+ */
47
+ export type {
48
+ Bupkis,
49
+ CreateAssertionFn,
50
+ CreateAsyncAssertionFn,
51
+ Expect,
52
+ ExpectAsync,
53
+ FailFn,
54
+ UseFn,
55
+ } from './types.js';
27
56
  export { createAssertion, createAsyncAssertion, fail, use };
28
- const { createAssertion, createAsyncAssertion, fail, use, ..._rest } =
29
- sacrificialExpect;
57
+ const {
58
+ /**
59
+ * The main factory function for creating asynchronous assertions.
60
+ *
61
+ * Exported from the entry point; is also a property of {@link Expect} and
62
+ * {@link ExpectAsync}.
63
+ *
64
+ * @function
65
+ */
66
+ createAssertion,
67
+ /**
68
+ * The main factory function for creating asynchronous assertions.
69
+ *
70
+ * Exported from the entry point; is also a property of {@link Expect} and
71
+ * {@link ExpectAsync}.
72
+ *
73
+ * @function
74
+ */
75
+ createAsyncAssertion,
76
+ /**
77
+ * {@inheritDoc FailFn}
78
+ *
79
+ * @function
80
+ */
81
+ fail,
82
+ /**
83
+ * {@inheritDoc UseFn}
84
+ *
85
+ * @function
86
+ */
87
+ use,
88
+ ..._rest
89
+ } = sacrificialExpect;
package/src/metadata.ts CHANGED
@@ -2,18 +2,17 @@
2
2
  * Defines Bupkis' Zod metadata registry
3
3
  *
4
4
  * @packageDocumentation
5
+ * @internal
5
6
  */
6
7
 
7
- import { z } from 'zod';
8
+ import { z } from 'zod/v4';
8
9
 
9
10
  import { kStringLiteral } from './constant.js';
10
11
 
11
12
  /**
12
13
  * Metadata stored in Zod registry
13
- *
14
- * @knipignore
15
14
  */
16
- export type BupkisMeta = z.infer<typeof BupkisRegistrySchema>;
15
+ type BupkisMeta = z.infer<typeof BupkisRegistrySchema>;
17
16
 
18
17
  /**
19
18
  * Zod metadata registry for Bupkis