bupkis 0.0.2 → 0.1.1

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 (217) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +138 -82
  3. package/dist/commonjs/api.d.ts +93 -0
  4. package/dist/commonjs/api.d.ts.map +1 -0
  5. package/dist/commonjs/api.js +8 -0
  6. package/dist/commonjs/api.js.map +1 -0
  7. package/dist/commonjs/assertion/assertion-async.d.ts +45 -0
  8. package/dist/commonjs/assertion/assertion-async.d.ts.map +1 -0
  9. package/dist/commonjs/assertion/assertion-async.js +154 -0
  10. package/dist/commonjs/assertion/assertion-async.js.map +1 -0
  11. package/dist/commonjs/assertion/assertion-sync.d.ts +66 -0
  12. package/dist/commonjs/assertion/assertion-sync.d.ts.map +1 -0
  13. package/dist/commonjs/assertion/assertion-sync.js +244 -0
  14. package/dist/commonjs/assertion/assertion-sync.js.map +1 -0
  15. package/dist/commonjs/assertion/assertion-types.d.ts +801 -0
  16. package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -0
  17. package/dist/commonjs/assertion/assertion-types.js +13 -0
  18. package/dist/commonjs/assertion/assertion-types.js.map +1 -0
  19. package/dist/commonjs/assertion/assertion.d.ts +74 -0
  20. package/dist/commonjs/assertion/assertion.d.ts.map +1 -0
  21. package/dist/commonjs/assertion/assertion.js +201 -0
  22. package/dist/commonjs/assertion/assertion.js.map +1 -0
  23. package/dist/commonjs/assertion/create.d.ts +109 -0
  24. package/dist/commonjs/assertion/create.d.ts.map +1 -0
  25. package/dist/commonjs/assertion/create.js +129 -0
  26. package/dist/commonjs/assertion/create.js.map +1 -0
  27. package/dist/commonjs/assertion/impl/async.d.ts +45 -0
  28. package/dist/commonjs/assertion/impl/async.d.ts.map +1 -0
  29. package/dist/commonjs/assertion/impl/async.js +257 -0
  30. package/dist/commonjs/assertion/impl/async.js.map +1 -0
  31. package/dist/commonjs/assertion/impl/index.d.ts +3 -0
  32. package/dist/commonjs/assertion/impl/index.d.ts.map +1 -0
  33. package/dist/commonjs/assertion/impl/index.js +8 -0
  34. package/dist/commonjs/assertion/impl/index.js.map +1 -0
  35. package/dist/commonjs/assertion/impl/sync-basic.d.ts +6 -0
  36. package/dist/commonjs/assertion/impl/sync-basic.d.ts.map +1 -0
  37. package/dist/commonjs/assertion/impl/sync-basic.js +68 -0
  38. package/dist/commonjs/assertion/impl/sync-basic.js.map +1 -0
  39. package/dist/commonjs/assertion/impl/sync-collection.d.ts +17 -0
  40. package/dist/commonjs/assertion/impl/sync-collection.d.ts.map +1 -0
  41. package/dist/commonjs/assertion/impl/sync-collection.js +66 -0
  42. package/dist/commonjs/assertion/impl/sync-collection.js.map +1 -0
  43. package/dist/commonjs/assertion/impl/sync-esoteric.d.ts +5 -0
  44. package/dist/commonjs/assertion/impl/sync-esoteric.d.ts.map +1 -0
  45. package/dist/commonjs/assertion/impl/sync-esoteric.js +14 -0
  46. package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -0
  47. package/dist/commonjs/assertion/impl/sync-parametric.d.ts +114 -0
  48. package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -0
  49. package/dist/commonjs/assertion/impl/sync-parametric.js +388 -0
  50. package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -0
  51. package/dist/commonjs/assertion/impl/sync.d.ts +144 -0
  52. package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -0
  53. package/dist/commonjs/assertion/impl/sync.js +28 -0
  54. package/dist/commonjs/assertion/impl/sync.js.map +1 -0
  55. package/dist/commonjs/assertion/index.d.ts +15 -0
  56. package/dist/commonjs/assertion/index.d.ts.map +1 -0
  57. package/dist/commonjs/assertion/index.js +34 -0
  58. package/dist/commonjs/assertion/index.js.map +1 -0
  59. package/dist/commonjs/assertion/slotify.d.ts +23 -0
  60. package/dist/commonjs/assertion/slotify.d.ts.map +1 -0
  61. package/dist/commonjs/assertion/slotify.js +67 -0
  62. package/dist/commonjs/assertion/slotify.js.map +1 -0
  63. package/dist/commonjs/bootstrap.d.ts +175 -0
  64. package/dist/commonjs/bootstrap.d.ts.map +1 -0
  65. package/dist/commonjs/bootstrap.js +35 -0
  66. package/dist/commonjs/bootstrap.js.map +1 -0
  67. package/dist/commonjs/constant.d.ts +12 -0
  68. package/dist/commonjs/constant.d.ts.map +1 -0
  69. package/dist/commonjs/constant.js +33 -0
  70. package/dist/commonjs/constant.js.map +1 -0
  71. package/dist/commonjs/error.d.ts +20 -0
  72. package/dist/commonjs/error.d.ts.map +1 -0
  73. package/dist/commonjs/error.js +40 -0
  74. package/dist/commonjs/error.js.map +1 -0
  75. package/dist/commonjs/expect.d.ts +9 -0
  76. package/dist/commonjs/expect.d.ts.map +1 -0
  77. package/dist/commonjs/expect.js +218 -0
  78. package/dist/commonjs/expect.js.map +1 -0
  79. package/dist/commonjs/guards.d.ts +93 -0
  80. package/dist/commonjs/guards.d.ts.map +1 -0
  81. package/dist/commonjs/guards.js +160 -0
  82. package/dist/commonjs/guards.js.map +1 -0
  83. package/dist/commonjs/index.d.ts +181 -0
  84. package/dist/commonjs/index.d.ts.map +1 -0
  85. package/dist/commonjs/index.js +62 -0
  86. package/dist/commonjs/index.js.map +1 -0
  87. package/dist/commonjs/metadata.d.ts +53 -0
  88. package/dist/commonjs/metadata.d.ts.map +1 -0
  89. package/dist/commonjs/metadata.js +51 -0
  90. package/dist/commonjs/metadata.js.map +1 -0
  91. package/dist/commonjs/package.json +3 -0
  92. package/dist/commonjs/schema.d.ts +373 -0
  93. package/dist/commonjs/schema.d.ts.map +1 -0
  94. package/dist/commonjs/schema.js +437 -0
  95. package/dist/commonjs/schema.js.map +1 -0
  96. package/dist/commonjs/types.d.ts +79 -0
  97. package/dist/commonjs/types.d.ts.map +1 -0
  98. package/dist/commonjs/types.js +9 -0
  99. package/dist/commonjs/types.js.map +1 -0
  100. package/dist/commonjs/use.d.ts +4 -0
  101. package/dist/commonjs/use.d.ts.map +1 -0
  102. package/dist/commonjs/use.js +33 -0
  103. package/dist/commonjs/use.js.map +1 -0
  104. package/dist/commonjs/util.d.ts +67 -0
  105. package/dist/commonjs/util.d.ts.map +1 -0
  106. package/dist/commonjs/util.js +187 -0
  107. package/dist/commonjs/util.js.map +1 -0
  108. package/dist/esm/api.d.ts +93 -0
  109. package/dist/esm/api.d.ts.map +1 -0
  110. package/dist/esm/api.js +7 -0
  111. package/dist/esm/api.js.map +1 -0
  112. package/dist/esm/assertion/assertion-async.d.ts +45 -0
  113. package/dist/esm/assertion/assertion-async.d.ts.map +1 -0
  114. package/dist/esm/assertion/assertion-async.js +145 -0
  115. package/dist/esm/assertion/assertion-async.js.map +1 -0
  116. package/dist/esm/assertion/assertion-sync.d.ts +66 -0
  117. package/dist/esm/assertion/assertion-sync.d.ts.map +1 -0
  118. package/dist/esm/assertion/assertion-sync.js +235 -0
  119. package/dist/esm/assertion/assertion-sync.js.map +1 -0
  120. package/dist/esm/assertion/assertion-types.d.ts +801 -0
  121. package/dist/esm/assertion/assertion-types.d.ts.map +1 -0
  122. package/dist/esm/assertion/assertion-types.js +12 -0
  123. package/dist/esm/assertion/assertion-types.js.map +1 -0
  124. package/dist/esm/assertion/assertion.d.ts +74 -0
  125. package/dist/esm/assertion/assertion.d.ts.map +1 -0
  126. package/dist/esm/assertion/assertion.js +194 -0
  127. package/dist/esm/assertion/assertion.js.map +1 -0
  128. package/dist/esm/assertion/create.d.ts +109 -0
  129. package/dist/esm/assertion/create.d.ts.map +1 -0
  130. package/dist/esm/assertion/create.js +125 -0
  131. package/dist/esm/assertion/create.js.map +1 -0
  132. package/dist/esm/assertion/impl/async.d.ts +45 -0
  133. package/dist/esm/assertion/impl/async.d.ts.map +1 -0
  134. package/dist/esm/assertion/impl/async.js +254 -0
  135. package/dist/esm/assertion/impl/async.js.map +1 -0
  136. package/dist/esm/assertion/impl/index.d.ts +3 -0
  137. package/dist/esm/assertion/impl/index.d.ts.map +1 -0
  138. package/dist/esm/assertion/impl/index.js +3 -0
  139. package/dist/esm/assertion/impl/index.js.map +1 -0
  140. package/dist/esm/assertion/impl/sync-basic.d.ts +6 -0
  141. package/dist/esm/assertion/impl/sync-basic.d.ts.map +1 -0
  142. package/dist/esm/assertion/impl/sync-basic.js +65 -0
  143. package/dist/esm/assertion/impl/sync-basic.js.map +1 -0
  144. package/dist/esm/assertion/impl/sync-collection.d.ts +17 -0
  145. package/dist/esm/assertion/impl/sync-collection.d.ts.map +1 -0
  146. package/dist/esm/assertion/impl/sync-collection.js +63 -0
  147. package/dist/esm/assertion/impl/sync-collection.js.map +1 -0
  148. package/dist/esm/assertion/impl/sync-esoteric.d.ts +5 -0
  149. package/dist/esm/assertion/impl/sync-esoteric.d.ts.map +1 -0
  150. package/dist/esm/assertion/impl/sync-esoteric.js +11 -0
  151. package/dist/esm/assertion/impl/sync-esoteric.js.map +1 -0
  152. package/dist/esm/assertion/impl/sync-parametric.d.ts +114 -0
  153. package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -0
  154. package/dist/esm/assertion/impl/sync-parametric.js +385 -0
  155. package/dist/esm/assertion/impl/sync-parametric.js.map +1 -0
  156. package/dist/esm/assertion/impl/sync.d.ts +144 -0
  157. package/dist/esm/assertion/impl/sync.d.ts.map +1 -0
  158. package/dist/esm/assertion/impl/sync.js +22 -0
  159. package/dist/esm/assertion/impl/sync.js.map +1 -0
  160. package/dist/esm/assertion/index.d.ts +15 -0
  161. package/dist/esm/assertion/index.d.ts.map +1 -0
  162. package/dist/esm/assertion/index.js +14 -0
  163. package/dist/esm/assertion/index.js.map +1 -0
  164. package/dist/esm/assertion/slotify.d.ts +23 -0
  165. package/dist/esm/assertion/slotify.d.ts.map +1 -0
  166. package/dist/esm/assertion/slotify.js +63 -0
  167. package/dist/esm/assertion/slotify.js.map +1 -0
  168. package/dist/esm/bootstrap.d.ts +175 -0
  169. package/dist/esm/bootstrap.d.ts.map +1 -0
  170. package/dist/esm/bootstrap.js +31 -0
  171. package/dist/esm/bootstrap.js.map +1 -0
  172. package/dist/esm/constant.d.ts +12 -0
  173. package/dist/esm/constant.d.ts.map +1 -0
  174. package/dist/esm/constant.js +30 -0
  175. package/dist/esm/constant.js.map +1 -0
  176. package/dist/esm/error.d.ts +20 -0
  177. package/dist/esm/error.d.ts.map +1 -0
  178. package/dist/esm/error.js +35 -0
  179. package/dist/esm/error.js.map +1 -0
  180. package/dist/esm/expect.d.ts +9 -0
  181. package/dist/esm/expect.d.ts.map +1 -0
  182. package/dist/esm/expect.js +210 -0
  183. package/dist/esm/expect.js.map +1 -0
  184. package/dist/esm/guards.d.ts +93 -0
  185. package/dist/esm/guards.d.ts.map +1 -0
  186. package/dist/esm/guards.js +141 -0
  187. package/dist/esm/guards.js.map +1 -0
  188. package/dist/esm/index.d.ts +181 -0
  189. package/dist/esm/index.d.ts.map +1 -0
  190. package/dist/esm/index.js +20 -0
  191. package/dist/esm/index.js.map +1 -0
  192. package/dist/esm/metadata.d.ts +53 -0
  193. package/dist/esm/metadata.d.ts.map +1 -0
  194. package/dist/esm/metadata.js +48 -0
  195. package/dist/esm/metadata.js.map +1 -0
  196. package/dist/esm/package.json +3 -0
  197. package/dist/esm/schema.d.ts +373 -0
  198. package/dist/esm/schema.d.ts.map +1 -0
  199. package/dist/esm/schema.js +434 -0
  200. package/dist/esm/schema.js.map +1 -0
  201. package/dist/esm/types.d.ts +79 -0
  202. package/dist/esm/types.d.ts.map +1 -0
  203. package/dist/esm/types.js +8 -0
  204. package/dist/esm/types.js.map +1 -0
  205. package/dist/esm/use.d.ts +4 -0
  206. package/dist/esm/use.d.ts.map +1 -0
  207. package/dist/esm/use.js +30 -0
  208. package/dist/esm/use.js.map +1 -0
  209. package/dist/esm/util.d.ts +67 -0
  210. package/dist/esm/util.d.ts.map +1 -0
  211. package/dist/esm/util.js +182 -0
  212. package/dist/esm/util.js.map +1 -0
  213. package/package.json +26 -16
  214. package/src/bootstrap.ts +1 -1
  215. package/src/expect.ts +1 -1
  216. package/src/types.ts +16 -10
  217. package/src/use.ts +4 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.1](https://github.com/boneskull/bupkis/compare/bupkis-v0.1.0...bupkis-v0.1.1) (2025-09-09)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * actually build before publish ([996c28e](https://github.com/boneskull/bupkis/commit/996c28e223ce488d07ea0b7633829ff25d510be3))
9
+
10
+ ## [0.1.0](https://github.com/boneskull/bupkis/compare/bupkis-v0.0.2...bupkis-v0.1.0) (2025-09-08)
11
+
12
+
13
+ ### Features
14
+
15
+ * **use:** use() returns an object with a use() in it ([fb383d6](https://github.com/boneskull/bupkis/commit/fb383d6fb2f541085d2300664fe73b25c6249e42))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **package:** add description and homepage ([2ff82ea](https://github.com/boneskull/bupkis/commit/2ff82ea715280098f59612ba32da808308aced0e))
21
+
3
22
  ## [0.0.2](https://github.com/boneskull/bupkis/compare/bupkis-v0.0.1...bupkis-v0.0.2) (2025-09-07)
4
23
 
5
24
 
package/README.md CHANGED
@@ -1,34 +1,51 @@
1
1
  <p align="center">
2
- <img src="https://github.com/boneskull/bupkis/blob/main/assets/bupkis-logo-256.png" width="256px" align="center" alt="BUPKIS logo"/>
3
- <h1 align="center"><em><span class="federo-caps">BUPKIS<span></em></h1>
2
+ <img src="./assets/bupkis-logo-512.png" width="512px" align="center" alt="BUPKIS logo"/>
3
+ <h1 align="center"><span class="twentieth-century-caps">⁓ BUPKIS ⁓<span></h1>
4
4
  <p align="center">
5
- A well-typed and easily exensible <em>BDD-style</em> assertion library.
5
+ Uncommonly extensible assertions for the beautiful people
6
6
  <br/>
7
- by <a href="https://github.com/boneskull">@boneskull</a>
7
+ <small>by <a href="https://github.com/boneskull">@boneskull</a></small>
8
8
  </p>
9
9
  </p>
10
10
 
11
+ ## Quick Links
12
+
13
+ - [BUPKIS' Homepage][docs] (<https://bupkis.zip>)
14
+ - [Assertion Reference][assertion-reference]
15
+ - [Guide: Basic Usage][basic-usage]
16
+ - [Guide: How to Create a Custom Assertion][create-a-custom-assertion]
17
+
11
18
  ## Motivation
12
19
 
20
+ > "_Another_ assertion library? You cannot be serious. My test framework has its own assertions!"
21
+ >
22
+ > ‒sickos, probably
23
+
13
24
  Look, I'm ~~old~~ ~~wizened~~ ~~experienced~~ knowledegable and I've written a lot of tests. I've used a lot of assertion libraries. There are ones I prefer and ones I don't.
14
25
 
15
- But none of them do quite what _this_ does. The main goals of this library are:
26
+ But none of them do quite what _BUPKIS_ does. I want an assertion library that prioritizes:
16
27
 
17
28
  - Type safety
18
- - Dead-simple creation of custom assertions
19
- - Minimal API surface
29
+ - Uncompromisable extensibility
30
+ - A small API surface
20
31
 
21
- A chainable API may provide type safety. But it seems to _guarantee_ implementing a custom assertion will be complicated. The API surface is necessarily a combinatoric explosion of methods.
32
+ I can think of several that tick two-thirds of those boxes! But _I demand the total package_ (And You Should Too).
22
33
 
23
- > [!WARNING]
34
+ > ⚠️ **Caution!**
35
+ >
36
+ > Assertion libraries tend come in two flavors: chainable or stiff & traditional. But because these styles are likely _familiar_ to you, you may hate _BUPKIS_.
37
+ >
38
+ > I _want_ you to like it, yes. But if you don't, I'm content with just making my point and asking the following favor of the reader:
24
39
  >
25
- > Because chainable APIs are familiar, you may hate _BUPKIS_ once you see some examples. You don't have to use it, but please: _don't confuse familiarity with usability_.
40
+ > **Do not confuse _familiarity_ with _usability_.**
26
41
 
27
- To achieve these goals, I've adopted the following design principles:
42
+ The following is a brief overview of the design choices We made to serve these goals.
28
43
 
29
- ### Natural-Language Assertions
44
+ ### Natural Language Assertions
30
45
 
31
- In `bupkis` (stylized as "_BUPKIS_"), you **don't** write this:
46
+ In _BUPKIS_, "natural language" is the means to the end of "a small API surface".
47
+
48
+ When you're using _BUPKIS_, you **don't** write this:
32
49
 
33
50
  ```js
34
51
  expect(actual).toEqual(expected);
@@ -50,7 +67,7 @@ expect(actual, 'is equal to', expected);
50
67
  expect(actual, 'to strictly equal', expected);
51
68
  ```
52
69
 
53
- If Chai wants you to write:
70
+ If another assertion library wants you to write:
54
71
 
55
72
  ```js
56
73
  expect(actual).to.be.a('string');
@@ -60,111 +77,150 @@ Then _BUPKIS_ wants you to write:
60
77
 
61
78
  ```js
62
79
  expect(actual, 'to be a string');
63
- // it is tolerant of poor/ironic grammar
80
+ // it is tolerant of poor/ironic grammar, sometimes
64
81
  expect(actual, 'to be an string');
65
82
  ```
66
83
 
67
- Can't remember the string? Did you forget a word or make a typo? Maybe you also forgot _BUPKIS_ is type-safe? You'll get a nice squiggly for your trouble.
84
+ Can't remember the phrase? Did you forget a word or make a typo? Maybe you also forgot **_BUPKIS_ is type-safe?!** You'll get a nice squiggly for your trouble.
68
85
 
69
- The "string" part of an expectation is known as a _phrase_. Every expectation will contain, at minimum, one phrase. As you can see from the above example, phrases often have aliases.
86
+ This isn't black magic. It ain't no _cauldron_. We're not just _throwing rat tails and `string`s into a function and stirring that shit up._
70
87
 
71
- You can negate just about any phrase:
88
+ > "Preposterous! Codswallop!"
89
+ >
90
+ > ‒the reader and/or more sickos
72
91
 
73
- ```js
74
- expect(actual, 'to be', expected);
75
- // did they not teach grammar in nerd school??
76
- expect(actual, 'not is', expected);
77
-
78
- expect(
79
- () => throw new TypeError('aww, shucks'),
80
- 'to throw a',
81
- TypeError,
82
- 'not satisfying',
83
- /gol durn/,
84
- );
85
- ```
92
+ You may wonder how this could this be anything _but_ loosey-goosey _senselessness_. On the contrary—we have _conventions_!
86
93
 
87
- ### Custom Assertions
94
+ #### Conventions of `expect()`
88
95
 
89
- In _BUPKIS_, custom assertions are _first-class citizens_. You can create your own assertions with minimal boilerplate. You don't have to learn a new API or a new DSL (maybe); you just use [Zod][]. _It's so easy, even a **archaic human** could do it!_
96
+ To formalize the conventions at a high level:
90
97
 
91
- Read [Guide: How to Create a Custom Assertion](https://boneskull.github.io/bupkis/documents/Guides.How_to_Create_a_Custom_Assertion) to learn more.
98
+ - The first parameter to a _BUPKIS_ assertion is always the _subject_ ([def.](https://bupkis.zip/documents/Reference.Glossary_of_Terms#subject)).
92
99
 
93
- ## Prerequisites
100
+ - The "string" part of a _BUPKIS_ assertion is known as a _phrase_. Every expectation will contain _at minimum_ one (1) phrase. As you can see from the above "to be a string" example, phrases often have aliases.
94
101
 
95
- _BUPKIS_ requires **Node.js ^20.19.0 || ^22.12.0 || >=23** and ships as a dual CJS/ESM package.
102
+ - Assertions may have multiple phrases or parameters, but the simplest assertions always look like this:
96
103
 
97
- The library has been designed for Node.js environments and testing frameworks.
104
+ ```ts
105
+ expect(subject, 'phrase');
106
+ ```
98
107
 
99
- ## Installation
108
+ ...and more complex assertions look like this:
100
109
 
101
- ```bash
102
- npm install bupkis -D
103
- ```
110
+ ```ts
111
+ expect(subject, 'phrase', [parameter?, phrase?, parameter?, ...]);
112
+ ```
104
113
 
105
- ## Usage
114
+ - One more convention worth mentioning is _negation_.
115
+
116
+ You can _negate_ just about any phrase by prepending it with `not` and a space. For example:
117
+
118
+ ```js
119
+ expect(actual, 'to be', expected);
120
+ expect(actual, 'not to be', expected);
121
+
122
+ expect(
123
+ () => throw new TypeError('aww, shucks'),
124
+ 'not to throw a',
125
+ TypeError,
126
+ 'satisfying',
127
+ /gol durn/,
128
+ );
129
+ ```
130
+
131
+ Handy!
132
+
133
+ ### Custom Assertions Built With Zod
134
+
135
+ [Zod][] is a popular object validation library which does some heavy lifting for _BUPKIS_—most of the built-in assertions are implemented using Zod schemas. And so _BUPKIS_ extends this capability to you.
106
136
 
107
- Here:
137
+ An example will be illuminating. What follows is a ~~stupid~~ ~~quick~~ _stupid_ example of a creating and "registering" a basic assertion _which can be invoked using two different phrases_:
108
138
 
109
139
  ```ts
110
- import { expect } from 'bupkis';
111
-
112
- // Basic type assertions
113
- expect('hello', 'to be a string');
114
- expect(42, 'to be a number');
115
- expect(true, 'to be a boolean');
116
-
117
- // Value comparisons
118
- expect(10, 'to equal', 10);
119
- expect('hello world', 'to contain', 'world');
120
- expect([1, 2, 3], 'to have length', 3);
121
-
122
- // Negation
123
- expect(42, 'not to be a string');
124
- expect('hello', 'not to equal', 'goodbye');
125
-
126
- // Object assertions
127
- const user = { name: 'Alice', age: 30 };
128
- expect(user, 'to be an object');
129
- expect(user, 'to have property', 'name');
130
- expect(user, 'to satisfy', { name: 'Alice' });
131
- ```
140
+ import { z, use, createAssertion } from 'bupkis';
132
141
 
133
- For comprehensive documentation and guides, visit the [project documentation](https://boneskull.github.io/bupkis/).
142
+ const stringAssertion = createAssertion(
143
+ z.string(),
144
+ [['to be based', 'to be bussin']],
145
+ z.string(),
146
+ );
134
147
 
135
- ### Worth Mentioning Right Now
148
+ const { expect } = use([stringAssertion]);
136
149
 
137
- _BUPKIS_ has two main exports:
150
+ expect('chat', 'to be based');
151
+ expect('fam', 'to be bussin');
138
152
 
139
- - `expect()`: the main entrypoint for synchronous assertions
140
- - `expectAsync()`: the main entrypoint for asynchronous assertions
153
+ // did you know? includes all builtin assertions!
154
+ expect('skiball lavatory', 'to be a string');
155
+ ```
141
156
 
142
- > [!IMPORTANT]
157
+ > 📒 Registration?
143
158
  >
144
- > As of this writing, the assertions available in `expectAsync()` are all `Promise`-related (and custom assertions can even use an async schema for the subject); they are completely disjoint from the assertions available in `expect()`. **This will likely change in the future.**
159
+ > "Registration" of an assertion (though there is no stateful "registry" anywhere) is as straightforward as passing an array of `Assertion` instances (created via `createAssertion()`/`createAsyncAssertion()`) to the `use()` function.
160
+ >
161
+ > `use()`, as exported from `bupkis`, returns a new `expect()`/`expectAsync()` pair that includes your custom assertions alongside all the built-in ones. The new `expect()`/`expectAsync()` functions are fully type-safe and aware of your custom assertions. They _also_ have a `.use()` method, which allows you to compose sets of assertions from disjoint sources.
162
+
163
+ **Zod makes it extremely easy to create most custom assertions**. But despite its power, it can't do _everything_ we need an assertion to do; for those situations, there's also a [function-based API][custom-assertion-function] for use with [parametric][] and behavioral (e.g., involving function execution) assertions.
164
+
165
+ 👉 For an assiduous guide on creating assertions, read [Guide: How to Create a Custom Assertion][create-a-custom-assertion].
166
+
167
+ ### Excruciating Type Safety
168
+
169
+ We have tried to make _BUPKIS_ is as type-safe as possible. To be clear, _that is pretty damn possible_. This means:
170
+
171
+ - Every built-in assertion is fully type-safe and is declared as an overload for `expect()` or `expectAsync()`.
172
+ - Every _custom_ assertion is _also_ fully type-safe and is declared as an overload for `expect()` or `expectAsync()` (as returned from `use()`)
173
+ - If an assertion demands the _subject_ be of a certain type, the TS compiler will squawk if you try to use an incompatible subject type. For example, `<Map> to have size <number>` will only accept a `Map` as the subject, and this will be obvious in your editor.
174
+
175
+ > _Note_: `expect()` is not and cannot be a type guard; see the ["Caveats" Reference doc](http://bupkis.zip/documents/Reference.Caveats#expect-is-not-a-type-guard) for more information.
176
+
177
+ ## Prerequisites
145
178
 
146
- ## Project Scope
179
+ **Node.js**: ^20.19.0, ^22.12.0, >=23
147
180
 
148
- 1. It's an assertion library
181
+ _BUPKIS_ has a peer dependency on [Zod][] v4+, but will install it as an optional dependency if you are not already using it.
149
182
 
150
- ### TODO
183
+ _BUPKIS_ ships as a dual CJS/ESM package.
151
184
 
152
- ## Prior Art & Appreciation
185
+ > Disclaimer: _BUPKIS_ has been designed to run on Node.js in a development environment. Anyone attempting to deploy _BUPKIS_ to some server somewhere will get what is coming to them.
186
+
187
+ ## Installation
188
+
189
+ ```bash
190
+ npm install bupkis -D
191
+ ```
192
+
193
+ ## Usage
194
+
195
+ 👉 See the [Basic Usage Guide](https://bupkis.zip/documents/guides.basic_usage) for a quick introduction.
196
+
197
+ 📖 Visit [https://bupkis.zip](https://bupkis.zip) for comprehensive guides and reference.
198
+
199
+ ## Acknowledgements
153
200
 
154
201
  - [Unexpected][] is the main inspiration for _BUPKIS_. However, creating types for this library is exceedingly difficult (and was in fact the first thing I tried). Despite that drawback, I find it more usable than any other assertion library I've tried.
155
- - [Zod][] is a popular object validation library which does most of the heavy lifting for _BUPKIS_. It's not an assertion library, but there's enough overlap in its use case that it makes sense to leverage it.
156
- - [fast-check][]: A big thanks to Nicholas Dubien for this library. There is **no better library** for an assertion library to use to test itself! Well, besides itself, I mean. How about _in addition to_ itself? Yes. Thank you!
202
+ - [Zod][] is a popular object validation library upon which _BUPKIS_ builds many of its own assertions.
203
+ - [fast-check][]: Thanks to Nicholas Dubien for this library. There is **no better library** for an assertion library to use to test itself! Well, besides itself, I mean. How about _in addition to_ itself? Yes. Thank you!
204
+ - [tshy][] from Isaac Schlueter. Thanks for making dual ESM/CJS packages easy and not too fancy.
205
+ - [TypeDoc][] it really documents the hell out of TypeScript projects.
206
+ - [@cjihrig](https://github.com/cjihrig) and other Node.js contributors for the thoughtfulness put into [`node:test`](https://nodejs.org/api/test.html) that make it my current test-runner-of-choice.
157
207
 
158
- ## A Note From The Author
208
+ ## Why is it called _BUPKIS_?
159
209
 
160
- > _"This is my assertion library. Many are like it, but this one is mine."_
161
- >
162
- > ‒boneskull, 2025
210
+ TODO: think of good reason and fill in later
163
211
 
164
212
  ## License
165
213
 
166
214
  Copyright © 2025 Christopher Hiller. Licensed under [BlueOak-1.0.0](https://blueoakcouncil.org/license/1.0.0).
167
215
 
168
216
  [zod]: https://zod.dev
217
+ [docs]: https://bupkis.zip
218
+ [basic-usage]: https://bupkis.zip/documents/guides.basic_usage
169
219
  [unexpected]: https://unexpected.js.org
170
220
  [fast-check]: https://fast-check.dev
221
+ [parametric]: https://bupkis.zip/documents/Reference.Glossary_of_Terms#parametric-assertion
222
+ [custom-assertion-function]: https://bupkis.zip/documents/guides.how_to_create_a_custom_assertion#using-a-function
223
+ [create-a-custom-assertion]: https://bupkis.zip/documents/Guides.How_to_Create_a_Custom_Assertion
224
+ [assertion-reference]: https://bupkis.zip/documents/reference.assertions
225
+ [tshy]: https://github.com/isaacs/tshy
226
+ [typedoc]: https://typedoc.org
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Contains the main API types
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import type { TupleToUnion, UnionToIntersection } from 'type-fest';
7
+ import type { AnyAsyncAssertion, AnyAsyncAssertions, AnySyncAssertion, AnySyncAssertions, BuiltinAsyncAssertions, BuiltinSyncAssertions } from './assertion/assertion-types.js';
8
+ import type { createAssertion, createAsyncAssertion } from './assertion/create.js';
9
+ import { type InferredExpectSlots, type MutableOrReadonly, type UseFn } from './types.js';
10
+ /**
11
+ * Base set of properties included in both {@link Expect} and {@link ExpectAsync}.
12
+ */
13
+ export interface BaseExpect {
14
+ /**
15
+ * Creates a new synchronous assertion.
16
+ */
17
+ createAssertion: typeof createAssertion;
18
+ /**
19
+ * Creates a new asynchronous assertion.
20
+ */
21
+ createAsyncAssertion: typeof createAsyncAssertion;
22
+ /**
23
+ * Fails immediately with optional `reason`.
24
+ *
25
+ * @param reason Reason for failure
26
+ * @throws {AssertionError}
27
+ */
28
+ fail(this: void, reason?: string): never;
29
+ }
30
+ /**
31
+ * The main synchronous assertion function.
32
+ *
33
+ * Contains properties in {@link ExpectSyncProps}.
34
+ *
35
+ * @template T All synchronous assertions available
36
+ * @template U All asynchronous assertions available; for use in
37
+ * {@link ExpectSyncProps.use}
38
+ * @useDeclaredType
39
+ * @see {@link expect}
40
+ */
41
+ export type Expect<T extends AnySyncAssertions = BuiltinSyncAssertions, U extends AnyAsyncAssertions = BuiltinAsyncAssertions> = ExpectFunction<T> & ExpectSyncProps<T, U>;
42
+ /**
43
+ * The main asynchronous assertion function.
44
+ *
45
+ * Contains properties in {@link ExpectSyncProps}.
46
+ *
47
+ * @useDeclaredType
48
+ * @see {@link expectAsync}
49
+ */
50
+ export type ExpectAsync<T extends AnyAsyncAssertions = BuiltinAsyncAssertions, U extends AnySyncAssertions = BuiltinSyncAssertions> = ExpectAsyncFunction<T> & ExpectAsyncProps<T, U>;
51
+ /**
52
+ * All function overloads for `expectAsync()`; part of {@link ExpectAsync}.
53
+ */
54
+ export type ExpectAsyncFunction<T extends AnyAsyncAssertions = BuiltinAsyncAssertions> = UnionToIntersection<TupleToUnion<{
55
+ [K in keyof T]: T[K] extends AnyAsyncAssertion ? (...args: MutableOrReadonly<InferredExpectSlots<T[K]['parts']>>) => Promise<void> : never;
56
+ }>>;
57
+ /**
58
+ * Properties available on `expectAsync()`; part of {@link ExpectAsync}.
59
+ */
60
+ export interface ExpectAsyncProps<T extends AnyAsyncAssertions, U extends AnySyncAssertions> extends BaseExpect {
61
+ /**
62
+ * Tuple of all assertions available in this `expect()`.
63
+ */
64
+ assertions: T;
65
+ /**
66
+ * Function to add more assertions to this `expect()`, returning a new
67
+ * `expect()` and `expectAsync()` pair with the combined assertions.
68
+ */
69
+ use: UseFn<U, T>;
70
+ }
71
+ /**
72
+ * All function overloads for `expect()`; part of {@link Expect}.
73
+ *
74
+ * @useDeclaredType
75
+ */
76
+ export type ExpectFunction<T extends AnySyncAssertions = BuiltinSyncAssertions> = UnionToIntersection<TupleToUnion<{
77
+ [K in keyof T]: T[K] extends AnySyncAssertion ? (...args: MutableOrReadonly<InferredExpectSlots<T[K]['parts']>>) => void : never;
78
+ }>>;
79
+ /**
80
+ * Properties for `expect()`; part of {@link Expect}.
81
+ */
82
+ export interface ExpectSyncProps<T extends AnySyncAssertions, U extends AnyAsyncAssertions> extends BaseExpect {
83
+ /**
84
+ * Tuple of all assertions available in this `expect()`.
85
+ */
86
+ assertions: T;
87
+ /**
88
+ * Function to add more assertions to this `expect()`, returning a new
89
+ * `expect()` and `expectAsync()` pair with the combined assertions.
90
+ */
91
+ use: UseFn<T, U>;
92
+ }
93
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEnE,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACrB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC;;OAEG;IACH,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAClD;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC1C;AAED;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,MAAM,CAChB,CAAC,SAAS,iBAAiB,GAAG,qBAAqB,EACnD,CAAC,SAAS,kBAAkB,GAAG,sBAAsB,IACnD,cAAc,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE9C;;;;;;;GAOG;AAEH,MAAM,MAAM,WAAW,CACrB,CAAC,SAAS,kBAAkB,GAAG,sBAAsB,EACrD,CAAC,SAAS,iBAAiB,GAAG,qBAAqB,IACjD,mBAAmB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEpD;;GAEG;AAEH,MAAM,MAAM,mBAAmB,CAC7B,CAAC,SAAS,kBAAkB,GAAG,sBAAsB,IACnD,mBAAmB,CACrB,YAAY,CAAC;KACV,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,iBAAiB,GAC1C,CACE,GAAG,IAAI,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAC3D,OAAO,CAAC,IAAI,CAAC,GAClB,KAAK;CACV,CAAC,CACH,CAAC;AAEF;;GAEG;AAEH,MAAM,WAAW,gBAAgB,CAC/B,CAAC,SAAS,kBAAkB,EAC5B,CAAC,SAAS,iBAAiB,CAC3B,SAAQ,UAAU;IAClB;;OAEG;IACH,UAAU,EAAE,CAAC,CAAC;IACd;;;OAGG;IACH,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,CACxB,CAAC,SAAS,iBAAiB,GAAG,qBAAqB,IACjD,mBAAmB,CACrB,YAAY,CAAC;KACV,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,gBAAgB,GACzC,CAAC,GAAG,IAAI,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,GACxE,KAAK;CACV,CAAC,CACH,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe,CAC9B,CAAC,SAAS,iBAAiB,EAC3B,CAAC,SAAS,kBAAkB,CAC5B,SAAQ,UAAU;IAClB;;OAEG;IACH,UAAU,EAAE,CAAC,CAAC;IAEd;;;OAGG;IACH,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAClB"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Contains the main API types
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":";AAAA;;;;GAIG"}
@@ -0,0 +1,45 @@
1
+ import { type AssertionAsync, type AssertionFunctionAsync, type AssertionImplAsync, type AssertionImplFnAsync, type AssertionImplSchemaAsync, type AssertionParts, type AssertionSchemaAsync, type AssertionSlots, type ParsedResult, type ParsedValues } from './assertion-types.js';
2
+ import { BupkisAssertion } from './assertion.js';
3
+ export declare abstract class BupkisAssertionAsync<Parts extends AssertionParts, Impl extends AssertionImplAsync<Parts>, Slots extends AssertionSlots<Parts>> extends BupkisAssertion<Parts, Impl, Slots> implements AssertionAsync<Parts, Impl, Slots> {
4
+ abstract executeAsync(parsedValues: ParsedValues<Parts>, args: unknown[], stackStartFn: (...args: any[]) => any, parseResult?: ParsedResult<Parts>): Promise<void>;
5
+ parseValuesAsync<Args extends readonly unknown[]>(args: Args): Promise<ParsedResult<Parts>>;
6
+ }
7
+ /**
8
+ * An assertion implemented as a Zod schema.
9
+ *
10
+ * Async schemas are supported via {@link expectAsync}.
11
+ */
12
+ /**
13
+ * Optimized schema assertion that performs subject validation during
14
+ * parseValues() to eliminate double parsing for simple schema-based
15
+ * assertions.
16
+ *
17
+ * This class implements Option 2 from the z.function() analysis - it caches the
18
+ * subject validation result during argument parsing and reuses it during
19
+ * execution, eliminating the double parsing overhead.
20
+ */
21
+ export declare class BupkisAssertionFunctionAsync<Parts extends AssertionParts, Impl extends AssertionImplFnAsync<Parts>, Slots extends AssertionSlots<Parts>> extends BupkisAssertionAsync<Parts, Impl, Slots> implements AssertionFunctionAsync<Parts, Impl, Slots> {
22
+ executeAsync(parsedValues: ParsedValues<Parts>, args: unknown[], stackStartFn: (...args: any[]) => any, _parseResult?: ParsedResult<Parts>): Promise<void>;
23
+ }
24
+ /**
25
+ * A class representing an assertion implemented as a function.
26
+ *
27
+ * This function may:
28
+ *
29
+ * 1. Return a `boolean` indicating pass/fail.
30
+ * 2. Return a `ZodType` which will be used to validate the subject.
31
+ * 3. Return a `Promise` resolving to either of the above (when called via
32
+ * {@link expectAsync})
33
+ * 4. Throw a {@link AssertionError}; when called via {@link expectAsync}, reject
34
+ * with an {@link AssertionError}
35
+ */
36
+ export declare class BupkisAssertionSchemaAsync<Parts extends AssertionParts, Impl extends AssertionImplSchemaAsync<Parts>, Slots extends AssertionSlots<Parts>> extends BupkisAssertionAsync<Parts, Impl, Slots> implements AssertionSchemaAsync<Parts, Impl, Slots> {
37
+ executeAsync(parsedValues: ParsedValues<Parts>, _args: unknown[], stackStartFn: (...args: any[]) => any, _parseResult?: ParsedResult<Parts>): Promise<void>;
38
+ /**
39
+ * Determines if this assertion can be optimized (simple single-subject
40
+ * schema). Only simple assertions like ['to be a string'] with z.string()
41
+ * qualify.
42
+ */
43
+ private isSimpleSchemaAssertion;
44
+ }
45
+ //# sourceMappingURL=assertion-async.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertion-async.d.ts","sourceRoot":"","sources":["../../../src/assertion/assertion-async.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,8BAAsB,oBAAoB,CACtC,KAAK,SAAS,cAAc,EAC5B,IAAI,SAAS,kBAAkB,CAAC,KAAK,CAAC,EACtC,KAAK,SAAS,cAAc,CAAC,KAAK,CAAC,CAErC,SAAQ,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAC1C,YAAW,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;IAE7C,QAAQ,CAAC,YAAY,CACnB,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,EACjC,IAAI,EAAE,OAAO,EAAE,EACf,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACrC,WAAW,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC;IAEV,gBAAgB,CAAC,IAAI,SAAS,SAAS,OAAO,EAAE,EACpD,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;CA0ChC;AACD;;;;GAIG;AACH;;;;;;;;GAQG;AAEH,qBAAa,4BAA4B,CACrC,KAAK,SAAS,cAAc,EAC5B,IAAI,SAAS,oBAAoB,CAAC,KAAK,CAAC,EACxC,KAAK,SAAS,cAAc,CAAC,KAAK,CAAC,CAErC,SAAQ,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAC/C,YAAW,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;IAEtC,YAAY,CACzB,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,EACjC,IAAI,EAAE,OAAO,EAAE,EACf,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACrC,YAAY,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC;CA8BjB;AAED;;;;;;;;;;;GAWG;AAEH,qBAAa,0BAA0B,CACnC,KAAK,SAAS,cAAc,EAC5B,IAAI,SAAS,wBAAwB,CAAC,KAAK,CAAC,EAC5C,KAAK,SAAS,cAAc,CAAC,KAAK,CAAC,CAErC,SAAQ,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAC/C,YAAW,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;IAEpC,YAAY,CACzB,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,EACjC,KAAK,EAAE,OAAO,EAAE,EAChB,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACrC,YAAY,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC;IAahB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;CAehC"}
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BupkisAssertionSchemaAsync = exports.BupkisAssertionFunctionAsync = exports.BupkisAssertionAsync = void 0;
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const util_1 = require("util");
9
+ const v4_1 = __importDefault(require("zod/v4"));
10
+ const constant_js_1 = require("../constant.js");
11
+ const error_js_1 = require("../error.js");
12
+ const guards_js_1 = require("../guards.js");
13
+ const metadata_js_1 = require("../metadata.js");
14
+ const assertion_js_1 = require("./assertion.js");
15
+ const debug = (0, debug_1.default)('bupkis:assertion:async');
16
+ class BupkisAssertionAsync extends assertion_js_1.BupkisAssertion {
17
+ async parseValuesAsync(args) {
18
+ const { slots } = this;
19
+ const parsedValues = [];
20
+ if (slots.length !== args.length) {
21
+ return {
22
+ success: false,
23
+ };
24
+ }
25
+ let exactMatch = true;
26
+ for (let i = 0; i < slots.length; i++) {
27
+ const slot = slots[i];
28
+ const arg = args[i];
29
+ const parsedLiteralResult = this.parseSlotForLiteral(slot, i, arg);
30
+ if (parsedLiteralResult === true) {
31
+ continue;
32
+ }
33
+ else if (parsedLiteralResult !== false) {
34
+ return parsedLiteralResult;
35
+ }
36
+ // unknown/any accept anything
37
+ // IMPORTANT: do not use a type guard here; it will break inference
38
+ if (slot.def.type === 'unknown' || slot.def.type === 'any') {
39
+ debug('Skipping unknown/any slot validation for arg', arg);
40
+ parsedValues.push(arg);
41
+ exactMatch = false;
42
+ continue;
43
+ }
44
+ const result = await slot.safeParseAsync(arg);
45
+ if (!result.success) {
46
+ return {
47
+ success: false,
48
+ };
49
+ }
50
+ parsedValues.push(result.data);
51
+ }
52
+ return {
53
+ exactMatch,
54
+ parsedValues: parsedValues,
55
+ success: true,
56
+ };
57
+ }
58
+ }
59
+ exports.BupkisAssertionAsync = BupkisAssertionAsync;
60
+ /**
61
+ * An assertion implemented as a Zod schema.
62
+ *
63
+ * Async schemas are supported via {@link expectAsync}.
64
+ */
65
+ /**
66
+ * Optimized schema assertion that performs subject validation during
67
+ * parseValues() to eliminate double parsing for simple schema-based
68
+ * assertions.
69
+ *
70
+ * This class implements Option 2 from the z.function() analysis - it caches the
71
+ * subject validation result during argument parsing and reuses it during
72
+ * execution, eliminating the double parsing overhead.
73
+ */
74
+ class BupkisAssertionFunctionAsync extends BupkisAssertionAsync {
75
+ async executeAsync(parsedValues, args, stackStartFn, _parseResult) {
76
+ const { impl } = this;
77
+ const result = await impl(...parsedValues);
78
+ if ((0, guards_js_1.isZodType)(result)) {
79
+ try {
80
+ await result.parseAsync(parsedValues[0]);
81
+ }
82
+ catch (error) {
83
+ if ((0, guards_js_1.isA)(error, v4_1.default.ZodError)) {
84
+ throw this.translateZodError(stackStartFn, error, ...parsedValues);
85
+ }
86
+ throw error;
87
+ }
88
+ }
89
+ else if ((0, guards_js_1.isBoolean)(result)) {
90
+ if (!result) {
91
+ throw new error_js_1.AssertionError({
92
+ message: `Assertion ${this} failed for arguments: ${(0, util_1.inspect)(args)}`,
93
+ });
94
+ }
95
+ }
96
+ else if ((0, guards_js_1.isAssertionFailure)(result)) {
97
+ throw new error_js_1.AssertionError({
98
+ actual: result.actual,
99
+ expected: result.expected,
100
+ message: result.message ?? `Assertion ${this} failed`,
101
+ });
102
+ }
103
+ else if (result) {
104
+ throw new TypeError(`Invalid return type from assertion ${this}; expected boolean, ZodType, or AssertionFailure`);
105
+ }
106
+ }
107
+ }
108
+ exports.BupkisAssertionFunctionAsync = BupkisAssertionFunctionAsync;
109
+ /**
110
+ * A class representing an assertion implemented as a function.
111
+ *
112
+ * This function may:
113
+ *
114
+ * 1. Return a `boolean` indicating pass/fail.
115
+ * 2. Return a `ZodType` which will be used to validate the subject.
116
+ * 3. Return a `Promise` resolving to either of the above (when called via
117
+ * {@link expectAsync})
118
+ * 4. Throw a {@link AssertionError}; when called via {@link expectAsync}, reject
119
+ * with an {@link AssertionError}
120
+ */
121
+ class BupkisAssertionSchemaAsync extends BupkisAssertionAsync {
122
+ async executeAsync(parsedValues, _args, stackStartFn, _parseResult) {
123
+ // For async, fall back to standard implementation for now
124
+ const [subject] = parsedValues;
125
+ try {
126
+ await this.impl.parseAsync(subject);
127
+ }
128
+ catch (error) {
129
+ if ((0, guards_js_1.isA)(error, v4_1.default.ZodError)) {
130
+ throw this.translateZodError(stackStartFn, error, ...parsedValues);
131
+ }
132
+ throw error;
133
+ }
134
+ }
135
+ /**
136
+ * Determines if this assertion can be optimized (simple single-subject
137
+ * schema). Only simple assertions like ['to be a string'] with z.string()
138
+ * qualify.
139
+ */
140
+ isSimpleSchemaAssertion() {
141
+ // Only optimize if we have exactly one subject slot + string literal slots
142
+ // and no complex argument processing
143
+ const hasSubjectSlot = this.slots.length > 0 &&
144
+ (this.slots[0]?.def.type === 'unknown' ||
145
+ this.slots[0]?.def.type === 'any');
146
+ const allOtherSlotsAreLiterals = this.slots.slice(1).every((slot) => {
147
+ const meta = metadata_js_1.BupkisRegistry.get(slot) ?? {};
148
+ return constant_js_1.kStringLiteral in meta;
149
+ });
150
+ return hasSubjectSlot && allOtherSlotsAreLiterals;
151
+ }
152
+ }
153
+ exports.BupkisAssertionSchemaAsync = BupkisAssertionSchemaAsync;
154
+ //# sourceMappingURL=assertion-async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertion-async.js","sourceRoot":"","sources":["../../../src/assertion/assertion-async.ts"],"names":[],"mappings":"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"}