bupkis 0.0.2

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.
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared constants and symbols used throughout the library.
3
+ *
4
+ * This module defines unique symbols and constants that are used internally for
5
+ * marking and identifying special values and types within the assertion
6
+ * system.
7
+ *
8
+ * @packageDocumentation
9
+ * @internal
10
+ */
11
+
12
+ /**
13
+ * Symbol flagging the value as a Bupkis-created string literal, which will be
14
+ * omitted from the parameters to an `AssertionImpl`.
15
+ *
16
+ * @internal
17
+ */
18
+
19
+ export const kStringLiteral: unique symbol = Symbol('bupkis:string-literal');
20
+
21
+ /**
22
+ * Symbol used to flag an `AssertionError` as our own.
23
+ *
24
+ * @internal
25
+ */
26
+
27
+ export const kBupkisAssertionError: unique symbol = Symbol('bupkis-error');
28
+
29
+ /**
30
+ * Symbol used to flag a `NegatedAssertionError`
31
+ *
32
+ * @internal
33
+ */
34
+
35
+ export const kBupkisNegatedAssertionError: unique symbol = Symbol(
36
+ 'bupkis-negated-error',
37
+ );
package/src/error.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Error types thrown by _BUPKIS_, including {@link AssertionError}.
3
+ *
4
+ * @privateRemarks
5
+ * Other custom errors should go here.
6
+ * @packageDocumentation
7
+ */
8
+
9
+ import { AssertionError as NodeAssertionError } from 'node:assert';
10
+
11
+ import {
12
+ kBupkisAssertionError,
13
+ kBupkisNegatedAssertionError,
14
+ } from './constant.js';
15
+ import { isA } from './guards.js';
16
+
17
+ /**
18
+ * _BUPKIS_' s custom `AssertionError` class, which is just a thin wrapper
19
+ * around Node.js' {@link NodeAssertionError AssertionError}.
20
+ *
21
+ * @public
22
+ */
23
+ export class AssertionError extends NodeAssertionError {
24
+ [kBupkisAssertionError] = true;
25
+
26
+ static isAssertionError(err: unknown): err is AssertionError {
27
+ return (
28
+ isA(err, NodeAssertionError) && Object.hasOwn(err, kBupkisAssertionError)
29
+ );
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Error type used internally to catch failed negated assertions.
35
+ *
36
+ * @internal
37
+ */
38
+ export class NegatedAssertionError extends AssertionError {
39
+ [kBupkisNegatedAssertionError] = true;
40
+
41
+ static isNegatedAssertionError(err: unknown): err is NegatedAssertionError {
42
+ return (
43
+ isA(err, AssertionError) &&
44
+ Object.hasOwn(err, kBupkisNegatedAssertionError)
45
+ );
46
+ }
47
+ }
package/src/expect.ts ADDED
@@ -0,0 +1,364 @@
1
+ import Debug from 'debug';
2
+ import { inspect } from 'util';
3
+
4
+ import {
5
+ type Expect,
6
+ type ExpectAsync,
7
+ type ExpectAsyncFunction,
8
+ type ExpectAsyncProps,
9
+ type ExpectFunction,
10
+ type ExpectSyncProps,
11
+ } from './api.js';
12
+ import {
13
+ type AnyAsyncAssertion,
14
+ type AnyAsyncAssertions,
15
+ type AnySyncAssertion,
16
+ type AnySyncAssertions,
17
+ type AssertionAsync,
18
+ type AssertionImplAsync,
19
+ type AssertionImplSync,
20
+ type AssertionParts,
21
+ type AssertionSlots,
22
+ type AssertionSync,
23
+ type ParsedResult,
24
+ type ParsedValues,
25
+ } from './assertion/assertion-types.js';
26
+ import { createAssertion, createAsyncAssertion } from './assertion/create.js';
27
+ import { AssertionError, NegatedAssertionError } from './error.js';
28
+ import { isAssertionFailure, isString } from './guards.js';
29
+ import { createUse } from './use.js';
30
+
31
+ const debug = Debug('bupkis:expect');
32
+
33
+ export function createExpectAsyncFunction<
34
+ T extends AnyAsyncAssertions,
35
+ U extends ExpectAsync<AnyAsyncAssertions>,
36
+ >(assertions: T, expect: U): ExpectAsyncFunction<T & U['assertions']>;
37
+ export function createExpectAsyncFunction<T extends AnyAsyncAssertions>(
38
+ assertions: T,
39
+ ): ExpectAsyncFunction<T>;
40
+ export function createExpectAsyncFunction<
41
+ T extends AnyAsyncAssertions,
42
+ U extends ExpectAsync<AnyAsyncAssertions>,
43
+ >(assertions: T, expect?: U) {
44
+ debug(
45
+ 'Creating expectAsync function with %d assertions',
46
+ assertions.length + (expect?.assertions.length ?? 0),
47
+ );
48
+ const expectAsyncFunction = async (...args: readonly unknown[]) => {
49
+ await Promise.resolve();
50
+ const [isNegated, processedArgs] = maybeProcessNegation(args);
51
+ const candidates: Array<{
52
+ assertion: AnyAsyncAssertion;
53
+ parseResult: ParsedResult<AssertionParts>;
54
+ }> = [];
55
+ for (const assertion of [...(expect?.assertions ?? []), ...assertions]) {
56
+ const parseResult = await assertion.parseValuesAsync(processedArgs);
57
+ const { exactMatch, parsedValues, success } = parseResult;
58
+
59
+ if (success) {
60
+ if (exactMatch) {
61
+ return executeAsync(
62
+ assertion,
63
+ parsedValues,
64
+ [...args],
65
+ expectAsyncFunction,
66
+ isNegated,
67
+ parseResult,
68
+ );
69
+ }
70
+ candidates.push({ assertion, parseResult });
71
+ }
72
+ }
73
+ if (candidates.length) {
74
+ const { assertion, parseResult } = candidates[0]!;
75
+ return executeAsync(
76
+ assertion as any,
77
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
78
+ parseResult.parsedValues as any,
79
+ [...args],
80
+ expectAsyncFunction,
81
+ isNegated,
82
+ parseResult,
83
+ );
84
+ }
85
+ throwInvalidParametersError(args);
86
+ };
87
+ return expectAsyncFunction;
88
+ }
89
+
90
+ export function createExpectSyncFunction<
91
+ T extends AnySyncAssertions,
92
+ U extends Expect<AnySyncAssertions>,
93
+ >(assertions: T, expect: U): ExpectFunction<T & U['assertions']>;
94
+
95
+ export function createExpectSyncFunction<T extends AnySyncAssertions>(
96
+ assertions: T,
97
+ ): ExpectFunction<T>;
98
+ export function createExpectSyncFunction<
99
+ T extends AnySyncAssertions,
100
+ U extends Expect<AnySyncAssertions>,
101
+ >(assertions: T, expect?: U) {
102
+ debug(
103
+ 'Creating expect function with %d assertions',
104
+ assertions.length + (expect?.assertions.length ?? 0),
105
+ );
106
+ const expectFunction = (...args: readonly unknown[]) => {
107
+ const [isNegated, processedArgs] = maybeProcessNegation(args);
108
+ const candidates: Array<{
109
+ assertion: AnySyncAssertion;
110
+ parseResult: ParsedResult<AssertionParts>;
111
+ }> = [];
112
+ for (const assertion of [...(expect?.assertions ?? []), ...assertions]) {
113
+ const parseResult = assertion.parseValues(processedArgs);
114
+ const { exactMatch, parsedValues, success } = parseResult;
115
+
116
+ if (success) {
117
+ if (exactMatch) {
118
+ return execute(
119
+ assertion,
120
+ parsedValues,
121
+ [...args],
122
+ expectFunction,
123
+ isNegated,
124
+ parseResult,
125
+ );
126
+ }
127
+ candidates.push({ assertion, parseResult });
128
+ }
129
+ }
130
+ if (candidates.length) {
131
+ const { assertion, parseResult } = candidates[0]!;
132
+ return execute(
133
+ assertion as any,
134
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
135
+ parseResult.parsedValues as any,
136
+ [...args],
137
+ expectFunction,
138
+ isNegated,
139
+ parseResult,
140
+ );
141
+ }
142
+ throwInvalidParametersError(args);
143
+ };
144
+ return expectFunction;
145
+ }
146
+
147
+ /**
148
+ * Executes an assertion with optional negation logic.
149
+ *
150
+ * @privateRemarks
151
+ * This is here because `Assertion` doesn't know anything about negation and
152
+ * probably shouldn't.
153
+ * @param assertion - The assertion to execute
154
+ * @param parsedValues - Parsed values for the assertion
155
+ * @param args - Original arguments passed to expect
156
+ * @param stackStartFn - Function for stack trace management
157
+ * @param isNegated - Whether the assertion should be negated
158
+ */
159
+ const execute = <
160
+ T extends AssertionSync<Parts, AssertionImplSync<Parts>, Slots>,
161
+ Parts extends AssertionParts,
162
+ Slots extends AssertionSlots<Parts>,
163
+ >(
164
+ assertion: T,
165
+ parsedValues: ParsedValues<Parts>,
166
+ args: unknown[],
167
+ stackStartFn: (...args: any[]) => any,
168
+ isNegated: boolean,
169
+ parseResult?: ParsedResult<Parts>,
170
+ ): void => {
171
+ if (!isNegated) {
172
+ return assertion.execute(parsedValues, args, stackStartFn, parseResult);
173
+ }
174
+
175
+ try {
176
+ debug('Executing negated assertion: %s', assertion);
177
+ const result = assertion.execute(
178
+ parsedValues,
179
+ args,
180
+ stackStartFn,
181
+ parseResult,
182
+ );
183
+ if (isAssertionFailure(result)) {
184
+ throw new NegatedAssertionError({
185
+ actual: result.actual,
186
+ expected: result.expected,
187
+ message:
188
+ result.message ??
189
+ `Expected assertion ${assertion} to fail (due to negation), but it passed`,
190
+ stackStartFn,
191
+ });
192
+ }
193
+ // If we reach here, the assertion passed but we expected it to fail
194
+ throw new NegatedAssertionError({
195
+ message: `Expected assertion to fail (due to negation), but it passed: ${assertion}`,
196
+ stackStartFn,
197
+ });
198
+ } catch (error) {
199
+ // Check if this is the negation error we just threw
200
+ if (NegatedAssertionError.isNegatedAssertionError(error)) {
201
+ // This is our negation error, re-throw it
202
+ throw error;
203
+ }
204
+
205
+ if (AssertionError.isAssertionError(error)) {
206
+ // The assertion failed as expected for negation - this is success
207
+ return;
208
+ }
209
+
210
+ debug('Non-assertion error thrown during negated assertion: %O', error);
211
+ // Re-throw non-assertion errors (like TypeErrors, etc.)
212
+ throw error;
213
+ }
214
+ };
215
+
216
+ /**
217
+ * Executes an assertion with optional negation logic (async version).
218
+ *
219
+ * @privateRemarks
220
+ * This is here because `Assertion` doesn't know anything about negation and
221
+ * probably shouldn't.
222
+ * @param assertion - The assertion to execute
223
+ * @param parsedValues - Parsed values for the assertion
224
+ * @param args - Original arguments passed to expectAsync
225
+ * @param stackStartFn - Function for stack trace management
226
+ * @param isNegated - Whether the assertion should be negated
227
+ */
228
+ export const executeAsync = async <
229
+ T extends AssertionAsync<Parts, AssertionImplAsync<Parts>, Slots>,
230
+ Parts extends AssertionParts,
231
+ Slots extends AssertionSlots<Parts>,
232
+ >(
233
+ assertion: T,
234
+ parsedValues: ParsedValues<Parts>,
235
+ args: unknown[],
236
+ stackStartFn: (...args: any[]) => any,
237
+ isNegated: boolean,
238
+ parseResult?: ParsedResult<Parts>,
239
+ ): Promise<void> => {
240
+ if (!isNegated) {
241
+ return assertion.executeAsync(
242
+ parsedValues,
243
+ args,
244
+ stackStartFn,
245
+ parseResult,
246
+ );
247
+ }
248
+
249
+ try {
250
+ debug('Executing negated async assertion: %s', assertion);
251
+ await assertion.executeAsync(parsedValues, args, stackStartFn, parseResult);
252
+ // If we reach here, the assertion passed but we expected it to fail
253
+ throw new NegatedAssertionError({
254
+ message: `Expected assertion to fail (due to negation), but it passed: ${assertion}`,
255
+ stackStartFn,
256
+ });
257
+ } catch (error) {
258
+ // Check if this is the negation error we just threw
259
+ if (NegatedAssertionError.isNegatedAssertionError(error)) {
260
+ // This is our negation error, re-throw it
261
+ throw error;
262
+ }
263
+
264
+ if (AssertionError.isAssertionError(error)) {
265
+ // The assertion failed as expected for negation - this is success
266
+ return;
267
+ }
268
+ debug(
269
+ 'Non-assertion error thrown during negated async assertion: %O',
270
+ error,
271
+ );
272
+ // Re-throw non-assertion errors (like TypeErrors, etc.)
273
+ throw error;
274
+ }
275
+ };
276
+
277
+ /**
278
+ * @internal
279
+ */
280
+ const maybeProcessNegation = (
281
+ args: readonly unknown[],
282
+ ): [isNegated: boolean, processedArgs: readonly unknown[]] => {
283
+ let isNegated = false;
284
+ let processedArgs = args;
285
+
286
+ if (args.length >= 2 && isString(args[1])) {
287
+ const { cleanedPhrase, isNegated: detected } = detectNegation(args[1]);
288
+ if (detected) {
289
+ isNegated = true;
290
+ processedArgs = [args[0], cleanedPhrase, ...args.slice(2)];
291
+ }
292
+ }
293
+ return [isNegated, processedArgs];
294
+ };
295
+
296
+ /**
297
+ * @internal
298
+ */
299
+ const throwInvalidParametersError = (args: readonly unknown[]): never => {
300
+ const inspectedArgs = inspect(args, { depth: 1 });
301
+ debug(`Invalid arguments. No assertion matched: ${inspectedArgs}`);
302
+ throw new TypeError(
303
+ `Invalid arguments. No assertion matched: ${inspectedArgs}`,
304
+ );
305
+ };
306
+
307
+ /**
308
+ * Detects if an assertion phrase starts with "not " and returns the cleaned
309
+ * phrase.
310
+ *
311
+ * @param phrase - The assertion phrase to check
312
+ * @returns Object with `isNegated` flag and `cleanedPhrase`
313
+ */
314
+
315
+ const detectNegation = (
316
+ phrase: string,
317
+ ): {
318
+ cleanedPhrase: string;
319
+ isNegated: boolean;
320
+ } => {
321
+ if (phrase.startsWith('not ')) {
322
+ return {
323
+ cleanedPhrase: phrase.substring(4), // Remove "not "
324
+ isNegated: true,
325
+ };
326
+ }
327
+ return {
328
+ cleanedPhrase: phrase,
329
+ isNegated: false,
330
+ };
331
+ };
332
+
333
+ const fail = (reason?: string): never => {
334
+ throw new AssertionError({ message: reason });
335
+ };
336
+
337
+ export function createBaseExpect<
338
+ T extends AnySyncAssertions,
339
+ U extends AnyAsyncAssertions,
340
+ >(syncAssertions: T, asyncAssertions: U, type: 'sync'): ExpectSyncProps<T, U>;
341
+ export function createBaseExpect<
342
+ T extends AnySyncAssertions,
343
+ U extends AnyAsyncAssertions,
344
+ >(syncAssertions: T, asyncAssertions: U, type: 'async'): ExpectAsyncProps<U, T>;
345
+ export function createBaseExpect<
346
+ T extends AnySyncAssertions,
347
+ U extends AnyAsyncAssertions,
348
+ >(syncAssertions: T, asyncAssertions: U, type: 'async' | 'sync') {
349
+ return type === 'sync'
350
+ ? {
351
+ assertions: syncAssertions,
352
+ createAssertion,
353
+ createAsyncAssertion,
354
+ fail,
355
+ use: createUse(syncAssertions, asyncAssertions),
356
+ }
357
+ : {
358
+ assertions: asyncAssertions,
359
+ createAssertion,
360
+ createAsyncAssertion,
361
+ fail,
362
+ use: createUse(syncAssertions, asyncAssertions),
363
+ };
364
+ }
package/src/guards.ts ADDED
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Type guard functions and runtime type checking utilities.
3
+ *
4
+ * This module provides various type guard functions for runtime type checking,
5
+ * including guards for Zod schemas, constructors, Promise-like objects, and
6
+ * assertion parts. These are used throughout the library for safe type
7
+ * narrowing and validation.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+
12
+ import { type Primitive } from 'type-fest';
13
+ import { z } from 'zod';
14
+
15
+ import type {
16
+ AssertionFailure,
17
+ AssertionPart,
18
+ PhraseLiteralChoice,
19
+ } from './assertion/assertion-types.js';
20
+ import type { Constructor } from './types.js';
21
+
22
+ /**
23
+ * Returns true if the given value looks like a Zod schema (v4), determined by
24
+ * the presence of an internal `def.type` field.
25
+ *
26
+ * Note: This relies on Zod's internal shape and is intended for runtime
27
+ * discrimination within this library.
28
+ *
29
+ * @template T
30
+ * @param value - Value to test
31
+ * @returns Whether the value is Zod-like
32
+ */
33
+ export const isZodType = (value: unknown): value is z.ZodType =>
34
+ !!(
35
+ value &&
36
+ typeof value === 'object' &&
37
+ 'def' in value &&
38
+ value.def &&
39
+ typeof value.def === 'object' &&
40
+ 'type' in value.def
41
+ );
42
+
43
+ /**
44
+ * Returns true if the given value is a {@link z.ZodPromise} schema.
45
+ *
46
+ * @param value - Value to test
47
+ * @returns `true` if the value is a `ZodPromise` schema; `false` otherwise
48
+ */
49
+ export const isZodPromise = (value: unknown): value is z.ZodPromise =>
50
+ isZodType(value) && value.def.type === 'promise';
51
+
52
+ /**
53
+ * Checks if a value is "promise-like", meaning it is a "thenable" object.
54
+ *
55
+ * @param value - Value to test
56
+ * @returns `true` if the value is promise-like, `false` otherwise
57
+ */
58
+ export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
59
+ !!(
60
+ value &&
61
+ typeof value === 'object' &&
62
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
63
+ typeof (value as any).then === 'function'
64
+ );
65
+
66
+ /**
67
+ * Returns true if the given value is a constructable function (i.e., a class).
68
+ *
69
+ * This may be the only way we can determine, at runtime, if a function is a
70
+ * constructor without actually calling it.
71
+ *
72
+ * @param fn - Function to test
73
+ * @returns Whether the function is constructable
74
+ */
75
+ export const isConstructable = (fn: any): fn is Constructor => {
76
+ try {
77
+ // this will throw if there is no `[[construct]]` slot.. or so I've heard.
78
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
79
+ new new Proxy(fn, { construct: () => ({}) })();
80
+ return true;
81
+ } catch {
82
+ return false;
83
+ }
84
+ };
85
+
86
+ /**
87
+ * Type guard for a boolean value
88
+ *
89
+ * @param value Value to check
90
+ * @returns `true` if the value is a boolean, `false` otherwise
91
+ */
92
+ export const isBoolean = (value: unknown): value is boolean =>
93
+ typeof value === 'boolean';
94
+
95
+ /**
96
+ * Type guard for a function value
97
+ *
98
+ * @param value Value to check
99
+ * @returns `true` if the value is a function, `false` otherwise
100
+ */
101
+ export const isFunction = (value: unknown): value is (...args: any[]) => any =>
102
+ typeof value === 'function';
103
+
104
+ const AssertionFailureSchema: z.ZodType<AssertionFailure> = z.object({
105
+ actual: z
106
+ .unknown()
107
+ .optional()
108
+ .describe('The actual value or description of what actually occurred'),
109
+ expected: z
110
+ .unknown()
111
+ .optional()
112
+ .describe(
113
+ 'The expected value or description of what was expected to occur',
114
+ ),
115
+ message: z
116
+ .string()
117
+ .optional()
118
+ .describe('A human-readable message describing the failure'),
119
+ });
120
+
121
+ export const isAssertionFailure = (value: unknown): value is AssertionFailure =>
122
+ AssertionFailureSchema.safeParse(value).success;
123
+
124
+ export const isAsyncFunction = (
125
+ value: unknown,
126
+ ): value is (...args: any[]) => Promise<any> =>
127
+ isFunction(value) && value.constructor.name === 'AsyncFunction';
128
+
129
+ /**
130
+ * Type guard for a string value
131
+ *
132
+ * @param value Value to check
133
+ * @returns `true` if the value is a string, `false` otherwise
134
+ */
135
+ export const isString = (value: unknown): value is string =>
136
+ typeof value === 'string';
137
+
138
+ /**
139
+ * Type guard for a non-null object value
140
+ *
141
+ * @param value Value to check
142
+ * @returns `true` if the value is an object and not null, `false` otherwise
143
+ */
144
+ export const isNonNullObject = (value: unknown): value is object =>
145
+ typeof value === 'object' && value !== null;
146
+
147
+ /**
148
+ * Type guard for a null or non-object value
149
+ *
150
+ * @param value Value to check
151
+ * @returns `true` if the value is null or not an object, `false` otherwise
152
+ */
153
+ export const isNullOrNonObject = (value: unknown): value is null | Primitive =>
154
+ typeof value !== 'object' || value === null;
155
+
156
+ /**
157
+ * Type guard for a {@link PhraseLiteralChoice}, which is a tuple of strings.
158
+ *
159
+ * @param value Assertion part to check
160
+ * @returns `true` if the part is a `PhraseLiteralChoice`, `false` otherwise
161
+ * @internal
162
+ */
163
+ export const isPhraseLiteralChoice = (
164
+ value: AssertionPart,
165
+ ): value is PhraseLiteralChoice =>
166
+ Array.isArray(value) && value.every(isPhraseLiteral);
167
+
168
+ /**
169
+ * Type guard for a {@link PhraseLiteral}, which is just a string that does not
170
+ * begin with `not `.
171
+ *
172
+ * @param value Assertion part to check
173
+ * @returns `true` if the part is a `PhraseLiteral`, `false` otherwise
174
+ * @internal
175
+ */
176
+ export const isPhraseLiteral = (value: AssertionPart): value is string =>
177
+ isString(value) && !value.startsWith('not ');
178
+
179
+ export type PrimitiveTypeName =
180
+ | 'bigint'
181
+ | 'boolean'
182
+ | 'function'
183
+ | 'null'
184
+ | 'number'
185
+ | 'object'
186
+ | 'string'
187
+ | 'symbol'
188
+ | 'undefined';
189
+
190
+ export type PrimitiveTypeNameToType<T extends PrimitiveTypeName> =
191
+ T extends 'undefined'
192
+ ? undefined
193
+ : T extends 'object'
194
+ ? null | object
195
+ : T extends 'function'
196
+ ? (...args: any[]) => any
197
+ : T extends 'string'
198
+ ? string
199
+ : T extends 'number'
200
+ ? number
201
+ : T extends 'boolean'
202
+ ? boolean
203
+ : T extends 'bigint'
204
+ ? bigint
205
+ : T extends 'symbol'
206
+ ? symbol
207
+ : never;
208
+
209
+ export const isType = <T extends PrimitiveTypeName>(
210
+ a: unknown,
211
+ b: T,
212
+ ): a is PrimitiveTypeNameToType<T> => {
213
+ return typeof a === b;
214
+ };
215
+
216
+ export const isA = <T extends Constructor>(
217
+ value: unknown,
218
+ ctor: T,
219
+ ): value is InstanceType<T> => {
220
+ return isNonNullObject(value) && value instanceof ctor;
221
+ };
222
+
223
+ export const isError = (value: unknown): value is Error => isA(value, Error);
package/src/index.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Main entry point for the Bupkis assertion library.
3
+ *
4
+ * This module exports all public APIs including the main `expect` function,
5
+ * asynchronous `expectAsync` function, assertion creation utilities, type
6
+ * guards, schema definitions, utility functions, and error types.
7
+ *
8
+ * @module bupkis
9
+ */
10
+
11
+ import { expect as sacrificialExpect } from './bootstrap.js';
12
+ export type * from './api.js';
13
+
14
+ export * as assertion from './assertion/index.js';
15
+ export { expect, expectAsync } from './bootstrap.js';
16
+
17
+ export * as error from './error.js';
18
+ export * as guards from './guards.js';
19
+
20
+ export type * as metadata from './metadata.js';
21
+ export * as schema from './schema.js';
22
+ export type * as types from './types.js';
23
+ export * as util from './util.js';
24
+
25
+ export { z } from 'zod/v4';
26
+
27
+ export { createAssertion, createAsyncAssertion, fail, use };
28
+ const { createAssertion, createAsyncAssertion, fail, use, ..._rest } =
29
+ sacrificialExpect;