@omniaura/scenario-sim 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 +118 -0
- package/dist/browser.d.ts +81 -0
- package/dist/browser.js +303 -0
- package/dist/browser.js.map +1 -0
- package/dist/chunk-44ZHRYG6.js +959 -0
- package/dist/chunk-44ZHRYG6.js.map +1 -0
- package/dist/chunk-7FWVWBX6.js +380 -0
- package/dist/chunk-7FWVWBX6.js.map +1 -0
- package/dist/chunk-7HBAY3QY.js +133 -0
- package/dist/chunk-7HBAY3QY.js.map +1 -0
- package/dist/chunk-KU4W4SKO.js +1236 -0
- package/dist/chunk-KU4W4SKO.js.map +1 -0
- package/dist/cli.js +131 -0
- package/dist/cli.js.map +1 -0
- package/dist/engine-2w32ngB2.d.ts +897 -0
- package/dist/engine-HE7MEQHD.js +15 -0
- package/dist/engine-HE7MEQHD.js.map +1 -0
- package/dist/examples.d.ts +38 -0
- package/dist/examples.js +233 -0
- package/dist/examples.js.map +1 -0
- package/dist/index.d.ts +322 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/pulse.d.ts +40 -0
- package/dist/pulse.js +80 -0
- package/dist/pulse.js.map +1 -0
- package/dist/scenario--CvzkzX_.d.ts +637 -0
- package/dist/server.d.ts +29 -0
- package/dist/server.js +14 -0
- package/dist/server.js.map +1 -0
- package/dist/vite.d.ts +27 -0
- package/dist/vite.js +48 -0
- package/dist/vite.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/rng.ts","../src/core/clock.ts","../src/core/store.ts","../src/core/streams.ts","../src/core/router.ts","../src/core/faults.ts","../src/core/scenario.ts","../src/core/engine.ts"],"sourcesContent":["/**\n * Seeded PRNG (sfc32). Every scenario run owns one, seeded from the run's\n * seed, so ids, latencies, jitter and fixture data replay identically for the\n * same seed — never use Math.random or wall-clock ids inside a scenario.\n */\n\nexport class Rng {\n private a: number;\n private b: number;\n private c: number;\n private d: number;\n /** Draws so far; useful when asserting determinism across runs. */\n draws = 0;\n\n constructor(public readonly seed: string | number) {\n const h = hashSeed(String(seed));\n this.a = h[0]!;\n this.b = h[1]!;\n this.c = h[2]!;\n this.d = h[3]!;\n for (let i = 0; i < 12; i++) this.next();\n this.draws = 0;\n }\n\n /** Uniform in [0, 1). */\n next(): number {\n this.draws++;\n this.a |= 0;\n this.b |= 0;\n this.c |= 0;\n this.d |= 0;\n const t = (((this.a + this.b) | 0) + this.d) | 0;\n this.d = (this.d + 1) | 0;\n this.a = this.b ^ (this.b >>> 9);\n this.b = (this.c + (this.c << 3)) | 0;\n this.c = (this.c << 21) | (this.c >>> 11);\n this.c = (this.c + t) | 0;\n return (t >>> 0) / 4294967296;\n }\n\n /** Integer in [min, max] inclusive. */\n int(min: number, max: number): number {\n if (max < min) [min, max] = [max, min];\n return min + Math.floor(this.next() * (max - min + 1));\n }\n\n float(min = 0, max = 1): number {\n return min + this.next() * (max - min);\n }\n\n chance(probability: number): boolean {\n return this.next() < probability;\n }\n\n pick<T>(items: readonly T[]): T {\n if (items.length === 0) throw new RangeError(\"pick() from an empty list\");\n return items[this.int(0, items.length - 1)]!;\n }\n\n shuffle<T>(items: readonly T[]): T[] {\n const out = [...items];\n for (let i = out.length - 1; i > 0; i--) {\n const j = this.int(0, i);\n [out[i], out[j]] = [out[j]!, out[i]!];\n }\n return out;\n }\n\n /** Deterministic opaque id: `<prefix>_<8 base36 chars>`. */\n id(prefix = \"id\"): string {\n let s = \"\";\n while (s.length < 8) s += Math.floor(this.next() * 36 ** 4).toString(36).padStart(4, \"0\");\n return `${prefix}_${s.slice(0, 8)}`;\n }\n\n /** RFC-4122-shaped id (version 4 bits set) from the seeded stream. */\n uuid(): string {\n const hex = () => Math.floor(this.next() * 0x10000).toString(16).padStart(4, \"0\");\n const v = hex();\n return `${hex()}${hex()}-${hex()}-4${hex().slice(1)}-${((parseInt(v[0]!, 16) & 0x3) | 0x8).toString(16)}${v.slice(1)}-${hex()}${hex()}${hex()}`;\n }\n\n /** Fork a child generator with a derived seed (stable across runs). */\n fork(label: string): Rng {\n return new Rng(`${this.seed}/${label}`);\n }\n}\n\n/** MurmurHash3-style mixing of a string into four 32-bit words. */\nfunction hashSeed(str: string): [number, number, number, number] {\n let h1 = 1779033703, h2 = 3144134277, h3 = 1013904242, h4 = 2773480762;\n for (let i = 0; i < str.length; i++) {\n const k = str.charCodeAt(i);\n h1 = h2 ^ Math.imul(h1 ^ k, 597399067);\n h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);\n h3 = h4 ^ Math.imul(h3 ^ k, 951274213);\n h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);\n }\n h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);\n h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);\n h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);\n h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);\n return [(h1 ^ h2 ^ h3 ^ h4) >>> 0, (h2 ^ h1) >>> 0, (h3 ^ h1) >>> 0, (h4 ^ h1) >>> 0];\n}\n","/**\n * Virtual clock. Everything time-related in a scenario — response delays,\n * scheduled stream events, disconnects, timestamps in fixtures — goes through\n * the run's clock so it can be replayed:\n *\n * realtime timers fire on real setTimeout scaled by `speed` (default 1)\n * manual nothing fires until `step(ms)` advances the clock (tests, and\n * \"step\" in the control plane / panel / CLI)\n *\n * `now()` is virtual milliseconds since the run started, and `wall()` maps it\n * onto a fixed epoch so fixture timestamps are stable across runs.\n */\n\nexport type ClockMode = \"realtime\" | \"manual\";\n\nexport interface ClockTimer {\n id: number;\n at: number;\n label: string;\n cancel(): void;\n}\n\ninterface Entry {\n id: number;\n at: number;\n label: string;\n fn: () => void;\n handle: ReturnType<typeof setTimeout> | null;\n}\n\n/** 2026-01-01T00:00:00Z — the virtual epoch every run starts at. */\nexport const VIRTUAL_EPOCH = Date.UTC(2026, 0, 1);\n\nexport class VirtualClock {\n private entries = new Map<number, Entry>();\n private nextId = 1;\n private base = 0;\n private startedReal = typeof performance !== \"undefined\" ? performance.now() : Date.now();\n private manualNow = 0;\n\n constructor(public mode: ClockMode = \"realtime\", public speed = 1, public readonly epoch = VIRTUAL_EPOCH) {}\n\n /** Virtual ms since the run started. */\n now(): number {\n if (this.mode === \"manual\") return this.manualNow;\n const real = (typeof performance !== \"undefined\" ? performance.now() : Date.now()) - this.startedReal;\n return this.base + real * this.speed;\n }\n\n /** Virtual wall-clock ms (epoch + now). */\n wall(): number {\n return this.epoch + Math.round(this.now());\n }\n\n iso(offsetMs = 0): string {\n return new Date(this.wall() + offsetMs).toISOString();\n }\n\n after(ms: number, fn: () => void, label = \"timer\"): ClockTimer {\n const id = this.nextId++;\n const at = this.now() + Math.max(0, ms);\n const entry: Entry = { id, at, label, fn, handle: null };\n this.entries.set(id, entry);\n if (this.mode === \"realtime\") {\n entry.handle = setTimeout(() => this.fire(entry), Math.max(0, ms) / (this.speed || 1));\n }\n return { id, at, label, cancel: () => this.cancel(id) };\n }\n\n /** Promise that resolves after `ms` virtual milliseconds. */\n sleep(ms: number, label = \"sleep\"): Promise<void> {\n return new Promise((resolve) => void this.after(ms, resolve, label));\n }\n\n cancel(id: number) {\n const e = this.entries.get(id);\n if (!e) return;\n if (e.handle) clearTimeout(e.handle);\n this.entries.delete(id);\n }\n\n /** Manual mode: advance by `ms`, firing due timers in order. Returns what fired. */\n step(ms: number): { now: number; fired: { id: number; at: number; label: string }[] } {\n if (this.mode !== \"manual\") throw new Error(\"step() requires manual clock mode\");\n const target = this.manualNow + Math.max(0, ms);\n const fired: { id: number; at: number; label: string }[] = [];\n for (;;) {\n let next: Entry | null = null;\n for (const e of this.entries.values()) if (e.at <= target && (!next || e.at < next.at || (e.at === next.at && e.id < next.id))) next = e;\n if (!next) break;\n this.manualNow = Math.max(this.manualNow, next.at);\n fired.push({ id: next.id, at: next.at, label: next.label });\n this.fire(next);\n }\n this.manualNow = target;\n return { now: this.manualNow, fired };\n }\n\n /** Switch modes; pending timers are re-armed (realtime) or parked (manual). */\n setMode(mode: ClockMode) {\n if (mode === this.mode) return;\n const now = this.now();\n this.mode = mode;\n if (mode === \"manual\") {\n this.manualNow = now;\n for (const e of this.entries.values()) {\n if (e.handle) clearTimeout(e.handle);\n e.handle = null;\n }\n } else {\n this.base = now;\n this.startedReal = typeof performance !== \"undefined\" ? performance.now() : Date.now();\n for (const e of this.entries.values()) e.handle = setTimeout(() => this.fire(e), Math.max(0, e.at - now) / (this.speed || 1));\n }\n }\n\n pending(): { id: number; at: number; label: string }[] {\n return [...this.entries.values()].sort((a, b) => a.at - b.at || a.id - b.id).map(({ id, at, label }) => ({ id, at, label }));\n }\n\n clear() {\n for (const e of this.entries.values()) if (e.handle) clearTimeout(e.handle);\n this.entries.clear();\n }\n\n private fire(e: Entry) {\n if (!this.entries.has(e.id)) return;\n this.entries.delete(e.id);\n try {\n e.fn();\n } catch (err) {\n console.warn(`[scenario-sim] timer \"${e.label}\" threw`, err);\n }\n }\n}\n","/**\n * Per-run state. Collections of records keyed by id, with every mutation\n * appended to a bounded event log and delivered to listeners — that is how\n * \"a PATCH publishes a stream event\" is wired without coupling routes to\n * transports: routes mutate state, stream routes subscribe to state.\n */\n\nexport type Record_ = Record<string, unknown> & { id: string };\n\nexport type StoreEventKind = \"insert\" | \"update\" | \"remove\" | \"clear\" | \"custom\";\n\nexport interface StoreEvent {\n seq: number;\n /** Virtual clock ms when it happened. */\n t: number;\n kind: StoreEventKind;\n collection: string;\n id?: string;\n record?: Record_;\n previous?: Record_;\n /** For `custom` events. */\n name?: string;\n data?: unknown;\n}\n\nexport type StoreListener = (event: StoreEvent) => void;\n\nexport class Store {\n private collections = new Map<string, Map<string, Record_>>();\n private listeners = new Set<StoreListener>();\n private log: StoreEvent[] = [];\n private seq = 0;\n\n constructor(private now: () => number, private logLimit = 5000) {}\n\n collection(name: string): Map<string, Record_> {\n let c = this.collections.get(name);\n if (!c) {\n c = new Map();\n this.collections.set(name, c);\n }\n return c;\n }\n\n collections_(): string[] {\n return [...this.collections.keys()];\n }\n\n insert<T extends Record_>(collection: string, record: T): T {\n const c = this.collection(collection);\n if (c.has(record.id)) throw new Error(`duplicate id ${record.id} in ${collection}`);\n c.set(record.id, record);\n this.emit({ kind: \"insert\", collection, id: record.id, record });\n return record;\n }\n\n upsert<T extends Record_>(collection: string, record: T): T {\n const c = this.collection(collection);\n const previous = c.get(record.id);\n c.set(record.id, record);\n this.emit(previous ? { kind: \"update\", collection, id: record.id, record, previous } : { kind: \"insert\", collection, id: record.id, record });\n return record;\n }\n\n get<T extends Record_ = Record_>(collection: string, id: string): T | undefined {\n return this.collections.get(collection)?.get(id) as T | undefined;\n }\n\n list<T extends Record_ = Record_>(collection: string, opts: { where?: (r: T) => boolean; sort?: (a: T, b: T) => number; offset?: number; limit?: number } = {}): T[] {\n let out = [...(this.collections.get(collection)?.values() ?? [])] as T[];\n if (opts.where) out = out.filter(opts.where);\n if (opts.sort) out.sort(opts.sort);\n if (opts.offset) out = out.slice(opts.offset);\n if (opts.limit !== undefined) out = out.slice(0, opts.limit);\n return out;\n }\n\n count(collection: string): number {\n return this.collections.get(collection)?.size ?? 0;\n }\n\n update<T extends Record_ = Record_>(collection: string, id: string, patch: Partial<T> | ((current: T) => T)): T | undefined {\n const c = this.collection(collection);\n const previous = c.get(id) as T | undefined;\n if (!previous) return undefined;\n const next = typeof patch === \"function\" ? patch(previous) : ({ ...previous, ...patch, id } as T);\n c.set(id, next);\n this.emit({ kind: \"update\", collection, id, record: next, previous });\n return next;\n }\n\n remove(collection: string, id: string): Record_ | undefined {\n const c = this.collection(collection);\n const previous = c.get(id);\n if (!previous) return undefined;\n c.delete(id);\n this.emit({ kind: \"remove\", collection, id, previous });\n return previous;\n }\n\n clear(collection?: string) {\n if (collection) this.collection(collection).clear();\n else this.collections.clear();\n this.emit({ kind: \"clear\", collection: collection ?? \"*\" });\n }\n\n /** Application-level event (not tied to a record), e.g. \"job.progress\". */\n custom(collection: string, name: string, data?: unknown, id?: string) {\n this.emit({ kind: \"custom\", collection, name, data, id });\n }\n\n on(listener: StoreListener) {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n events(opts: { since?: number; collection?: string; limit?: number } = {}): StoreEvent[] {\n const out = this.log.filter((e) => e.seq > (opts.since ?? 0) && (!opts.collection || e.collection === opts.collection));\n const limit = opts.limit ?? 200;\n return out.length > limit ? out.slice(out.length - limit) : out;\n }\n\n get lastSeq() {\n return this.seq;\n }\n\n snapshot(): Record<string, Record_[]> {\n const out: Record<string, Record_[]> = {};\n for (const [name, c] of this.collections) out[name] = [...c.values()];\n return out;\n }\n\n counts(): Record<string, number> {\n const out: Record<string, number> = {};\n for (const [name, c] of this.collections) out[name] = c.size;\n return out;\n }\n\n private emit(partial: Omit<StoreEvent, \"seq\" | \"t\">) {\n const event: StoreEvent = { seq: ++this.seq, t: this.now(), ...partial };\n this.log.push(event);\n if (this.log.length > this.logLimit) this.log.splice(0, this.log.length - this.logLimit);\n for (const l of this.listeners) {\n try {\n l(event);\n } catch (err) {\n console.warn(\"[scenario-sim] store listener threw\", err);\n }\n }\n }\n}\n","/**\n * Streams: topics with per-topic monotonic event ids and bounded replay, plus\n * SSE and WebSocket routes whose connections the control plane can list,\n * pause, disconnect or hard-drop. Transports are pluggable — a Bun/Node\n * server, Vite's http server, or an in-page fake WebSocket — and the scenario\n * code never sees the difference.\n */\n\nimport type { Rng } from \"./rng.js\";\nimport type { VirtualClock } from \"./clock.js\";\nimport type { Store } from \"./store.js\";\n\nexport interface TopicEvent {\n topic: string;\n /** Monotonic per topic, as a decimal string (matches how most protocols carry it). */\n eventID: string;\n t: number;\n data: Record<string, unknown>;\n}\n\nexport class TopicLog {\n private seqs = new Map<string, number>();\n private logs = new Map<string, TopicEvent[]>();\n\n constructor(private now: () => number, public readonly retain = 500) {}\n\n publish(topic: string, data: Record<string, unknown>): TopicEvent {\n const seq = (this.seqs.get(topic) ?? 0) + 1;\n this.seqs.set(topic, seq);\n const event: TopicEvent = { topic, eventID: String(seq), t: this.now(), data };\n const log = this.logs.get(topic) ?? [];\n log.push(event);\n if (log.length > this.retain) log.splice(0, log.length - this.retain);\n this.logs.set(topic, log);\n return event;\n }\n\n last(topic: string): string {\n return String(this.seqs.get(topic) ?? 0);\n }\n\n /**\n * Events after `since`. `missed` is true when `since` predates the retained\n * window. `null`/`undefined` means \"no resume point\": live only, nothing is\n * replayed (what a fresh EventSource without Last-Event-ID gets). Pass \"0\"\n * to replay everything retained.\n */\n replay(topic: string, since: string | number | null | undefined): { events: TopicEvent[]; missed: boolean } {\n if (since === null || since === undefined || since === \"\") return { events: [], missed: false };\n const s = Number(since);\n const log = this.logs.get(topic) ?? [];\n const oldest = log[0] ? Number(log[0].eventID) : (this.seqs.get(topic) ?? 0) + 1;\n const missed = s > 0 && s < oldest - 1;\n return { events: log.filter((e) => Number(e.eventID) > s), missed };\n }\n\n topics(): string[] {\n return [...this.seqs.keys()];\n }\n\n clear() {\n this.seqs.clear();\n this.logs.clear();\n }\n}\n\nexport interface StreamContext {\n request: Request;\n url: URL;\n params: Record<string, string>;\n query: URLSearchParams;\n state: Store;\n rng: Rng;\n clock: VirtualClock;\n streams: StreamHub;\n run: { id: string; scenario: string; seed: string };\n log(message: string, data?: unknown): void;\n}\n\n/** What a transport must provide for one WebSocket connection. */\nexport interface SocketTransport {\n send(data: string | ArrayBuffer): void;\n /** Graceful close with a close frame. */\n close(code?: number, reason?: string): void;\n /** Hard drop: no close frame; the client sees an abnormal close (1006). */\n drop(): void;\n}\n\nexport type Serializer = (event: TopicEvent) => string;\nconst defaultSerializer: Serializer = (e) => JSON.stringify({ ...e.data, topic: e.topic, eventID: e.eventID });\n\ninterface Subscription {\n topic: string;\n serialize: Serializer;\n}\n\ninterface ConnectionBase {\n id: string;\n kind: \"ws\" | \"sse\";\n path: string;\n url: URL;\n openedAt: number;\n sent: number;\n received: number;\n paused: boolean;\n subscriptions: Map<string, Subscription>;\n meta: Record<string, unknown>;\n}\n\nexport interface SimSocket extends ConnectionBase {\n kind: \"ws\";\n protocol: string | null;\n readyState: \"open\" | \"closed\";\n send(data: string | ArrayBuffer | Record<string, unknown>): void;\n close(code?: number, reason?: string): void;\n drop(): void;\n /** Replay `since` then stay subscribed; returns what was replayed. */\n subscribe(topic: string, opts?: { since?: string | number | null; serialize?: Serializer }): { replayed: number; missed: boolean; last: string };\n unsubscribe(topic: string): void;\n onClose(cb: (code: number, reason: string) => void): void;\n}\n\nexport interface SimSseStream extends ConnectionBase {\n kind: \"sse\";\n lastEventId: string | null;\n /** Send one SSE message. Objects are JSON-encoded. */\n send(data: unknown, opts?: { event?: string; id?: string; retry?: number }): void;\n comment(text: string): void;\n close(): void;\n drop(): void;\n subscribe(topic: string, opts?: { since?: string | number | null; event?: (e: TopicEvent) => string }): { replayed: number; missed: boolean; last: string };\n unsubscribe(topic: string): void;\n onClose(cb: () => void): void;\n /** The Response to return to the client. */\n response: Response;\n}\n\nexport interface WsRoute {\n kind: \"ws\";\n path: string;\n name?: string;\n /** Subprotocols the server will select from (first match with the client's list wins). */\n protocols?: string[];\n onOpen?(ctx: StreamContext, socket: SimSocket): void | Promise<void>;\n onMessage?(ctx: StreamContext, socket: SimSocket, data: string | ArrayBuffer): void | Promise<void>;\n onClose?(ctx: StreamContext, socket: SimSocket, code: number, reason: string): void;\n}\n\nexport interface SseRoute {\n kind: \"sse\";\n path: string;\n name?: string;\n /** Some APIs open SSE with POST (request body = the prompt). Default GET. */\n method?: \"GET\" | \"POST\";\n /** Keepalive comment interval in virtual ms (default 15000; 0 disables). */\n keepaliveMs?: number;\n onOpen(ctx: StreamContext, stream: SimSseStream): void | Promise<void>;\n}\n\nexport type StreamRoute = WsRoute | SseRoute;\n\nexport const ws = (path: string, def: Omit<WsRoute, \"kind\" | \"path\">): WsRoute => ({ kind: \"ws\", path, ...def });\nexport const sse = (path: string, onOpen: SseRoute[\"onOpen\"], def: Omit<SseRoute, \"kind\" | \"path\" | \"onOpen\"> = {}): SseRoute => ({ kind: \"sse\", path, onOpen, ...def });\n\nexport interface ConnectionSummary {\n id: string;\n kind: \"ws\" | \"sse\";\n path: string;\n openedAt: number;\n sent: number;\n received: number;\n paused: boolean;\n topics: string[];\n protocol?: string | null;\n meta: Record<string, unknown>;\n}\n\nexport class StreamHub {\n readonly topics: TopicLog;\n private connections = new Map<string, SimSocket | SimSseStream>();\n private nextId = 1;\n /** Extra virtual latency applied to every outgoing stream message. */\n latencyMs = 0;\n\n constructor(private clock: VirtualClock, private log: (message: string, data?: unknown) => void) {\n this.topics = new TopicLog(() => clock.now());\n }\n\n /** Publish to a topic: appended to the replay log and fanned out to subscribers. */\n publish(topic: string, data: Record<string, unknown>): TopicEvent {\n const event = this.topics.publish(topic, data);\n for (const conn of this.connections.values()) {\n const sub = conn.subscriptions.get(topic);\n if (!sub) continue;\n if (conn.kind === \"ws\") this.deliver(conn, sub.serialize(event));\n else this.deliverSse(conn, event, sub.serialize as unknown as (e: TopicEvent) => string);\n }\n return event;\n }\n\n private deliver(conn: SimSocket, payload: string) {\n if (conn.readyState !== \"open\") return;\n const doSend = () => {\n if (conn.readyState !== \"open\") return;\n if (conn.paused) {\n (conn.meta.__queued as string[] | undefined)?.push(payload) ?? (conn.meta.__queued = [payload]);\n return;\n }\n conn.send(payload);\n };\n if (this.latencyMs > 0) this.clock.after(this.latencyMs, doSend, `ws latency ${conn.id}`);\n else doSend();\n }\n\n private deliverSse(conn: SimSseStream, event: TopicEvent, eventName: (e: TopicEvent) => string) {\n const doSend = () => conn.send(event.data, { event: eventName(event), id: event.eventID });\n if (this.latencyMs > 0) this.clock.after(this.latencyMs, doSend, `sse latency ${conn.id}`);\n else doSend();\n }\n\n /** Called by a transport adapter once the WebSocket handshake completed. */\n openSocket(route: WsRoute, ctx: StreamContext, transport: SocketTransport, protocol: string | null): SimSocket {\n const id = `ws_${this.nextId++}`;\n const closeCbs: Array<(code: number, reason: string) => void> = [];\n const hub = this;\n const socket: SimSocket = {\n id,\n kind: \"ws\",\n path: route.path,\n url: ctx.url,\n openedAt: this.clock.now(),\n sent: 0,\n received: 0,\n paused: false,\n subscriptions: new Map(),\n meta: {},\n protocol,\n readyState: \"open\",\n send(data) {\n if (socket.readyState !== \"open\") return;\n socket.sent++;\n transport.send(typeof data === \"string\" || data instanceof ArrayBuffer ? data : JSON.stringify(data));\n },\n close(code = 1000, reason = \"\") {\n if (socket.readyState !== \"open\") return;\n socket.readyState = \"closed\";\n transport.close(code, reason);\n hub.finish(socket, code, reason, closeCbs);\n },\n drop() {\n if (socket.readyState !== \"open\") return;\n socket.readyState = \"closed\";\n transport.drop();\n hub.finish(socket, 1006, \"dropped\", closeCbs);\n },\n subscribe(topic, opts = {}) {\n const serialize = opts.serialize ?? defaultSerializer;\n socket.subscriptions.set(topic, { topic, serialize });\n const { events, missed } = hub.topics.replay(topic, opts.since);\n for (const e of events) hub.deliver(socket, serialize(e));\n return { replayed: events.length, missed, last: hub.topics.last(topic) };\n },\n unsubscribe(topic) {\n socket.subscriptions.delete(topic);\n },\n onClose(cb) {\n closeCbs.push(cb);\n },\n };\n this.connections.set(id, socket);\n this.log(`ws open ${id} ${route.path}`, { protocol });\n void Promise.resolve(route.onOpen?.(ctx, socket)).catch((err) => this.log(`ws onOpen threw ${id}`, String(err)));\n return socket;\n }\n\n /** Transport → hub: a client frame arrived. */\n receive(route: WsRoute, ctx: StreamContext, socket: SimSocket, data: string | ArrayBuffer) {\n if (socket.readyState !== \"open\") return;\n socket.received++;\n void Promise.resolve(route.onMessage?.(ctx, socket, data)).catch((err) => this.log(`ws onMessage threw ${socket.id}`, String(err)));\n }\n\n /** Transport → hub: the client closed. */\n clientClosed(route: WsRoute, ctx: StreamContext, socket: SimSocket, code: number, reason: string) {\n if (socket.readyState !== \"open\") return;\n socket.readyState = \"closed\";\n this.connections.delete(socket.id);\n this.log(`ws closed by client ${socket.id}`, { code, reason });\n route.onClose?.(ctx, socket, code, reason);\n }\n\n private finish(conn: SimSocket, code: number, reason: string, cbs: Array<(code: number, reason: string) => void>) {\n this.connections.delete(conn.id);\n this.log(`ws closed ${conn.id}`, { code, reason });\n for (const cb of cbs) cb(code, reason);\n }\n\n /** Build the SSE Response for a route; the adapter just returns it. */\n openSse(route: SseRoute, ctx: StreamContext): SimSseStream {\n const id = `sse_${this.nextId++}`;\n const encoder = new TextEncoder();\n let controller: ReadableStreamDefaultController<Uint8Array> | null = null;\n let closed = false;\n const closeCbs: Array<() => void> = [];\n const hub = this;\n const lastEventId = ctx.request.headers.get(\"last-event-id\") ?? ctx.query.get(\"lastEventId\") ?? ctx.query.get(\"sinceEventID\");\n let keepalive: { cancel(): void } | null = null;\n const teardown = () => {\n if (closed) return;\n closed = true;\n keepalive?.cancel();\n hub.connections.delete(id);\n hub.log(`sse closed ${id}`);\n for (const cb of closeCbs) cb();\n };\n const write = (chunk: string) => {\n if (closed || !controller) return;\n try {\n controller.enqueue(encoder.encode(chunk));\n } catch {\n teardown();\n }\n };\n // `start` runs synchronously inside the constructor, so it only captures\n // the controller; the greeting, keepalive and onOpen run once `stream` exists.\n const body = new ReadableStream<Uint8Array>({\n start(c) {\n controller = c;\n },\n cancel() {\n teardown();\n },\n });\n const stream: SimSseStream = {\n id,\n kind: \"sse\",\n path: route.path,\n url: ctx.url,\n openedAt: this.clock.now(),\n sent: 0,\n received: 0,\n paused: false,\n subscriptions: new Map(),\n meta: {},\n lastEventId,\n send(data, opts = {}) {\n if (closed) return;\n const payload = typeof data === \"string\" ? data : JSON.stringify(data);\n const frame = [\n opts.event ? `event: ${opts.event}` : null,\n opts.id !== undefined ? `id: ${opts.id}` : null,\n opts.retry !== undefined ? `retry: ${opts.retry}` : null,\n ...payload.split(\"\\n\").map((line) => `data: ${line}`),\n ]\n .filter((l) => l !== null)\n .join(\"\\n\");\n if (stream.paused) {\n (stream.meta.__queued as string[] | undefined)?.push(frame + \"\\n\\n\") ?? (stream.meta.__queued = [frame + \"\\n\\n\"]);\n return;\n }\n stream.sent++;\n write(frame + \"\\n\\n\");\n },\n comment(text) {\n write(`: ${text}\\n\\n`);\n },\n close() {\n if (closed) return;\n try {\n controller?.close();\n } catch {\n // already closed\n }\n teardown();\n },\n drop() {\n if (closed) return;\n try {\n controller?.error(new Error(\"connection dropped\"));\n } catch {\n // already errored\n }\n teardown();\n },\n subscribe(topic, opts = {}) {\n const eventName = opts.event ?? ((e: TopicEvent) => String(e.data.type ?? \"message\"));\n stream.subscriptions.set(topic, { topic, serialize: eventName as unknown as Serializer });\n const { events, missed } = hub.topics.replay(topic, opts.since ?? lastEventId);\n for (const e of events) stream.send(e.data, { event: eventName(e), id: e.eventID });\n return { replayed: events.length, missed, last: hub.topics.last(topic) };\n },\n unsubscribe(topic) {\n stream.subscriptions.delete(topic);\n },\n onClose(cb) {\n closeCbs.push(cb);\n },\n response: new Response(body, { status: 200, headers: { \"content-type\": \"text/event-stream\", \"cache-control\": \"no-store\", connection: \"keep-alive\", \"x-accel-buffering\": \"no\" } }),\n };\n this.connections.set(id, stream);\n this.log(`sse open ${id} ${route.path}`, { lastEventId });\n write(`: scenario-sim ${id}\\n\\n`);\n const ka = route.keepaliveMs ?? 15_000;\n if (ka > 0) {\n const tick = () => {\n if (closed) return;\n write(\":keepalive\\n\\n\");\n keepalive = hub.clock.after(ka, tick, `sse keepalive ${id}`);\n };\n keepalive = hub.clock.after(ka, tick, `sse keepalive ${id}`);\n }\n void Promise.resolve(route.onOpen(ctx, stream)).catch((err) => hub.log(`sse onOpen threw ${id}`, String(err)));\n return stream;\n }\n\n // ── control plane ────────────────────────────────────────────────\n\n list(): ConnectionSummary[] {\n return [...this.connections.values()].map((c) => ({\n id: c.id,\n kind: c.kind,\n path: c.path,\n openedAt: c.openedAt,\n sent: c.sent,\n received: c.received,\n paused: c.paused,\n topics: [...c.subscriptions.keys()],\n ...(c.kind === \"ws\" ? { protocol: c.protocol } : {}),\n meta: Object.fromEntries(Object.entries(c.meta).filter(([k]) => !k.startsWith(\"__\"))),\n }));\n }\n\n get(id: string) {\n return this.connections.get(id) ?? null;\n }\n\n /**\n * Disconnect connections: by id, by topic, by path, or all. `drop` cuts the\n * connection without a close frame (what a network blip looks like).\n */\n disconnect(target: { id?: string; topic?: string; path?: string; all?: boolean }, opts: { code?: number; reason?: string; drop?: boolean } = {}): string[] {\n const hit: string[] = [];\n for (const c of [...this.connections.values()]) {\n const matches = target.all || (target.id && c.id === target.id) || (target.topic && c.subscriptions.has(target.topic)) || (target.path && c.path === target.path);\n if (!matches) continue;\n hit.push(c.id);\n if (opts.drop) c.drop();\n else if (c.kind === \"ws\") c.close(opts.code ?? 1001, opts.reason ?? \"disconnected by scenario control\");\n else c.close();\n }\n return hit;\n }\n\n /** Pause delivery on a connection (messages queue); resume flushes them in order. */\n pause(id: string, paused: boolean): boolean {\n const c = this.connections.get(id);\n if (!c) return false;\n c.paused = paused;\n if (!paused) {\n const queued = (c.meta.__queued as string[] | undefined) ?? [];\n c.meta.__queued = [];\n for (const payload of queued) {\n if (c.kind === \"ws\") c.send(payload);\n else {\n c.sent++;\n // Raw frame already formatted.\n (c as unknown as { __raw?: (s: string) => void }).__raw?.(payload);\n }\n }\n }\n return true;\n }\n\n closeAll() {\n for (const c of [...this.connections.values()]) c.close();\n this.connections.clear();\n }\n}\n","/**\n * HTTP routing on Web `Request`/`Response`, so the same scenario runs on a\n * Bun/Node server, inside Vite's dev server, or entirely in the browser.\n */\n\nimport type { Rng } from \"./rng.js\";\nimport type { VirtualClock } from \"./clock.js\";\nimport type { Store } from \"./store.js\";\nimport type { StreamHub } from \"./streams.js\";\n\nexport type Method = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"*\";\n\nexport interface RouteContext {\n request: Request;\n url: URL;\n method: string;\n /** `:name` captures. */\n params: Record<string, string>;\n query: URLSearchParams;\n /** Parsed JSON body (or `{}` when absent/invalid). */\n body<T = Record<string, unknown>>(): Promise<T>;\n state: Store;\n rng: Rng;\n clock: VirtualClock;\n streams: StreamHub;\n /** Run id and scenario name, for logging or per-run behaviour. */\n run: { id: string; scenario: string; seed: string };\n /** Per-route call counter (1-based) — handy for \"fail on the 3rd call\". */\n calls: number;\n log(message: string, data?: unknown): void;\n}\n\nexport type Handler = (ctx: RouteContext) => Response | Promise<Response>;\n\nexport interface Route {\n method: Method;\n path: string;\n handler: Handler;\n /** Optional label for logs and the control plane. */\n name?: string;\n}\n\nexport interface CompiledRoute extends Route {\n match: (pathname: string) => Record<string, string> | null;\n calls: number;\n}\n\nconst JSON_HEADERS = { \"content-type\": \"application/json\", \"cache-control\": \"no-store\" };\n\nexport function json(body: unknown, init: ResponseInit & { status?: number } = {}): Response {\n return new Response(body === undefined ? null : JSON.stringify(body), { ...init, status: init.status ?? 200, headers: { ...JSON_HEADERS, ...(init.headers as Record<string, string> | undefined) } });\n}\n\nexport function text(body: string, init: ResponseInit = {}): Response {\n return new Response(body, { ...init, headers: { \"content-type\": \"text/plain; charset=utf-8\", ...(init.headers as Record<string, string> | undefined) } });\n}\n\nexport function empty(status = 204, init: ResponseInit = {}): Response {\n return new Response(null, { ...init, status });\n}\n\n/** RFC 9457 problem details, the shape most APIs use for errors. */\nexport function problem(status: number, detail: string, extra: Record<string, unknown> = {}): Response {\n return json({ type: \"about:blank\", title: statusText(status), status, detail, ...extra }, { status, headers: { \"content-type\": \"application/problem+json\" } });\n}\n\nexport type MalformedKind = \"invalid-json\" | \"wrong-content-type\" | \"truncated\" | \"empty-200\" | \"html-500\" | \"schema-drift\";\n\n/** Deliberately broken responses for exercising client validation paths. */\nexport function malformed(kind: MalformedKind, extra?: unknown): Response {\n switch (kind) {\n case \"invalid-json\":\n return new Response('{\"items\": [1, 2,', { status: 200, headers: JSON_HEADERS });\n case \"wrong-content-type\":\n return new Response(JSON.stringify(extra ?? { ok: true }), { status: 200, headers: { \"content-type\": \"text/html\" } });\n case \"truncated\":\n return new Response(JSON.stringify(extra ?? { items: [{ id: \"a\" }] }).slice(0, -6), { status: 200, headers: JSON_HEADERS });\n case \"empty-200\":\n return new Response(\"\", { status: 200, headers: JSON_HEADERS });\n case \"html-500\":\n return new Response(\"<html><body><h1>502 Bad Gateway</h1></body></html>\", { status: 502, headers: { \"content-type\": \"text/html\" } });\n case \"schema-drift\":\n return json(extra ?? { data: { items: \"not-an-array\" }, meta: null });\n }\n}\n\n/**\n * Cycle through responses call by call: `sequence([ok, fail, ok])` answers\n * ok, fail, ok, then repeats the last unless `loop` is set. Each entry may be a\n * Response factory or a handler.\n */\nexport function sequence(steps: Array<Handler | Response>, opts: { loop?: boolean } = {}): Handler {\n let i = 0;\n return async (ctx) => {\n const idx = opts.loop ? i % steps.length : Math.min(i, steps.length - 1);\n i++;\n const step = steps[idx]!;\n if (step instanceof Response) return step.clone();\n return step(ctx);\n };\n}\n\n/** Wrap a handler so it answers after `ms` virtual milliseconds. */\nexport function delayed(ms: number | ((ctx: RouteContext) => number), handler: Handler): Handler {\n return async (ctx) => {\n await ctx.clock.sleep(typeof ms === \"function\" ? ms(ctx) : ms, `delay ${ctx.method} ${ctx.url.pathname}`);\n return handler(ctx);\n };\n}\n\nexport function compileRoute(route: Route): CompiledRoute {\n const keys: string[] = [];\n const pattern = route.path\n .split(\"/\")\n .map((seg) => {\n if (seg === \"*\") return \"(?:.*)\";\n if (seg.startsWith(\":\")) {\n keys.push(seg.slice(1));\n return \"([^/]+)\";\n }\n return seg.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n })\n .join(\"/\");\n const re = new RegExp(`^${pattern}/?$`);\n return {\n ...route,\n calls: 0,\n match(pathname) {\n const m = re.exec(pathname);\n if (!m) return null;\n const params: Record<string, string> = {};\n keys.forEach((k, i) => (params[k] = decodeURIComponent(m[i + 1] ?? \"\")));\n return params;\n },\n };\n}\n\nexport function statusText(status: number): string {\n return (\n {\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 402: \"Payment Required\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 409: \"Conflict\",\n 422: \"Unprocessable Content\",\n 429: \"Too Many Requests\",\n 500: \"Internal Server Error\",\n 502: \"Bad Gateway\",\n 503: \"Service Unavailable\",\n 504: \"Gateway Timeout\",\n }[status] ?? \"Error\"\n );\n}\n\n/** Route helpers so scenario files read like a table. */\nexport const route = {\n get: (path: string, handler: Handler, name?: string): Route => ({ method: \"GET\", path, handler, name }),\n post: (path: string, handler: Handler, name?: string): Route => ({ method: \"POST\", path, handler, name }),\n put: (path: string, handler: Handler, name?: string): Route => ({ method: \"PUT\", path, handler, name }),\n patch: (path: string, handler: Handler, name?: string): Route => ({ method: \"PATCH\", path, handler, name }),\n delete: (path: string, handler: Handler, name?: string): Route => ({ method: \"DELETE\", path, handler, name }),\n any: (path: string, handler: Handler, name?: string): Route => ({ method: \"*\", path, handler, name }),\n};\n\n/**\n * A full CRUD resource in one line:\n * crud(\"/api/notes\", \"notes\", { create: (body, ctx) => ({ id: ctx.rng.id(\"note\"), ...body }) })\n * Emits store events on every mutation, which stream routes can forward.\n */\nexport function crud<T extends Record<string, unknown> & { id: string }>(\n path: string,\n collection: string,\n opts: {\n create: (body: Record<string, unknown>, ctx: RouteContext) => T;\n update?: (current: T, body: Record<string, unknown>, ctx: RouteContext) => T;\n list?: (items: T[], ctx: RouteContext) => unknown;\n sort?: (a: T, b: T) => number;\n validate?: (body: Record<string, unknown>, ctx: RouteContext) => string | null;\n },\n): Route[] {\n const wrap = opts.list ?? ((items: T[]) => ({ items }));\n return [\n route.get(path, (ctx) => json(wrap(ctx.state.list<T>(collection, { sort: opts.sort }), ctx)), `${collection}.list`),\n route.post(path, async (ctx) => {\n const body = await ctx.body();\n const invalid = opts.validate?.(body, ctx);\n if (invalid) return problem(422, invalid);\n const record = opts.create(body, ctx);\n ctx.state.insert(collection, record);\n return json(record, { status: 201 });\n }, `${collection}.create`),\n route.get(`${path}/:id`, (ctx) => {\n const rec = ctx.state.get<T>(collection, ctx.params.id!);\n return rec ? json(rec) : problem(404, `${collection} ${ctx.params.id} not found`);\n }, `${collection}.read`),\n route.patch(`${path}/:id`, async (ctx) => {\n const body = await ctx.body();\n const invalid = opts.validate?.(body, ctx);\n if (invalid) return problem(422, invalid);\n const next = ctx.state.update<T>(collection, ctx.params.id!, (cur) => (opts.update ? opts.update(cur, body, ctx) : ({ ...cur, ...body, id: cur.id } as T)));\n return next ? json(next) : problem(404, `${collection} ${ctx.params.id} not found`);\n }, `${collection}.update`),\n route.put(`${path}/:id`, async (ctx) => {\n const body = await ctx.body();\n const invalid = opts.validate?.(body, ctx);\n if (invalid) return problem(422, invalid);\n const next = ctx.state.update<T>(collection, ctx.params.id!, (cur) => (opts.update ? opts.update(cur, body, ctx) : ({ ...cur, ...body, id: cur.id } as T)));\n return next ? json(next) : problem(404, `${collection} ${ctx.params.id} not found`);\n }, `${collection}.replace`),\n route.delete(`${path}/:id`, (ctx) => {\n const removed = ctx.state.remove(collection, ctx.params.id!);\n return removed ? empty(204) : problem(404, `${collection} ${ctx.params.id} not found`);\n }, `${collection}.delete`),\n ];\n}\n","/**\n * Fault layer: latency (+ seeded jitter), fail modes and per-endpoint\n * overrides. Overrides are checked before routing, so a test can force any\n * endpoint — even one the scenario never defined — to answer with a given\n * status/body, once (`times`) or until cleared — the classic in-bundle mock\n * \"endpoint override\" contract, generalised.\n */\n\nimport type { Rng } from \"./rng.js\";\nimport { json, malformed, type MalformedKind, problem } from \"./router.js\";\n\nexport type FailMode = \"off\" | \"data\" | \"all\";\n\nexport interface OverrideInput {\n /** Substring of the path, or `{ regex, flags }`. */\n matcher: string | { regex: string; flags?: string };\n method?: string;\n status?: number;\n body?: unknown;\n /** Auto-expire after N matches. */\n times?: number;\n delayMs?: number;\n malformed?: MalformedKind;\n name?: string;\n}\n\nexport interface Override extends OverrideInput {\n id: string;\n remaining: number;\n hits: number;\n}\n\nexport interface FaultSnapshot {\n latencyMs: number;\n jitterMs: number;\n failMode: FailMode;\n shellPaths: string[];\n overrides: Override[];\n}\n\nexport class FaultLayer {\n latencyMs = 0;\n jitterMs = 0;\n failMode: FailMode = \"off\";\n /** Paths the `data` fail mode leaves alone so the app shell still boots. */\n shellPaths = new Set<string>();\n private overrides: Override[] = [];\n private nextId = 1;\n\n constructor(private rng: Rng) {}\n\n configure(patch: Partial<{ latencyMs: number; jitterMs: number; failMode: FailMode; shellPaths: string[] }>) {\n if (patch.latencyMs !== undefined) this.latencyMs = Math.max(0, patch.latencyMs);\n if (patch.jitterMs !== undefined) this.jitterMs = Math.max(0, patch.jitterMs);\n if (patch.failMode !== undefined) this.failMode = patch.failMode;\n if (patch.shellPaths !== undefined) this.shellPaths = new Set(patch.shellPaths);\n }\n\n /** Effective delay for one request (seeded jitter keeps it reproducible). */\n delayFor(extra = 0): number {\n const jitter = this.jitterMs > 0 ? this.rng.int(0, this.jitterMs) : 0;\n return this.latencyMs + jitter + extra;\n }\n\n setOverride(input: OverrideInput): Override {\n const key = matcherKey(input.matcher, input.method);\n this.overrides = this.overrides.filter((o) => matcherKey(o.matcher, o.method) !== key);\n const o: Override = { ...input, id: `ov_${this.nextId++}`, remaining: input.times ?? Infinity, hits: 0 };\n this.overrides.unshift(o);\n return o;\n }\n\n clearOverride(target: string | OverrideInput[\"matcher\"], method?: string): boolean {\n const before = this.overrides.length;\n this.overrides = this.overrides.filter((o) => o.id !== target && matcherKey(o.matcher, o.method) !== matcherKey(target as OverrideInput[\"matcher\"], method));\n return this.overrides.length !== before;\n }\n\n clearOverrides() {\n this.overrides = [];\n }\n\n listOverrides(): Override[] {\n return this.overrides.map((o) => ({ ...o }));\n }\n\n /** Find (and consume) the first matching override. */\n matchOverride(path: string, method: string): Override | null {\n for (const o of this.overrides) {\n if (o.method && o.method.toUpperCase() !== method.toUpperCase()) continue;\n const hit = typeof o.matcher === \"string\" ? path.includes(o.matcher) : new RegExp(o.matcher.regex, o.matcher.flags).test(path);\n if (!hit) continue;\n o.hits++;\n o.remaining--;\n if (o.remaining <= 0) this.overrides = this.overrides.filter((x) => x !== o);\n return o;\n }\n return null;\n }\n\n /** Build the response an override dictates. */\n overrideResponse(o: Override): Response {\n if (o.malformed) return malformed(o.malformed, o.body);\n const status = o.status ?? 500;\n if (status === 204 || (o.body === undefined && status >= 200 && status < 300)) return new Response(null, { status });\n if (o.body === undefined) return problem(status, `forced by override ${o.id}`);\n return json(o.body, { status });\n }\n\n /** Fail-mode response, or null when the request should proceed. */\n failResponse(path: string): Response | null {\n if (this.failMode === \"off\") return null;\n if (this.failMode === \"data\" && this.shellPaths.has(path)) return null;\n return problem(503, `scenario fail mode \"${this.failMode}\"`);\n }\n\n snapshot(): FaultSnapshot {\n return { latencyMs: this.latencyMs, jitterMs: this.jitterMs, failMode: this.failMode, shellPaths: [...this.shellPaths], overrides: this.listOverrides() };\n }\n}\n\nfunction matcherKey(matcher: OverrideInput[\"matcher\"], method?: string) {\n const m = typeof matcher === \"string\" ? `str:${matcher}` : `re:${matcher.regex}:${matcher.flags ?? \"\"}`;\n return `${(method ?? \"*\").toUpperCase()} ${m}`;\n}\n","/**\n * A scenario is a pure description: how to seed state, which HTTP routes and\n * streams exist, default faults and clock mode, and named actions the control\n * plane can trigger. A Run is one live instance of a scenario with a seed —\n * runs are isolated from each other (own state, clock, RNG, streams, faults).\n */\n\nimport { Rng } from \"./rng.js\";\nimport { VirtualClock, type ClockMode } from \"./clock.js\";\nimport { Store } from \"./store.js\";\nimport { StreamHub, type StreamRoute, type WsRoute, type SseRoute } from \"./streams.js\";\nimport { FaultLayer, type FailMode } from \"./faults.js\";\nimport { compileRoute, type CompiledRoute, type Route } from \"./router.js\";\n\nexport interface SetupContext {\n state: Store;\n rng: Rng;\n clock: VirtualClock;\n streams: StreamHub;\n run: { id: string; scenario: string; seed: string };\n log(message: string, data?: unknown): void;\n}\n\nexport interface ActionContext extends SetupContext {\n args: Record<string, unknown>;\n}\n\nexport interface ScenarioDefinition {\n /** URL-safe identifier. */\n name: string;\n label?: string;\n description?: string;\n /** Default seed (default: the scenario name). Runs may override. */\n seed?: string;\n clock?: { mode?: ClockMode; speed?: number };\n faults?: { latencyMs?: number; jitterMs?: number; failMode?: FailMode; shellPaths?: string[] };\n tags?: string[];\n /** Seed the store. Runs once per run creation/reset, with the run's RNG. */\n setup?(ctx: SetupContext): void | Promise<void>;\n routes?: Route[];\n streams?: StreamRoute[];\n /**\n * Named actions exposed as `POST /__sim/action {name,args}` — and therefore\n * as `scenario.action` in the pulse panel/CLI. Use them for \"emit a burst\",\n * \"complete the running job\", \"drop the stream mid-turn\".\n */\n actions?: Record<string, (ctx: ActionContext) => unknown | Promise<unknown>>;\n /** Hints for QA harnesses (routes worth rendering, expected error states…). */\n qa?: Record<string, unknown>;\n}\n\nexport function defineScenario(def: ScenarioDefinition): ScenarioDefinition {\n if (!/^[a-zA-Z0-9._-]+$/.test(def.name)) throw new Error(`scenario name must be URL-safe: ${def.name}`);\n return def;\n}\n\nexport interface RunLogEntry {\n seq: number;\n t: number;\n message: string;\n data?: unknown;\n}\n\nexport class Run {\n readonly rng: Rng;\n readonly clock: VirtualClock;\n readonly state: Store;\n readonly streams: StreamHub;\n readonly faults: FaultLayer;\n readonly routes: CompiledRoute[];\n readonly wsRoutes: WsRoute[];\n readonly sseRoutes: SseRoute[];\n readonly createdWall = Date.now();\n requests = 0;\n private logEntries: RunLogEntry[] = [];\n private logSeq = 0;\n ready: Promise<void>;\n\n constructor(\n public readonly id: string,\n public readonly scenario: ScenarioDefinition,\n public readonly seed: string,\n private readonly sink?: (line: string) => void,\n ) {\n this.rng = new Rng(seed);\n this.clock = new VirtualClock(scenario.clock?.mode ?? \"realtime\", scenario.clock?.speed ?? 1);\n this.state = new Store(() => this.clock.now());\n this.streams = new StreamHub(this.clock, (m, d) => this.log(m, d));\n this.faults = new FaultLayer(this.rng.fork(\"faults\"));\n if (scenario.faults) this.faults.configure(scenario.faults);\n this.routes = (scenario.routes ?? []).map(compileRoute);\n this.wsRoutes = (scenario.streams ?? []).filter((s): s is WsRoute => s.kind === \"ws\");\n this.sseRoutes = (scenario.streams ?? []).filter((s): s is SseRoute => s.kind === \"sse\");\n this.ready = Promise.resolve(scenario.setup?.(this.setupContext())).then(() => this.log(`run ready (${scenario.name}, seed ${seed})`));\n }\n\n info() {\n return { id: this.id, scenario: this.scenario.name, seed: this.seed };\n }\n\n setupContext(): SetupContext {\n return { state: this.state, rng: this.rng, clock: this.clock, streams: this.streams, run: this.info(), log: (m, d) => this.log(m, d) };\n }\n\n log(message: string, data?: unknown) {\n const entry: RunLogEntry = { seq: ++this.logSeq, t: Math.round(this.clock.now()), message, ...(data !== undefined ? { data } : {}) };\n this.logEntries.push(entry);\n if (this.logEntries.length > 2000) this.logEntries.splice(0, this.logEntries.length - 2000);\n this.sink?.(`[sim ${this.id}] ${message}`);\n }\n\n logs(opts: { since?: number; limit?: number } = {}): RunLogEntry[] {\n const out = this.logEntries.filter((e) => e.seq > (opts.since ?? 0));\n const limit = opts.limit ?? 200;\n return out.length > limit ? out.slice(out.length - limit) : out;\n }\n\n async action(name: string, args: Record<string, unknown> = {}): Promise<unknown> {\n const fn = this.scenario.actions?.[name];\n if (!fn) throw new Error(`unknown action \"${name}\" (available: ${Object.keys(this.scenario.actions ?? {}).join(\", \") || \"none\"})`);\n this.log(`action ${name}`, args);\n return fn({ ...this.setupContext(), args });\n }\n\n status() {\n return {\n run: this.id,\n scenario: this.scenario.name,\n label: this.scenario.label ?? null,\n seed: this.seed,\n createdWall: this.createdWall,\n requests: this.requests,\n clock: { mode: this.clock.mode, speed: this.clock.speed, now: Math.round(this.clock.now()), wall: this.clock.iso(), pending: this.clock.pending() },\n state: { collections: this.state.counts(), lastSeq: this.state.lastSeq },\n streams: this.streams.list(),\n topics: this.streams.topics.topics().map((t) => ({ topic: t, last: this.streams.topics.last(t) })),\n faults: this.faults.snapshot(),\n routes: this.routes.map((r) => ({ method: r.method, path: r.path, calls: r.calls, name: r.name ?? null })),\n streamRoutes: [...this.wsRoutes.map((r) => ({ kind: \"ws\", path: r.path, protocols: r.protocols ?? [] })), ...this.sseRoutes.map((r) => ({ kind: \"sse\", path: r.path, method: r.method ?? \"GET\" }))],\n actions: Object.keys(this.scenario.actions ?? {}),\n };\n }\n\n dispose() {\n this.streams.closeAll();\n this.clock.clear();\n this.log(\"run disposed\");\n }\n}\n","/**\n * The simulator: resolves a run per request, applies faults, routes HTTP/SSE/\n * WebSocket, and serves the control plane under `/__sim`. Runtime-neutral:\n * takes a Web `Request`, returns a Web `Response`. Adapters (Bun/Node server,\n * Vite middleware, in-browser) only translate transports and WebSocket\n * upgrades.\n *\n * Control plane (all also available as `sim.api.*` in-process):\n * GET /__sim/scenarios\n * GET /__sim/status?run=\n * POST /__sim/select {scenario, seed?, run?} bind a run to a scenario (fresh state)\n * POST /__sim/reset {run?, seed?} rebuild the run's scenario\n * POST /__sim/step {run?, ms} advance a manual clock\n * POST /__sim/clock {run?, mode?, speed?}\n * GET /__sim/state?run=&collection= inspect the store\n * GET /__sim/events?run=&since=&limit=&collection= store mutation log\n * GET /__sim/log?run=&since=&limit= run log (requests, streams, actions)\n * GET /__sim/streams?run=\n * POST /__sim/streams/disconnect {run?, id?|topic?|path?|all?, code?, reason?, drop?}\n * POST /__sim/streams/pause {run?, id, paused}\n * POST /__sim/publish {run?, topic, data} publish a stream event by hand\n * GET/POST/DELETE /__sim/overrides {run?, matcher, method?, status?, body?, times?, delayMs?, malformed?}\n * POST /__sim/faults {run?, latencyMs?, jitterMs?, failMode?, shellPaths?}\n * POST /__sim/action {run?, name, args?}\n * GET /__sim/runs · DELETE /__sim/runs?run=\n *\n * Run selection per request: `X-Sim-Run` header → `sim_run` cookie →\n * `?__run=` → \"default\". Scenario for a *new* run: `X-Sim-Scenario` → cookie\n * → `?__scenario=` → `?scenario=` → default scenario.\n */\n\nimport type { ScenarioDefinition } from \"./scenario.js\";\nimport { Run } from \"./scenario.js\";\nimport { json, problem, type RouteContext } from \"./router.js\";\nimport type { StreamContext, WsRoute } from \"./streams.js\";\nimport type { OverrideInput } from \"./faults.js\";\n\nexport interface SimulatorOptions {\n scenarios: ScenarioDefinition[];\n defaultScenario?: string;\n /** Control-plane prefix (default `/__sim`). */\n controlPath?: string;\n /** Default run id (default `default`). */\n defaultRun?: string;\n /** Add permissive CORS headers (default true — the mock is a dev tool). */\n cors?: boolean;\n log?: (line: string) => void;\n}\n\n/**\n * Marker for \"the adapter completed the WebSocket upgrade\". Node's undici\n * refuses `new Response(null, { status: 101 })`, so adapters return a 200 with\n * this header instead of a 101.\n */\nexport const UPGRADED_HEADER = \"x-sim-upgraded\";\nexport const upgradedResponse = () => new Response(null, { status: 200, headers: { [UPGRADED_HEADER]: \"1\" } });\nexport const isUpgraded = (response: Response) => response.headers.get(UPGRADED_HEADER) === \"1\";\n\n/** How an adapter completes a WebSocket upgrade for a matched route. */\nexport type UpgradeHook = (route: WsRoute, ctx: StreamContext, run: Run, protocol: string | null) => Response | Promise<Response>;\n\nexport interface HandleOptions {\n upgrade?: UpgradeHook;\n}\n\nexport class Simulator {\n readonly scenarios = new Map<string, ScenarioDefinition>();\n readonly runs = new Map<string, Run>();\n readonly controlPath: string;\n readonly defaultRun: string;\n readonly defaultScenario: string;\n private readonly cors: boolean;\n private readonly log: (line: string) => void;\n\n constructor(options: SimulatorOptions) {\n if (options.scenarios.length === 0) throw new Error(\"at least one scenario is required\");\n for (const s of options.scenarios) {\n if (this.scenarios.has(s.name)) throw new Error(`duplicate scenario ${s.name}`);\n this.scenarios.set(s.name, s);\n }\n this.defaultScenario = options.defaultScenario ?? options.scenarios[0]!.name;\n if (!this.scenarios.has(this.defaultScenario)) throw new Error(`unknown default scenario ${this.defaultScenario}`);\n this.controlPath = (options.controlPath ?? \"/__sim\").replace(/\\/$/, \"\");\n this.defaultRun = options.defaultRun ?? \"default\";\n this.cors = options.cors ?? true;\n this.log = options.log ?? (() => {});\n }\n\n // ── runs ─────────────────────────────────────────────────────────\n\n private cookie(request: Request, name: string): string | null {\n const header = request.headers.get(\"cookie\") ?? \"\";\n for (const part of header.split(\";\")) {\n const [k, ...rest] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(rest.join(\"=\"));\n }\n return null;\n }\n\n runIdFor(request: Request, url: URL): string {\n return request.headers.get(\"x-sim-run\") ?? this.cookie(request, \"sim_run\") ?? url.searchParams.get(\"__run\") ?? this.defaultRun;\n }\n\n scenarioNameFor(request: Request, url: URL): string {\n return request.headers.get(\"x-sim-scenario\") ?? this.cookie(request, \"sim_scenario\") ?? url.searchParams.get(\"__scenario\") ?? url.searchParams.get(\"scenario\") ?? this.defaultScenario;\n }\n\n /** Get or lazily create the run for a request. */\n async runFor(request: Request, url: URL): Promise<Run> {\n const id = this.runIdFor(request, url);\n let run = this.runs.get(id);\n if (!run) {\n const name = this.scenarioNameFor(request, url);\n run = this.createRun(id, name);\n }\n await run.ready;\n return run;\n }\n\n createRun(id: string, scenarioName: string, seed?: string): Run {\n const scenario = this.scenarios.get(scenarioName);\n if (!scenario) throw new Error(`unknown scenario \"${scenarioName}\" (available: ${[...this.scenarios.keys()].join(\", \")})`);\n this.runs.get(id)?.dispose();\n const run = new Run(id, scenario, seed ?? scenario.seed ?? scenario.name, this.log);\n this.runs.set(id, run);\n return run;\n }\n\n getRun(id = this.defaultRun): Run | null {\n return this.runs.get(id) ?? null;\n }\n\n // ── request handling ─────────────────────────────────────────────\n\n async handle(request: Request, options: HandleOptions = {}): Promise<Response> {\n const url = new URL(request.url);\n try {\n if (request.method === \"OPTIONS\" && this.cors) return this.withCors(request, new Response(null, { status: 204 }));\n if (url.pathname === this.controlPath || url.pathname.startsWith(`${this.controlPath}/`)) {\n return this.withCors(request, await this.control(request, url));\n }\n const run = await this.runFor(request, url);\n run.requests++;\n const path = url.pathname;\n const method = request.method.toUpperCase();\n\n // Per-request fault overrides (compatible with console-style mocks):\n // `X-Sim-Latency: <ms>` and `X-Sim-Fail: off|data|all` apply to this\n // request only, on top of the run's configured faults.\n const latencyHeader = request.headers.get(\"x-sim-latency\");\n const failHeader = request.headers.get(\"x-sim-fail\");\n const extraLatency = latencyHeader !== null && latencyHeader !== \"\" && Number.isFinite(Number(latencyHeader)) ? Math.max(0, Number(latencyHeader)) : 0;\n const failOverride = failHeader === \"off\" || failHeader === \"data\" || failHeader === \"all\" ? failHeader : null;\n const failResponse = (p: string) => {\n if (failOverride === null) return run.faults.failResponse(p);\n if (failOverride === \"off\") return null;\n if (failOverride === \"data\" && run.faults.shellPaths.has(p)) return null;\n return problem(503, `scenario fail mode \"${failOverride}\" (X-Sim-Fail)`);\n };\n\n // Overrides first: a forced answer for any path, even unrouted ones.\n const override = run.faults.matchOverride(path, method);\n if (override) {\n run.log(`${method} ${path} → override ${override.id}`, { status: override.status ?? null, malformed: override.malformed ?? null });\n await run.clock.sleep(run.faults.delayFor(override.delayMs ?? 0), `override ${override.id}`);\n return this.withCors(request, run.faults.overrideResponse(override));\n }\n\n // WebSocket routes. `Upgrade`/`Sec-WebSocket-Protocol` are forbidden\n // request headers in browsers (silently dropped), so adapters also send\n // `x-sim-upgrade` / `x-sim-websocket-protocol` mirrors.\n const upgradeHeader = request.headers.get(\"upgrade\") ?? request.headers.get(\"x-sim-upgrade\");\n if (upgradeHeader?.toLowerCase() === \"websocket\") {\n for (const route of run.wsRoutes) {\n const params = matchPath(route.path, path);\n if (!params) continue;\n if (!options.upgrade) return this.withCors(request, problem(426, \"WebSocket upgrade requires a transport adapter\"));\n const requested = (request.headers.get(\"sec-websocket-protocol\") ?? request.headers.get(\"x-sim-websocket-protocol\") ?? \"\").split(\",\").map((s) => s.trim()).filter(Boolean);\n const protocol = route.protocols?.find((p) => requested.includes(p)) ?? null;\n if (route.protocols?.length && requested.length && !protocol) return this.withCors(request, problem(400, `unsupported subprotocol (server offers ${route.protocols.join(\", \")})`));\n const failed = failResponse(path);\n if (failed) return this.withCors(request, failed);\n return options.upgrade(route, this.streamContext(run, request, url, params), run, protocol);\n }\n return this.withCors(request, problem(404, `no WebSocket route matches ${path}`));\n }\n\n // Fail mode (after overrides so a test can still force a success).\n const failed = failResponse(path);\n if (failed) {\n run.log(`${method} ${path} → fail mode ${failOverride ?? run.faults.failMode}`);\n await run.clock.sleep(run.faults.delayFor(extraLatency), \"fail-mode latency\");\n return this.withCors(request, failed);\n }\n\n // SSE routes.\n for (const route of run.sseRoutes) {\n if ((route.method ?? \"GET\") !== method) continue;\n const params = matchPath(route.path, path);\n if (!params) continue;\n await run.clock.sleep(run.faults.delayFor(extraLatency), \"sse open latency\");\n const stream = run.streams.openSse(route, this.streamContext(run, request, url, params));\n return this.withCors(request, stream.response);\n }\n\n // HTTP routes (definition order; first match wins).\n for (const route of run.routes) {\n if (route.method !== \"*\" && route.method !== method) continue;\n const params = route.match(path);\n if (!params) continue;\n route.calls++;\n const ctx = this.routeContext(run, request, url, params, route.calls);\n const started = run.clock.now();\n await run.clock.sleep(run.faults.delayFor(extraLatency), `latency ${method} ${path}`);\n const response = await route.handler(ctx);\n run.log(`${method} ${path} → ${response.status}`, { route: route.name ?? route.path, ms: Math.round(run.clock.now() - started) });\n return this.withCors(request, response);\n }\n\n run.log(`${method} ${path} → 404 (no route)`);\n return this.withCors(request, problem(404, `scenario \"${run.scenario.name}\" has no route for ${method} ${path}`, { hint: `${this.controlPath}/status lists routes; set an override to force an answer` }));\n } catch (err) {\n this.log(`[sim] error handling ${request.method} ${url.pathname}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);\n return this.withCors(request, problem(500, `simulator error: ${err instanceof Error ? err.message : String(err)}`));\n }\n }\n\n private routeContext(run: Run, request: Request, url: URL, params: Record<string, string>, calls: number): RouteContext {\n let parsed: Promise<unknown> | null = null;\n return {\n request,\n url,\n method: request.method.toUpperCase(),\n params,\n query: url.searchParams,\n body: <T,>() => {\n parsed ??= request\n .clone()\n .text()\n .then((t) => (t ? JSON.parse(t) : {}))\n .catch(() => ({}));\n return parsed as Promise<T>;\n },\n state: run.state,\n rng: run.rng,\n clock: run.clock,\n streams: run.streams,\n run: run.info(),\n calls,\n log: (m, d) => run.log(m, d),\n };\n }\n\n streamContext(run: Run, request: Request, url: URL, params: Record<string, string>): StreamContext {\n return { request, url, params, query: url.searchParams, state: run.state, rng: run.rng, clock: run.clock, streams: run.streams, run: run.info(), log: (m, d) => run.log(m, d) };\n }\n\n private withCors(request: Request, response: Response): Response {\n if (!this.cors) return response;\n const origin = request.headers.get(\"origin\");\n if (!origin) return response;\n const headers = new Headers(response.headers);\n headers.set(\"access-control-allow-origin\", origin);\n headers.set(\"access-control-allow-credentials\", \"true\");\n headers.set(\"access-control-allow-methods\", \"GET,POST,PUT,PATCH,DELETE,OPTIONS\");\n headers.set(\"access-control-allow-headers\", request.headers.get(\"access-control-request-headers\") ?? \"*\");\n headers.set(\"access-control-expose-headers\", \"*\");\n headers.set(\"vary\", \"origin\");\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers });\n }\n\n // ── control plane ────────────────────────────────────────────────\n\n private async control(request: Request, url: URL): Promise<Response> {\n const route = url.pathname.slice(this.controlPath.length).replace(/\\/$/, \"\") || \"/\";\n const method = request.method.toUpperCase();\n const body = method === \"GET\" || method === \"DELETE\" ? {} : await request.clone().text().then((t) => (t ? (JSON.parse(t) as Record<string, unknown>) : {})).catch(() => ({} as Record<string, unknown>));\n const runId = String((body as { run?: string }).run ?? url.searchParams.get(\"run\") ?? request.headers.get(\"x-sim-run\") ?? this.cookie(request, \"sim_run\") ?? this.defaultRun);\n const api = this.api;\n const q = (k: string) => url.searchParams.get(k);\n const num = (v: unknown, d: number) => (v === undefined || v === null || v === \"\" ? d : Number(v));\n\n switch (`${method} ${route}`) {\n case \"GET /\":\n return json({ tool: \"@omniaura/scenario-sim\", controlPath: this.controlPath, defaultRun: this.defaultRun, defaultScenario: this.defaultScenario, runs: [...this.runs.keys()] });\n case \"GET /scenarios\":\n return json({ default: this.defaultScenario, scenarios: api.scenarios() });\n case \"GET /status\":\n return json(await api.status(runId));\n case \"POST /select\": {\n const b = body as { scenario?: string; seed?: string };\n if (!b.scenario) return problem(400, \"scenario is required\");\n return json(await api.select(runId, b.scenario, b.seed), { headers: { \"set-cookie\": `sim_scenario=${encodeURIComponent(b.scenario)}; Path=/; SameSite=Lax` } });\n }\n case \"POST /reset\":\n return json(await api.reset(runId, (body as { seed?: string }).seed));\n case \"POST /step\":\n return json(api.step(runId, num((body as { ms?: number }).ms ?? q(\"ms\"), 1000)));\n case \"POST /clock\":\n return json(api.clock(runId, body as { mode?: \"manual\" | \"realtime\"; speed?: number }));\n case \"GET /state\":\n return json(api.state(runId, q(\"collection\") ?? undefined));\n case \"GET /events\":\n return json(api.events(runId, { since: num(q(\"since\"), 0), limit: num(q(\"limit\"), 200), collection: q(\"collection\") ?? undefined }));\n case \"GET /log\":\n return json(api.log(runId, { since: num(q(\"since\"), 0), limit: num(q(\"limit\"), 200) }));\n case \"GET /streams\":\n return json(api.streams(runId));\n case \"POST /streams/disconnect\": {\n const b = body as { id?: string; topic?: string; path?: string; all?: boolean; code?: number; reason?: string; drop?: boolean };\n return json(api.disconnect(runId, b, b));\n }\n case \"POST /streams/pause\": {\n const b = body as { id?: string; paused?: boolean };\n if (!b.id) return problem(400, \"id is required\");\n return json({ ok: api.pause(runId, b.id, b.paused ?? true) });\n }\n case \"POST /publish\": {\n const b = body as { topic?: string; data?: Record<string, unknown> };\n if (!b.topic) return problem(400, \"topic is required\");\n return json(api.publish(runId, b.topic, b.data ?? {}));\n }\n case \"GET /overrides\":\n return json(api.overrides(runId));\n case \"POST /overrides\": {\n const b = body as unknown as OverrideInput;\n if (!b.matcher) return problem(400, \"matcher is required\");\n return json(api.setOverride(runId, b));\n }\n case \"DELETE /overrides\": {\n const matcher = q(\"matcher\");\n const id = q(\"id\");\n return json(api.clearOverrides(runId, id ?? matcher ?? undefined, q(\"method\") ?? undefined));\n }\n case \"POST /faults\":\n return json(api.faults(runId, body as Parameters<typeof api.faults>[1]));\n case \"POST /action\": {\n const b = body as { name?: string; args?: Record<string, unknown> };\n if (!b.name) return problem(400, \"name is required\");\n return json({ result: await api.action(runId, b.name, b.args ?? {}) });\n }\n case \"GET /runs\":\n return json(api.runs());\n case \"DELETE /runs\":\n return json({ deleted: api.deleteRun(runId) });\n default:\n return problem(404, `unknown control route ${method} ${route}`);\n }\n }\n\n /** In-process control API — the same operations the HTTP control plane offers. */\n readonly api = {\n scenarios: () =>\n [...this.scenarios.values()].map((s) => ({ name: s.name, label: s.label ?? null, description: s.description ?? null, seed: s.seed ?? s.name, tags: s.tags ?? [], clock: s.clock ?? { mode: \"realtime\" }, actions: Object.keys(s.actions ?? {}), qa: s.qa ?? null })),\n status: async (runId = this.defaultRun) => {\n const run = this.runs.get(runId);\n if (!run) return { run: runId, scenario: null, exists: false, defaultScenario: this.defaultScenario };\n await run.ready;\n return { exists: true, ...run.status() };\n },\n select: async (runId: string, scenario: string, seed?: string) => {\n const run = this.createRun(runId, scenario, seed);\n await run.ready;\n return run.status();\n },\n reset: async (runId: string, seed?: string) => {\n const existing = this.runs.get(runId);\n const run = this.createRun(runId, existing?.scenario.name ?? this.defaultScenario, seed ?? existing?.seed);\n await run.ready;\n return run.status();\n },\n step: (runId: string, ms: number) => {\n const run = this.require(runId);\n if (run.clock.mode !== \"manual\") run.clock.setMode(\"manual\");\n const r = run.clock.step(ms);\n run.log(`step ${ms}ms → now ${r.now}`, { fired: r.fired.length });\n return { ...r, pending: run.clock.pending() };\n },\n clock: (runId: string, patch: { mode?: \"manual\" | \"realtime\"; speed?: number }) => {\n const run = this.require(runId);\n if (patch.speed !== undefined) run.clock.speed = Math.max(0.01, Number(patch.speed));\n if (patch.mode) run.clock.setMode(patch.mode);\n return { mode: run.clock.mode, speed: run.clock.speed, now: Math.round(run.clock.now()), pending: run.clock.pending() };\n },\n state: (runId: string, collection?: string) => {\n const run = this.require(runId);\n const snap = run.state.snapshot();\n return collection ? { run: runId, collection, items: snap[collection] ?? [] } : { run: runId, counts: run.state.counts(), collections: snap };\n },\n events: (runId: string, opts: { since?: number; limit?: number; collection?: string }) => {\n const run = this.require(runId);\n const events = run.state.events(opts);\n return { run: runId, last: run.state.lastSeq, count: events.length, events };\n },\n log: (runId: string, opts: { since?: number; limit?: number }) => ({ run: runId, entries: this.require(runId).logs(opts) }),\n streams: (runId: string) => {\n const run = this.require(runId);\n return { run: runId, connections: run.streams.list(), topics: run.streams.topics.topics().map((t) => ({ topic: t, last: run.streams.topics.last(t) })), latencyMs: run.streams.latencyMs };\n },\n disconnect: (runId: string, target: { id?: string; topic?: string; path?: string; all?: boolean }, opts: { code?: number; reason?: string; drop?: boolean } = {}) => {\n const run = this.require(runId);\n const closed = run.streams.disconnect(target, opts);\n run.log(`disconnect ${JSON.stringify(target)}`, { closed, drop: opts.drop ?? false });\n return { closed };\n },\n pause: (runId: string, id: string, paused: boolean) => this.require(runId).streams.pause(id, paused),\n publish: (runId: string, topic: string, data: Record<string, unknown>) => this.require(runId).streams.publish(topic, data),\n overrides: (runId: string) => ({ run: runId, overrides: this.require(runId).faults.listOverrides() }),\n setOverride: (runId: string, input: OverrideInput) => {\n const run = this.require(runId);\n const o = run.faults.setOverride(input);\n run.log(`override set ${o.id}`, { matcher: o.matcher, method: o.method ?? \"*\", status: o.status ?? null, times: o.times ?? null });\n return o;\n },\n clearOverrides: (runId: string, target?: string, method?: string) => {\n const run = this.require(runId);\n if (target) return { cleared: run.faults.clearOverride(target, method) };\n run.faults.clearOverrides();\n return { cleared: true };\n },\n faults: (runId: string, patch: { latencyMs?: number; jitterMs?: number; failMode?: \"off\" | \"data\" | \"all\"; shellPaths?: string[]; streamLatencyMs?: number }) => {\n const run = this.require(runId);\n run.faults.configure(patch);\n if (patch.streamLatencyMs !== undefined) run.streams.latencyMs = Math.max(0, patch.streamLatencyMs);\n run.log(\"faults configured\", patch);\n return { ...run.faults.snapshot(), streamLatencyMs: run.streams.latencyMs };\n },\n action: (runId: string, name: string, args: Record<string, unknown> = {}) => this.require(runId).action(name, args),\n runs: () => [...this.runs.values()].map((r) => ({ id: r.id, scenario: r.scenario.name, seed: r.seed, requests: r.requests, createdWall: r.createdWall })),\n deleteRun: (runId: string) => {\n const run = this.runs.get(runId);\n if (!run) return false;\n run.dispose();\n this.runs.delete(runId);\n return true;\n },\n };\n\n private require(runId = this.defaultRun): Run {\n const run = this.runs.get(runId) ?? this.createRun(runId, this.defaultScenario);\n return run;\n }\n\n dispose() {\n for (const run of this.runs.values()) run.dispose();\n this.runs.clear();\n }\n}\n\nexport function matchPath(pattern: string, pathname: string): Record<string, string> | null {\n const keys: string[] = [];\n const re = new RegExp(\n \"^\" +\n pattern\n .split(\"/\")\n .map((seg) => {\n if (seg === \"*\") return \"(?:.*)\";\n if (seg.startsWith(\":\")) {\n keys.push(seg.slice(1));\n return \"([^/]+)\";\n }\n return seg.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n })\n .join(\"/\") +\n \"/?$\",\n );\n const m = re.exec(pathname);\n if (!m) return null;\n const params: Record<string, string> = {};\n keys.forEach((k, i) => (params[k] = decodeURIComponent(m[i + 1] ?? \"\")));\n return params;\n}\n"],"mappings":"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","names":["o","failed"]}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
var HELP = `scenario-sim \u2014 deterministic scenario simulator control
|
|
5
|
+
|
|
6
|
+
scenario-sim serve <scenarios.(ts|js)> [--port 4100] [--host 127.0.0.1] [--scenario name]
|
|
7
|
+
scenario-sim scenarios | status | state [collection=] | events [since=] | log | streams | runs | overrides
|
|
8
|
+
scenario-sim select name=<scenario> [seed=] | reset [seed=] | step ms=1000 | clock mode=manual speed=2
|
|
9
|
+
scenario-sim disconnect all=true drop=true | override matcher=/api/notes status=503 times=1 | override.clear
|
|
10
|
+
scenario-sim faults latencyMs=800 failMode=data | publish topic=notes data='{"type":"ping"}' | action name=<a> args='{}'
|
|
11
|
+
|
|
12
|
+
Options: --url <http://host:port[/__sim]> (or SCENARIO_SIM_URL; default http://localhost:4100/__sim)
|
|
13
|
+
--run <id> --json`;
|
|
14
|
+
function parse(argv) {
|
|
15
|
+
const flags = {};
|
|
16
|
+
const positional = [];
|
|
17
|
+
const kv = {};
|
|
18
|
+
for (let i = 0; i < argv.length; i++) {
|
|
19
|
+
const a = argv[i];
|
|
20
|
+
if (a.startsWith("--")) {
|
|
21
|
+
const [k, inline] = a.slice(2).split("=", 2);
|
|
22
|
+
if (inline !== void 0) flags[k] = inline;
|
|
23
|
+
else if (argv[i + 1] !== void 0 && !argv[i + 1].startsWith("--") && !argv[i + 1].includes("=")) flags[k] = argv[++i];
|
|
24
|
+
else flags[k] = true;
|
|
25
|
+
} else if (a.includes("=") && positional.length > 0) {
|
|
26
|
+
const idx = a.indexOf("=");
|
|
27
|
+
const v = a.slice(idx + 1);
|
|
28
|
+
kv[a.slice(0, idx)] = /^(true|false|null|-?\d+(\.\d+)?|\[.*\]|\{.*\}|".*")$/s.test(v) ? safeJson(v) : v;
|
|
29
|
+
} else positional.push(a);
|
|
30
|
+
}
|
|
31
|
+
return { flags, positional, kv };
|
|
32
|
+
}
|
|
33
|
+
function safeJson(v) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(v);
|
|
36
|
+
} catch {
|
|
37
|
+
return v;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function main() {
|
|
41
|
+
const { flags, positional, kv } = parse(process.argv.slice(2));
|
|
42
|
+
const cmd = positional[0];
|
|
43
|
+
if (!cmd || cmd === "help" || flags.help) return void process.stdout.write(HELP + "\n");
|
|
44
|
+
if (cmd === "serve") {
|
|
45
|
+
const file = positional[1];
|
|
46
|
+
if (!file) throw new Error("serve needs a scenarios file");
|
|
47
|
+
const { pathToFileURL } = await import("url");
|
|
48
|
+
const { resolve } = await import("path");
|
|
49
|
+
const mod = await import(pathToFileURL(resolve(file)).href);
|
|
50
|
+
const scenarios = Array.isArray(mod.default) ? mod.default : Array.isArray(mod.scenarios) ? mod.scenarios : mod.default?.scenarios;
|
|
51
|
+
if (!scenarios?.length) throw new Error("scenarios file must export an array (default or `scenarios`)");
|
|
52
|
+
const { Simulator } = await import("./engine-HE7MEQHD.js");
|
|
53
|
+
const { serveSimulator } = await import("./server.js");
|
|
54
|
+
const sim = new Simulator({ scenarios, defaultScenario: typeof flags.scenario === "string" ? flags.scenario : void 0, log: (l) => console.error(l) });
|
|
55
|
+
const running = await serveSimulator(sim, { port: flags.port ? Number(flags.port) : 4100, host: typeof flags.host === "string" ? flags.host : "127.0.0.1", log: (l) => console.error(l) });
|
|
56
|
+
console.error(`[scenario-sim] scenarios: ${scenarios.map((s) => s.name).join(", ")} \xB7 control ${running.controlUrl}/status`);
|
|
57
|
+
await new Promise(() => {
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const base = (typeof flags.url === "string" ? flags.url : process.env.SCENARIO_SIM_URL ?? "http://localhost:4100/__sim").replace(/\/$/, "");
|
|
62
|
+
const root = base.endsWith("/__sim") || /\/__[a-z]+$/.test(base) ? base : `${base}/__sim`;
|
|
63
|
+
const run = typeof flags.run === "string" ? flags.run : void 0;
|
|
64
|
+
const out = (v) => process.stdout.write((flags.json ? JSON.stringify(v) : JSON.stringify(v, null, 2)) + "\n");
|
|
65
|
+
const call = async (method, route, body, query) => {
|
|
66
|
+
const url = new URL(`${root}${route}`);
|
|
67
|
+
if (run) url.searchParams.set("run", run);
|
|
68
|
+
for (const [k, v] of Object.entries(query ?? {})) if (v !== void 0) url.searchParams.set(k, String(v));
|
|
69
|
+
let res;
|
|
70
|
+
try {
|
|
71
|
+
res = await fetch(url, { method, headers: { "content-type": "application/json" }, body: body ? JSON.stringify({ ...body, run }) : void 0 });
|
|
72
|
+
} catch (err) {
|
|
73
|
+
throw new Error(`cannot reach simulator at ${root} (${err.message})`);
|
|
74
|
+
}
|
|
75
|
+
const text = await res.text();
|
|
76
|
+
const parsed = text ? safeJson(text) : {};
|
|
77
|
+
if (!res.ok) throw new Error(String(parsed.detail ?? text));
|
|
78
|
+
return parsed;
|
|
79
|
+
};
|
|
80
|
+
switch (cmd) {
|
|
81
|
+
case "scenarios":
|
|
82
|
+
return out(await call("GET", "/scenarios"));
|
|
83
|
+
case "status":
|
|
84
|
+
return out(await call("GET", "/status"));
|
|
85
|
+
case "state":
|
|
86
|
+
return out(await call("GET", "/state", void 0, { collection: kv.collection }));
|
|
87
|
+
case "events":
|
|
88
|
+
return out(await call("GET", "/events", void 0, { since: kv.since, limit: kv.limit, collection: kv.collection }));
|
|
89
|
+
case "log":
|
|
90
|
+
return out(await call("GET", "/log", void 0, { since: kv.since, limit: kv.limit }));
|
|
91
|
+
case "streams":
|
|
92
|
+
return out(await call("GET", "/streams"));
|
|
93
|
+
case "runs":
|
|
94
|
+
return out(await call("GET", "/runs"));
|
|
95
|
+
case "overrides":
|
|
96
|
+
return out(await call("GET", "/overrides"));
|
|
97
|
+
case "select":
|
|
98
|
+
return out(await call("POST", "/select", { scenario: kv.name, seed: kv.seed }));
|
|
99
|
+
case "reset":
|
|
100
|
+
return out(await call("POST", "/reset", { seed: kv.seed }));
|
|
101
|
+
case "step":
|
|
102
|
+
return out(await call("POST", "/step", { ms: kv.ms ?? 1e3 }));
|
|
103
|
+
case "clock":
|
|
104
|
+
return out(await call("POST", "/clock", { mode: kv.mode, speed: kv.speed }));
|
|
105
|
+
case "disconnect":
|
|
106
|
+
return out(await call("POST", "/streams/disconnect", kv));
|
|
107
|
+
case "pause":
|
|
108
|
+
return out(await call("POST", "/streams/pause", { id: kv.id, paused: kv.paused ?? true }));
|
|
109
|
+
case "publish":
|
|
110
|
+
return out(await call("POST", "/publish", { topic: kv.topic, data: kv.data ?? {} }));
|
|
111
|
+
case "override":
|
|
112
|
+
return out(await call("POST", "/overrides", kv));
|
|
113
|
+
case "override.clear":
|
|
114
|
+
return out(await call("DELETE", "/overrides", void 0, { matcher: kv.matcher, id: kv.id, method: kv.method }));
|
|
115
|
+
case "faults":
|
|
116
|
+
return out(await call("POST", "/faults", kv));
|
|
117
|
+
case "action":
|
|
118
|
+
return out(await call("POST", "/action", { name: kv.name, args: kv.args ?? {} }));
|
|
119
|
+
case "delete-run":
|
|
120
|
+
return out(await call("DELETE", "/runs"));
|
|
121
|
+
default:
|
|
122
|
+
throw new Error(`unknown command ${cmd}
|
|
123
|
+
${HELP}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
main().catch((err) => {
|
|
127
|
+
process.stderr.write(`scenario-sim: ${err instanceof Error ? err.message : String(err)}
|
|
128
|
+
`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
131
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * scenario-sim CLI — drive a running simulator's control plane, or serve one.\n *\n * scenario-sim serve <scenarios.ts|.js> [--port 4100] [--host 127.0.0.1] [--scenario name]\n * scenario-sim scenarios | status | state [collection=] | events [since=] | log | streams | runs | overrides\n * scenario-sim select name=<scenario> [seed=] | reset [seed=] | step ms=1000 | clock mode=manual\n * scenario-sim disconnect all=true drop=true | override matcher=/api/notes status=503 times=1 | faults latencyMs=800\n * scenario-sim publish topic=notes data='{\"type\":\"ping\"}' | action name=<action> args='{}'\n *\n * Options: --url <http://host:port[/__sim]> (or SCENARIO_SIM_URL; default http://localhost:4100/__sim)\n * --run <id> --json\n */\n\nconst HELP = `scenario-sim — deterministic scenario simulator control\n\n scenario-sim serve <scenarios.(ts|js)> [--port 4100] [--host 127.0.0.1] [--scenario name]\n scenario-sim scenarios | status | state [collection=] | events [since=] | log | streams | runs | overrides\n scenario-sim select name=<scenario> [seed=] | reset [seed=] | step ms=1000 | clock mode=manual speed=2\n scenario-sim disconnect all=true drop=true | override matcher=/api/notes status=503 times=1 | override.clear\n scenario-sim faults latencyMs=800 failMode=data | publish topic=notes data='{\"type\":\"ping\"}' | action name=<a> args='{}'\n\nOptions: --url <http://host:port[/__sim]> (or SCENARIO_SIM_URL; default http://localhost:4100/__sim)\n --run <id> --json`;\n\nfunction parse(argv: string[]) {\n const flags: Record<string, string | boolean> = {};\n const positional: string[] = [];\n const kv: Record<string, unknown> = {};\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (a.startsWith(\"--\")) {\n const [k, inline] = a.slice(2).split(\"=\", 2) as [string, string | undefined];\n if (inline !== undefined) flags[k] = inline;\n else if (argv[i + 1] !== undefined && !argv[i + 1]!.startsWith(\"--\") && !argv[i + 1]!.includes(\"=\")) flags[k] = argv[++i]!;\n else flags[k] = true;\n } else if (a.includes(\"=\") && positional.length > 0) {\n const idx = a.indexOf(\"=\");\n const v = a.slice(idx + 1);\n kv[a.slice(0, idx)] = /^(true|false|null|-?\\d+(\\.\\d+)?|\\[.*\\]|\\{.*\\}|\".*\")$/s.test(v) ? safeJson(v) : v;\n } else positional.push(a);\n }\n return { flags, positional, kv };\n}\n\nfunction safeJson(v: string): unknown {\n try {\n return JSON.parse(v);\n } catch {\n return v;\n }\n}\n\nasync function main() {\n const { flags, positional, kv } = parse(process.argv.slice(2));\n const cmd = positional[0];\n if (!cmd || cmd === \"help\" || flags.help) return void process.stdout.write(HELP + \"\\n\");\n\n if (cmd === \"serve\") {\n const file = positional[1];\n if (!file) throw new Error(\"serve needs a scenarios file\");\n const { pathToFileURL } = await import(\"node:url\");\n const { resolve } = await import(\"node:path\");\n const mod = (await import(pathToFileURL(resolve(file)).href)) as { default?: unknown; scenarios?: unknown };\n const scenarios = (Array.isArray(mod.default) ? mod.default : Array.isArray(mod.scenarios) ? mod.scenarios : (mod.default as { scenarios?: unknown })?.scenarios) as import(\"./core/scenario.js\").ScenarioDefinition[] | undefined;\n if (!scenarios?.length) throw new Error(\"scenarios file must export an array (default or `scenarios`)\");\n const { Simulator } = await import(\"./core/engine.js\");\n const { serveSimulator } = await import(\"./server.js\");\n const sim = new Simulator({ scenarios, defaultScenario: typeof flags.scenario === \"string\" ? flags.scenario : undefined, log: (l) => console.error(l) });\n const running = await serveSimulator(sim, { port: flags.port ? Number(flags.port) : 4100, host: typeof flags.host === \"string\" ? flags.host : \"127.0.0.1\", log: (l) => console.error(l) });\n console.error(`[scenario-sim] scenarios: ${scenarios.map((s) => s.name).join(\", \")} · control ${running.controlUrl}/status`);\n await new Promise(() => {});\n return;\n }\n\n const base = (typeof flags.url === \"string\" ? flags.url : process.env.SCENARIO_SIM_URL ?? \"http://localhost:4100/__sim\").replace(/\\/$/, \"\");\n const root = base.endsWith(\"/__sim\") || /\\/__[a-z]+$/.test(base) ? base : `${base}/__sim`;\n const run = typeof flags.run === \"string\" ? flags.run : undefined;\n const out = (v: unknown) => process.stdout.write((flags.json ? JSON.stringify(v) : JSON.stringify(v, null, 2)) + \"\\n\");\n const call = async (method: string, route: string, body?: Record<string, unknown>, query?: Record<string, unknown>) => {\n const url = new URL(`${root}${route}`);\n if (run) url.searchParams.set(\"run\", run);\n for (const [k, v] of Object.entries(query ?? {})) if (v !== undefined) url.searchParams.set(k, String(v));\n let res: Response;\n try {\n res = await fetch(url, { method, headers: { \"content-type\": \"application/json\" }, body: body ? JSON.stringify({ ...body, run }) : undefined });\n } catch (err) {\n throw new Error(`cannot reach simulator at ${root} (${(err as Error).message})`);\n }\n const text = await res.text();\n const parsed = text ? safeJson(text) : {};\n if (!res.ok) throw new Error(String((parsed as { detail?: string }).detail ?? text));\n return parsed;\n };\n\n switch (cmd) {\n case \"scenarios\": return out(await call(\"GET\", \"/scenarios\"));\n case \"status\": return out(await call(\"GET\", \"/status\"));\n case \"state\": return out(await call(\"GET\", \"/state\", undefined, { collection: kv.collection }));\n case \"events\": return out(await call(\"GET\", \"/events\", undefined, { since: kv.since, limit: kv.limit, collection: kv.collection }));\n case \"log\": return out(await call(\"GET\", \"/log\", undefined, { since: kv.since, limit: kv.limit }));\n case \"streams\": return out(await call(\"GET\", \"/streams\"));\n case \"runs\": return out(await call(\"GET\", \"/runs\"));\n case \"overrides\": return out(await call(\"GET\", \"/overrides\"));\n case \"select\": return out(await call(\"POST\", \"/select\", { scenario: kv.name, seed: kv.seed }));\n case \"reset\": return out(await call(\"POST\", \"/reset\", { seed: kv.seed }));\n case \"step\": return out(await call(\"POST\", \"/step\", { ms: kv.ms ?? 1000 }));\n case \"clock\": return out(await call(\"POST\", \"/clock\", { mode: kv.mode, speed: kv.speed }));\n case \"disconnect\": return out(await call(\"POST\", \"/streams/disconnect\", kv));\n case \"pause\": return out(await call(\"POST\", \"/streams/pause\", { id: kv.id, paused: kv.paused ?? true }));\n case \"publish\": return out(await call(\"POST\", \"/publish\", { topic: kv.topic, data: kv.data ?? {} }));\n case \"override\": return out(await call(\"POST\", \"/overrides\", kv));\n case \"override.clear\": return out(await call(\"DELETE\", \"/overrides\", undefined, { matcher: kv.matcher, id: kv.id, method: kv.method }));\n case \"faults\": return out(await call(\"POST\", \"/faults\", kv));\n case \"action\": return out(await call(\"POST\", \"/action\", { name: kv.name, args: kv.args ?? {} }));\n case \"delete-run\": return out(await call(\"DELETE\", \"/runs\"));\n default: throw new Error(`unknown command ${cmd}\\n${HELP}`);\n }\n}\n\nmain().catch((err) => {\n process.stderr.write(`scenario-sim: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|