@rewindkit/runtime 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/bundle-io.d.ts +8 -0
- package/dist/bundle-io.d.ts.map +1 -0
- package/dist/bundle.d.ts +83 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/configure.d.ts +27 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/enhancer.d.ts +10 -0
- package/dist/enhancer.d.ts.map +1 -0
- package/dist/env.d.ts +4 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +736 -0
- package/dist/index.js.map +19 -0
- package/dist/player.d.ts +13 -0
- package/dist/player.d.ts.map +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +497 -0
- package/dist/react/index.js.map +16 -0
- package/dist/recorder.d.ts +52 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/redaction.d.ts +13 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/replay.d.ts +10 -0
- package/dist/replay.d.ts.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["..\\src\\bundle.ts", "..\\src\\env.ts", "..\\src\\redaction.ts", "..\\src\\recorder.ts", "..\\src\\enhancer.ts", "..\\src\\replay.ts", "..\\src\\configure.ts", "..\\src\\player.ts", "..\\src\\bundle-io.ts", "..\\src\\index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"// SessionBundle + event types — the on-the-wire/export format (ADR-4 + specs/05 §2).\n//\n// TWO distinct version fields, never overloaded (ADR-4):\n// - `version: \"1.0\"` — the delta-format discriminant. Drives the fold math. Bump only when\n// the fold algorithm changes.\n// - `schemaVersion` — a separate envelope schema id. Bump on envelope/event-shape changes.\n// Starts at 2 because `feed_message` (specs/05 §2) is included.\n\nexport type ReplayEventType =\n | \"action\"\n | \"state_snapshot\"\n | \"component_render\"\n | \"route\"\n | \"feed_message\";\n\nexport interface BaseEvent {\n // Tolerate unknown future types — old players skip them (the fold never throws).\n type: ReplayEventType | (string & {});\n tsMs: number; // ms since startTime\n origin: \"host\" | string; // remote name\n storeKey: string; // 'host' or remote name\n seq: number; // monotonic per-bundle ordinal\n}\n\nexport interface ActionEvent extends BaseEvent {\n type: \"action\";\n action: { type: string; payload?: unknown };\n}\n\nexport interface StateSnapshotEvent extends BaseEvent {\n type: \"state_snapshot\";\n kind: \"keyframe\" | \"delta\";\n slices: Record<string, unknown>; // RAW values (the raw stash)\n removedSlices?: string[]; // slices deleted since the prior snapshot\n}\n\nexport interface ComponentRenderEvent extends BaseEvent {\n type: \"component_render\";\n componentId: string;\n props?: Record<string, unknown>; // redacted/serialized\n}\n\nexport interface RouteEvent extends BaseEvent {\n type: \"route\";\n path: string;\n search?: string;\n}\n\n// Captured by the app-owned mamps transport tap, NOT the Redux enhancer (specs/05 §2).\nexport interface FeedMessageEvent extends BaseEvent {\n type: \"feed_message\";\n channel: \"sow_page\" | \"tick\" | \"ack\" | \"oof\"; // kind of mamps traffic\n topic: string; // 'quotes' | 'orders' | ...\n request?: { filter?: string; orderBy?: string; topN?: number; skipN?: number };\n rows?: unknown[]; // sow_page rows (raw, redacted)\n matched?: number; // sow_page total match count\n key?: string; // tick/oof keyed record id (symbol or orderId)\n data?: unknown; // tick/oof record (raw, redacted)\n feedSeq?: number; // mamps Message.seq — server-wide \"newer-than\", tie-breaks tsMs (R-T9)\n}\n\nexport type ReplayEvent =\n | ActionEvent\n | StateSnapshotEvent\n | ComponentRenderEvent\n | RouteEvent\n | FeedMessageEvent\n | BaseEvent; // unknown-future fallthrough\n\nexport interface RemoteInfo {\n name: string;\n storeKey: string;\n registeredAtMs?: number; // absent => present from start (host/eager)\n}\n\nexport interface SessionBundle {\n version: \"1.0\"; // delta-format discriminant — NOT the schema id\n schemaVersion: number; // distinct envelope schema id (2: feed_message included)\n mode: \"state-snapshot\";\n sessionId: string;\n startTime: number; // epoch ms\n duration: number; // ms\n remotes: RemoteInfo[];\n events: ReplayEvent[]; // append-only, seq-ordered\n}\n\nexport const SCHEMA_VERSION = 2;\nexport const DELTA_FORMAT_VERSION = \"1.0\" as const;\n\n// Branded snapshot types make ADR-3's \"restore from RAW, never display\" a COMPILE-TIME guarantee.\n// A DisplaySnapshot cannot be passed where a RawSnapshot is required.\nexport interface RawSnapshot {\n readonly __brand: \"raw\";\n readonly slices: Record<string, unknown>;\n}\n\nexport interface DisplaySnapshot {\n readonly __brand: \"display\";\n readonly slices: Record<string, unknown>;\n}\n\n// The reconstructed feed window for replay (foldFeedAt), shaped like a mamps SowPage.\nexport interface FeedView {\n rows: unknown[];\n matched: number;\n}\n\nexport interface BundleDiff {\n // Events present in b but not a, keyed by seq+storeKey identity.\n added: ReplayEvent[];\n // Events present in a but not b.\n removed: ReplayEvent[];\n // storeKeys whose folded final state differs between the two bundles.\n changedStoreKeys: string[];\n}\n",
|
|
6
|
+
"// Env-gate — the ONE source of truth for \"are we in prod?\" (ADR-5 / guardrail 1).\n//\n// Vite statically replaces `process.env.NODE_ENV` with a literal at build, so no `process`\n// reference survives in the browser bundle. Where it isn't replaced and `process` is undefined\n// (raw browser, some test runners), the catch returns false — a dev-safe default.\n//\n// NEVER `typeof process` (undefined in a Vite browser bundle), NEVER `import.meta.env.DEV` alone\n// (undefined under node/test). This branch is NOT meaningfully unit-testable in bun (NODE_ENV is\n// set by the runner); it needs real-browser verification.\nexport const isProd = (): boolean => {\n try {\n return process.env.NODE_ENV === \"production\";\n } catch {\n return false;\n }\n};\n\n// Dev-only warnings that must fire AT MOST once per message (PII notice, fold anomalies, etc.).\nconst warned = new Set<string>();\n\nexport const warnOnce = (message: string): void => {\n // [fix auditor-L] Check isProd() BEFORE adding to the set so production never populates it.\n if (isProd()) return;\n if (warned.has(message)) return;\n warned.add(message);\n console.warn(`[rewindkit] ${message}`);\n};\n\n// Test-only: reset the warn-once memory so each test starts clean.\nexport const __resetWarnOnce = (): void => {\n warned.clear();\n};\n",
|
|
7
|
+
"// Redaction + display serialization (guardrail 5 + guardrail 3 + ADR-T2).\n//\n// TWO distinct, deliberately-separate transforms:\n// - __redact — what gets STORED in the raw stash / sent to the sink. Allowlist top-level\n// slices, deep-mask `redactKeys`, warn-once on PII. Lossless for kept values.\n// - serializeForDisplay — what the PANEL shows. Depth/size-capped, LOSSY (Map/Set/Date/Error\n// → previews). NEVER fed back into restore (re-drive uses the raw value).\nimport { warnOnce } from \"./env\";\n\nexport interface RedactOptions {\n includeStateKeys?: string[]; // ALLOWLIST of top-level slices; undefined => all (warn once)\n redactKeys?: string[]; // deep key names whose values are masked\n}\n\nconst REDACTED = \"‹redacted›\";\n\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n// Deep-mask any property whose key is in `redactKeys`, anywhere in the tree.\nconst maskKeys = (value: unknown, redactKeys: Set<string>): unknown => {\n if (Array.isArray(value)) return value.map((item) => maskKeys(item, redactKeys));\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (redactKeys.has(key)) {\n warnOnce(`redacted key \"${key}\" — value masked before capture`);\n out[key] = REDACTED;\n } else {\n out[key] = maskKeys(val, redactKeys);\n }\n }\n return out;\n }\n return value;\n};\n\n// Deep-mask `redactKeys` anywhere in an arbitrary value (no allowlist). Used by the feed tap on\n// `rows`/`data`, where the value IS the record and its own top-level keys must be checked (ADR-T2).\nexport const __maskDeep = (value: unknown, redactKeys: string[] | undefined): unknown =>\n redactKeys && redactKeys.length ? maskKeys(value, new Set(redactKeys)) : value;\n\n// Apply the slice allowlist + key-masking to a top-level state object before it is captured.\n// `value` is expected to be a top-level state record (slices keyed by name).\nexport const __redact = (value: unknown, opts: RedactOptions): unknown => {\n const redactKeys = new Set(opts.redactKeys ?? []);\n\n if (!isPlainObject(value)) {\n // Not a slice record (e.g. a single feed payload) — just mask keys.\n return redactKeys.size ? maskKeys(value, redactKeys) : value;\n }\n\n let entries = Object.entries(value);\n if (opts.includeStateKeys === undefined) {\n warnOnce(\n \"includeStateKeys is not set — capturing ALL slices. Set an allowlist in production (PII risk).\",\n );\n } else {\n const allow = new Set(opts.includeStateKeys);\n entries = entries.filter(([key]) => allow.has(key));\n }\n\n const out: Record<string, unknown> = {};\n for (const [key, val] of entries) {\n out[key] = redactKeys.size ? maskKeys(val, redactKeys) : val;\n }\n return out;\n};\n\nexport interface DisplayOptions {\n depth?: number; // max nesting depth (default 6)\n nodes?: number; // max total nodes rendered (default 200)\n strLen?: number; // max string length before truncation (default 10000)\n}\n\nconst DEFAULTS: Required<DisplayOptions> = { depth: 6, nodes: 200, strLen: 10000 };\n\n// Lossy, capped serialization for the panel value-tree. Renders Map/Set/Date/Error/empty\n// meaningfully so the inspector is readable. DISTINCT from the raw stash (guardrail 3).\nexport const serializeForDisplay = (value: unknown, opts: DisplayOptions = {}): unknown => {\n const cfg = { ...DEFAULTS, ...opts };\n let budget = cfg.nodes;\n\n const walk = (v: unknown, depth: number): unknown => {\n budget -= 1;\n if (budget < 0) return \"… (capped)\";\n\n if (typeof v === \"string\") {\n return v.length > cfg.strLen ? `${v.slice(0, cfg.strLen)}… (${v.length} chars)` : v;\n }\n if (typeof v === \"bigint\") return `${v}n`;\n if (typeof v === \"function\") return `‹function ${v.name || \"anonymous\"}›`;\n if (typeof v === \"symbol\") return v.toString();\n if (v === null || typeof v !== \"object\") return v; // number/boolean/undefined\n\n if (v instanceof Date) return `Date(${v.toISOString()})`;\n if (v instanceof Error) return `${v.name}: ${v.message}`;\n if (v instanceof Map) {\n if (v.size === 0) return \"Map(0) {}\";\n if (depth >= cfg.depth) return `Map(${v.size}) …`;\n const obj: Record<string, unknown> = {};\n for (const [k, val] of v) obj[String(k)] = walk(val, depth + 1);\n return { \"‹Map›\": obj };\n }\n if (v instanceof Set) {\n if (v.size === 0) return \"Set(0) {}\";\n if (depth >= cfg.depth) return `Set(${v.size}) …`;\n return { \"‹Set›\": [...v].map((item) => walk(item, depth + 1)) };\n }\n\n if (Array.isArray(v)) {\n if (v.length === 0) return [];\n if (depth >= cfg.depth) return `[Array(${v.length})]`;\n return v.map((item) => walk(item, depth + 1));\n }\n\n const keys = Object.keys(v as Record<string, unknown>);\n if (keys.length === 0) return {};\n if (depth >= cfg.depth) return `{…(${keys.length} keys)}`;\n const out: Record<string, unknown> = {};\n for (const key of keys) out[key] = walk((v as Record<string, unknown>)[key], depth + 1);\n return out;\n };\n\n return walk(value, 0);\n};\n",
|
|
8
|
+
"// The singleton recorder (ADR-1, ADR-2, ADR-3 suppression).\n//\n// ONE recorder for the whole federation, acquired first-write-wins on a globalThis key so plain\n// apps and MF setups both converge on a single instance. Holds the ONE SessionBundle, a RAW stash\n// (separate from any display serialization — guardrail 3), a reentrancy-counted suppression depth\n// (guardrail 4), the registered stores, and the live config.\n//\n// Honest zero-cost (guardrail 2): capture entry points build NO payload object unless recording is\n// armed AND suppression depth is 0. The guard comes first; allocation comes after.\nimport type { Store } from \"redux\";\nimport {\n DELTA_FORMAT_VERSION,\n SCHEMA_VERSION,\n type FeedMessageEvent,\n type ReplayEvent,\n type SessionBundle,\n} from \"./bundle\";\nimport { __maskDeep, __redact } from \"./redaction\";\nimport { warnOnce } from \"./env\";\n\nexport interface ReplaySink {\n push(event: ReplayEvent): void;\n flush?(): Promise<void>;\n}\n\nexport interface RecorderConfig {\n sink?: ReplaySink;\n redactKeys?: string[];\n includeStateKeys?: string[]; // allowlist; undefined => all (warn)\n includeFeedTopics?: string[]; // feed-topic allowlist; empty/undefined => record no feed\n keyframeEvery: number; // FULL/DELTA cadence in actions (default 25)\n fullKeyframeRatio: number; // FULL every keyframeEvery*ratio (default 8)\n sample: number; // 0..1 action sampling (default 1)\n maxEventsPerSecond: number; // throttle cap (default 200)\n}\n\nconst DEFAULT_CONFIG: RecorderConfig = {\n keyframeEvery: 25,\n fullKeyframeRatio: 8,\n sample: 1,\n maxEventsPerSecond: 200,\n};\n\nexport interface Suppression {\n depth: number; // 0 => armed, >0 => suppressed\n}\n\nconst UNSERIALIZABLE = \"__unserializable\" as const;\n\nexport interface Recorder {\n readonly suppression: Suppression;\n config: RecorderConfig;\n recording: boolean;\n // origin tag for events that don't name a store (default \"host\").\n origin: string;\n // injectable clock so tests stay deterministic (defaults to Date.now).\n now: () => number;\n\n start(): void;\n stop(): void;\n isRecording(): boolean;\n\n configure(opts: Partial<RecorderConfig> & { origin?: string; now?: () => number }): void;\n\n registerStore(name: string, store: Store): void;\n unregisterStore(name: string): void;\n getStore(name: string): Store | undefined;\n\n // capture entry points — no-ops unless recording && suppression.depth === 0.\n captureAction(storeKey: string, action: { type: string; payload?: unknown }): void;\n captureKeyframe(storeKey: string): void;\n captureRoute(path: string, search?: string): void;\n pushFeedEvent(\n ev: Omit<FeedMessageEvent, \"tsMs\" | \"seq\" | \"type\" | \"origin\" | \"storeKey\"> & {\n storeKey?: string;\n },\n ): void;\n\n // suppression — covers BOTH the enhancer and the feed tap (specs/05 §4).\n suppress(): void;\n unsuppress(): void;\n\n getBundle(): SessionBundle;\n}\n\nconst KEY = \"__REPLAYKIT_RECORDER__\";\n\nconst createRecorder = (): Recorder => {\n const bundle: SessionBundle = {\n version: DELTA_FORMAT_VERSION,\n schemaVersion: SCHEMA_VERSION,\n mode: \"state-snapshot\",\n // [fix devil-L2] crypto.randomUUID() gives a proper UUID; Math.random() had only ~4 bits of collision resistance per segment.\n sessionId: crypto.randomUUID(),\n startTime: Date.now(),\n duration: 0,\n remotes: [],\n events: [],\n };\n\n const stores = new Map<string, Store>();\n // raw stash: last captured raw slice values per storeKey — the source of truth for fold/restore.\n const rawStash = new Map<string, Record<string, unknown>>();\n // [fix devil-H2] Track PREVIOUS LIVE slice references (pre-clone). Redux only returns a new\n // object reference for a slice that actually changed (reducer immutability contract). Comparing\n // live refs with Object.is correctly identifies changed slices; comparing clones always differs\n // because structuredClone always produces a fresh reference.\n const prevLiveSlices = new Map<string, Record<string, unknown>>();\n // per-store action counter driving the keyframe/delta cadence.\n const actionCount = new Map<string, number>();\n // throttle window: events emitted in the current 1s bucket.\n let windowStart = 0;\n let windowCount = 0;\n\n const suppression: Suppression = { depth: 0 };\n\n const rec: Recorder = {\n suppression,\n config: { ...DEFAULT_CONFIG },\n recording: false,\n origin: \"host\",\n now: () => Date.now(),\n\n start() {\n rec.recording = true;\n // Initial keyframe for every already-registered store so fold has a base from t=0 on.\n for (const [name, store] of stores) emitKeyframe(name, store.getState(), true);\n },\n stop() {\n rec.recording = false;\n // [fix devil-H3b] Do NOT reset suppression.depth here — returnToLive() in the ReplayHandle\n // drives the depth accounting through the normal unsuppress path. If we zeroed depth\n // directly and returnToLive() then called unsuppress(), depth would go negative and throw.\n },\n isRecording() {\n return rec.recording;\n },\n\n configure(opts) {\n const { origin, now, ...cfg } = opts;\n rec.config = { ...rec.config, ...cfg };\n if (origin !== undefined) rec.origin = origin;\n if (now !== undefined) {\n rec.now = now;\n // Re-anchor startTime to the injected clock so tsMs (= now - startTime) stays consistent.\n bundle.startTime = now();\n }\n },\n\n registerStore(name, store) {\n if (stores.has(name)) {\n warnOnce(`store \"${name}\" already registered — names must be unique (ignoring re-register)`);\n return;\n }\n stores.set(name, store);\n const registeredAtMs = rec.now() - bundle.startTime;\n bundle.remotes.push({ name, storeKey: name, registeredAtMs });\n // Lazy remote: synthetic keyframe at registration so state exists from this point on.\n // State BEFORE registration stays honestly absent (no entry in the raw stash / fold).\n if (rec.recording) emitKeyframe(name, store.getState(), true);\n },\n unregisterStore(name) {\n stores.delete(name);\n },\n getStore(name) {\n return stores.get(name);\n },\n\n captureAction(storeKey, action) {\n if (!armed()) return; // guardrail 2: no payload built past this point unless armed\n if (rec.config.sample < 1 && Math.random() > rec.config.sample) return;\n if (throttled()) return;\n // TODO(redaction, BTE): action payloads are not yet masked — redactKeys currently covers\n // state slices + feed only. A token/password in an action payload leaks into the bundle.\n // Fix: payload: redactKeys?.length ? __maskDeep(action.payload, redactKeys) : action.payload\n append({\n type: \"action\",\n tsMs: tsMs(),\n origin: rec.origin,\n storeKey,\n seq: nextSeq(),\n action: { type: action.type, payload: action.payload },\n });\n },\n\n captureKeyframe(storeKey) {\n if (!armed()) return;\n const store = stores.get(storeKey);\n if (!store) return;\n const count = (actionCount.get(storeKey) ?? 0) + 1;\n actionCount.set(storeKey, count);\n const fullEvery = rec.config.keyframeEvery * rec.config.fullKeyframeRatio;\n if (count % rec.config.keyframeEvery !== 0) return; // not a snapshot tick\n const full = count % fullEvery === 0;\n emitKeyframe(storeKey, store.getState(), full);\n },\n\n captureRoute(path, search) {\n if (!armed()) return;\n if (throttled()) return;\n append({\n type: \"route\",\n tsMs: tsMs(),\n origin: rec.origin,\n storeKey: rec.origin,\n seq: nextSeq(),\n path,\n search,\n });\n },\n\n pushFeedEvent(ev) {\n if (!armed()) return;\n const topics = rec.config.includeFeedTopics;\n if (!topics || topics.length === 0) {\n warnOnce(\"includeFeedTopics is empty — no feed_message recorded. Allowlist a topic.\");\n return;\n }\n if (!topics.includes(ev.topic)) return;\n if (throttled()) return;\n // [fix devil-M5/auditor] Redact rows/data at capture (ADR-T2) and structuredClone them so\n // the caller mutating their array/object after pushFeedEvent can't corrupt the bundle.\n const redactKeys = rec.config.redactKeys;\n const rows = ev.rows\n ? (structuredClone(__maskDeep(ev.rows, redactKeys)) as unknown[])\n : ev.rows;\n const data =\n ev.data !== undefined ? structuredClone(__maskDeep(ev.data, redactKeys)) : ev.data;\n append({\n ...ev,\n rows,\n data,\n type: \"feed_message\",\n tsMs: tsMs(),\n origin: rec.origin,\n storeKey: ev.storeKey ?? rec.origin,\n seq: nextSeq(),\n });\n },\n\n suppress() {\n suppression.depth += 1;\n },\n unsuppress() {\n suppression.depth -= 1;\n if (suppression.depth < 0) {\n suppression.depth = 0;\n if (!isProdSafe()) throw new Error(\"[replaykit] suppression depth went negative\");\n }\n },\n\n getBundle() {\n bundle.duration = rawStashMaxTs();\n return bundle;\n },\n };\n\n // ---- internals ----\n\n const armed = (): boolean => rec.recording && suppression.depth === 0;\n\n const tsMs = (): number => rec.now() - bundle.startTime;\n\n const nextSeq = (): number => bundle.events.length;\n\n const append = (event: ReplayEvent): void => {\n bundle.events.push(event);\n rec.config.sink?.push(event);\n };\n\n const throttled = (): boolean => {\n const t = rec.now();\n if (t - windowStart >= 1000) {\n windowStart = t;\n windowCount = 0;\n }\n if (windowCount >= rec.config.maxEventsPerSecond) return true;\n windowCount += 1;\n return false;\n };\n\n // Build a keyframe or delta from the current raw state, applying the allowlist + masking,\n // updating the raw stash, and appending the snapshot event.\n const emitKeyframe = (storeKey: string, state: unknown, full: boolean): void => {\n // `state` is the Redux root state — a record of top-level slice references.\n const liveSlices = (state && typeof state === \"object\" && !Array.isArray(state))\n ? (state as Record<string, unknown>)\n : {} as Record<string, unknown>;\n\n const redacted = __redact(state, {\n includeStateKeys: rec.config.includeStateKeys,\n redactKeys: rec.config.redactKeys,\n }) as Record<string, unknown>;\n\n const prevStash = rawStash.get(storeKey);\n const prevLive = prevLiveSlices.get(storeKey);\n\n if (full || !prevStash || !prevLive) {\n // Full keyframe: clone every allowed slice.\n const cloned = cloneSlices(redacted);\n rawStash.set(storeKey, cloned);\n prevLiveSlices.set(storeKey, { ...liveSlices });\n append({\n type: \"state_snapshot\",\n kind: \"keyframe\",\n tsMs: tsMs(),\n origin: rec.origin,\n storeKey,\n seq: nextSeq(),\n slices: cloned,\n });\n return;\n }\n\n // [fix devil-H2] Delta: use LIVE slice references for change detection, not cloned values.\n // Redux returns the same reference for unchanged slices, a new reference for changed ones.\n // Only clone slices that actually changed to keep the fast path truly cheap.\n const changedStash: Record<string, unknown> = {};\n const nextStash: Record<string, unknown> = { ...prevStash };\n for (const key of Object.keys(redacted)) {\n if (!Object.is(prevLive[key], liveSlices[key])) {\n // This slice changed — clone and record it.\n const clonedVal = cloneSingle(redacted[key]);\n changedStash[key] = clonedVal;\n nextStash[key] = clonedVal;\n }\n // Unchanged slice: nextStash already holds the old clone from prevStash.\n }\n const removed = Object.keys(prevStash).filter((key) => !(key in redacted));\n for (const key of removed) delete nextStash[key];\n\n rawStash.set(storeKey, nextStash);\n prevLiveSlices.set(storeKey, { ...liveSlices });\n append({\n type: \"state_snapshot\",\n kind: \"delta\",\n tsMs: tsMs(),\n origin: rec.origin,\n storeKey,\n seq: nextSeq(),\n slices: changedStash,\n ...(removed.length ? { removedSlices: removed } : {}),\n });\n };\n\n // structuredClone a single slice value; returns __unserializable sentinel on failure (ADR-2 / R-2).\n const cloneSingle = (val: unknown): unknown => {\n try {\n return structuredClone(val);\n } catch {\n return { [UNSERIALIZABLE]: true };\n }\n };\n\n // Clone every slice in a state record (used for full keyframes).\n const cloneSlices = (state: Record<string, unknown>): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(state)) out[key] = cloneSingle(val);\n return out;\n };\n\n const rawStashMaxTs = (): number => {\n let max = 0;\n for (const ev of bundle.events) if (ev.tsMs > max) max = ev.tsMs;\n return max;\n };\n\n return rec;\n};\n\nconst isProdSafe = (): boolean => {\n try {\n return process.env.NODE_ENV === \"production\";\n } catch {\n return false;\n }\n};\n\n// TODO(BTE, auditor-MEDIUM): ComponentRenderEvent.props has no capture path yet. When component\n// capture is built, props MUST pass through __redact/redactKeys before storage — the same\n// allowlist + mask contract as state slices. Do not ship component capture without it.\n\n// TODO(BTE, R-1): globalThis key is not tamper-proof across multiple realms (iframes/workers).\n// A hostile frame could pre-populate __REPLAYKIT_RECORDER__ with a fake object. Out of scope for\n// v1 (single-realm assumption); document in the security posture before cross-origin iframe use.\n\n// Acquire the singleton: adopt an existing instance, else pin the first one (first-write-wins).\n// Module evaluation is single-threaded per realm so check-then-set can't interleave within a realm\n// (multi-realm is the accepted out-of-scope gap, R-1).\nexport const acquireRecorder = (): Recorder => {\n const g = globalThis as Record<string, unknown>;\n const existing = g[KEY] as Recorder | undefined;\n if (existing) return existing;\n const r = createRecorder();\n g[KEY] = r;\n return (g[KEY] as Recorder) ?? r;\n};\n\n// Test-only: read the current installed recorder without creating one.\nexport const __peekRecorder = (): Recorder | undefined =>\n (globalThis as Record<string, unknown>)[KEY] as Recorder | undefined;\n\n// Test-only: drop the singleton so each test starts from a clean realm.\nexport const __resetRecorder = (): void => {\n delete (globalThis as Record<string, unknown>)[KEY];\n};\n",
|
|
9
|
+
"// replayEnhancer — wraps the store to (a) handle the internal __REPLAYKIT_RESTORE action by\n// replacing named raw slices, and (b) capture actions + keyframes through the singleton recorder.\n//\n// Honest zero-cost (guardrail 2): if no recorder is installed (the singleton was never acquired by\n// configureReplay), the enhancer adds only one identity check per dispatch and builds NOTHING.\nimport type { Reducer, StoreEnhancer, StoreEnhancerStoreCreator, UnknownAction } from \"redux\";\nimport { __peekRecorder } from \"./recorder\";\n\nexport const RESTORE_TYPE = \"__REPLAYKIT_RESTORE\";\n\nexport interface RestoreAction {\n type: typeof RESTORE_TYPE;\n // raw slice values to splice into the named store's state.\n slices: Record<string, unknown>;\n removedSlices?: string[];\n [extra: string]: unknown;\n}\n\nconst isRestore = (action: unknown): action is RestoreAction =>\n typeof action === \"object\" &&\n action !== null &&\n (action as { type?: unknown }).type === RESTORE_TYPE;\n\n// Wrap a reducer so __REPLAYKIT_RESTORE replaces the named slices with their RAW values (ADR-3).\nconst wrapReducer =\n <S>(reducer: Reducer<S>): Reducer<S> =>\n (state, action) => {\n if (isRestore(action)) {\n const base = (state ?? {}) as Record<string, unknown>;\n const next = { ...base, ...action.slices };\n for (const key of action.removedSlices ?? []) delete next[key];\n return next as S;\n }\n return reducer(state, action as UnknownAction);\n };\n\nconst enhancer = (createStore: StoreEnhancerStoreCreator): StoreEnhancerStoreCreator =>\n ((reducer: Reducer, preloadedState?: unknown) => {\n const store = createStore(wrapReducer(reducer), preloadedState);\n\n const dispatch = ((action: UnknownAction) => {\n const recorder = __peekRecorder();\n // No recorder installed => pure pass-through, zero allocation.\n if (!recorder) return store.dispatch(action);\n\n // __REPLAYKIT_RESTORE flows through untouched; suppression is owned by the seek path.\n const result = store.dispatch(action);\n\n // The recorder's own guards make these no-ops unless armed; nothing is built past the guard.\n if (!isRestore(action) && typeof action.type === \"string\") {\n const storeKey = recorder.origin;\n recorder.captureAction(storeKey, {\n type: action.type,\n payload: (action as { payload?: unknown }).payload,\n });\n recorder.captureKeyframe(storeKey);\n }\n return result;\n }) as typeof store.dispatch;\n\n return { ...store, dispatch };\n }) as unknown as StoreEnhancerStoreCreator;\n\nexport const replayEnhancer: StoreEnhancer = enhancer as StoreEnhancer;\n",
|
|
10
|
+
"// Re-drive + the live/history signal (ADR-3, NOQ-2, NOQ-4, NOQ-6).\n//\n// THE single source of truth for replay mode is the module-level `mode` below. It is flipped by\n// ONE synchronous transition (`setMode`) that ALSO bumps recorder suppression in the SAME tick, so\n// the two flags can never skew (NOQ-2). Side-channel handlers (WS onmessage) MUST read\n// getReplayMode()/subscribeReplayMode — never the React hook, which lags the event loop (TQ1-A).\nimport type { RawSnapshot } from \"./bundle\";\nimport type { ReplayPlayer } from \"./player\";\nimport { acquireRecorder } from \"./recorder\";\nimport { RESTORE_TYPE, type RestoreAction } from \"./enhancer\";\nimport { warnOnce } from \"./env\";\n\nexport type ReplayMode = \"live\" | \"history\";\n\nlet mode: ReplayMode = \"live\";\nconst listeners = new Set<(m: ReplayMode) => void>();\n\nexport const getReplayMode = (): ReplayMode => mode; // synchronous read — use in side-channels\n\n// Deliver the CURRENT mode synchronously on subscribe (NOQ-4 initial-value semantics) so a remote\n// mounting mid-history gates correctly before its first dispatch. Returns an unsubscribe.\nexport const subscribeReplayMode = (cb: (m: ReplayMode) => void): (() => void) => {\n listeners.add(cb);\n cb(mode);\n return () => {\n listeners.delete(cb);\n };\n};\n\nconst notify = (): void => {\n for (const cb of listeners) cb(mode);\n};\n\n// The ONE atomic transition: flip mode AND bump suppression together, in one synchronous tick.\n// history => suppression on (depth+1); back to live => suppression off (depth-1). Never skews.\nconst setMode = (next: ReplayMode): void => {\n if (next === mode) return;\n const recorder = acquireRecorder();\n if (next === \"history\") {\n recorder.suppress();\n mode = \"history\";\n } else {\n mode = \"live\";\n recorder.unsuppress();\n }\n notify();\n};\n\n// Apply a RAW snapshot to a store (ADR-3). RAW value only — branded RawSnapshot makes feeding\n// display data here a compile error.\n//\n// [fix devil-C2] Self-suppresses around the restore dispatch (reentrancy-counted, so nesting\n// under seek()'s own suppress is safe). Without this a public bare call while recording would let\n// a synchronous store-subscriber reaction get captured — a feedback echo identical to the one\n// seek() guards against.\nexport const applyReplayState = (snapshot: RawSnapshot, storeKey?: string): void => {\n const recorder = acquireRecorder();\n const key = storeKey ?? recorder.origin;\n const store = recorder.getStore(key);\n if (!store) {\n warnOnce(`applyReplayState: no store registered for \"${key}\"`);\n return;\n }\n recorder.suppress();\n try {\n const action: RestoreAction = { type: RESTORE_TYPE, slices: { ...snapshot.slices } };\n store.dispatch(action);\n } finally {\n recorder.unsuppress();\n }\n};\n\n// Seek to tMs: the synchronous suppressed batch (NOQ-6). Fold ALL stores first (pure), then apply\n// every restore inside ONE synchronous suppress/unsuppress so the reentrancy counter never leaks\n// across an await. Cross-store atomicity (Q2) falls out for free.\nexport const seek = (player: ReplayPlayer, tMs: number): void => {\n const states = player.statesAt(tMs); // pure fold, no dispatch\n if (mode !== \"history\") setMode(\"history\"); // atomic: mode + suppression together\n\n const recorder = acquireRecorder();\n recorder.suppress(); // extra guard around the batch itself (reentrancy-counted)\n try {\n for (const [storeKey, snap] of Object.entries(states)) {\n if (snap) applyReplayState(snap, storeKey);\n }\n } finally {\n recorder.unsuppress();\n }\n};\n\n// Return to live: flip back to live + drop the suppression bump, atomically.\nexport const returnToLive = (): void => {\n setMode(\"live\");\n // [fix devil-H3a] The leak guard belongs HERE, after the mode flip, not inside seek() where\n // mode is always \"history\" making the check dead. After returning to live, depth must be 0\n // (the setMode(\"live\") call dropped the mode-suppression; any seek()-internal suppress was\n // already released in seek's finally block). A non-zero depth means a caller leaked.\n const recorder = acquireRecorder();\n if (recorder.suppression.depth !== 0) {\n warnOnce(\"suppression depth non-zero after returnToLive — a suppress/unsuppress pair leaked\");\n }\n};\n\n// Test-only: reset the module-level mode + listeners between tests.\nexport const __resetReplayMode = (): void => {\n mode = \"live\";\n listeners.clear();\n};\n",
|
|
11
|
+
"// configureReplay — the opt-in entry point. Recording is OFF by default and gated by isProd()\n// unless `force` is set (ADR-5). Returns a ReplayHandle to start/stop/inspect.\nimport type { Store } from \"redux\";\nimport { acquireRecorder } from \"./recorder\";\nimport type { ReplaySink } from \"./recorder\";\nimport type { SessionBundle } from \"./bundle\";\nimport { isProd, warnOnce } from \"./env\";\nimport { returnToLive } from \"./replay\";\n\nexport interface ConfigureReplayOptions {\n store?: Store; // shared-store topology — registered as \"host\"\n sink?: ReplaySink; // app-owned transport; default = in-memory bundle only\n redactKeys?: string[];\n includeStateKeys?: string[]; // ALLOWLIST of slices; undefined => all (warn)\n includeFeedTopics?: string[]; // feed-topic allowlist; empty => record no feed\n keyframeEvery?: number; // default 25 actions\n fullKeyframeRatio?: number; // default 8\n sample?: number; // 0..1, default 1\n maxEventsPerSecond?: number; // default 200\n origin?: string; // origin tag for this realm's events (default \"host\")\n now?: () => number; // injectable clock (tests / custom epoch)\n force?: boolean; // allow recording in prod (staging demos)\n}\n\nexport interface ReplayHandle {\n start(): void;\n stop(): void;\n isRecording(): boolean;\n getBundle(): SessionBundle;\n}\n\nexport const configureReplay = (opts: ConfigureReplayOptions = {}): ReplayHandle => {\n const recorder = acquireRecorder();\n\n recorder.configure({\n sink: opts.sink,\n redactKeys: opts.redactKeys,\n includeStateKeys: opts.includeStateKeys,\n includeFeedTopics: opts.includeFeedTopics,\n ...(opts.keyframeEvery !== undefined ? { keyframeEvery: opts.keyframeEvery } : {}),\n ...(opts.fullKeyframeRatio !== undefined ? { fullKeyframeRatio: opts.fullKeyframeRatio } : {}),\n ...(opts.sample !== undefined ? { sample: opts.sample } : {}),\n ...(opts.maxEventsPerSecond !== undefined\n ? { maxEventsPerSecond: opts.maxEventsPerSecond }\n : {}),\n origin: opts.origin,\n now: opts.now,\n });\n\n // Shared-store topology: register the host store under \"host\" so keyframes can read its state.\n if (opts.store) recorder.registerStore(opts.origin ?? \"host\", opts.store);\n\n const allowed = opts.force === true || !isProd();\n if (!allowed) {\n warnOnce(\"recording disabled in production — pass force: true to enable (staging demos only).\");\n }\n\n return {\n start() {\n if (!allowed) return;\n recorder.start();\n },\n stop() {\n // [fix devil-H3b] Reset mode to live AND suppression depth together so a later start() records.\n recorder.stop();\n returnToLive();\n },\n isRecording() {\n return recorder.isRecording();\n },\n getBundle() {\n // [fix devil-M2] Return a deep clone so a held reference doesn't keep growing as the live\n // bundle accumulates new events. The caller gets a stable point-in-time snapshot.\n return structuredClone(recorder.getBundle());\n },\n };\n};\n\nexport const registerStore = (name: string, store: Store): void => {\n acquireRecorder().registerStore(name, store);\n};\n\nexport const unregisterStore = (name: string): void => {\n acquireRecorder().unregisterStore(name);\n};\n",
|
|
12
|
+
"// Player / time-travel (ADR-4 tolerant fold + specs/05 foldFeedAt + diffBundles).\n//\n// The fold is skip-unknown and NEVER throws: action/route/render/feed/UNKNOWN event types are\n// silently skipped by state folding; a delta before any keyframe warns once and is skipped.\n// `undefined` return = honestly absent (store not yet registered) — distinct from `{}` (known-empty).\nimport {\n type BundleDiff,\n type FeedMessageEvent,\n type FeedView,\n type RawSnapshot,\n type RouteEvent,\n type SessionBundle,\n type StateSnapshotEvent,\n} from \"./bundle\";\nimport { warnOnce } from \"./env\";\n\nconst asRaw = (slices: Record<string, unknown>): RawSnapshot => ({ __brand: \"raw\", slices });\n\n// TODO(BTE, devil-M4): foldStateAt is O(events) — it scans every event on every call. For long\n// sessions with dense dispatch this becomes a scale ceiling. Future fix: build a per-storeKey\n// keyframe index so fold can binary-search to the nearest keyframe and replay only the trailing\n// deltas. Not implementing now; the correctness contract is unchanged.\n\n// Fold keyframe+deltas for one store up to tMs. Returns undefined if no keyframe exists by tMs.\nexport const foldStateAt = (\n bundle: SessionBundle,\n storeKey: string,\n tMs: number,\n): RawSnapshot | undefined => {\n let acc: Record<string, unknown> | undefined;\n for (const ev of bundle.events) {\n if (ev.tsMs > tMs) break;\n if (ev.storeKey !== storeKey) continue;\n if (ev.type !== \"state_snapshot\") continue; // action/route/render/feed/UNKNOWN -> skip silently\n const snap = ev as StateSnapshotEvent;\n if (snap.kind === \"keyframe\") {\n acc = { ...snap.slices };\n } else if (snap.kind === \"delta\") {\n if (!acc) {\n warnOnce(\"delta before keyframe — skipped\");\n continue;\n }\n acc = { ...acc, ...snap.slices };\n for (const k of snap.removedSlices ?? []) delete acc[k];\n } else {\n warnOnce(`unknown snapshot kind — skipped`);\n }\n }\n return acc === undefined ? undefined : asRaw(acc);\n};\n\n// All storeKeys that ever appear in a state_snapshot, so statesAt() can fold each.\nconst snapshotStoreKeys = (bundle: SessionBundle): Set<string> => {\n const keys = new Set<string>();\n for (const ev of bundle.events) {\n if (ev.type === \"state_snapshot\") keys.add(ev.storeKey);\n }\n return keys;\n};\n\n// Reconstruct the feed window for one origin+topic up to tMs (specs/05 §2). Ordered by bundle seq,\n// tie-broken by feedSeq (mamps server seq), NEVER tsMs alone (R-T9). undefined => feed not yet seen.\nexport const foldFeedAt = (\n bundle: SessionBundle,\n origin: string,\n topic: string,\n tMs: number,\n): FeedView | undefined => {\n const relevant = bundle.events\n .filter(\n (ev): ev is FeedMessageEvent =>\n ev.type === \"feed_message\" &&\n ev.origin === origin &&\n (ev as FeedMessageEvent).topic === topic &&\n ev.tsMs <= tMs,\n )\n .sort((a, b) => a.seq - b.seq || (a.feedSeq ?? 0) - (b.feedSeq ?? 0));\n\n if (relevant.length === 0) return undefined;\n\n // Base window = the latest sow_page <= tMs; then apply ticks/oof by key in order.\n let base: FeedMessageEvent | undefined;\n for (const ev of relevant) if (ev.channel === \"sow_page\") base = ev;\n if (!base) return undefined;\n\n const rows = new Map<string, unknown>();\n let nextKey = 0;\n for (const row of base.rows ?? []) {\n const key = keyOf(row) ?? `__idx${nextKey++}`;\n rows.set(key, row);\n }\n let matched = base.matched ?? rows.size;\n\n // [fix devil-H5] Apply ticks/oof that came after the base sow_page.\n // Track the highest feedSeq seen per key so a later-arriving message with a *lower* feedSeq\n // (stale mamps server frame) never overwrites a fresher one. feedSeq is the mamps \"newer-than\"\n // monotonic counter — it is the correct authority for \"which tick is newest\" (R-T9).\n const lastFeedSeq = new Map<string, number>();\n const baseSeq = base.seq;\n for (const ev of relevant) {\n if (ev.seq <= baseSeq) continue;\n const key = ev.key;\n if (ev.channel === \"tick\" || ev.channel === \"ack\") {\n if (key === undefined) continue;\n const evFeedSeq = ev.feedSeq ?? 0;\n const seenFeedSeq = lastFeedSeq.get(key) ?? -1;\n if (evFeedSeq >= seenFeedSeq) {\n rows.set(key, ev.data);\n lastFeedSeq.set(key, evFeedSeq);\n }\n } else if (ev.channel === \"oof\") {\n if (key !== undefined && rows.delete(key)) {\n matched = Math.max(0, matched - 1);\n lastFeedSeq.delete(key);\n }\n }\n }\n\n return { rows: [...rows.values()], matched };\n};\n\nconst keyOf = (row: unknown): string | undefined => {\n if (row && typeof row === \"object\") {\n const r = row as Record<string, unknown>;\n for (const cand of [\"symbol\", \"orderId\", \"id\", \"key\"]) {\n if (typeof r[cand] === \"string\") return r[cand] as string;\n }\n }\n return undefined;\n};\n\nexport const routeAt = (bundle: SessionBundle, tMs: number): RouteEvent | undefined => {\n let last: RouteEvent | undefined;\n for (const ev of bundle.events) {\n if (ev.tsMs > tMs) break;\n if (ev.type === \"route\") last = ev as RouteEvent;\n }\n return last;\n};\n\n// [fix devil-M3] Event identity includes a content fingerprint so the same seq+type+tsMs with\n// different payload (e.g. two differently-valued actions at the same position) reports as changed.\nconst eventId = (ev: SessionBundle[\"events\"][number]): string => {\n // Cheap content hash: JSON-stringify the event-type-specific payload field.\n let content = \"\";\n const e = ev as unknown as Record<string, unknown>;\n if (ev.type === \"action\") content = JSON.stringify(e[\"action\"]);\n else if (ev.type === \"state_snapshot\") content = JSON.stringify(e[\"slices\"]);\n else if (ev.type === \"feed_message\") content = JSON.stringify(e[\"data\"] ?? e[\"rows\"]);\n else if (ev.type === \"route\") content = String(e[\"path\"]);\n return `${ev.seq}|${ev.storeKey}|${ev.type}|${ev.tsMs}|${content}`;\n};\n\nexport const diffBundles = (a: SessionBundle, b: SessionBundle): BundleDiff => {\n const aIds = new Set(a.events.map(eventId));\n const bIds = new Set(b.events.map(eventId));\n const added = b.events.filter((ev) => !aIds.has(eventId(ev)));\n const removed = a.events.filter((ev) => !bIds.has(eventId(ev)));\n\n const tA = a.duration;\n const tB = b.duration;\n const keys = new Set([...snapshotStoreKeys(a), ...snapshotStoreKeys(b)]);\n const changedStoreKeys: string[] = [];\n for (const key of keys) {\n const sa = foldStateAt(a, key, tA);\n const sb = foldStateAt(b, key, tB);\n if (JSON.stringify(sa?.slices) !== JSON.stringify(sb?.slices)) changedStoreKeys.push(key);\n }\n\n return { added, removed, changedStoreKeys };\n};\n\nexport interface ReplayPlayer {\n statesAt(tMs: number): Record<string, RawSnapshot | undefined>;\n routeAt(tMs: number): RouteEvent | undefined;\n feedAt(origin: string, topic: string, tMs: number): FeedView | undefined;\n diffBundles(a: SessionBundle, b: SessionBundle): BundleDiff;\n}\n\nexport const createPlayer = (bundle: SessionBundle): ReplayPlayer => ({\n statesAt(tMs) {\n const out: Record<string, RawSnapshot | undefined> = {};\n for (const key of snapshotStoreKeys(bundle)) out[key] = foldStateAt(bundle, key, tMs);\n return out;\n },\n routeAt(tMs) {\n return routeAt(bundle, tMs);\n },\n feedAt(origin, topic, tMs) {\n return foldFeedAt(bundle, origin, topic, tMs);\n },\n diffBundles(a, b) {\n return diffBundles(a, b);\n },\n});\n",
|
|
13
|
+
"// Bundle export/import (ADR-4). Export deep-clones the raw stash so the caller can't mutate the\n// live recorder's bundle; import validates before returning.\nimport type { SessionBundle } from \"./bundle\";\nimport { DELTA_FORMAT_VERSION, SCHEMA_VERSION } from \"./bundle\";\nimport { acquireRecorder } from \"./recorder\";\n\n// [fix devil-M1] Validate a bundle before use. A wrong `version` (e.g. \"2.0\") means the fold\n// algorithm doesn't apply, producing garbage. Reject rather than silently corrupt.\nexport class BundleValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BundleValidationError\";\n }\n}\n\nexport const validateBundle = (b: unknown): SessionBundle => {\n if (!b || typeof b !== \"object\") throw new BundleValidationError(\"bundle is not an object\");\n const bundle = b as Record<string, unknown>;\n\n if (bundle.version !== DELTA_FORMAT_VERSION) {\n throw new BundleValidationError(\n `unsupported bundle version \"${String(bundle.version)}\" — expected \"${DELTA_FORMAT_VERSION}\". ` +\n `A different fold algorithm may be required.`,\n );\n }\n if (typeof bundle.schemaVersion !== \"number\" || bundle.schemaVersion > SCHEMA_VERSION) {\n throw new BundleValidationError(\n `unknown schemaVersion ${String(bundle.schemaVersion)} — this runtime understands up to ${SCHEMA_VERSION}`,\n );\n }\n if (!Array.isArray(bundle.events)) {\n throw new BundleValidationError(\"bundle.events must be an array\");\n }\n if (!bundle.mode) {\n throw new BundleValidationError(\"bundle.mode is missing\");\n }\n return b as SessionBundle;\n};\n\nexport const exportBundle = (): SessionBundle => {\n const bundle = acquireRecorder().getBundle();\n return structuredClone(bundle);\n};\n\nexport const importBundle = (json: string | SessionBundle): SessionBundle => {\n const parsed = typeof json === \"string\" ? (JSON.parse(json) as unknown) : json;\n return validateBundle(parsed);\n};\n",
|
|
14
|
+
"// @rewindkit/runtime — core entry (TIER 2: recorder, enhancer, player, fold, redaction, bundle I/O).\n//\n// Public API contract: specs/02-architecture.md §B. `__`-prefixed exports are internal/unstable and\n// exempt from semver. The React companion (ReplayProvider/ReplayPanel/useReplayMode) lives behind\n// the \"./react\" subpath so non-React consumers don't pull React.\n// Keep in sync with package.json version. A build-time generator is deferred (OQ-P7) — not worth the\n// complexity for a single-package alpha. Bump this when bumping package.json.\nexport const VERSION = \"0.1.0-alpha.1\";\n\n// ---- bundle format + branded snapshots ----\nexport type {\n BaseEvent,\n ActionEvent,\n StateSnapshotEvent,\n ComponentRenderEvent,\n RouteEvent,\n FeedMessageEvent,\n ReplayEvent,\n ReplayEventType,\n RemoteInfo,\n SessionBundle,\n RawSnapshot,\n DisplaySnapshot,\n FeedView,\n BundleDiff,\n} from \"./bundle\";\nexport { SCHEMA_VERSION, DELTA_FORMAT_VERSION } from \"./bundle\";\n\n// ---- configuration (opt-in, OFF by default) ----\nexport { configureReplay, registerStore, unregisterStore } from \"./configure\";\nexport type { ConfigureReplayOptions, ReplayHandle } from \"./configure\";\n\n// ---- the Redux enhancer + app-owned transport contract ----\nexport { replayEnhancer } from \"./enhancer\";\nexport type { ReplaySink } from \"./recorder\";\n\n// ---- public live/history signal (subscribable; distinct from recorder suppression) ----\nexport {\n getReplayMode,\n subscribeReplayMode,\n applyReplayState,\n seek,\n returnToLive,\n} from \"./replay\";\nexport type { ReplayMode } from \"./replay\";\n\n// ---- player / time-travel ----\nexport { createPlayer, foldStateAt, foldFeedAt, routeAt, diffBundles } from \"./player\";\nexport type { ReplayPlayer } from \"./player\";\n\n// ---- bundle I/O ----\nexport { exportBundle, importBundle } from \"./bundle-io\";\n\n// ---- redaction (internal/unstable) ----\nexport { __redact, serializeForDisplay } from \"./redaction\";\nexport type { RedactOptions, DisplayOptions } from \"./redaction\";\n"
|
|
15
|
+
],
|
|
16
|
+
"mappings": "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",
|
|
17
|
+
"debugId": "093C993363C31DB564756E2164756E21",
|
|
18
|
+
"names": []
|
|
19
|
+
}
|
package/dist/player.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type BundleDiff, type FeedView, type RawSnapshot, type RouteEvent, type SessionBundle } from "./bundle";
|
|
2
|
+
export declare const foldStateAt: (bundle: SessionBundle, storeKey: string, tMs: number) => RawSnapshot | undefined;
|
|
3
|
+
export declare const foldFeedAt: (bundle: SessionBundle, origin: string, topic: string, tMs: number) => FeedView | undefined;
|
|
4
|
+
export declare const routeAt: (bundle: SessionBundle, tMs: number) => RouteEvent | undefined;
|
|
5
|
+
export declare const diffBundles: (a: SessionBundle, b: SessionBundle) => BundleDiff;
|
|
6
|
+
export interface ReplayPlayer {
|
|
7
|
+
statesAt(tMs: number): Record<string, RawSnapshot | undefined>;
|
|
8
|
+
routeAt(tMs: number): RouteEvent | undefined;
|
|
9
|
+
feedAt(origin: string, topic: string, tMs: number): FeedView | undefined;
|
|
10
|
+
diffBundles(a: SessionBundle, b: SessionBundle): BundleDiff;
|
|
11
|
+
}
|
|
12
|
+
export declare const createPlayer: (bundle: SessionBundle) => ReplayPlayer;
|
|
13
|
+
//# sourceMappingURL=player.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,aAAa,EAEnB,MAAM,UAAU,CAAC;AAWlB,eAAO,MAAM,WAAW,WACd,aAAa,YACX,MAAM,OACX,MAAM,KACV,WAAW,GAAG,SAqBhB,CAAC;AAaF,eAAO,MAAM,UAAU,WACb,aAAa,UACb,MAAM,SACP,MAAM,OACR,MAAM,KACV,QAAQ,GAAG,SAoDb,CAAC;AAYF,eAAO,MAAM,OAAO,WAAY,aAAa,OAAO,MAAM,KAAG,UAAU,GAAG,SAOzE,CAAC;AAeF,eAAO,MAAM,WAAW,MAAO,aAAa,KAAK,aAAa,KAAG,UAiBhE,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC,CAAC;IAC/D,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzE,WAAW,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,UAAU,CAAC;CAC7D;AAED,eAAO,MAAM,YAAY,WAAY,aAAa,KAAG,YAenD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAsC,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAEhF,eAAO,MAAM,aAAa,QAAO,UACwC,CAAC"}
|