{"_id":"@omniaura/scenario-sim","_rev":"4-5273ddeaeaa8a9ab1c0611b279298ba4","name":"@omniaura/scenario-sim","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@omniaura/scenario-sim","version":"0.1.0","keywords":["mock","simulator","scenario","msw","sse","websocket","deterministic","seeded","qa","vite-plugin","solid","tanstack-query"],"author":{"name":"omniaura"},"license":"MIT","_id":"@omniaura/scenario-sim@0.1.0","maintainers":[{"name":"futuretrees","email":"peyton@omniaura.ai"}],"homepage":"https://github.com/omniaura/solid-pulse/tree/main/packages/scenario-sim#readme","bugs":{"url":"https://github.com/omniaura/solid-pulse/issues"},"bin":{"scenario-sim":"dist/cli.js"},"dist":{"shasum":"43b9b0e8e4fe72a983c5b553357754af32ea5254","tarball":"https://registry.npmjs.org/@omniaura/scenario-sim/-/scenario-sim-0.1.0.tgz","fileCount":34,"integrity":"sha512-TDaEWde+OBmlD0CYPgOF0vDK14HYVsKaSglp/ea9f6ALUJXpSZhrnhFGiNoMMFHu0m5OQ45TuWt10cankmetmg==","signatures":[{"sig":"MEUCID5cENEEw/ow5a3PhGZCxfGia8AjCMtv/bjMsvkfEnAUAiEAgM6CtTpUoDJpPKj43ipJkdwef3/BzqLYJ6+wqbdfJPc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":546516},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vite":{"types":"./dist/vite.d.ts","import":"./dist/vite.js"},"./pulse":{"types":"./dist/pulse.d.ts","import":"./dist/pulse.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/browser.js"},"./examples":{"types":"./dist/examples.d.ts","import":"./dist/examples.js"}},"gitHead":"93cfcdb3a8eae6432e21332279a8ff5a7421e30c","scripts":{"test":"bun test","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"_npmUser":{"name":"futuretrees","email":"peyton@omniaura.ai"},"repository":{"url":"git+https://github.com/omniaura/solid-pulse.git","type":"git","directory":"packages/scenario-sim"},"_npmVersion":"11.19.1","description":"Deterministic scenario simulator for frontend QA: stateful CRUD routes, seeded RNG, virtual clock, response sequences, faults and overrides, SSE and WebSocket routes with scheduled events and resume, per-run isolation, inspectable event log. Runtime-neutr","directories":{},"_nodeVersion":"26.8.2","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.5.12"},"peerDependencies":{"vite":">=5.0.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/scenario-sim_0.1.0_1789311475587_0.23609127303710942","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@omniaura/scenario-sim","version":"0.1.1","keywords":["mock","simulator","scenario","msw","sse","websocket","deterministic","seeded","qa","vite-plugin","solid","tanstack-query"],"author":{"name":"omniaura"},"license":"MIT","_id":"@omniaura/scenario-sim@0.1.1","maintainers":[{"name":"futuretrees","email":"peyton@omniaura.ai"}],"homepage":"https://github.com/omniaura/scenario-sim#readme","bugs":{"url":"https://github.com/omniaura/scenario-sim/issues"},"bin":{"scenario-sim":"dist/cli.js"},"dist":{"shasum":"3c023c8bde6e4114bde1c4a2ec76c0e7c898ce36","tarball":"https://registry.npmjs.org/@omniaura/scenario-sim/-/scenario-sim-0.1.1.tgz","fileCount":35,"integrity":"sha512-xukw3a+cbwOEz6c5i895bEqTEhtVKdksC4TXEVMxMilMMY6brPG5UYuWl7gR+Kg3kLlr+ewbOyOO0GSHysLglA==","signatures":[{"sig":"MEUCIQCF/7HLBWZmV/7dNM+s3dGaUK9PFxu75GF3DaKk5/LjwQIgT58pNUlHF6UAkNbVThzgXu4dZDUcfljowmQP4QRrTjI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIH1EKE1riBCpo5/KxoYGUOi18F3WragZrNRnwKp7xOHBAiEAqgDQB6o9/ohQGtkPWNDUmixf7/sQSF7Rm12IiLhcSZU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@omniaura%2fscenario-sim@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":550456},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vite":{"types":"./dist/vite.d.ts","import":"./dist/vite.js"},"./pulse":{"types":"./dist/pulse.d.ts","import":"./dist/pulse.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/browser.js"},"./examples":{"types":"./dist/examples.d.ts","import":"./dist/examples.js"}},"gitHead":"18cd6c9f8d2c36c98f8b9b7ba61c1ed9c2793bff","scripts":{"dev":"tsup --watch","test":"bun --conditions=browser --conditions=development test tests && bun --conditions=browser --conditions=development test e2e","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:eef70b15-fae9-4a45-b6e6-3d9d23f11716"}},"repository":{"url":"git+https://github.com/omniaura/scenario-sim.git","type":"git"},"_npmVersion":"11.19.1","description":"Deterministic scenario simulator for frontend QA: stateful CRUD routes, seeded RNG, virtual clock, response sequences, faults and overrides, SSE and WebSocket routes with scheduled events and resume, per-run isolation, inspectable event log. Runtime-neutr","directories":{},"_nodeVersion":"22.23.2","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vite":"^7.0.0","@types/ws":"^8.5.12","happy-dom":"^20.14.5","@types/bun":"latest","typescript":"^5.4.0","@types/node":"^22","semantic-release":"25","@semantic-release/git":"^10.0.1","@happy-dom/global-registrator":"^20.14.5"},"peerDependencies":{"vite":">=5.0.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/scenario-sim_0.1.1_1789325891804_0.906799224707703","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@omniaura/scenario-sim","version":"0.2.0","keywords":["mock","simulator","scenario","msw","sse","websocket","deterministic","seeded","qa","vite-plugin","solid","tanstack-query"],"author":{"name":"omniaura"},"license":"MIT","_id":"@omniaura/scenario-sim@0.2.0","maintainers":[{"name":"futuretrees","email":"peyton@omniaura.ai"}],"homepage":"https://github.com/omniaura/scenario-sim#readme","bugs":{"url":"https://github.com/omniaura/scenario-sim/issues"},"bin":{"scenario-sim":"dist/cli.js"},"dist":{"shasum":"ddef1085e24062d0912dc301fe7ee2cbacbcd460","tarball":"https://registry.npmjs.org/@omniaura/scenario-sim/-/scenario-sim-0.2.0.tgz","fileCount":41,"integrity":"sha512-RAE09OMevUYCYWObgd11UyMBwce2+19b8iHsIoIN6E1K6oP9fdhD958VOqdWToINfvWDVNEZG5epbtiS6b71GA==","signatures":[{"sig":"MEQCIHNSIbyXuk0iJ6xMa5lH15qozFCB7cqoy7XldypO1yqfAiADKRIJF3g4hQk9xvNHkzmba6OWaM8WgjvK1rgNz7/wjQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIACBEdonFlxeRdRNbWRREQU4Yojbw42ehjkT879O5TKVAiEAz63uAN8G+V9I818Ns0Tcd3UxFZoaVkA4sFzY48XxGTI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@omniaura%2fscenario-sim@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":571837},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vite":{"types":"./dist/vite.d.ts","import":"./dist/vite.js"},"./pulse":{"types":"./dist/pulse.d.ts","import":"./dist/pulse.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/browser.js"},"./codegen":{"types":"./dist/codegen.d.ts","import":"./dist/codegen.js"},"./examples":{"types":"./dist/examples.d.ts","import":"./dist/examples.js"}},"gitHead":"a8f14437b74e6940e322c1a175046ab45a97dfdc","scripts":{"dev":"tsup --watch","test":"bun --conditions=browser --conditions=development test tests && bun --conditions=browser --conditions=development test e2e","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:eef70b15-fae9-4a45-b6e6-3d9d23f11716"}},"repository":{"url":"git+https://github.com/omniaura/scenario-sim.git","type":"git"},"_npmVersion":"11.19.1","description":"Deterministic scenario simulator for frontend QA: stateful CRUD routes, seeded RNG, virtual clock, response sequences, faults and overrides, SSE and WebSocket routes with scheduled events and resume, per-run isolation, inspectable event log. Runtime-neutr","directories":{},"_nodeVersion":"22.23.2","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vite":"^7.0.0","@types/ws":"^8.5.12","happy-dom":"^20.14.5","@types/bun":"latest","typescript":"^5.4.0","@types/node":"^22","semantic-release":"25","@semantic-release/git":"^10.0.1","@happy-dom/global-registrator":"^20.14.5"},"peerDependencies":{"vite":">=5.0.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/scenario-sim_0.2.0_1789331972399_0.81462190358827","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@omniaura/scenario-sim@0.3.0","bin":{"scenario-sim":"dist/cli.js"},"bugs":{"url":"https://github.com/omniaura/scenario-sim/issues"},"dist":{"shasum":"1e2e016e9baab2ac9d4a57c07be0b5c7956103c9","tarball":"https://registry.npmjs.org/@omniaura/scenario-sim/-/scenario-sim-0.3.0.tgz","fileCount":46,"integrity":"sha512-tcf8rgZ3e50mnPzHApKp6tHFGVJSDxJQBSqBTpb9vF88Oqxw86htiiRUmTSCO1w5joRJm3dpLRNd/mvKpn+xJA==","signatures":[{"sig":"MEUCICjPKIzLdgxaa0XpOkGmAElnWNE+yqh86i8l5lgzU63JAiEAthAiEXY0KMYSU1G7MvsctNg4Z0L3n1kQqYZbNCXC4uc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFsxGf68MVReD1pVGMXxAH/AJlW0IFoCEm+s390JU0upAiEAs+KhtYayDMKhmYVY+p+HL1vSBHalXS0zBX9/EfKxo50="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@omniaura%2fscenario-sim@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":577672},"main":"./dist/index.js","name":"@omniaura/scenario-sim","type":"module","types":"./dist/index.d.ts","author":{"name":"omniaura"},"module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vite":{"types":"./dist/vite.d.ts","import":"./dist/vite.js"},"./ports":{"types":"./dist/ports.d.ts","import":"./dist/ports.js"},"./pulse":{"types":"./dist/pulse.d.ts","import":"./dist/pulse.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/browser.js"},"./codegen":{"types":"./dist/codegen.d.ts","import":"./dist/codegen.js"},"./examples":{"types":"./dist/examples.d.ts","import":"./dist/examples.js"}},"gitHead":"514b4bed22989c68ce773093f300b1962255b887","license":"MIT","scripts":{"dev":"tsup --watch","test":"bun --conditions=browser --conditions=development test tests && bun --conditions=browser --conditions=development test e2e","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"version":"0.3.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:eef70b15-fae9-4a45-b6e6-3d9d23f11716"}},"homepage":"https://github.com/omniaura/scenario-sim#readme","keywords":["mock","simulator","scenario","msw","sse","websocket","deterministic","seeded","qa","vite-plugin","solid","tanstack-query"],"repository":{"url":"git+https://github.com/omniaura/scenario-sim.git","type":"git"},"_npmVersion":"11.19.1","description":"Deterministic scenario simulator for frontend QA: stateful CRUD routes, seeded RNG, virtual clock, response sequences, faults and overrides, SSE and WebSocket routes with scheduled events and resume, per-run isolation, inspectable event log. Runtime-neutr","directories":{},"maintainers":[{"name":"futuretrees","email":"peyton@omniaura.ai"}],"_nodeVersion":"22.23.2","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vite":"^7.0.0","@types/ws":"^8.5.12","happy-dom":"^20.14.5","@types/bun":"latest","typescript":"^5.4.0","@types/node":"^22","semantic-release":"25","@semantic-release/git":"^10.0.1","@happy-dom/global-registrator":"^20.14.5"},"peerDependencies":{"vite":">=5.0.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/scenario-sim_0.3.0_1789356264668_0.09665301345297705"}}},"time":{"created":"2026-09-13T14:57:55.410Z","modified":"2026-09-14T03:24:25.139Z","0.1.0":"2026-09-13T14:57:55.731Z","0.1.1":"2026-09-13T18:58:11.917Z","0.2.0":"2026-09-13T20:39:32.492Z","0.3.0":"2026-09-14T03:24:24.793Z"},"bugs":{"url":"https://github.com/omniaura/scenario-sim/issues"},"author":{"name":"omniaura"},"license":"MIT","homepage":"https://github.com/omniaura/scenario-sim#readme","keywords":["mock","simulator","scenario","msw","sse","websocket","deterministic","seeded","qa","vite-plugin","solid","tanstack-query"],"repository":{"url":"git+https://github.com/omniaura/scenario-sim.git","type":"git"},"description":"Deterministic scenario simulator for frontend QA: stateful CRUD routes, seeded RNG, virtual clock, response sequences, faults and overrides, SSE and WebSocket routes with scheduled events and resume, per-run isolation, inspectable event log. Runtime-neutr","maintainers":[{"name":"futuretrees","email":"peyton@omniaura.ai"}],"readme":"# @omniaura/scenario-sim\n\n[![npm](https://img.shields.io/npm/v/@omniaura/scenario-sim)](https://www.npmjs.com/package/@omniaura/scenario-sim) · pairs with [`@omniaura/solid-pulse`](https://github.com/omniaura/solid-pulse) (the panel/CLI that drives it) — see [`docs/migrating-header-driven-mocks.md`](docs/migrating-header-driven-mocks.md) to migrate an existing mock.\n\n> A mock backend that behaves like a backend: stateful, seeded, reproducible, streaming — and controllable by humans and agents alike.\n\nMost mock layers return fixtures. `scenario-sim` runs **scenarios**: a seeded world with CRUD state, a virtual clock, response sequences and faults, SSE and WebSocket routes whose events come from state mutations, and a control plane to select/reset/step/inspect everything. The core is runtime-neutral (Web `Request`/`Response`) so the same scenario runs as a Bun/Node server, inside the Vite dev server, or entirely inside the browser.\n\n## Define a scenario\n\n### Smart local ports\n\nThe CLI starts at port 4100 and tries the next port if it is occupied. It prints the actual control URL; pass that URL to subsequent commands with --url (or SCENARIO_SIM_URL). Explicit --port N and --strict-port never silently move. No existing listener is stopped or reused.\n\nProgrammatic servers preserve strict-port behavior by default. Opt in with serveSimulator(sim, { port: 4100, strictPort: false }); the returned port, url and controlUrl describe the real bound listener. Port 0 requests an OS-assigned port. The Node-only @omniaura/scenario-sim/ports export provides withPortFallback(bind, options) for other adapters: retry only EADDRINUSE, bound attempts, and bind the real service instead of probing and releasing a temporary socket. A rejected bind callback must clean up its own partial resources.\n\nBrowser devtools cannot allocate listening ports. The launcher/server selects them before boot and passes the actual API URL to the panel and command adapters. Vite integrations should use Vite's own strictPort: false and read httpServer.address() after listen(), rather than probing an unrelated HTTP server for readiness.\n\n```ts\nimport { defineScenario, crud, route, json, problem, sequence, malformed, ws, sse } from \"@omniaura/scenario-sim\";\n\nexport const happy = defineScenario({\n  name: \"notes-happy\",\n  seed: \"notes-happy\",                       // same seed → same ids, same fixtures, same jitter\n  clock: { mode: \"realtime\" },               // or \"manual\": nothing time-based happens until stepped\n  faults: { latencyMs: 0 },\n  setup({ state, rng, clock, streams }) {\n    for (let i = 0; i < 8; i++) state.insert(\"notes\", { id: rng.id(\"note\"), title: rng.pick(WORDS), done: false, createdAt: clock.iso() });\n    // Mutations → stream events. Routes mutate state; streams subscribe to it.\n    state.on((e) => e.collection === \"notes\" && streams.publish(\"notes\", { type: `notes.${e.kind}`, id: e.id, note: e.record ?? null }));\n  },\n  routes: [\n    ...crud(\"/api/notes\", \"notes\", { create: (body, ctx) => ({ id: ctx.rng.id(\"note\"), title: String(body.title), done: false, createdAt: ctx.clock.iso() }) }),\n    route.get(\"/api/flaky\", (ctx) => (ctx.calls % 3 === 0 ? problem(503, \"flaky upstream\") : json({ ok: true }))),\n    route.get(\"/api/drift\", sequence([() => malformed(\"invalid-json\"), () => malformed(\"schema-drift\"), () => json({ ok: true })])),\n  ],\n  streams: [\n    sse(\"/api/notes/events\", (ctx, stream) => stream.subscribe(\"notes\")),           // resumes from Last-Event-ID\n    ws(\"/api/chat/ws\", {\n      protocols: [\"chat-v1\"],\n      onOpen: (ctx, socket) => socket.send({ type: \"ready\" }),\n      onMessage(ctx, socket, raw) {\n        const frame = JSON.parse(String(raw));\n        if (frame.type === \"subscribe\") {\n          const r = socket.subscribe(frame.topic, { since: frame.sinceEventID });   // replay > sinceEventID, then live\n          socket.send({ type: \"subscribed\", replayed: r.replayed, last: r.last });\n          if (r.missed) socket.send({ type: \"resume.miss\" });\n        }\n      },\n    }),\n  ],\n  actions: { burst: ({ state, rng, args }) => { /* insert N notes */ } },   // POST /__sim/action {name:\"burst\"}\n});\n```\n\nEverything in a scenario is deterministic given the seed: `rng.id()`, `rng.pick()`, jitter, virtual timestamps (`clock.iso()` starts at 2026-01-01T00:00:00Z). Streams carry a per-topic monotonic `eventID` with a bounded replay log; `subscribe(topic, { since })` replays what a reconnecting client missed and reports `missed` when the resume point fell out of the window (so you can send the protocol's \"resync\" frame).\n\n`crud(path, collection, …)` gives you `GET /path`, `POST /path` → 201, `GET/PATCH/PUT/DELETE /path/:id` with 404/422 handling and store events for every mutation.\n\n## Run it\n\n```ts\n// as a server\nimport { Simulator } from \"@omniaura/scenario-sim\";\nimport { serveSimulator } from \"@omniaura/scenario-sim/server\";\nconst sim = new Simulator({ scenarios: [happy, slow, flaky] });\nawait serveSimulator(sim, { port: 4100 });      // http://127.0.0.1:4100/__sim/status\n\n// inside Vite (same origin as the app, WebSocket upgrades included)\nimport scenarioSim from \"@omniaura/scenario-sim/vite\";\nplugins: [scenarioSim({ scenarios: () => import(\"./scenarios\"), match: (p) => p.startsWith(\"/api/\") })]\n\n// entirely in the browser (static builds, demo islands, Playwright against `vite preview`)\nimport { installBrowserSimulator } from \"@omniaura/scenario-sim/browser\";\nconst { sim } = installBrowserSimulator({ scenarios, run: \"tab-1\" });   // patches fetch/WebSocket/EventSource for matching URLs\n```\n\n```bash\nscenario-sim serve ./scenarios.ts --port 4100 --scenario notes-happy\n```\n\n## Control plane (HTTP, CLI, JS, pulse panel — the same operations)\n\n| Operation | HTTP | CLI | pulse command |\n| --- | --- | --- | --- |\n| list scenarios | `GET /__sim/scenarios` | `scenario-sim scenarios` | `scenario.list` |\n| status (scenario, seed, clock, state counts, streams, faults, routes) | `GET /__sim/status?run=` | `status` | `scenario.status` |\n| select scenario (fresh state) | `POST /__sim/select {scenario, seed?, run?}` | `select name= seed=` | `scenario.select` |\n| reset (rebuild from seed) | `POST /__sim/reset {run?, seed?}` | `reset` | `scenario.reset` |\n| step the virtual clock | `POST /__sim/step {ms}` | `step ms=1000` | `scenario.step` |\n| clock mode/speed | `POST /__sim/clock {mode, speed}` | `clock mode=manual` | `scenario.clock` |\n| inspect state | `GET /__sim/state?collection=` | `state collection=notes` | `scenario.state` |\n| mutation log | `GET /__sim/events?since=` | `events` | `scenario.events` |\n| run log | `GET /__sim/log` | `log` | `scenario.log` |\n| open streams + topics | `GET /__sim/streams` | `streams` | `scenario.streams` |\n| disconnect / hard-drop streams | `POST /__sim/streams/disconnect {id\\|topic\\|path\\|all, drop}` | `disconnect all=true drop=true` | `scenario.disconnect` |\n| pause / resume delivery | `POST /__sim/streams/pause {id, paused}` | `pause id=ws_1` | — |\n| publish an event by hand | `POST /__sim/publish {topic, data}` | `publish topic= data=` | `scenario.publish` |\n| overrides (force any endpoint) | `GET/POST/DELETE /__sim/overrides` | `override matcher= status= times=` | `scenario.override`, `.override.clear`, `.overrides` |\n| faults | `POST /__sim/faults {latencyMs, jitterMs, failMode, streamLatencyMs}` | `faults latencyMs=800` | `scenario.faults` |\n| scenario action | `POST /__sim/action {name, args}` | `action name=burst args='{\"count\":5}'` | `scenario.action` |\n| runs (isolation) | `GET /__sim/runs`, `DELETE /__sim/runs?run=` | `runs` | `scenario.runs` |\n\n**Runs** isolate state: pick one per request with `X-Sim-Run` (or the `sim_run` cookie / `?__run=`). Each run has its own store, clock, RNG, streams and faults, so two tabs or two agents never see each other's mutations. A scenario for a *new* run comes from `X-Sim-Scenario` / cookie / `?scenario=` / the default.\n\n**Overrides** win over routing and even over unrouted paths: `{matcher: \"/api/notes\", method: \"GET\", status: 503, times: 1}` fails the next list once, then the endpoint recovers. `malformed: \"invalid-json\" | \"wrong-content-type\" | \"truncated\" | \"empty-200\" | \"html-500\" | \"schema-drift\"` exercises client validation paths.\n\n**Fail modes**: `off`, `data` (5xx everything except `shellPaths`, so the app shell still boots), `all`. Per request, `X-Sim-Latency: <ms>` and `X-Sim-Fail: off|data|all` headers layer on top of the run's faults (the same contract as header-driven console mocks, so a tab or a curl can opt into its own faults).\n\nAttach to solid-pulse so the panel's Scenarios tab and `solid-pulse scenario.*` drive it:\n\n```ts\nimport { attachScenarioCommands } from \"@omniaura/scenario-sim/pulse\";\nattachScenarioCommands(pulse, { kind: \"remote\", controlUrl: \"/__sim\" });     // Vite/server\nattachScenarioCommands(pulse, { kind: \"local\", sim, run: \"tab-1\" });         // in-browser\n```\n\n## Shipped example world\n\n`@omniaura/scenario-sim/examples` exports a generic notes + chat world (no product data): `notes-happy`, `notes-empty`, `notes-slow`, `notes-flaky`, `notes-malformed`, `chat-stream-drop` (the socket is hard-dropped halfway through every reply; clients must reconnect with `sinceEventID`), `notes-manual-clock` (deterministic streaming for tests). Its WebSocket protocol (`chat-v1`: subscribe/send → ready/subscribed/message/stream.start/chat.content/stream.done/resume.miss) is shaped like real chat backends so reconnect logic gets a workout. The package tests are the reference for CRUD → stream events, resume, drop, isolation and manual-clock determinism.\n\n## Notes on transports\n\n- SSE is served as a `Response` with a `ReadableStream`, so it works on every adapter; `Last-Event-ID` (or `?lastEventId=` / `?sinceEventID=`) resumes. Both `GET` and `POST`-opened SSE routes are supported (`method: \"POST\"`).\n- WebSocket upgrades are performed by the adapter (`ws` on Node/Bun, a fake `WebSocket` class in the browser); scenario code only sees `SimSocket`. Browsers drop `Upgrade`/`Sec-WebSocket-Protocol` from `Request` headers, so adapters also send `x-sim-upgrade` / `x-sim-websocket-protocol`, which the engine honours.\n- `drop()` cuts a connection without a close frame (the client sees 1006), unlike `close()`. Stream delivery latency is separate from HTTP latency (`streamLatencyMs`).\n\nMIT © omniaura\n# TypeScript authoring\n\nSee [typed scenarios and file discovery](docs/typescript-scenarios.md) for route-derived\nbuilders, generated static imports, and Vite-loaded configuration files.\n","readmeFilename":"README.md"}