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.
- package/CHANGELOG.md +19 -0
- package/README.md +138 -82
- package/dist/commonjs/api.d.ts +93 -0
- package/dist/commonjs/api.d.ts.map +1 -0
- package/dist/commonjs/api.js +8 -0
- package/dist/commonjs/api.js.map +1 -0
- package/dist/commonjs/assertion/assertion-async.d.ts +45 -0
- package/dist/commonjs/assertion/assertion-async.d.ts.map +1 -0
- package/dist/commonjs/assertion/assertion-async.js +154 -0
- package/dist/commonjs/assertion/assertion-async.js.map +1 -0
- package/dist/commonjs/assertion/assertion-sync.d.ts +66 -0
- package/dist/commonjs/assertion/assertion-sync.d.ts.map +1 -0
- package/dist/commonjs/assertion/assertion-sync.js +244 -0
- package/dist/commonjs/assertion/assertion-sync.js.map +1 -0
- package/dist/commonjs/assertion/assertion-types.d.ts +801 -0
- package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -0
- package/dist/commonjs/assertion/assertion-types.js +13 -0
- package/dist/commonjs/assertion/assertion-types.js.map +1 -0
- package/dist/commonjs/assertion/assertion.d.ts +74 -0
- package/dist/commonjs/assertion/assertion.d.ts.map +1 -0
- package/dist/commonjs/assertion/assertion.js +201 -0
- package/dist/commonjs/assertion/assertion.js.map +1 -0
- package/dist/commonjs/assertion/create.d.ts +109 -0
- package/dist/commonjs/assertion/create.d.ts.map +1 -0
- package/dist/commonjs/assertion/create.js +129 -0
- package/dist/commonjs/assertion/create.js.map +1 -0
- package/dist/commonjs/assertion/impl/async.d.ts +45 -0
- package/dist/commonjs/assertion/impl/async.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/async.js +257 -0
- package/dist/commonjs/assertion/impl/async.js.map +1 -0
- package/dist/commonjs/assertion/impl/index.d.ts +3 -0
- package/dist/commonjs/assertion/impl/index.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/index.js +8 -0
- package/dist/commonjs/assertion/impl/index.js.map +1 -0
- package/dist/commonjs/assertion/impl/sync-basic.d.ts +6 -0
- package/dist/commonjs/assertion/impl/sync-basic.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/sync-basic.js +68 -0
- package/dist/commonjs/assertion/impl/sync-basic.js.map +1 -0
- package/dist/commonjs/assertion/impl/sync-collection.d.ts +17 -0
- package/dist/commonjs/assertion/impl/sync-collection.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/sync-collection.js +66 -0
- package/dist/commonjs/assertion/impl/sync-collection.js.map +1 -0
- package/dist/commonjs/assertion/impl/sync-esoteric.d.ts +5 -0
- package/dist/commonjs/assertion/impl/sync-esoteric.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/sync-esoteric.js +14 -0
- package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -0
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts +114 -0
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/sync-parametric.js +388 -0
- package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -0
- package/dist/commonjs/assertion/impl/sync.d.ts +144 -0
- package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/sync.js +28 -0
- package/dist/commonjs/assertion/impl/sync.js.map +1 -0
- package/dist/commonjs/assertion/index.d.ts +15 -0
- package/dist/commonjs/assertion/index.d.ts.map +1 -0
- package/dist/commonjs/assertion/index.js +34 -0
- package/dist/commonjs/assertion/index.js.map +1 -0
- package/dist/commonjs/assertion/slotify.d.ts +23 -0
- package/dist/commonjs/assertion/slotify.d.ts.map +1 -0
- package/dist/commonjs/assertion/slotify.js +67 -0
- package/dist/commonjs/assertion/slotify.js.map +1 -0
- package/dist/commonjs/bootstrap.d.ts +175 -0
- package/dist/commonjs/bootstrap.d.ts.map +1 -0
- package/dist/commonjs/bootstrap.js +35 -0
- package/dist/commonjs/bootstrap.js.map +1 -0
- package/dist/commonjs/constant.d.ts +12 -0
- package/dist/commonjs/constant.d.ts.map +1 -0
- package/dist/commonjs/constant.js +33 -0
- package/dist/commonjs/constant.js.map +1 -0
- package/dist/commonjs/error.d.ts +20 -0
- package/dist/commonjs/error.d.ts.map +1 -0
- package/dist/commonjs/error.js +40 -0
- package/dist/commonjs/error.js.map +1 -0
- package/dist/commonjs/expect.d.ts +9 -0
- package/dist/commonjs/expect.d.ts.map +1 -0
- package/dist/commonjs/expect.js +218 -0
- package/dist/commonjs/expect.js.map +1 -0
- package/dist/commonjs/guards.d.ts +93 -0
- package/dist/commonjs/guards.d.ts.map +1 -0
- package/dist/commonjs/guards.js +160 -0
- package/dist/commonjs/guards.js.map +1 -0
- package/dist/commonjs/index.d.ts +181 -0
- package/dist/commonjs/index.d.ts.map +1 -0
- package/dist/commonjs/index.js +62 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/metadata.d.ts +53 -0
- package/dist/commonjs/metadata.d.ts.map +1 -0
- package/dist/commonjs/metadata.js +51 -0
- package/dist/commonjs/metadata.js.map +1 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/schema.d.ts +373 -0
- package/dist/commonjs/schema.d.ts.map +1 -0
- package/dist/commonjs/schema.js +437 -0
- package/dist/commonjs/schema.js.map +1 -0
- package/dist/commonjs/types.d.ts +79 -0
- package/dist/commonjs/types.d.ts.map +1 -0
- package/dist/commonjs/types.js +9 -0
- package/dist/commonjs/types.js.map +1 -0
- package/dist/commonjs/use.d.ts +4 -0
- package/dist/commonjs/use.d.ts.map +1 -0
- package/dist/commonjs/use.js +33 -0
- package/dist/commonjs/use.js.map +1 -0
- package/dist/commonjs/util.d.ts +67 -0
- package/dist/commonjs/util.d.ts.map +1 -0
- package/dist/commonjs/util.js +187 -0
- package/dist/commonjs/util.js.map +1 -0
- package/dist/esm/api.d.ts +93 -0
- package/dist/esm/api.d.ts.map +1 -0
- package/dist/esm/api.js +7 -0
- package/dist/esm/api.js.map +1 -0
- package/dist/esm/assertion/assertion-async.d.ts +45 -0
- package/dist/esm/assertion/assertion-async.d.ts.map +1 -0
- package/dist/esm/assertion/assertion-async.js +145 -0
- package/dist/esm/assertion/assertion-async.js.map +1 -0
- package/dist/esm/assertion/assertion-sync.d.ts +66 -0
- package/dist/esm/assertion/assertion-sync.d.ts.map +1 -0
- package/dist/esm/assertion/assertion-sync.js +235 -0
- package/dist/esm/assertion/assertion-sync.js.map +1 -0
- package/dist/esm/assertion/assertion-types.d.ts +801 -0
- package/dist/esm/assertion/assertion-types.d.ts.map +1 -0
- package/dist/esm/assertion/assertion-types.js +12 -0
- package/dist/esm/assertion/assertion-types.js.map +1 -0
- package/dist/esm/assertion/assertion.d.ts +74 -0
- package/dist/esm/assertion/assertion.d.ts.map +1 -0
- package/dist/esm/assertion/assertion.js +194 -0
- package/dist/esm/assertion/assertion.js.map +1 -0
- package/dist/esm/assertion/create.d.ts +109 -0
- package/dist/esm/assertion/create.d.ts.map +1 -0
- package/dist/esm/assertion/create.js +125 -0
- package/dist/esm/assertion/create.js.map +1 -0
- package/dist/esm/assertion/impl/async.d.ts +45 -0
- package/dist/esm/assertion/impl/async.d.ts.map +1 -0
- package/dist/esm/assertion/impl/async.js +254 -0
- package/dist/esm/assertion/impl/async.js.map +1 -0
- package/dist/esm/assertion/impl/index.d.ts +3 -0
- package/dist/esm/assertion/impl/index.d.ts.map +1 -0
- package/dist/esm/assertion/impl/index.js +3 -0
- package/dist/esm/assertion/impl/index.js.map +1 -0
- package/dist/esm/assertion/impl/sync-basic.d.ts +6 -0
- package/dist/esm/assertion/impl/sync-basic.d.ts.map +1 -0
- package/dist/esm/assertion/impl/sync-basic.js +65 -0
- package/dist/esm/assertion/impl/sync-basic.js.map +1 -0
- package/dist/esm/assertion/impl/sync-collection.d.ts +17 -0
- package/dist/esm/assertion/impl/sync-collection.d.ts.map +1 -0
- package/dist/esm/assertion/impl/sync-collection.js +63 -0
- package/dist/esm/assertion/impl/sync-collection.js.map +1 -0
- package/dist/esm/assertion/impl/sync-esoteric.d.ts +5 -0
- package/dist/esm/assertion/impl/sync-esoteric.d.ts.map +1 -0
- package/dist/esm/assertion/impl/sync-esoteric.js +11 -0
- package/dist/esm/assertion/impl/sync-esoteric.js.map +1 -0
- package/dist/esm/assertion/impl/sync-parametric.d.ts +114 -0
- package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -0
- package/dist/esm/assertion/impl/sync-parametric.js +385 -0
- package/dist/esm/assertion/impl/sync-parametric.js.map +1 -0
- package/dist/esm/assertion/impl/sync.d.ts +144 -0
- package/dist/esm/assertion/impl/sync.d.ts.map +1 -0
- package/dist/esm/assertion/impl/sync.js +22 -0
- package/dist/esm/assertion/impl/sync.js.map +1 -0
- package/dist/esm/assertion/index.d.ts +15 -0
- package/dist/esm/assertion/index.d.ts.map +1 -0
- package/dist/esm/assertion/index.js +14 -0
- package/dist/esm/assertion/index.js.map +1 -0
- package/dist/esm/assertion/slotify.d.ts +23 -0
- package/dist/esm/assertion/slotify.d.ts.map +1 -0
- package/dist/esm/assertion/slotify.js +63 -0
- package/dist/esm/assertion/slotify.js.map +1 -0
- package/dist/esm/bootstrap.d.ts +175 -0
- package/dist/esm/bootstrap.d.ts.map +1 -0
- package/dist/esm/bootstrap.js +31 -0
- package/dist/esm/bootstrap.js.map +1 -0
- package/dist/esm/constant.d.ts +12 -0
- package/dist/esm/constant.d.ts.map +1 -0
- package/dist/esm/constant.js +30 -0
- package/dist/esm/constant.js.map +1 -0
- package/dist/esm/error.d.ts +20 -0
- package/dist/esm/error.d.ts.map +1 -0
- package/dist/esm/error.js +35 -0
- package/dist/esm/error.js.map +1 -0
- package/dist/esm/expect.d.ts +9 -0
- package/dist/esm/expect.d.ts.map +1 -0
- package/dist/esm/expect.js +210 -0
- package/dist/esm/expect.js.map +1 -0
- package/dist/esm/guards.d.ts +93 -0
- package/dist/esm/guards.d.ts.map +1 -0
- package/dist/esm/guards.js +141 -0
- package/dist/esm/guards.js.map +1 -0
- package/dist/esm/index.d.ts +181 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +20 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/metadata.d.ts +53 -0
- package/dist/esm/metadata.d.ts.map +1 -0
- package/dist/esm/metadata.js +48 -0
- package/dist/esm/metadata.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/schema.d.ts +373 -0
- package/dist/esm/schema.d.ts.map +1 -0
- package/dist/esm/schema.js +434 -0
- package/dist/esm/schema.js.map +1 -0
- package/dist/esm/types.d.ts +79 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +8 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/use.d.ts +4 -0
- package/dist/esm/use.d.ts.map +1 -0
- package/dist/esm/use.js +30 -0
- package/dist/esm/use.js.map +1 -0
- package/dist/esm/util.d.ts +67 -0
- package/dist/esm/util.d.ts.map +1 -0
- package/dist/esm/util.js +182 -0
- package/dist/esm/util.js.map +1 -0
- package/package.json +26 -16
- package/src/bootstrap.ts +1 -1
- package/src/expect.ts +1 -1
- package/src/types.ts +16 -10
- 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="
|
|
3
|
-
<h1 align="center"><
|
|
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
|
-
|
|
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
|
|
26
|
+
But none of them do quite what _BUPKIS_ does. I want an assertion library that prioritizes:
|
|
16
27
|
|
|
17
28
|
- Type safety
|
|
18
|
-
-
|
|
19
|
-
-
|
|
29
|
+
- Uncompromisable extensibility
|
|
30
|
+
- A small API surface
|
|
20
31
|
|
|
21
|
-
|
|
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
|
-
>
|
|
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
|
-
>
|
|
40
|
+
> **Do not confuse _familiarity_ with _usability_.**
|
|
26
41
|
|
|
27
|
-
|
|
42
|
+
The following is a brief overview of the design choices We made to serve these goals.
|
|
28
43
|
|
|
29
|
-
### Natural
|
|
44
|
+
### Natural Language Assertions
|
|
30
45
|
|
|
31
|
-
In
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
88
|
+
> "Preposterous! Codswallop!"
|
|
89
|
+
>
|
|
90
|
+
> ‒the reader and/or more sickos
|
|
72
91
|
|
|
73
|
-
|
|
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
|
-
|
|
94
|
+
#### Conventions of `expect()`
|
|
88
95
|
|
|
89
|
-
|
|
96
|
+
To formalize the conventions at a high level:
|
|
90
97
|
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
102
|
+
- Assertions may have multiple phrases or parameters, but the simplest assertions always look like this:
|
|
96
103
|
|
|
97
|
-
|
|
104
|
+
```ts
|
|
105
|
+
expect(subject, 'phrase');
|
|
106
|
+
```
|
|
98
107
|
|
|
99
|
-
|
|
108
|
+
...and more complex assertions look like this:
|
|
100
109
|
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
```
|
|
110
|
+
```ts
|
|
111
|
+
expect(subject, 'phrase', [parameter?, phrase?, parameter?, ...]);
|
|
112
|
+
```
|
|
104
113
|
|
|
105
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
142
|
+
const stringAssertion = createAssertion(
|
|
143
|
+
z.string(),
|
|
144
|
+
[['to be based', 'to be bussin']],
|
|
145
|
+
z.string(),
|
|
146
|
+
);
|
|
134
147
|
|
|
135
|
-
|
|
148
|
+
const { expect } = use([stringAssertion]);
|
|
136
149
|
|
|
137
|
-
|
|
150
|
+
expect('chat', 'to be based');
|
|
151
|
+
expect('fam', 'to be bussin');
|
|
138
152
|
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
// did you know? includes all builtin assertions!
|
|
154
|
+
expect('skiball lavatory', 'to be a string');
|
|
155
|
+
```
|
|
141
156
|
|
|
142
|
-
>
|
|
157
|
+
> 📒 Registration?
|
|
143
158
|
>
|
|
144
|
-
>
|
|
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
|
-
|
|
179
|
+
**Node.js**: ^20.19.0, ^22.12.0, >=23
|
|
147
180
|
|
|
148
|
-
|
|
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
|
-
|
|
183
|
+
_BUPKIS_ ships as a dual CJS/ESM package.
|
|
151
184
|
|
|
152
|
-
|
|
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
|
|
156
|
-
- [fast-check][]:
|
|
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
|
-
##
|
|
208
|
+
## Why is it called _BUPKIS_?
|
|
159
209
|
|
|
160
|
-
|
|
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 @@
|
|
|
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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"}
|