vimonkey 0.2.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vimonkey",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Fuzz testing with auto-shrinking and composable chaos stream transformers for Vitest",
5
5
  "keywords": [
6
6
  "async-generator",
@@ -29,8 +29,7 @@
29
29
  "url": "https://github.com/beorn/vimonkey.git"
30
30
  },
31
31
  "files": [
32
- "src",
33
- "dist"
32
+ "src"
34
33
  ],
35
34
  "type": "module",
36
35
  "module": "./src/index.ts",
@@ -59,5 +58,8 @@
59
58
  },
60
59
  "peerDependencies": {
61
60
  "vitest": ">=4.0.0"
61
+ },
62
+ "engines": {
63
+ "node": ">=23.6.0"
62
64
  }
63
- }
65
+ }
@@ -1,81 +0,0 @@
1
- /**
2
- * Chaos stream transformers for fuzz testing
3
- *
4
- * Composable async iterable transformers that sit between gen() and take()
5
- * to simulate unreliable delivery: dropped messages, reordering, duplicates,
6
- * bursts, delays, and gaps.
7
- *
8
- * @example
9
- * ```typescript
10
- * import { gen, take } from "vimonkey"
11
- * import { drop, reorder, chaos } from "vimonkey/chaos"
12
- *
13
- * const base = gen(picker)
14
- * const chaotic = reorder(drop(base, 0.2, rng), 5, rng)
15
- * for await (const event of take(chaotic, 100)) { ... }
16
- * ```
17
- */
18
- import type { SeededRandom } from "../random.js";
19
- /**
20
- * Skip items with probability `rate`.
21
- * Simulates message loss, queue overflow, network drops.
22
- */
23
- export declare function drop<T>(source: AsyncIterable<T>, rate: number, rng: SeededRandom): AsyncGenerator<T>;
24
- /**
25
- * Buffer up to `windowSize` items, shuffle, yield when buffer is full.
26
- * Simulates out-of-order delivery, non-deterministic event ordering.
27
- */
28
- export declare function reorder<T>(source: AsyncIterable<T>, windowSize: number, rng: SeededRandom): AsyncGenerator<T>;
29
- /**
30
- * With probability `rate`, yield the item twice.
31
- * Simulates duplicate delivery, at-least-once semantics.
32
- */
33
- export declare function duplicate<T>(source: AsyncIterable<T>, rate: number, rng: SeededRandom): AsyncGenerator<T>;
34
- /**
35
- * Collect `burstSize` items, then yield them all at once.
36
- * Simulates bursty delivery, batched network packets.
37
- */
38
- export declare function burst<T>(source: AsyncIterable<T>, burstSize: number): AsyncGenerator<T>;
39
- /**
40
- * Skip the first `count` items.
41
- * Simulates missed events during initialization, late subscriber.
42
- */
43
- export declare function initGap<T>(source: AsyncIterable<T>, count: number): AsyncGenerator<T>;
44
- /**
45
- * Add a random delay before yielding each item.
46
- * Simulates slow I/O, network latency, disk delays.
47
- */
48
- export declare function delay<T>(source: AsyncIterable<T>, minMs: number, maxMs: number, rng: SeededRandom): AsyncGenerator<T>;
49
- /** Configuration for a chaos transformer in the pipeline */
50
- export interface ChaosConfig {
51
- type: string;
52
- params: Record<string, unknown>;
53
- }
54
- /** Registry of transformer factories keyed by config type */
55
- export type ChaosRegistry<T> = Record<string, (source: AsyncIterable<T>, params: Record<string, unknown>, rng: SeededRandom) => AsyncIterable<T>>;
56
- /**
57
- * Compose multiple chaos transformer configs into a single async iterable pipeline.
58
- *
59
- * Uses built-in transformers by default. Pass a custom `registry` to add
60
- * domain-specific transformers (e.g., FS-specific atomicSave, coalesce).
61
- *
62
- * @example
63
- * ```typescript
64
- * const chaotic = chaos(source, [
65
- * { type: "drop", params: { rate: 0.2 } },
66
- * { type: "reorder", params: { windowSize: 5 } },
67
- * ], rng)
68
- * ```
69
- *
70
- * @example Custom registry
71
- * ```typescript
72
- * const chaotic = chaos(source, configs, rng, {
73
- * ...builtinChaosRegistry,
74
- * atomic_save: (s, p, rng) => atomicSave(s, p.rate, rng),
75
- * })
76
- * ```
77
- */
78
- export declare function chaos<T>(source: AsyncIterable<T>, configs: ChaosConfig[], rng: SeededRandom, registry?: ChaosRegistry<T>): AsyncIterable<T>;
79
- /** Re-export the built-in registry for extension */
80
- export declare const builtinChaosRegistry: ChaosRegistry<unknown>;
81
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chaos/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAMhD;;;GAGG;AACH,wBAAuB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,cAAc,CAAC,CAAC,CAAC,CAI3G;AAED;;;GAGG;AACH,wBAAuB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,cAAc,CAAC,CAAC,CAAC,CAcpH;AAED;;;GAGG;AACH,wBAAuB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,cAAc,CAAC,CAAC,CAAC,CAKhH;AAED;;;GAGG;AACH,wBAAuB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAU9F;AAED;;;GAGG;AACH,wBAAuB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAS5F;AAED;;;GAGG;AACH,wBAAuB,KAAK,CAAC,CAAC,EAC5B,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,YAAY,GAChB,cAAc,CAAC,CAAC,CAAC,CAMnB;AAMD,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,6DAA6D;AAC7D,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,MAAM,CACnC,MAAM,EACN,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,YAAY,KAAK,aAAa,CAAC,CAAC,CAAC,CACnG,CAAA;AAYD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,CAAC,EACrB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,OAAO,EAAE,WAAW,EAAE,EACtB,GAAG,EAAE,YAAY,EACjB,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAC1B,aAAa,CAAC,CAAC,CAAC,CAUlB;AAED,oDAAoD;AACpD,eAAO,MAAM,oBAAoB,EAAE,aAAa,CAAC,OAAO,CAAoB,CAAA"}
@@ -1,148 +0,0 @@
1
- /**
2
- * Chaos stream transformers for fuzz testing
3
- *
4
- * Composable async iterable transformers that sit between gen() and take()
5
- * to simulate unreliable delivery: dropped messages, reordering, duplicates,
6
- * bursts, delays, and gaps.
7
- *
8
- * @example
9
- * ```typescript
10
- * import { gen, take } from "vimonkey"
11
- * import { drop, reorder, chaos } from "vimonkey/chaos"
12
- *
13
- * const base = gen(picker)
14
- * const chaotic = reorder(drop(base, 0.2, rng), 5, rng)
15
- * for await (const event of take(chaotic, 100)) { ... }
16
- * ```
17
- */
18
- // ---------------------------------------------------------------------------
19
- // Individual transformers
20
- // ---------------------------------------------------------------------------
21
- /**
22
- * Skip items with probability `rate`.
23
- * Simulates message loss, queue overflow, network drops.
24
- */
25
- export async function* drop(source, rate, rng) {
26
- for await (const item of source) {
27
- if (!rng.bool(rate))
28
- yield item;
29
- }
30
- }
31
- /**
32
- * Buffer up to `windowSize` items, shuffle, yield when buffer is full.
33
- * Simulates out-of-order delivery, non-deterministic event ordering.
34
- */
35
- export async function* reorder(source, windowSize, rng) {
36
- const buffer = [];
37
- for await (const item of source) {
38
- buffer.push(item);
39
- if (buffer.length >= windowSize) {
40
- const shuffled = rng.shuffle(buffer);
41
- buffer.length = 0;
42
- for (const e of shuffled)
43
- yield e;
44
- }
45
- }
46
- if (buffer.length > 0) {
47
- const shuffled = rng.shuffle(buffer);
48
- for (const e of shuffled)
49
- yield e;
50
- }
51
- }
52
- /**
53
- * With probability `rate`, yield the item twice.
54
- * Simulates duplicate delivery, at-least-once semantics.
55
- */
56
- export async function* duplicate(source, rate, rng) {
57
- for await (const item of source) {
58
- yield item;
59
- if (rng.bool(rate))
60
- yield item;
61
- }
62
- }
63
- /**
64
- * Collect `burstSize` items, then yield them all at once.
65
- * Simulates bursty delivery, batched network packets.
66
- */
67
- export async function* burst(source, burstSize) {
68
- const buffer = [];
69
- for await (const item of source) {
70
- buffer.push(item);
71
- if (buffer.length >= burstSize) {
72
- for (const e of buffer)
73
- yield e;
74
- buffer.length = 0;
75
- }
76
- }
77
- for (const e of buffer)
78
- yield e;
79
- }
80
- /**
81
- * Skip the first `count` items.
82
- * Simulates missed events during initialization, late subscriber.
83
- */
84
- export async function* initGap(source, count) {
85
- let skipped = 0;
86
- for await (const item of source) {
87
- if (skipped < count) {
88
- skipped++;
89
- continue;
90
- }
91
- yield item;
92
- }
93
- }
94
- /**
95
- * Add a random delay before yielding each item.
96
- * Simulates slow I/O, network latency, disk delays.
97
- */
98
- export async function* delay(source, minMs, maxMs, rng) {
99
- for await (const item of source) {
100
- const ms = rng.int(minMs, maxMs);
101
- await new Promise((resolve) => setTimeout(resolve, ms));
102
- yield item;
103
- }
104
- }
105
- /** Built-in transformer registry (works for any T) */
106
- const BUILTIN_REGISTRY = {
107
- drop: (s, p, rng) => drop(s, p.rate ?? 0.2, rng),
108
- reorder: (s, p, rng) => reorder(s, p.windowSize ?? 5, rng),
109
- duplicate: (s, p, rng) => duplicate(s, p.rate ?? 0.3, rng),
110
- burst: (s, p) => burst(s, p.burstSize ?? 10),
111
- initGap: (s, p) => initGap(s, p.count ?? 5),
112
- delay: (s, p, rng) => delay(s, p.minMs ?? 1, p.maxMs ?? 5, rng),
113
- };
114
- /**
115
- * Compose multiple chaos transformer configs into a single async iterable pipeline.
116
- *
117
- * Uses built-in transformers by default. Pass a custom `registry` to add
118
- * domain-specific transformers (e.g., FS-specific atomicSave, coalesce).
119
- *
120
- * @example
121
- * ```typescript
122
- * const chaotic = chaos(source, [
123
- * { type: "drop", params: { rate: 0.2 } },
124
- * { type: "reorder", params: { windowSize: 5 } },
125
- * ], rng)
126
- * ```
127
- *
128
- * @example Custom registry
129
- * ```typescript
130
- * const chaotic = chaos(source, configs, rng, {
131
- * ...builtinChaosRegistry,
132
- * atomic_save: (s, p, rng) => atomicSave(s, p.rate, rng),
133
- * })
134
- * ```
135
- */
136
- export function chaos(source, configs, rng, registry) {
137
- const reg = registry ?? BUILTIN_REGISTRY;
138
- let pipeline = source;
139
- for (const config of configs) {
140
- const factory = reg[config.type];
141
- if (factory) {
142
- pipeline = factory(pipeline, config.params, rng);
143
- }
144
- }
145
- return pipeline;
146
- }
147
- /** Re-export the built-in registry for extension */
148
- export const builtinChaosRegistry = BUILTIN_REGISTRY;
package/dist/env.d.ts DELETED
@@ -1,38 +0,0 @@
1
- /**
2
- * TEST_SYS environment handling
3
- *
4
- * Controls the implementation used during tests:
5
- * - fake: Use fake/mock implementations (fast, isolated)
6
- * - real:mem: Real implementation with in-memory storage
7
- * - real:disk: Real implementation with temporary disk storage
8
- */
9
- export type TestSys = "fake" | "real:mem" | "real:disk";
10
- /**
11
- * Get the current TEST_SYS value
12
- *
13
- * @returns The test system type, defaults to 'fake'
14
- *
15
- * @example
16
- * ```typescript
17
- * import { getTestSys } from 'vimonkey'
18
- *
19
- * const sys = getTestSys()
20
- * if (sys === 'fake') {
21
- * // use mock
22
- * } else if (sys === 'real:mem') {
23
- * // use real with in-memory storage
24
- * } else {
25
- * // use real with disk storage
26
- * }
27
- * ```
28
- */
29
- export declare function getTestSys(): TestSys;
30
- /**
31
- * Check if running with real implementation
32
- */
33
- export declare function isRealSys(): boolean;
34
- /**
35
- * Check if running with disk storage
36
- */
37
- export declare function isDiskSys(): boolean;
38
- //# sourceMappingURL=env.d.ts.map
package/dist/env.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAA;AAIvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAepC;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC"}
package/dist/env.js DELETED
@@ -1,52 +0,0 @@
1
- /**
2
- * TEST_SYS environment handling
3
- *
4
- * Controls the implementation used during tests:
5
- * - fake: Use fake/mock implementations (fast, isolated)
6
- * - real:mem: Real implementation with in-memory storage
7
- * - real:disk: Real implementation with temporary disk storage
8
- */
9
- const VALID_VALUES = ["fake", "real:mem", "real:disk"];
10
- /**
11
- * Get the current TEST_SYS value
12
- *
13
- * @returns The test system type, defaults to 'fake'
14
- *
15
- * @example
16
- * ```typescript
17
- * import { getTestSys } from 'vimonkey'
18
- *
19
- * const sys = getTestSys()
20
- * if (sys === 'fake') {
21
- * // use mock
22
- * } else if (sys === 'real:mem') {
23
- * // use real with in-memory storage
24
- * } else {
25
- * // use real with disk storage
26
- * }
27
- * ```
28
- */
29
- export function getTestSys() {
30
- const value = process.env.TEST_SYS;
31
- if (!value) {
32
- return "fake";
33
- }
34
- if (!VALID_VALUES.includes(value)) {
35
- console.warn(`Invalid TEST_SYS value: "${value}". ` + `Valid values: ${VALID_VALUES.join(", ")}. ` + `Defaulting to "fake".`);
36
- return "fake";
37
- }
38
- return value;
39
- }
40
- /**
41
- * Check if running with real implementation
42
- */
43
- export function isRealSys() {
44
- const sys = getTestSys();
45
- return sys === "real:mem" || sys === "real:disk";
46
- }
47
- /**
48
- * Check if running with disk storage
49
- */
50
- export function isDiskSys() {
51
- return getTestSys() === "real:disk";
52
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Fuzz test context using AsyncLocalStorage for auto-tracking
3
- *
4
- * When running inside test.fuzz(), take() automatically records
5
- * yielded values for shrinking and regression testing.
6
- */
7
- import { AsyncLocalStorage } from "node:async_hooks";
8
- export interface FuzzContext {
9
- /** Recorded history of yielded values */
10
- history: unknown[];
11
- /** If replaying, the sequence to replay */
12
- replaySequence: unknown[] | null;
13
- /** Current index in replay sequence */
14
- replayIndex: number;
15
- /** The seed used for this test run */
16
- seed: number;
17
- }
18
- /** AsyncLocalStorage for fuzz test context */
19
- export declare const fuzzContext: AsyncLocalStorage<FuzzContext>;
20
- /**
21
- * Create a new fuzz context
22
- */
23
- export declare function createFuzzContext(seed: number): FuzzContext;
24
- /**
25
- * Create a replay context from a saved sequence
26
- */
27
- export declare function createReplayContext(sequence: unknown[], seed: number): FuzzContext;
28
- /**
29
- * Check if we're currently in a fuzz context
30
- */
31
- export declare function isInFuzzContext(): boolean;
32
- /**
33
- * Get the current fuzz context or undefined
34
- */
35
- export declare function getFuzzContext(): FuzzContext | undefined;
36
- //# sourceMappingURL=context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/fuzz/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEpD,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,OAAO,EAAE,OAAO,EAAE,CAAA;IAClB,2CAA2C;IAC3C,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAChC,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAA;IACnB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,8CAA8C;AAC9C,eAAO,MAAM,WAAW,gCAAuC,CAAA;AAE/D;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAO3D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAOlF;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,SAAS,CAExD"}
@@ -1,43 +0,0 @@
1
- /**
2
- * Fuzz test context using AsyncLocalStorage for auto-tracking
3
- *
4
- * When running inside test.fuzz(), take() automatically records
5
- * yielded values for shrinking and regression testing.
6
- */
7
- import { AsyncLocalStorage } from "node:async_hooks";
8
- /** AsyncLocalStorage for fuzz test context */
9
- export const fuzzContext = new AsyncLocalStorage();
10
- /**
11
- * Create a new fuzz context
12
- */
13
- export function createFuzzContext(seed) {
14
- return {
15
- history: [],
16
- replaySequence: null,
17
- replayIndex: 0,
18
- seed,
19
- };
20
- }
21
- /**
22
- * Create a replay context from a saved sequence
23
- */
24
- export function createReplayContext(sequence, seed) {
25
- return {
26
- history: [],
27
- replaySequence: sequence,
28
- replayIndex: 0,
29
- seed,
30
- };
31
- }
32
- /**
33
- * Check if we're currently in a fuzz context
34
- */
35
- export function isInFuzzContext() {
36
- return fuzzContext.getStore() !== undefined;
37
- }
38
- /**
39
- * Get the current fuzz context or undefined
40
- */
41
- export function getFuzzContext() {
42
- return fuzzContext.getStore();
43
- }
@@ -1,70 +0,0 @@
1
- /**
2
- * Ergonomic generator API for fuzz testing
3
- *
4
- * gen(picker) creates an infinite async generator from a picker.
5
- * take(generator, n) limits iterations and auto-tracks in test.fuzz().
6
- */
7
- import { type SeededRandom } from "../random.js";
8
- /**
9
- * Picker context passed to picker functions
10
- */
11
- export interface PickerContext {
12
- /** Seeded random number generator */
13
- random: SeededRandom;
14
- /** Current iteration (0-indexed) */
15
- iteration: number;
16
- }
17
- /**
18
- * Result type for picker functions - can return single value, array, or iterable
19
- */
20
- type PickerResult<T> = T | T[] | Iterable<T>;
21
- /**
22
- * Sync picker function
23
- */
24
- type SyncPickerFn<T> = (ctx: PickerContext) => PickerResult<T>;
25
- /**
26
- * Async picker function (for AI mode)
27
- */
28
- type AsyncPickerFn<T> = (ctx: PickerContext) => Promise<PickerResult<T>>;
29
- /**
30
- * Picker types:
31
- * - T[] - random from array
32
- * - [number, T][] - weighted random (pairs of [weight, value])
33
- * - (ctx) => T | T[] | Iterable<T> - sync function
34
- * - (ctx) => Promise<T | T[] | Iterable<T>> - async function
35
- */
36
- export type Picker<T> = T[] | [number, T][] | SyncPickerFn<T> | AsyncPickerFn<T>;
37
- /**
38
- * Create an infinite async generator from a picker
39
- *
40
- * @example
41
- * // Random from array
42
- * gen(['j', 'k', 'h', 'l'])
43
- *
44
- * @example
45
- * // Weighted random
46
- * gen([[40, 'j'], [40, 'k'], [20, 'Enter']])
47
- *
48
- * @example
49
- * // Custom picker function
50
- * gen(({ random }) => random.pick(['j', 'k']))
51
- *
52
- * @example
53
- * // Picker returns array (flattened)
54
- * gen(() => ['j', 'j', 'Enter']) // yields: j, j, Enter, j, j, Enter, ...
55
- */
56
- export declare function gen<T>(picker: Picker<T>, seed?: number): AsyncGenerator<T>;
57
- /**
58
- * Limit an async generator to n iterations
59
- *
60
- * When running inside test.fuzz(), automatically tracks yielded values
61
- * for shrinking and regression testing.
62
- *
63
- * @example
64
- * for await (const key of take(gen(['j', 'k']), 100)) {
65
- * await handle.press(key)
66
- * }
67
- */
68
- export declare function take<T>(generator: AsyncIterable<T>, n: number): AsyncGenerator<T>;
69
- export {};
70
- //# sourceMappingURL=gen.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gen.d.ts","sourceRoot":"","sources":["../../src/fuzz/gen.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA8C,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAG5F;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,MAAM,EAAE,YAAY,CAAA;IACpB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;AAE5C;;GAEG;AACH,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,KAAK,YAAY,CAAC,CAAC,CAAC,CAAA;AAE9D;;GAEG;AACH,KAAK,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;AAExE;;;;;;GAMG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;AAmDhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAuB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAYjF;AAED;;;;;;;;;;GAUG;AACH,wBAAuB,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAmBxF"}
package/dist/fuzz/gen.js DELETED
@@ -1,113 +0,0 @@
1
- /**
2
- * Ergonomic generator API for fuzz testing
3
- *
4
- * gen(picker) creates an infinite async generator from a picker.
5
- * take(generator, n) limits iterations and auto-tracks in test.fuzz().
6
- */
7
- import { createSeededRandom, weightedPickFromTuples } from "../random.js";
8
- import { fuzzContext } from "./context.js";
9
- /**
10
- * Type guard for weighted tuples
11
- */
12
- function isWeightedTuple(picker) {
13
- return Array.isArray(picker) && picker.length > 0 && Array.isArray(picker[0]) && typeof picker[0][0] === "number";
14
- }
15
- /**
16
- * Type guard for iterables (excluding strings)
17
- */
18
- function isIterable(value) {
19
- return value !== null && typeof value === "object" && Symbol.iterator in value && typeof value !== "string";
20
- }
21
- /**
22
- * Flatten picker result: single value, array, or iterable → individual items
23
- */
24
- function* flatten(result) {
25
- if (Array.isArray(result)) {
26
- for (const item of result)
27
- yield item;
28
- }
29
- else if (isIterable(result)) {
30
- for (const item of result)
31
- yield item;
32
- }
33
- else {
34
- yield result;
35
- }
36
- }
37
- /**
38
- * Create a picker function from various picker specs
39
- */
40
- function createPicker(picker, random) {
41
- // Function picker - use as-is
42
- if (typeof picker === "function") {
43
- return picker;
44
- }
45
- // Weighted tuple picker
46
- if (isWeightedTuple(picker)) {
47
- const items = picker;
48
- return () => weightedPickFromTuples(items, random.float());
49
- }
50
- // Array picker - random from array
51
- return () => picker[Math.floor(random.float() * picker.length)];
52
- }
53
- /**
54
- * Create an infinite async generator from a picker
55
- *
56
- * @example
57
- * // Random from array
58
- * gen(['j', 'k', 'h', 'l'])
59
- *
60
- * @example
61
- * // Weighted random
62
- * gen([[40, 'j'], [40, 'k'], [20, 'Enter']])
63
- *
64
- * @example
65
- * // Custom picker function
66
- * gen(({ random }) => random.pick(['j', 'k']))
67
- *
68
- * @example
69
- * // Picker returns array (flattened)
70
- * gen(() => ['j', 'j', 'Enter']) // yields: j, j, Enter, j, j, Enter, ...
71
- */
72
- export async function* gen(picker, seed) {
73
- // Use context seed if available, otherwise provided seed or Date.now()
74
- const ctx = fuzzContext.getStore();
75
- const random = createSeededRandom(seed ?? ctx?.seed ?? Date.now());
76
- const pick = createPicker(picker, random);
77
- let iteration = 0;
78
- while (true) {
79
- const pickerCtx = { random, iteration: iteration++ };
80
- const result = await pick(pickerCtx);
81
- yield* flatten(result);
82
- }
83
- }
84
- /**
85
- * Limit an async generator to n iterations
86
- *
87
- * When running inside test.fuzz(), automatically tracks yielded values
88
- * for shrinking and regression testing.
89
- *
90
- * @example
91
- * for await (const key of take(gen(['j', 'k']), 100)) {
92
- * await handle.press(key)
93
- * }
94
- */
95
- export async function* take(generator, n) {
96
- const ctx = fuzzContext.getStore();
97
- let i = 0;
98
- // Replay mode: yield from saved sequence instead of generator
99
- if (ctx?.replaySequence) {
100
- while (i < n && ctx.replayIndex < ctx.replaySequence.length) {
101
- yield ctx.replaySequence[ctx.replayIndex++];
102
- i++;
103
- }
104
- return;
105
- }
106
- // Normal mode: yield from generator, optionally record
107
- for await (const item of generator) {
108
- if (i++ >= n)
109
- break;
110
- ctx?.history.push(item); // Track if in fuzz context
111
- yield item;
112
- }
113
- }
@@ -1,57 +0,0 @@
1
- /**
2
- * Fuzz testing API
3
- *
4
- * @example
5
- * ```typescript
6
- * import { test, gen, take, createSeededRandom } from 'vimonkey/fuzz'
7
- *
8
- * // Simple random from array
9
- * test('navigation', async () => {
10
- * const handle = await run(<Board />, { cols: 80, rows: 24 })
11
- * for await (const key of take(gen(['j','k','h','l']), 100)) {
12
- * await handle.press(key)
13
- * expect(handle.locator('[data-cursor]').count()).toBe(1)
14
- * }
15
- * })
16
- *
17
- * // Weighted random
18
- * test('weighted', async () => {
19
- * for await (const key of take(gen([[40,'j'], [40,'k'], [20,'Enter']]), 100)) {
20
- * await handle.press(key)
21
- * }
22
- * })
23
- *
24
- * // Stateful with closure
25
- * test('stateful', async () => {
26
- * const handle = await app.run(<Board />)
27
- * const random = createSeededRandom(Date.now())
28
- *
29
- * const keys = async function*() {
30
- * while (true) {
31
- * const state = handle.store.getState()
32
- * yield state.cursor === 0 ? random.pick(['j','l']) : random.pick(['j','k','h','l'])
33
- * }
34
- * }
35
- *
36
- * for await (const key of take(keys(), 100)) {
37
- * await handle.press(key)
38
- * }
39
- * })
40
- *
41
- * // With auto-tracking and shrinking
42
- * test.fuzz('cursor invariants', async () => {
43
- * for await (const key of take(gen(['j','k']), 100)) {
44
- * await handle.press(key)
45
- * expect(...) // On failure: shrinks, saves to __fuzz_cases__/
46
- * }
47
- * })
48
- * ```
49
- */
50
- export { gen, take, type Picker, type PickerContext } from "./gen.js";
51
- export { test, FuzzError, type FuzzTestOptions } from "./test-fuzz.js";
52
- export { describe, expect, it, beforeAll, afterAll, beforeEach, afterEach } from "./test-fuzz.js";
53
- export { fuzzContext, getFuzzContext, isInFuzzContext, createFuzzContext, createReplayContext, type FuzzContext, } from "./context.js";
54
- export { shrinkSequence, formatShrinkResult, type ShrinkOptions, type ShrinkResult } from "./shrink.js";
55
- export { saveCase, loadCases, loadCasesForTest, deleteCase, clearCases, getFuzzCasesDir, type SavedCase, } from "./regression.js";
56
- export { createSeededRandom, weightedPickFromTuples, parseSeed, parseRepeats, deriveSeeds, type SeededRandom, } from "../random.js";
57
- //# sourceMappingURL=index.d.ts.map