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
@@ -1,2 +1,2 @@
1
1
  export { AsyncAssertions } from './async.js';
2
- export { SyncAssertions } from './sync.js';
2
+ export { SyncAssertions as SyncAssertions } from './sync.js';
@@ -1,10 +1,10 @@
1
1
  import { z } from 'zod/v4';
2
2
 
3
- import { NullProtoObjectSchema, PropertyKeySchema } from '../../schema.js';
3
+ import { DictionarySchema, PropertyKeySchema } from '../../schema.js';
4
4
  import { createAssertion } from '../create.js';
5
5
 
6
6
  export const EsotericAssertions = [
7
- createAssertion(['to have a null prototype'], NullProtoObjectSchema),
7
+ createAssertion(['to have a null prototype'], DictionarySchema),
8
8
  createAssertion(
9
9
  [PropertyKeySchema, 'to be an enumerable property of', z.looseObject({})],
10
10
  (subject, obj) =>
@@ -11,7 +11,11 @@ import {
11
11
  StrongSetSchema,
12
12
  WrappedPromiseLikeSchema,
13
13
  } from '../../schema.js';
14
- import { valueToSchema } from '../../util.js';
14
+ import {
15
+ valueToSchema,
16
+ valueToSchemaOptionsForDeepEqual,
17
+ valueToSchemaOptionsForSatisfies,
18
+ } from '../../value-to-schema.js';
15
19
  import { createAssertion } from '../create.js';
16
20
 
17
21
  const trapError = (fn: () => unknown): unknown => {
@@ -135,7 +139,7 @@ export const ParametricAssertions = [
135
139
 
136
140
  // Number range and approximation assertions
137
141
  createAssertion(
138
- [z.number(), 'to be within', z.number(), z.number()],
142
+ [z.number(), ['to be within', 'to be between'], z.number(), z.number()],
139
143
  (subject, min, max) => {
140
144
  if (subject < min || subject > max) {
141
145
  return {
@@ -304,19 +308,19 @@ export const ParametricAssertions = [
304
308
  }
305
309
  },
306
310
  ),
307
- // @ts-expect-error fix later
308
311
  createAssertion(
309
312
  [
310
313
  z.looseObject({}),
311
314
  ['to deep equal', 'to deeply equal'],
312
315
  z.looseObject({}),
313
316
  ],
314
- (_, expected) => valueToSchema(expected, { strict: true }),
317
+ (_, expected) => valueToSchema(expected, valueToSchemaOptionsForDeepEqual),
315
318
  ),
316
- // @ts-expect-error fix later
317
319
  createAssertion(
318
320
  [ArrayLikeSchema, ['to deep equal', 'to deeply equal'], ArrayLikeSchema],
319
- (_, expected) => valueToSchema(expected, { strict: true }),
321
+ (_, expected) => {
322
+ return valueToSchema(expected, valueToSchemaOptionsForDeepEqual);
323
+ },
320
324
  ),
321
325
  createAssertion([FunctionSchema, 'to throw'], (subject) => {
322
326
  const error = trapError(subject);
@@ -376,10 +380,7 @@ export const ParametricAssertions = [
376
380
  .or(z.coerce.string().regex(param))
377
381
  .safeParse(error).success;
378
382
  } else if (isNonNullObject(param)) {
379
- const schema = valueToSchema(param, {
380
- literalPrimitives: true,
381
- strict: true,
382
- });
383
+ const schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
383
384
  return schema.safeParse(error).success;
384
385
  } else {
385
386
  throw new TypeError(`Invalid parameter schema: ${inspect(param)}`);
@@ -396,6 +397,7 @@ export const ParametricAssertions = [
396
397
  ],
397
398
  (subject, ctor, param) => {
398
399
  const error = trapError(subject);
400
+
399
401
  if (!isA(error, ctor)) {
400
402
  return {
401
403
  actual: error,
@@ -405,47 +407,32 @@ export const ParametricAssertions = [
405
407
  : `Expected function to throw an instance of ${ctor.name}, but it threw a non-object value: ${error as unknown}`,
406
408
  };
407
409
  }
408
-
410
+ let schema: undefined | z.ZodType;
411
+ // TODO: can valueToSchema handle the first two conditional branches?
409
412
  if (isString(param)) {
410
- const result = z
413
+ schema = z
411
414
  .looseObject({
412
- message: z.coerce.string().refine((msg) => msg.includes(param)),
415
+ message: z.coerce.string().pipe(z.literal(param)),
413
416
  })
414
- .or(z.coerce.string().refine((str) => str.includes(param)))
415
- .safeParse(error);
416
- if (!result.success) {
417
- return {
418
- actual: isError(error) ? error.message : String(error),
419
- expected: `error with message containing "${param}"`,
420
- message: `Expected error message to contain "${param}", but got: ${isError(error) ? error.message : String(error)}`,
421
- };
422
- }
417
+ .or(z.coerce.string().pipe(z.literal(param)));
423
418
  } else if (isA(param, RegExp)) {
424
- const result = z
419
+ schema = z
425
420
  .looseObject({
426
421
  message: z.coerce.string().regex(param),
427
422
  })
428
- .or(z.coerce.string().regex(param))
429
- .safeParse(error);
430
- if (!result.success) {
431
- return {
432
- actual: isError(error) ? error.message : String(error),
433
- expected: `error with message matching ${param}`,
434
- message: `Expected error message to match ${param}, but got: ${isError(error) ? error.message : String(error)}`,
435
- };
436
- }
423
+ .or(z.coerce.string().regex(param));
437
424
  } else if (isNonNullObject(param)) {
438
- const schema = valueToSchema(param);
439
- const result = schema.safeParse(error);
440
- if (!result.success) {
441
- return {
442
- actual: error as unknown,
443
- expected: param,
444
- message: `Expected error to match object: ${inspect(param)}, but got: ${inspect(error)}`,
445
- };
446
- }
447
- } else {
448
- throw new TypeError(`Invalid parameter schema: ${inspect(param)}`);
425
+ schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
426
+ }
427
+ if (!schema) {
428
+ throw new TypeError(
429
+ `Invalid parameter schema: ${inspect(param, { depth: 2 })}`,
430
+ );
431
+ }
432
+
433
+ const result = schema.safeParse(error);
434
+ if (!result.success) {
435
+ return result.error;
449
436
  }
450
437
  },
451
438
  ),
@@ -455,7 +442,15 @@ export const ParametricAssertions = [
455
442
  ['includes', 'contains', 'to include', 'to contain'],
456
443
  z.string(),
457
444
  ],
458
- (subject, expected) => subject.includes(expected),
445
+ (subject, expected) => {
446
+ if (!subject.includes(expected)) {
447
+ return {
448
+ actual: subject,
449
+ expected: `string including "${expected}"`,
450
+ message: `Expected "${subject}" to include "${expected}"`,
451
+ };
452
+ }
453
+ },
459
454
  ),
460
455
 
461
456
  createAssertion([z.string(), 'to match', RegExpSchema], (subject, regex) =>
@@ -467,14 +462,11 @@ export const ParametricAssertions = [
467
462
  ['to satisfy', 'to be like'],
468
463
  z.looseObject({}),
469
464
  ],
470
- (_subject, shape) =>
471
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
472
- valueToSchema(shape) as unknown as z.ZodType<{}, z.core.$loose>,
465
+ (_subject, shape) => valueToSchema(shape, valueToSchemaOptionsForSatisfies),
473
466
  ),
474
467
  createAssertion(
475
468
  [ArrayLikeSchema, ['to satisfy', 'to be like'], ArrayLikeSchema],
476
- (_subject, shape) =>
477
- valueToSchema(shape) as unknown as typeof ArrayLikeSchema,
469
+ (_subject, shape) => valueToSchema(shape, valueToSchemaOptionsForSatisfies),
478
470
  ),
479
471
  createAssertion(
480
472
  [FunctionSchema, 'to have arity', z.number().int().nonnegative()],
@@ -9,6 +9,7 @@
9
9
  * @packageDocumentation
10
10
  */
11
11
 
12
+ import { CallbackSyncAssertions } from './callback.js';
12
13
  import { BasicAssertions } from './sync-basic.js';
13
14
  import { CollectionAssertions } from './sync-collection.js';
14
15
  import { EsotericAssertions } from './sync-esoteric.js';
@@ -19,10 +20,12 @@ export const SyncAssertions = [
19
20
  ...BasicAssertions,
20
21
  ...EsotericAssertions,
21
22
  ...ParametricAssertions,
23
+ ...CallbackSyncAssertions,
22
24
  ] as const;
23
25
 
24
26
  export {
25
27
  BasicAssertions,
28
+ CallbackSyncAssertions,
26
29
  CollectionAssertions,
27
30
  EsotericAssertions,
28
31
  ParametricAssertions,
package/src/bootstrap.ts CHANGED
@@ -1,23 +1,21 @@
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';
12
- import { SyncAssertions } from './assertion/impl/sync.js';
13
- import { AsyncAssertions } from './assertion/index.js';
10
+ import { AsyncAssertions, SyncAssertions } from './assertion/index.js';
11
+ import { type Expect, type ExpectAsync } from './types.js';
14
12
  import { createUse } from './use.js';
15
13
 
16
14
  /**
17
15
  * Factory function that creates both synchronous and asynchronous assertion
18
16
  * engines.
19
17
  *
20
- * @returns Object containing `expect` and `expectAsync` functions
18
+ * @returns Object containing {@link expect} and {@link expectAsync} functions
21
19
  * @internal
22
20
  */
23
21
  const bootstrap = (): {
@@ -34,9 +32,21 @@ const bootstrap = (): {
34
32
 
35
33
  const api = bootstrap();
36
34
 
37
- /** {@inheritDoc Expect} */
38
- const { expect } = api;
39
- /** {@inheritDoc ExpectAsync} */
40
- const { expectAsync } = api;
35
+ const {
36
+ /**
37
+ * The main synchronous assertion function which can execute only built-in
38
+ * assertions.
39
+ *
40
+ * @function
41
+ */
42
+ expect,
43
+ /**
44
+ * The main asynchronous assertion function which can execute only built-in
45
+ * assertions.
46
+ *
47
+ * @function
48
+ */
49
+ expectAsync,
50
+ } = api;
41
51
 
42
52
  export { expect, expectAsync };
package/src/constant.ts CHANGED
@@ -26,6 +26,14 @@ export const kStringLiteral: unique symbol = Symbol('bupkis:string-literal');
26
26
 
27
27
  export const kBupkisAssertionError: unique symbol = Symbol('bupkis-error');
28
28
 
29
+ /**
30
+ * Symbol used to flag a `FailAssertionError`
31
+ *
32
+ * @internal
33
+ */
34
+ export const kBupkisFailAssertionError: unique symbol =
35
+ Symbol('bupkis-fail-error');
36
+
29
37
  /**
30
38
  * Symbol used to flag a `NegatedAssertionError`
31
39
  *
package/src/error.ts CHANGED
@@ -7,25 +7,94 @@
7
7
  */
8
8
 
9
9
  import { AssertionError as NodeAssertionError } from 'node:assert';
10
+ import { z } from 'zod/v4';
10
11
 
11
12
  import {
12
13
  kBupkisAssertionError,
14
+ kBupkisFailAssertionError,
13
15
  kBupkisNegatedAssertionError,
14
16
  } from './constant.js';
15
17
  import { isA } from './guards.js';
18
+ import { type AssertionParts, type ParsedValues } from './types.js';
16
19
 
17
20
  /**
18
21
  * _BUPKIS_' s custom `AssertionError` class, which is just a thin wrapper
19
22
  * around Node.js' {@link NodeAssertionError AssertionError}.
20
- *
21
- * @public
22
23
  */
23
24
  export class AssertionError extends NodeAssertionError {
25
+ /**
26
+ * @internal
27
+ */
24
28
  [kBupkisAssertionError] = true;
25
29
 
26
- static isAssertionError(err: unknown): err is AssertionError {
30
+ override name = 'AssertionError';
31
+
32
+ /**
33
+ * Translates a {@link z.ZodError} into an {@link AssertionError} with a
34
+ * human-friendly message.
35
+ *
36
+ * @remarks
37
+ * This does not handle parameterized assertions with more than one parameter
38
+ * too cleanly; it's unclear how a test runner would display the expected
39
+ * values. This will probably need a fix in the future.
40
+ * @param stackStartFn The function to start the stack trace from
41
+ * @param zodError The original `ZodError`
42
+ * @param values Values which caused the error
43
+ * @returns New `AssertionError`
44
+ */
45
+ static fromZodError<Parts extends AssertionParts>(
46
+ zodError: z.ZodError,
47
+ stackStartFn: (...args: any[]) => any,
48
+ values: ParsedValues<Parts>,
49
+ ): AssertionError {
50
+ const flat = z.flattenError(zodError);
51
+
52
+ let pretty = flat.formErrors.join('; ');
53
+ for (const [keypath, errors] of Object.entries(flat.fieldErrors)) {
54
+ pretty += `; ${keypath}: ${(errors as unknown[]).join('; ')}`;
55
+ }
56
+
57
+ const [actual, ...expected] = values as unknown as [unknown, ...unknown[]];
58
+
59
+ return new AssertionError({
60
+ actual,
61
+ expected: expected.length === 1 ? expected[0] : expected,
62
+ message: `Assertion ${this} failed: ${pretty}`,
63
+ operator: `${this}`,
64
+ stackStartFn,
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Type guard for an instance of this error.
70
+ *
71
+ * @param value Some value
72
+ * @returns `true` if `value` is an instance of `AssertionError`
73
+ */
74
+ static isAssertionError(value: unknown): value is AssertionError {
27
75
  return (
28
- isA(err, NodeAssertionError) && Object.hasOwn(err, kBupkisAssertionError)
76
+ isA(value, NodeAssertionError) &&
77
+ Object.hasOwn(value, kBupkisAssertionError)
78
+ );
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Variant of an {@link AssertionError} that is thrown when
84
+ * {@link bupkis!expect.fail} is called.
85
+ */
86
+ export class FailAssertionError extends AssertionError {
87
+ /**
88
+ * @internal
89
+ */
90
+ [kBupkisFailAssertionError] = true;
91
+
92
+ override name = 'FailAssertionError';
93
+
94
+ static isFailAssertionError(err: unknown): err is FailAssertionError {
95
+ return (
96
+ isA(err, FailAssertionError) &&
97
+ Object.hasOwn(err, kBupkisFailAssertionError)
29
98
  );
30
99
  }
31
100
  }
@@ -38,6 +107,8 @@ export class AssertionError extends NodeAssertionError {
38
107
  export class NegatedAssertionError extends AssertionError {
39
108
  [kBupkisNegatedAssertionError] = true;
40
109
 
110
+ override name = 'NegatedAssertionError';
111
+
41
112
  static isNegatedAssertionError(err: unknown): err is NegatedAssertionError {
42
113
  return (
43
114
  isA(err, AssertionError) &&