crashlab 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +390 -0
- package/dist/cli.cjs +6 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +25 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# CrashLab — Deterministic Simulation Testing for Node.js
|
|
2
|
+
|
|
3
|
+
**Find 1-in-a-million race conditions in milliseconds, not months.**
|
|
4
|
+
|
|
5
|
+
CrashLab runs your application code inside a fully controlled simulation: virtual time, seeded randomness, and deterministic I/O scheduling. Every concurrency bug that would normally require weeks of load testing to surface can be reproduced on demand, debugged with a single seed, and guarded against regression forever.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The Problem with Conventional Testing
|
|
10
|
+
|
|
11
|
+
Imagine a payment handler:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
async function charge(userId: string, amount: number) {
|
|
15
|
+
const balance = await db.query('SELECT balance FROM accounts WHERE id = $1', [userId]);
|
|
16
|
+
if (balance.rows[0].balance < amount) throw new Error('Insufficient funds');
|
|
17
|
+
await stripe.charge(userId, amount); // ~200ms network call
|
|
18
|
+
await db.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [amount, userId]);
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
A **double-payment race condition** is buried here. Two concurrent requests both read the same balance, both pass the guard, and both charge the card — but only one debits the account. This bug requires two requests to arrive within a ~200ms window. In a Jest or Vitest test suite, your async calls resolve sequentially; the window never opens and the test always passes.
|
|
23
|
+
|
|
24
|
+
**CrashLab compresses virtual time and shuffles I/O resolution order.** Across 1,000 seeds it explores every possible interleaving of those two awaits. Seed 847 opens the exact window. You get a failing test, a full timeline, and a replay command — before this ships.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
**`scenarios/charge.scenario.ts`** — the file your team ships alongside the code:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import type { SimEnv } from 'crashlab';
|
|
34
|
+
|
|
35
|
+
export default async function chargeScenario(env: SimEnv) {
|
|
36
|
+
// Mock Stripe: 200ms virtual latency, deterministic response
|
|
37
|
+
env.http.mock('POST https://api.stripe.com/v1/charges', {
|
|
38
|
+
status: 200,
|
|
39
|
+
body: JSON.stringify({ id: 'ch_sim', status: 'succeeded' }),
|
|
40
|
+
latency: 200,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Seed Postgres with a user who has $100
|
|
44
|
+
env.pg.seedData('accounts', [{ id: 'user_1', balance: 100 }]);
|
|
45
|
+
await env.pg.ready();
|
|
46
|
+
|
|
47
|
+
// Fire two concurrent charge requests at the same virtual instant
|
|
48
|
+
const req = () =>
|
|
49
|
+
fetch('http://localhost:3000/charge', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
body: JSON.stringify({ userId: 'user_1', amount: 100 }),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const [r1, r2] = await Promise.all([req(), req()]);
|
|
55
|
+
|
|
56
|
+
// Advance virtual clock past the Stripe latency — both callbacks resolve
|
|
57
|
+
await env.clock.advance(250);
|
|
58
|
+
|
|
59
|
+
const result = await env.pg.query<{ balance: number }>(
|
|
60
|
+
'SELECT balance FROM accounts WHERE id = $1', ['user_1']
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
env.timeline.record({
|
|
64
|
+
timestamp: env.clock.now(),
|
|
65
|
+
type: 'ASSERT',
|
|
66
|
+
detail: `final balance: ${result.rows[0].balance}`,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// The balance must be 0 — any other value is a double-charge
|
|
70
|
+
if (result.rows[0].balance !== 0) {
|
|
71
|
+
throw new Error(`Double charge detected! Balance is ${result.rows[0].balance}, expected 0`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**`crashlab.config.js`** — wire it to the harness:
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
import { Simulation } from 'crashlab';
|
|
80
|
+
import { resolve } from 'node:path';
|
|
81
|
+
|
|
82
|
+
const sim = new Simulation({ timeout: 15_000 });
|
|
83
|
+
|
|
84
|
+
sim.scenario('double charge guard', resolve('./scenarios/charge.scenario.ts'));
|
|
85
|
+
|
|
86
|
+
export default sim;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## How It Works — The Three Pillars
|
|
92
|
+
|
|
93
|
+
### 1. Virtual Clock
|
|
94
|
+
`Date.now()`, `performance.now()`, `setTimeout`, and `setInterval` are replaced with a fully controllable `VirtualClock`. Time only moves when you call `env.clock.advance(ms)`. A scenario that would take 200ms in production takes **0 wall-clock milliseconds** in simulation.
|
|
95
|
+
|
|
96
|
+
### 2. Seeded PRNG
|
|
97
|
+
`Math.random()` and `crypto.randomBytes()` are replaced with a deterministic Mulberry32-based generator seeded per-run. Given the same seed, every random value produced during the scenario is identical — every time, on every machine.
|
|
98
|
+
|
|
99
|
+
### 3. I/O Scheduler
|
|
100
|
+
Concurrent `await` calls that resolve at the same virtual timestamp are queued and **shuffled by the seed** before being delivered. Seed 0 might resolve DB-then-Stripe. Seed 847 resolves Stripe-then-DB. Running 1,000 seeds explores 1,000 distinct interleavings of every concurrent I/O operation in your code.
|
|
101
|
+
|
|
102
|
+
These three pillars together mean: **if a race condition is possible, a seed will find it.**
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## CLI Usage
|
|
107
|
+
|
|
108
|
+
CrashLab has two operating modes and a replay command:
|
|
109
|
+
|
|
110
|
+
| | `run` | `hunt` |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| **What you specify** | Seed count | Time budget |
|
|
113
|
+
| **When it stops** | After N seeds (or first failure by default) | On first failure or timeout |
|
|
114
|
+
| **What it outputs** | Pass/fail summary with counts | Live per-seed status, full failure report |
|
|
115
|
+
| **Memory** | Only failures retained | Never accumulates passing results |
|
|
116
|
+
| **When to use** | CI / regression suites | Local debugging, "find me a bug" sessions |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### `crashlab run` — fixed seed count, CI mode
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npx crashlab run --seeds=1000
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Stops at the **first failure** by default (`stopOnFirstFailure: true`). To collect all failures across all seeds:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
npx crashlab run --seeds=1000 --stop-on-first-failure=false
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Output:**
|
|
133
|
+
```
|
|
134
|
+
✗ [seed=847] double charge guard: Double charge detected! Balance is 100, expected 0
|
|
135
|
+
Timeline:
|
|
136
|
+
[0ms] START: Scenario: double charge guard, seed: 847
|
|
137
|
+
[0ms] DB: SELECT balance → 100 (request A)
|
|
138
|
+
[0ms] DB: SELECT balance → 100 (request B)
|
|
139
|
+
[200ms] HTTP: POST /v1/charges → succeeded (request A)
|
|
140
|
+
[200ms] HTTP: POST /v1/charges → succeeded (request B)
|
|
141
|
+
[200ms] DB: UPDATE balance = 0 (request A)
|
|
142
|
+
[200ms] DB: UPDATE balance = 0 (request B)
|
|
143
|
+
[200ms] ASSERT: final balance: 100
|
|
144
|
+
[200ms] FAIL: Double charge detected! Balance is 100, expected 0
|
|
145
|
+
|
|
146
|
+
0/1000 passed, 1 failed
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### `crashlab hunt` — time-budget mode, local debugging
|
|
152
|
+
|
|
153
|
+
Hunt mode runs as many seeds as it can fit within a time budget and stops the moment it finds a failure. There is no seed count — just run until you find something.
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
npx crashlab hunt ./scenarios/charge.scenario.ts
|
|
157
|
+
npx crashlab hunt ./scenarios/charge.scenario.ts --timeout=10m
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Duration format: `30s` | `5m` | `1h`. Default: `5m`.
|
|
161
|
+
|
|
162
|
+
**Live output:**
|
|
163
|
+
```
|
|
164
|
+
Hunting: charge.scenario.ts (timeout: 5m)
|
|
165
|
+
|
|
166
|
+
[OK ] Seed 482910341
|
|
167
|
+
[OK ] Seed 482910342
|
|
168
|
+
[OK ] Seed 482910343
|
|
169
|
+
[FAIL] Seed 482910344
|
|
170
|
+
|
|
171
|
+
────────────────────────────────────────────────────────────
|
|
172
|
+
FAILURE FOUND after 4 seeds in 2s
|
|
173
|
+
Scenario : charge.scenario
|
|
174
|
+
Seed : 482910344
|
|
175
|
+
Error : Double charge detected! Balance is 100, expected 0
|
|
176
|
+
|
|
177
|
+
Timeline:
|
|
178
|
+
[0ms] START: ...
|
|
179
|
+
...
|
|
180
|
+
|
|
181
|
+
Replay command:
|
|
182
|
+
crashlab replay --seed=482910344 --scenario="charge.scenario" --config=crashlab.config.js
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
If no failure is found within the budget:
|
|
186
|
+
```
|
|
187
|
+
No failure found after 1247 seeds in 5m 0s (timeout after 5m 0s).
|
|
188
|
+
Your scenario may be correct, or the bug requires a specific condition not yet explored.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Press **Ctrl+C** to stop early — CrashLab finishes the current seed, discards its result (it may have been interrupted mid-flight), and exits cleanly with code `0`:
|
|
192
|
+
```
|
|
193
|
+
No failure found after 1247 seeds in 2m 14s (interrupted by Ctrl+C).
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### `crashlab replay` — reproduce a specific failure
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
npx crashlab replay --seed=847 --scenario="double charge guard"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
> [!TIP]
|
|
205
|
+
> **The same seed always produces the same failure.** You can share `--seed=847` with a colleague, add it to a CI regression suite, or step through it in a debugger. The entire execution is deterministic.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### `sim.run()` API — programmatic use
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// Default: stop on first failure, only store failing results
|
|
213
|
+
const result = await sim.run({ seeds: 1000 });
|
|
214
|
+
// result.passed → boolean
|
|
215
|
+
// result.passes → number of seeds that passed
|
|
216
|
+
// result.failures → ScenarioResult[] (only failures; passing results are not retained)
|
|
217
|
+
|
|
218
|
+
// Opt out of early stop to collect all failures
|
|
219
|
+
const result = await sim.run({ seeds: 1000, stopOnFirstFailure: false });
|
|
220
|
+
|
|
221
|
+
// Replay always returns the full result regardless of pass/fail
|
|
222
|
+
const replay = await sim.replay({ seed: 847, scenario: 'double charge guard' });
|
|
223
|
+
// replay.passed → boolean
|
|
224
|
+
// replay.result → ScenarioResult (always present, including timeline)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Custom config path
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
npx crashlab run --config=./tests/sim/crashlab.config.js --seeds=500
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Installation
|
|
236
|
+
|
|
237
|
+
### Batteries-included (recommended)
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
npm install --save-dev crashlab
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`crashlab` includes every mock: Postgres (PGlite), MongoDB (MongoMemoryServer), Redis (ioredis-mock), HTTP, TCP, virtual clock, PRNG, filesystem. Install this and you are done.
|
|
244
|
+
|
|
245
|
+
### À la carte
|
|
246
|
+
|
|
247
|
+
If you only need a subset of the mocks — say, virtual time and HTTP interception with no database overhead — install the lightweight engine and only the layers you need:
|
|
248
|
+
|
|
249
|
+
```sh
|
|
250
|
+
npm install --save-dev @crashlab/core @crashlab/clock @crashlab/http-proxy
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`@crashlab/core` ships the `Simulation` class, CLI runner, and worker engine. It has **no dependency on PGlite, MongoDB, or Redis**. Mocks that are not installed simply appear as `null` on `env.pg`, `env.redis`, and `env.mongo`.
|
|
254
|
+
|
|
255
|
+
Available sub-packages:
|
|
256
|
+
|
|
257
|
+
| Package | What it provides |
|
|
258
|
+
|---|---|
|
|
259
|
+
| `@crashlab/core` | `Simulation` class, CLI, worker engine |
|
|
260
|
+
| `@crashlab/clock` | `VirtualClock` |
|
|
261
|
+
| `@crashlab/random` | `SeededRandom` |
|
|
262
|
+
| `@crashlab/scheduler` | `Scheduler` |
|
|
263
|
+
| `@crashlab/http-proxy` | `HttpInterceptor` |
|
|
264
|
+
| `@crashlab/tcp` | `TcpInterceptor` |
|
|
265
|
+
| `@crashlab/filesystem` | `VirtualFS` |
|
|
266
|
+
| `@crashlab/pg-mock` | `PgMock` (PGlite) |
|
|
267
|
+
| `@crashlab/redis-mock` | `RedisMock` (ioredis-mock) |
|
|
268
|
+
| `@crashlab/mongo` | `MongoMock` (MongoMemoryServer) |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Support Matrix
|
|
273
|
+
|
|
274
|
+
| Protocol / Driver | CrashLab Support | Notes |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| **PostgreSQL** | ✅ Full | PGlite in-process — wire-protocol compatible |
|
|
277
|
+
| **MongoDB** | ✅ Full | Proxied to MongoMemoryServer per-run |
|
|
278
|
+
| **Redis** | ✅ Full | In-process RESP protocol handler |
|
|
279
|
+
| **HTTP / Fetch** | ✅ Full | `http.request`, `https.request`, `globalThis.fetch` |
|
|
280
|
+
| **Prisma** | ✅ Compatible | Loopback TCP servers on 5432 / 27017 / 6379 |
|
|
281
|
+
| **ioredis / mongoose / pg** | ✅ Compatible | Client-side module patch — zero config |
|
|
282
|
+
| **MySQL** | ❌ Not supported | Port 3306 throws `CrashLabUnsupportedProtocolError` in v1.0 |
|
|
283
|
+
|
|
284
|
+
> [!NOTE]
|
|
285
|
+
> **Prisma compatibility:** CrashLab binds real loopback TCP servers on 127.0.0.1:5432, :6379, and :27017, so Prisma's out-of-process Rust query engine connects to the same mocks as your in-process drivers. If a real database is running on those ports, CrashLab records a `WARNING` in the timeline and falls back to the client-side interceptor.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Honest Limitations
|
|
290
|
+
|
|
291
|
+
CrashLab is precise about what it controls. Senior engineers deserve a straight answer:
|
|
292
|
+
|
|
293
|
+
| Limitation | Reason |
|
|
294
|
+
|---|---|
|
|
295
|
+
| **Native C++ addons** | Native code runs outside the V8 sandbox. `require('better-sqlite3')` or `bcrypt` bypass all module patches. Use pure-JS alternatives in scenarios, or wrap them in an HTTP service that CrashLab can mock. |
|
|
296
|
+
| **Engine-level microtask interleaving** | V8's microtask queue is not observable from userland. CrashLab controls macro-task and I/O scheduling; it cannot reorder `Promise.resolve()` chains that don't yield to the event loop. |
|
|
297
|
+
| **`worker_threads` spawned by your app** | Child workers inherit real globals, not CrashLab's patched ones. Scenarios should avoid code paths that spawn workers; use the simulation environment's own concurrency tools instead. |
|
|
298
|
+
| **True wall-clock timers** | Any library that calls the real `setTimeout` before CrashLab installs its patch (e.g. at module evaluation time) will use real time. Import order matters. |
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Scenario API Reference
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import type { SimEnv } from 'crashlab';
|
|
306
|
+
|
|
307
|
+
export default async function myScenario(env: SimEnv) {
|
|
308
|
+
env.clock // VirtualClock — advance(), now(), setTimeout(), setInterval()
|
|
309
|
+
env.random // SeededRandom — next() → [0,1), nextInt(n)
|
|
310
|
+
env.scheduler // Scheduler — enqueueCompletion(), runTick()
|
|
311
|
+
env.http // HttpInterceptor — mock(), calls, unmatched handling
|
|
312
|
+
env.tcp // TcpInterceptor — mock(), addLocalServer()
|
|
313
|
+
env.pg // PgMock — seedData(), query(), ready(), createHandler()
|
|
314
|
+
env.redis // RedisMock — seedData(), createHandler()
|
|
315
|
+
env.mongo // MongoMock — find(), drop(), createHandler()
|
|
316
|
+
env.fs // VirtualFS — readFileSync(), writeFileSync(), existsSync()
|
|
317
|
+
env.faults // FaultInjector — diskFull(), clockSkew(), networkPartition()
|
|
318
|
+
env.timeline // Timeline — record({ timestamp, type, detail })
|
|
319
|
+
env.seed // number — the current run's seed value
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Architecture
|
|
326
|
+
|
|
327
|
+
### Package split
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
npm install crashlab ← batteries-included (re-exports @crashlab/core + all mocks)
|
|
331
|
+
npm install @crashlab/core ← lightweight engine only (no PGlite / MongoDB / Redis)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`@crashlab/core` declares the heavy mock packages as **optional peer dependencies**. If they are not installed `env.pg`, `env.redis`, and `env.mongo` are `null`. The `crashlab` wrapper lists them as required dependencies, guaranteeing they are always present.
|
|
335
|
+
|
|
336
|
+
### Runtime flow
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
Simulation.run({ seeds: N }) ← lives in @crashlab/core
|
|
340
|
+
│
|
|
341
|
+
├─ _startMongo() → MongoMemoryServer (skipped if not installed)
|
|
342
|
+
│
|
|
343
|
+
└─ for each seed × scenario
|
|
344
|
+
└─ Worker thread (isolated globals)
|
|
345
|
+
├─ createEnv(seed) → VirtualClock, PRNG, Scheduler, lightweight mocks
|
|
346
|
+
│ ├─ try import('@crashlab/pg-mock') → PgMock | null
|
|
347
|
+
│ ├─ try import('@crashlab/redis-mock') → RedisMock | null
|
|
348
|
+
│ └─ try import('@crashlab/mongo') → MongoMock | null
|
|
349
|
+
├─ install patches → Date.now, Math.random, net.createConnection, fetch
|
|
350
|
+
├─ import(scenario) → dynamic ES module load (file-based)
|
|
351
|
+
├─ await scenarioFn(env)
|
|
352
|
+
├─ timeline.toString() → posted to parent
|
|
353
|
+
└─ finally: uninstall patches, drop mongo db, worker.terminate()
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Each worker is fully isolated. Patches applied inside one worker never leak to the main thread or sibling workers. After `run()` returns there are zero zombie workers and zero mongod instances.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Contributing
|
|
361
|
+
|
|
362
|
+
```sh
|
|
363
|
+
git clone https://github.com/your-org/crashlab
|
|
364
|
+
npm install
|
|
365
|
+
npm run build
|
|
366
|
+
npm test # vitest — 176 tests
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
All packages live under `packages/`. The monorepo uses npm workspaces + tsup for building. PRs must pass `npm test` with zero failures.
|
|
370
|
+
|
|
371
|
+
### Releasing
|
|
372
|
+
|
|
373
|
+
This repo uses [Changesets](https://github.com/changesets/changesets) for versioning and publishing. All packages are versioned together (fixed group).
|
|
374
|
+
|
|
375
|
+
```sh
|
|
376
|
+
# 1. Describe your change (prompts for bump type + summary)
|
|
377
|
+
npx changeset
|
|
378
|
+
|
|
379
|
+
# 2. Apply version bumps — updates all package.json versions and cross-package pins
|
|
380
|
+
npm run version
|
|
381
|
+
|
|
382
|
+
# 3. Build and publish to npm
|
|
383
|
+
npm run release
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## License
|
|
389
|
+
|
|
390
|
+
MIT
|
package/dist/cli.cjs
ADDED
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport '@crashlab/core/cli';\n"],"mappings":";;;;AACA,iBAAO;","names":[]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,oBAAoB,CAAC"}
|
package/dist/cli.js
ADDED
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport '@crashlab/core/cli';\n"],"mappings":";;;AACA,OAAO;","names":[]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
15
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
16
|
+
|
|
17
|
+
// src/index.ts
|
|
18
|
+
var index_exports = {};
|
|
19
|
+
module.exports = __toCommonJS(index_exports);
|
|
20
|
+
__reExport(index_exports, require("@crashlab/core"), module.exports);
|
|
21
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
22
|
+
0 && (module.exports = {
|
|
23
|
+
...require("@crashlab/core")
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from '@crashlab/core';\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,2BAAd;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from '@crashlab/core';\n"],"mappings":";AAAA,cAAc;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crashlab",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"crashlab",
|
|
7
|
+
"simulation",
|
|
8
|
+
"deterministic",
|
|
9
|
+
"testing",
|
|
10
|
+
"race-conditions",
|
|
11
|
+
"nodejs",
|
|
12
|
+
"virtual-clock",
|
|
13
|
+
"fault-injection"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "dist/index.cjs",
|
|
17
|
+
"module": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"bin": {
|
|
20
|
+
"crashlab": "dist/cli.js"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"require": "./dist/index.cjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup && tsc --build tsconfig.json --force",
|
|
34
|
+
"clean": "rimraf dist",
|
|
35
|
+
"prepack": "npm run build",
|
|
36
|
+
"build:pkg": "tsup && tsc --build tsconfig.json --force"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@crashlab/core": "0.1.0",
|
|
40
|
+
"@crashlab/mongo": "0.1.0",
|
|
41
|
+
"@crashlab/pg-mock": "0.1.0",
|
|
42
|
+
"@crashlab/redis-mock": "0.1.0",
|
|
43
|
+
"mongodb-memory-server": "^10.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|