aeon-test 0.1.1 → 0.2.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/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../scheduler/dist/index.js","../../types/dist/index.js","../src/marble.ts","../src/testEvent.ts","../src/collect.ts","../src/assert.ts"],"sourcesContent":["/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n\n/** Clock using performance.now() — high-resolution, monotonic. */ class PerformanceClock {\n now() {\n return toTime(performance.now());\n }\n}\n/** Clock using Date.now() — fallback for environments without performance API. */ class DateClock {\n now() {\n return toTime(Date.now());\n }\n}\n/** Manually advanceable clock for deterministic testing. */ class VirtualClock {\n constructor(initialTime = toTime(0)){\n this.time = initialTime;\n }\n now() {\n return this.time;\n }\n setTime(time) {\n this.time = time;\n }\n advance(ms) {\n this.time = toTime(this.time + ms);\n }\n}\n\n/**\n * Binary min-heap for the scheduler's timer queue.\n *\n * Keyed on a numeric priority (scheduled time). Uses a pre-allocated\n * backing array to minimize per-node allocation.\n */ class BinaryHeap {\n constructor(initialCapacity = 64){\n this.items = [];\n // Pre-allocate hint (V8 will grow as needed)\n if (initialCapacity > 0) {\n this.items.length = 0;\n }\n }\n get size() {\n return this.items.length;\n }\n peek() {\n return this.items[0];\n }\n insert(value, priority) {\n const entry = {\n value,\n priority,\n index: this.items.length\n };\n this.items.push(entry);\n this.siftUp(entry.index);\n return entry;\n }\n extractMin() {\n if (this.items.length === 0) return undefined;\n const min = this.items[0];\n this.removeAt(0);\n return min;\n }\n remove(entry) {\n if (entry.index < 0 || entry.index >= this.items.length) return false;\n if (this.items[entry.index] !== entry) return false;\n this.removeAt(entry.index);\n return true;\n }\n removeAt(index) {\n const last = this.items.length - 1;\n if (index === last) {\n this.items.pop();\n return;\n }\n const moved = this.items[last];\n this.items[index] = moved;\n moved.index = index;\n this.items.pop();\n // Restore heap property\n const parent = index - 1 >>> 1;\n if (index > 0 && moved.priority < this.items[parent].priority) {\n this.siftUp(index);\n } else {\n this.siftDown(index);\n }\n }\n siftUp(index) {\n const item = this.items[index];\n while(index > 0){\n const parentIndex = index - 1 >>> 1;\n const parent = this.items[parentIndex];\n if (item.priority >= parent.priority) break;\n this.items[index] = parent;\n parent.index = index;\n index = parentIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n siftDown(index) {\n const item = this.items[index];\n const halfLength = this.items.length >>> 1;\n while(index < halfLength){\n let childIndex = 2 * index + 1;\n let child = this.items[childIndex];\n const rightIndex = childIndex + 1;\n if (rightIndex < this.items.length && this.items[rightIndex].priority < child.priority) {\n childIndex = rightIndex;\n child = this.items[rightIndex];\n }\n if (item.priority <= child.priority) break;\n this.items[index] = child;\n child.index = index;\n index = childIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n}\n\nclass DefaultScheduler {\n constructor(clock = new PerformanceClock()){\n this.clock = clock;\n this.heap = new BinaryHeap();\n this.microtaskQueued = false;\n this.microtaskBuffer = [];\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const now = this.clock.now();\n const time = timeAdd(now, delay);\n const delayMs = delay;\n const pending = {\n task,\n time,\n cancelled: false,\n timerId: undefined,\n heapEntry: undefined\n };\n if (delayMs <= 0) {\n // Schedule as microtask\n this.microtaskBuffer.push(pending);\n if (!this.microtaskQueued) {\n this.microtaskQueued = true;\n queueMicrotask(()=>this.flushMicrotasks());\n }\n } else {\n // Schedule as setTimeout\n pending.heapEntry = this.heap.insert(pending, time);\n pending.timerId = setTimeout(()=>{\n if (!pending.cancelled) {\n if (pending.heapEntry) {\n this.heap.remove(pending.heapEntry);\n }\n this.runTask(pending);\n }\n }, delayMs);\n }\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.timerId !== undefined) {\n clearTimeout(pending.timerId);\n }\n }\n }\n };\n }\n relative(offset) {\n return new RelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n flushMicrotasks() {\n this.microtaskQueued = false;\n const buffer = this.microtaskBuffer.splice(0);\n for (const pending of buffer){\n if (!pending.cancelled) {\n this.runTask(pending);\n }\n }\n }\n runTask(pending) {\n try {\n pending.task.run(this.clock.now());\n } catch (err) {\n pending.task.error(this.clock.now(), err);\n }\n }\n}\nlet RelativeScheduler = class RelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new RelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nclass VirtualScheduler {\n constructor(initialTime = TIME_ZERO){\n this.clock = new VirtualClock(initialTime);\n this.heap = new BinaryHeap();\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const time = timeAdd(this.clock.now(), delay);\n const pending = {\n task,\n time,\n cancelled: false,\n heapEntry: undefined\n };\n pending.heapEntry = this.heap.insert(pending, time);\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.heapEntry) ;\n }\n }\n };\n }\n relative(offset) {\n return new VirtualRelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n /** Advance time by a duration, executing all tasks that fall within range. */ advance(duration) {\n const target = timeAdd(this.clock.now(), duration);\n this.advanceTo(target);\n }\n /** Advance to an exact time, executing all tasks up to and including that time. */ advanceTo(time) {\n while(this.heap.size > 0){\n const next = this.heap.peek();\n if (next.priority > time) break;\n this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n this.clock.setTime(time);\n }\n /** Execute all pending tasks regardless of time. */ flush() {\n while(this.heap.size > 0){\n const next = this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n }\n /** Number of pending (non-cancelled) tasks. */ get pendingCount() {\n return this.heap.size;\n }\n}\nlet VirtualRelativeScheduler = class VirtualRelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new VirtualRelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nexport { BinaryHeap, DateClock, DefaultScheduler, PerformanceClock, VirtualClock, VirtualScheduler };\n//# sourceMappingURL=index.js.map\n","/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Wrap a raw millisecond value as a Duration. */ const toDuration = (ms)=>ms;\n/** Wrap a raw millisecond value as an Offset. */ const toOffset = (ms)=>ms;\n// --- Arithmetic ---\n/** Compute the Duration between two Time points. */ const timeDiff = (a, b)=>a - b;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n/** Advance a Time by an Offset. */ const timeShift = (t, o)=>t + o;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n/** Zero duration. */ const DURATION_ZERO = 0;\n/** Zero offset. */ const OFFSET_ZERO = 0;\n\n/**\n * Higher-Kinded Type encoding via URI-indexed Kind map.\n *\n * Uses module augmentation (Effect-TS / fp-ts style) so that each\n * package can register its own types without circular imports.\n */ // biome-ignore lint/suspicious/noEmptyInterface: open for module augmentation\n// --- Derived combinators ---\n/** Lift a binary function over two Applicative values. */ const liftA2 = (A)=>(f, fa1, fa2)=>A.ap(A.map((a1)=>(a2)=>f(a1, a2), fa1), fa2);\n/** Lift a ternary function over three Applicative values. */ const liftA3 = (A)=>(f, fa1, fa2, fa3)=>A.ap(A.ap(A.map((a1)=>(a2)=>(a3)=>f(a1, a2, a3), fa1), fa2), fa3);\n\n/**\n * Core runtime interfaces for the push/pull reactive system.\n *\n * These define the contracts that packages/core and packages/scheduler implement.\n * Denotational meanings are stated in JSDoc.\n */ // --- URI constants for HKT registration ---\nconst EventURI = \"Event\";\nconst BehaviorURI = \"Behavior\";\n\nexport { BehaviorURI, DURATION_ZERO, EventURI, OFFSET_ZERO, TIME_ZERO, liftA2, liftA3, timeAdd, timeDiff, timeShift, toDuration, toOffset, toTime };\n//# sourceMappingURL=index.js.map\n","/**\n * Marble testing DSL for Pulse.\n *\n * Marble notation:\n * - `-` : one time unit passes (no emission)\n * - `a` : value emission (looked up in values map)\n * - `|` : end (stream completes)\n * - `#` : error (uses provided error value)\n * - `(` : group start — multiple events at the same time\n * - `)` : group end\n *\n * Examples:\n * \"--a--b--c--|\" → emits a at t=2, b at t=5, c at t=8, ends at t=11\n * \"--(ab)--c--|\" → emits a,b both at t=2, c at t=8, ends at t=11\n * \"--a--#\" → emits a at t=2, errors at t=5\n *\n * The `timeUnit` parameter controls how many ms each `-` represents.\n * Default is 1ms per unit.\n */\n\nimport type { Time } from \"aeon-types\";\nimport { toTime } from \"aeon-types\";\n\n/** A parsed marble event. */\nexport type MarbleEntry<A, E> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/**\n * Parse a marble string into a sequence of timed entries.\n *\n * @param marble - The marble notation string\n * @param values - Map from single-character keys to values\n * @param error - The error value for `#`\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const parseMarble = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit = 1,\n): MarbleEntry<A, E>[] => {\n const entries: MarbleEntry<A, E>[] = [];\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n\n switch (ch) {\n case \"-\":\n if (!inGroup) time += timeUnit;\n break;\n\n case \"(\":\n inGroup = true;\n break;\n\n case \")\":\n inGroup = false;\n if (!inGroup) time += timeUnit;\n break;\n\n case \"|\":\n entries.push({ type: \"end\", time: toTime(time) });\n if (!inGroup) time += timeUnit;\n break;\n\n case \"#\":\n entries.push({ type: \"error\", time: toTime(time), error: error as E });\n if (!inGroup) time += timeUnit;\n break;\n\n case \" \":\n break;\n\n default: {\n const value = values[ch];\n if (value === undefined) {\n throw new Error(`Marble character '${ch}' not found in values map`);\n }\n entries.push({ type: \"event\", time: toTime(time), value });\n if (!inGroup) time += timeUnit;\n break;\n }\n }\n }\n\n return entries;\n};\n\n/**\n * Compute the total duration of a marble string (in time units).\n */\nexport const marbleDuration = (marble: string, timeUnit = 1): number => {\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n if (ch === \"(\") {\n inGroup = true;\n continue;\n }\n if (ch === \")\") {\n inGroup = false;\n time += timeUnit;\n continue;\n }\n if (ch === \" \") continue;\n if (!inGroup) time += timeUnit;\n }\n\n return time;\n};\n","/**\n * Test stream creation from marble notation.\n *\n * Creates a pulse Event that emits values according to a marble string,\n * scheduled on a VirtualScheduler.\n */\n\nimport type {\n Disposable,\n Duration,\n Event as PulseEvent,\n Scheduler,\n Sink,\n Source,\n Time,\n} from \"aeon-types\";\nimport { toDuration, toTime } from \"aeon-types\";\nimport { type MarbleEntry, parseMarble } from \"./marble.js\";\n\nclass MarbleSource<A, E> implements Source<A, E> {\n declare readonly entries: MarbleEntry<A, E>[];\n\n constructor(entries: MarbleEntry<A, E>[]) {\n this.entries = entries;\n }\n\n run(sink: Sink<A, E>, scheduler: Scheduler): Disposable {\n const disposables: Disposable[] = [];\n const currentTime = scheduler.currentTime() as number;\n\n for (const entry of this.entries) {\n const delay = toDuration((entry.time as number) - currentTime);\n\n switch (entry.type) {\n case \"event\": {\n const value = entry.value;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.event(t, value);\n },\n error(t: Time, err: unknown) {\n sink.error(t, err as E);\n },\n dispose() {},\n }),\n );\n break;\n }\n case \"error\": {\n const error = entry.error;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.error(t, error);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n case \"end\": {\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.end(t);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n }\n }\n\n return {\n dispose() {\n for (const d of disposables) d.dispose();\n },\n };\n }\n}\n\n/**\n * Create a pulse Event from a marble string.\n *\n * Events are scheduled on the provided scheduler. Use with VirtualScheduler\n * and advance/flush to control time.\n *\n * @param marble - Marble notation string\n * @param values - Map from single-character keys to values\n * @param error - Error value for `#` in the marble string\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const testEvent = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit?: number,\n): PulseEvent<A, E> => {\n const entries = parseMarble<A, E>(marble, values, error, timeUnit);\n return new MarbleSource(entries) as unknown as PulseEvent<A, E>;\n};\n","/**\n * Test collection helpers.\n *\n * Helpers for running a pulse Event and collecting its output\n * as a structured list of timed entries for assertion.\n */\n\nimport type { Disposable, Event as PulseEvent, Scheduler, Sink, Source, Time } from \"aeon-types\";\n\n/** A collected event entry (value, error, or end). */\nexport type CollectedEntry<A, E = never> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/** Result of collecting events from a stream. */\nexport interface CollectResult<A, E = never> {\n /** All collected entries in order. */\n readonly entries: CollectedEntry<A, E>[];\n /** Just the values (convenience accessor). */\n readonly values: A[];\n /** Whether the stream ended. */\n readonly ended: boolean;\n /** Whether the stream errored. */\n readonly errored: boolean;\n /** The error value, if any. */\n readonly error: E | undefined;\n /** Disposable to stop collection. */\n readonly disposable: Disposable;\n}\n\n/**\n * Subscribe to a pulse Event and collect all emissions.\n *\n * Returns a CollectResult whose `entries`, `values`, `ended`, etc.\n * update live as the scheduler advances. Use with VirtualScheduler:\n *\n * ```typescript\n * const scheduler = new VirtualScheduler();\n * const result = collectEvents(myEvent, scheduler);\n * scheduler.advanceTo(toTime(100));\n * expect(result.values).toEqual([1, 2, 3]);\n * expect(result.ended).toBe(true);\n * result.disposable.dispose();\n * ```\n */\nexport const collectEvents = <A, E = never>(\n event: PulseEvent<A, E>,\n scheduler: Scheduler,\n): CollectResult<A, E> => {\n const entries: CollectedEntry<A, E>[] = [];\n const values: A[] = [];\n const result: CollectResult<A, E> = {\n entries,\n values,\n ended: false,\n errored: false,\n error: undefined,\n disposable: { dispose() {} },\n };\n\n const sink: Sink<A, E> = {\n event(time: Time, value: A) {\n entries.push({ type: \"event\", time, value });\n values.push(value);\n },\n error(time: Time, err: E) {\n entries.push({ type: \"error\", time, error: err });\n (result as { errored: boolean }).errored = true;\n (result as { error: E | undefined }).error = err;\n },\n end(time: Time) {\n entries.push({ type: \"end\", time });\n (result as { ended: boolean }).ended = true;\n },\n };\n\n const source = event as unknown as Source<A, E>;\n const disposable = source.run(sink, scheduler);\n (result as { disposable: Disposable }).disposable = disposable;\n\n return result;\n};\n\n/**\n * Collect all values from a synchronous Event.\n *\n * For events backed by synchronous sources (fromArray, now, empty),\n * all values are available immediately without advancing time.\n */\nexport const collectSync = <A>(event: PulseEvent<A, never>, scheduler: Scheduler): A[] => {\n const result = collectEvents(event, scheduler);\n result.disposable.dispose();\n return result.values;\n};\n","/**\n * Assertion helpers for marble-based stream testing.\n */\n\nimport type { Time } from \"aeon-types\";\nimport type { CollectedEntry } from \"./collect.js\";\nimport type { MarbleEntry } from \"./marble.js\";\n\n/**\n * Compare collected entries against expected marble entries.\n *\n * Returns `{ pass: true }` if they match, or `{ pass: false, message }`\n * with a human-readable diff if they don't.\n *\n * This is framework-agnostic — use it with any assertion library:\n * ```typescript\n * const check = assertEvents(result.entries, expected);\n * if (!check.pass) throw new Error(check.message);\n * ```\n */\nexport const assertEvents = <A, E>(\n actual: readonly CollectedEntry<A, E>[],\n expected: readonly MarbleEntry<A, E>[],\n): { pass: true } | { pass: false; message: string } => {\n if (actual.length !== expected.length) {\n return {\n pass: false,\n message:\n `Expected ${expected.length} entries, got ${actual.length}.\\n` +\n ` actual: ${formatEntries(actual)}\\n` +\n ` expected: ${formatEntries(expected)}`,\n };\n }\n\n for (let i = 0; i < actual.length; i++) {\n const a = actual[i]!;\n const e = expected[i]!;\n\n if (a.type !== e.type) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected type '${e.type}', got '${a.type}'.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if ((a.time as number) !== (e.time as number)) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected time ${e.time as number}, got ${a.time as number}.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if (a.type === \"event\" && e.type === \"event\") {\n if (!Object.is(a.value, e.value)) {\n return {\n pass: false,\n message: `Entry ${i}: expected value ${JSON.stringify(e.value)}, got ${JSON.stringify(a.value)}.`,\n };\n }\n }\n\n if (a.type === \"error\" && e.type === \"error\") {\n if (!Object.is(a.error, e.error)) {\n return {\n pass: false,\n message: `Entry ${i}: expected error ${JSON.stringify(e.error)}, got ${JSON.stringify(a.error)}.`,\n };\n }\n }\n }\n\n return { pass: true };\n};\n\nconst formatEntry = (\n e: CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>,\n): string => {\n switch (e.type) {\n case \"event\":\n return `event(${e.time as number}, ${JSON.stringify(e.value)})`;\n case \"error\":\n return `error(${e.time as number}, ${JSON.stringify(e.error)})`;\n case \"end\":\n return `end(${e.time as number})`;\n }\n};\n\nconst formatEntries = (\n entries: readonly (CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>)[],\n): string => `[${entries.map(formatEntry).join(\", \")}]`;\n"],"names":["toTime","ms","timeAdd","t","d","TIME_ZERO","toDuration","parseMarble","marble","values","error","timeUnit","entries","time","inGroup","i","length","ch","push","type","value","undefined","Error","marbleDuration","MarbleSource","run","sink","scheduler","disposables","currentTime","entry","delay","scheduleTask","event","err","dispose","end","testEvent","collectEvents","result","ended","errored","disposable","source","collectSync","assertEvents","actual","expected","pass","message","formatEntries","a","e","formatEntry","Object","is","JSON","stringify","map","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../scheduler/dist/index.js","../../types/dist/index.js","../src/marble.ts","../src/testEvent.ts","../src/collect.ts","../src/assert.ts"],"sourcesContent":["/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n\n/** Clock using performance.now() — high-resolution, monotonic. */ class PerformanceClock {\n now() {\n return toTime(performance.now());\n }\n}\n/** Clock using Date.now() — fallback for environments without performance API. */ class DateClock {\n now() {\n return toTime(Date.now());\n }\n}\n/** Manually advanceable clock for deterministic testing. */ class VirtualClock {\n constructor(initialTime = toTime(0)){\n this.time = initialTime;\n }\n now() {\n return this.time;\n }\n setTime(time) {\n this.time = time;\n }\n advance(ms) {\n this.time = toTime(this.time + ms);\n }\n}\n\n/**\n * Binary min-heap for the scheduler's timer queue.\n *\n * Keyed on a numeric priority (scheduled time). Uses a pre-allocated\n * backing array to minimize per-node allocation.\n */ class BinaryHeap {\n constructor(initialCapacity = 64){\n this.items = [];\n // Pre-allocate hint (V8 will grow as needed)\n if (initialCapacity > 0) {\n this.items.length = 0;\n }\n }\n get size() {\n return this.items.length;\n }\n peek() {\n return this.items[0];\n }\n insert(value, priority) {\n const entry = {\n value,\n priority,\n index: this.items.length\n };\n this.items.push(entry);\n this.siftUp(entry.index);\n return entry;\n }\n extractMin() {\n if (this.items.length === 0) return undefined;\n const min = this.items[0];\n this.removeAt(0);\n return min;\n }\n remove(entry) {\n if (entry.index < 0 || entry.index >= this.items.length) return false;\n if (this.items[entry.index] !== entry) return false;\n this.removeAt(entry.index);\n return true;\n }\n removeAt(index) {\n const last = this.items.length - 1;\n if (index === last) {\n this.items.pop();\n return;\n }\n const moved = this.items[last];\n this.items[index] = moved;\n moved.index = index;\n this.items.pop();\n // Restore heap property\n const parent = index - 1 >>> 1;\n if (index > 0 && moved.priority < this.items[parent].priority) {\n this.siftUp(index);\n } else {\n this.siftDown(index);\n }\n }\n siftUp(index) {\n const item = this.items[index];\n while(index > 0){\n const parentIndex = index - 1 >>> 1;\n const parent = this.items[parentIndex];\n if (item.priority >= parent.priority) break;\n this.items[index] = parent;\n parent.index = index;\n index = parentIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n siftDown(index) {\n const item = this.items[index];\n const halfLength = this.items.length >>> 1;\n while(index < halfLength){\n let childIndex = 2 * index + 1;\n let child = this.items[childIndex];\n const rightIndex = childIndex + 1;\n if (rightIndex < this.items.length && this.items[rightIndex].priority < child.priority) {\n childIndex = rightIndex;\n child = this.items[rightIndex];\n }\n if (item.priority <= child.priority) break;\n this.items[index] = child;\n child.index = index;\n index = childIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n}\n\nclass DefaultScheduler {\n constructor(clock = new PerformanceClock()){\n this.clock = clock;\n this.heap = new BinaryHeap();\n this.microtaskQueued = false;\n this.microtaskBuffer = [];\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const now = this.clock.now();\n const time = timeAdd(now, delay);\n const delayMs = delay;\n const pending = {\n task,\n time,\n cancelled: false,\n timerId: undefined,\n heapEntry: undefined\n };\n if (delayMs <= 0) {\n // Schedule as microtask\n this.microtaskBuffer.push(pending);\n if (!this.microtaskQueued) {\n this.microtaskQueued = true;\n queueMicrotask(()=>this.flushMicrotasks());\n }\n } else {\n // Schedule as setTimeout\n pending.heapEntry = this.heap.insert(pending, time);\n pending.timerId = setTimeout(()=>{\n if (!pending.cancelled) {\n if (pending.heapEntry) {\n this.heap.remove(pending.heapEntry);\n }\n this.runTask(pending);\n }\n }, delayMs);\n }\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.timerId !== undefined) {\n clearTimeout(pending.timerId);\n }\n }\n }\n };\n }\n relative(offset) {\n return new RelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n flushMicrotasks() {\n this.microtaskQueued = false;\n const buffer = this.microtaskBuffer.splice(0);\n for (const pending of buffer){\n if (!pending.cancelled) {\n this.runTask(pending);\n }\n }\n }\n runTask(pending) {\n try {\n pending.task.run(this.clock.now());\n } catch (err) {\n pending.task.error(this.clock.now(), err);\n }\n }\n}\nlet RelativeScheduler = class RelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new RelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nclass VirtualScheduler {\n constructor(initialTime = TIME_ZERO){\n this.clock = new VirtualClock(initialTime);\n this.heap = new BinaryHeap();\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const time = timeAdd(this.clock.now(), delay);\n const pending = {\n task,\n time,\n cancelled: false,\n heapEntry: undefined\n };\n pending.heapEntry = this.heap.insert(pending, time);\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.heapEntry) ;\n }\n }\n };\n }\n relative(offset) {\n return new VirtualRelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n /** Advance time by a duration, executing all tasks that fall within range. */ advance(duration) {\n const target = timeAdd(this.clock.now(), duration);\n this.advanceTo(target);\n }\n /** Advance to an exact time, executing all tasks up to and including that time. */ advanceTo(time) {\n while(this.heap.size > 0){\n const next = this.heap.peek();\n if (next.priority > time) break;\n this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n this.clock.setTime(time);\n }\n /** Execute all pending tasks regardless of time. */ flush() {\n while(this.heap.size > 0){\n const next = this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n }\n /** Number of pending (non-cancelled) tasks. */ get pendingCount() {\n return this.heap.size;\n }\n}\nlet VirtualRelativeScheduler = class VirtualRelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new VirtualRelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nexport { BinaryHeap, DateClock, DefaultScheduler, PerformanceClock, VirtualClock, VirtualScheduler };\n//# sourceMappingURL=index.js.map\n","/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Wrap a raw millisecond value as a Duration. */ const toDuration = (ms)=>ms;\n/** Wrap a raw millisecond value as an Offset. */ const toOffset = (ms)=>ms;\n// --- Arithmetic ---\n/** Compute the Duration between two Time points. */ const timeDiff = (a, b)=>a - b;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n/** Advance a Time by an Offset. */ const timeShift = (t, o)=>t + o;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n/** Zero duration. */ const DURATION_ZERO = 0;\n/** Zero offset. */ const OFFSET_ZERO = 0;\n\nexport { DURATION_ZERO, OFFSET_ZERO, TIME_ZERO, timeAdd, timeDiff, timeShift, toDuration, toOffset, toTime };\n//# sourceMappingURL=index.js.map\n","/**\n * Marble testing DSL for Pulse.\n *\n * Marble notation:\n * - `-` : one time unit passes (no emission)\n * - `a` : value emission (looked up in values map)\n * - `|` : end (stream completes)\n * - `#` : error (uses provided error value)\n * - `(` : group start — multiple events at the same time\n * - `)` : group end\n *\n * Examples:\n * \"--a--b--c--|\" → emits a at t=2, b at t=5, c at t=8, ends at t=11\n * \"--(ab)--c--|\" → emits a,b both at t=2, c at t=8, ends at t=11\n * \"--a--#\" → emits a at t=2, errors at t=5\n *\n * The `timeUnit` parameter controls how many ms each `-` represents.\n * Default is 1ms per unit.\n */\n\nimport type { Time } from \"aeon-types\";\nimport { toTime } from \"aeon-types\";\n\n/** A parsed marble event. */\nexport type MarbleEntry<A, E> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/**\n * Parse a marble string into a sequence of timed entries.\n *\n * @param marble - The marble notation string\n * @param values - Map from single-character keys to values\n * @param error - The error value for `#`\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const parseMarble = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit = 1,\n): MarbleEntry<A, E>[] => {\n const entries: MarbleEntry<A, E>[] = [];\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n\n switch (ch) {\n case \"-\":\n if (!inGroup) time += timeUnit;\n break;\n\n case \"(\":\n inGroup = true;\n break;\n\n case \")\":\n inGroup = false;\n if (!inGroup) time += timeUnit;\n break;\n\n case \"|\":\n entries.push({ type: \"end\", time: toTime(time) });\n if (!inGroup) time += timeUnit;\n break;\n\n case \"#\":\n entries.push({ type: \"error\", time: toTime(time), error: error as E });\n if (!inGroup) time += timeUnit;\n break;\n\n case \" \":\n break;\n\n default: {\n const value = values[ch];\n if (value === undefined) {\n throw new Error(`Marble character '${ch}' not found in values map`);\n }\n entries.push({ type: \"event\", time: toTime(time), value });\n if (!inGroup) time += timeUnit;\n break;\n }\n }\n }\n\n return entries;\n};\n\n/**\n * Compute the total duration of a marble string (in time units).\n */\nexport const marbleDuration = (marble: string, timeUnit = 1): number => {\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n if (ch === \"(\") {\n inGroup = true;\n continue;\n }\n if (ch === \")\") {\n inGroup = false;\n time += timeUnit;\n continue;\n }\n if (ch === \" \") continue;\n if (!inGroup) time += timeUnit;\n }\n\n return time;\n};\n","/**\n * Test stream creation from marble notation.\n *\n * Creates a pulse Event that emits values according to a marble string,\n * scheduled on a VirtualScheduler.\n */\n\nimport type {\n Disposable,\n Duration,\n Event as PulseEvent,\n Scheduler,\n Sink,\n Source,\n Time,\n} from \"aeon-types\";\nimport { toDuration, toTime } from \"aeon-types\";\nimport { type MarbleEntry, parseMarble } from \"./marble.js\";\n\nclass MarbleSource<A, E> implements Source<A, E> {\n declare readonly entries: MarbleEntry<A, E>[];\n\n constructor(entries: MarbleEntry<A, E>[]) {\n this.entries = entries;\n }\n\n run(sink: Sink<A, E>, scheduler: Scheduler): Disposable {\n const disposables: Disposable[] = [];\n const currentTime = scheduler.currentTime() as number;\n\n for (const entry of this.entries) {\n const delay = toDuration((entry.time as number) - currentTime);\n\n switch (entry.type) {\n case \"event\": {\n const value = entry.value;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.event(t, value);\n },\n error(t: Time, err: unknown) {\n sink.error(t, err as E);\n },\n dispose() {},\n }),\n );\n break;\n }\n case \"error\": {\n const error = entry.error;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.error(t, error);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n case \"end\": {\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.end(t);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n }\n }\n\n return {\n dispose() {\n for (const d of disposables) d.dispose();\n },\n };\n }\n}\n\n/**\n * Create a pulse Event from a marble string.\n *\n * Events are scheduled on the provided scheduler. Use with VirtualScheduler\n * and advance/flush to control time.\n *\n * @param marble - Marble notation string\n * @param values - Map from single-character keys to values\n * @param error - Error value for `#` in the marble string\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const testEvent = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit?: number,\n): PulseEvent<A, E> => {\n const entries = parseMarble<A, E>(marble, values, error, timeUnit);\n return new MarbleSource(entries) as unknown as PulseEvent<A, E>;\n};\n","/**\n * Test collection helpers.\n *\n * Helpers for running a pulse Event and collecting its output\n * as a structured list of timed entries for assertion.\n */\n\nimport type { Disposable, Event as PulseEvent, Scheduler, Sink, Source, Time } from \"aeon-types\";\n\n/** A collected event entry (value, error, or end). */\nexport type CollectedEntry<A, E = never> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/** Result of collecting events from a stream. */\nexport interface CollectResult<A, E = never> {\n /** All collected entries in order. */\n readonly entries: CollectedEntry<A, E>[];\n /** Just the values (convenience accessor). */\n readonly values: A[];\n /** Whether the stream ended. */\n readonly ended: boolean;\n /** Whether the stream errored. */\n readonly errored: boolean;\n /** The error value, if any. */\n readonly error: E | undefined;\n /** Disposable to stop collection. */\n readonly disposable: Disposable;\n}\n\n/**\n * Subscribe to a pulse Event and collect all emissions.\n *\n * Returns a CollectResult whose `entries`, `values`, `ended`, etc.\n * update live as the scheduler advances. Use with VirtualScheduler:\n *\n * ```typescript\n * const scheduler = new VirtualScheduler();\n * const result = collectEvents(myEvent, scheduler);\n * scheduler.advanceTo(toTime(100));\n * expect(result.values).toEqual([1, 2, 3]);\n * expect(result.ended).toBe(true);\n * result.disposable.dispose();\n * ```\n */\nexport const collectEvents = <A, E = never>(\n event: PulseEvent<A, E>,\n scheduler: Scheduler,\n): CollectResult<A, E> => {\n const entries: CollectedEntry<A, E>[] = [];\n const values: A[] = [];\n const result: CollectResult<A, E> = {\n entries,\n values,\n ended: false,\n errored: false,\n error: undefined,\n disposable: { dispose() {} },\n };\n\n const sink: Sink<A, E> = {\n event(time: Time, value: A) {\n entries.push({ type: \"event\", time, value });\n values.push(value);\n },\n error(time: Time, err: E) {\n entries.push({ type: \"error\", time, error: err });\n (result as { errored: boolean }).errored = true;\n (result as { error: E | undefined }).error = err;\n },\n end(time: Time) {\n entries.push({ type: \"end\", time });\n (result as { ended: boolean }).ended = true;\n },\n };\n\n const source = event as unknown as Source<A, E>;\n const disposable = source.run(sink, scheduler);\n (result as { disposable: Disposable }).disposable = disposable;\n\n return result;\n};\n\n/**\n * Collect all values from a synchronous Event.\n *\n * For events backed by synchronous sources (fromArray, now, empty),\n * all values are available immediately without advancing time.\n */\nexport const collectSync = <A>(event: PulseEvent<A, never>, scheduler: Scheduler): A[] => {\n const result = collectEvents(event, scheduler);\n result.disposable.dispose();\n return result.values;\n};\n","/**\n * Assertion helpers for marble-based stream testing.\n */\n\nimport type { Time } from \"aeon-types\";\nimport type { CollectedEntry } from \"./collect.js\";\nimport type { MarbleEntry } from \"./marble.js\";\n\n/**\n * Compare collected entries against expected marble entries.\n *\n * Returns `{ pass: true }` if they match, or `{ pass: false, message }`\n * with a human-readable diff if they don't.\n *\n * This is framework-agnostic — use it with any assertion library:\n * ```typescript\n * const check = assertEvents(result.entries, expected);\n * if (!check.pass) throw new Error(check.message);\n * ```\n */\nexport const assertEvents = <A, E>(\n actual: readonly CollectedEntry<A, E>[],\n expected: readonly MarbleEntry<A, E>[],\n): { pass: true } | { pass: false; message: string } => {\n if (actual.length !== expected.length) {\n return {\n pass: false,\n message:\n `Expected ${expected.length} entries, got ${actual.length}.\\n` +\n ` actual: ${formatEntries(actual)}\\n` +\n ` expected: ${formatEntries(expected)}`,\n };\n }\n\n for (let i = 0; i < actual.length; i++) {\n const a = actual[i]!;\n const e = expected[i]!;\n\n if (a.type !== e.type) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected type '${e.type}', got '${a.type}'.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if ((a.time as number) !== (e.time as number)) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected time ${e.time as number}, got ${a.time as number}.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if (a.type === \"event\" && e.type === \"event\") {\n if (!Object.is(a.value, e.value)) {\n return {\n pass: false,\n message: `Entry ${i}: expected value ${JSON.stringify(e.value)}, got ${JSON.stringify(a.value)}.`,\n };\n }\n }\n\n if (a.type === \"error\" && e.type === \"error\") {\n if (!Object.is(a.error, e.error)) {\n return {\n pass: false,\n message: `Entry ${i}: expected error ${JSON.stringify(e.error)}, got ${JSON.stringify(a.error)}.`,\n };\n }\n }\n }\n\n return { pass: true };\n};\n\nconst formatEntry = (\n e: CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>,\n): string => {\n switch (e.type) {\n case \"event\":\n return `event(${e.time as number}, ${JSON.stringify(e.value)})`;\n case \"error\":\n return `error(${e.time as number}, ${JSON.stringify(e.error)})`;\n case \"end\":\n return `end(${e.time as number})`;\n }\n};\n\nconst formatEntries = (\n entries: readonly (CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>)[],\n): string => `[${entries.map(formatEntry).join(\", \")}]`;\n"],"names":["toTime","ms","timeAdd","t","d","TIME_ZERO","toDuration","parseMarble","marble","values","error","timeUnit","entries","time","inGroup","i","length","ch","push","type","value","undefined","Error","marbleDuration","MarbleSource","run","sink","scheduler","disposables","currentTime","entry","delay","scheduleTask","event","err","dispose","end","testEvent","collectEvents","result","ended","errored","disposable","source","collectSync","assertEvents","actual","expected","pass","message","formatEntries","a","e","formatEntry","Object","is","JSON","stringify","map","join"],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../scheduler/dist/index.js","../../types/dist/index.js","../src/marble.ts","../src/testEvent.ts","../src/collect.ts","../src/assert.ts"],"sourcesContent":["/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n\n/** Clock using performance.now() — high-resolution, monotonic. */ class PerformanceClock {\n now() {\n return toTime(performance.now());\n }\n}\n/** Clock using Date.now() — fallback for environments without performance API. */ class DateClock {\n now() {\n return toTime(Date.now());\n }\n}\n/** Manually advanceable clock for deterministic testing. */ class VirtualClock {\n constructor(initialTime = toTime(0)){\n this.time = initialTime;\n }\n now() {\n return this.time;\n }\n setTime(time) {\n this.time = time;\n }\n advance(ms) {\n this.time = toTime(this.time + ms);\n }\n}\n\n/**\n * Binary min-heap for the scheduler's timer queue.\n *\n * Keyed on a numeric priority (scheduled time). Uses a pre-allocated\n * backing array to minimize per-node allocation.\n */ class BinaryHeap {\n constructor(initialCapacity = 64){\n this.items = [];\n // Pre-allocate hint (V8 will grow as needed)\n if (initialCapacity > 0) {\n this.items.length = 0;\n }\n }\n get size() {\n return this.items.length;\n }\n peek() {\n return this.items[0];\n }\n insert(value, priority) {\n const entry = {\n value,\n priority,\n index: this.items.length\n };\n this.items.push(entry);\n this.siftUp(entry.index);\n return entry;\n }\n extractMin() {\n if (this.items.length === 0) return undefined;\n const min = this.items[0];\n this.removeAt(0);\n return min;\n }\n remove(entry) {\n if (entry.index < 0 || entry.index >= this.items.length) return false;\n if (this.items[entry.index] !== entry) return false;\n this.removeAt(entry.index);\n return true;\n }\n removeAt(index) {\n const last = this.items.length - 1;\n if (index === last) {\n this.items.pop();\n return;\n }\n const moved = this.items[last];\n this.items[index] = moved;\n moved.index = index;\n this.items.pop();\n // Restore heap property\n const parent = index - 1 >>> 1;\n if (index > 0 && moved.priority < this.items[parent].priority) {\n this.siftUp(index);\n } else {\n this.siftDown(index);\n }\n }\n siftUp(index) {\n const item = this.items[index];\n while(index > 0){\n const parentIndex = index - 1 >>> 1;\n const parent = this.items[parentIndex];\n if (item.priority >= parent.priority) break;\n this.items[index] = parent;\n parent.index = index;\n index = parentIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n siftDown(index) {\n const item = this.items[index];\n const halfLength = this.items.length >>> 1;\n while(index < halfLength){\n let childIndex = 2 * index + 1;\n let child = this.items[childIndex];\n const rightIndex = childIndex + 1;\n if (rightIndex < this.items.length && this.items[rightIndex].priority < child.priority) {\n childIndex = rightIndex;\n child = this.items[rightIndex];\n }\n if (item.priority <= child.priority) break;\n this.items[index] = child;\n child.index = index;\n index = childIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n}\n\nclass DefaultScheduler {\n constructor(clock = new PerformanceClock()){\n this.clock = clock;\n this.heap = new BinaryHeap();\n this.microtaskQueued = false;\n this.microtaskBuffer = [];\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const now = this.clock.now();\n const time = timeAdd(now, delay);\n const delayMs = delay;\n const pending = {\n task,\n time,\n cancelled: false,\n timerId: undefined,\n heapEntry: undefined\n };\n if (delayMs <= 0) {\n // Schedule as microtask\n this.microtaskBuffer.push(pending);\n if (!this.microtaskQueued) {\n this.microtaskQueued = true;\n queueMicrotask(()=>this.flushMicrotasks());\n }\n } else {\n // Schedule as setTimeout\n pending.heapEntry = this.heap.insert(pending, time);\n pending.timerId = setTimeout(()=>{\n if (!pending.cancelled) {\n if (pending.heapEntry) {\n this.heap.remove(pending.heapEntry);\n }\n this.runTask(pending);\n }\n }, delayMs);\n }\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.timerId !== undefined) {\n clearTimeout(pending.timerId);\n }\n }\n }\n };\n }\n relative(offset) {\n return new RelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n flushMicrotasks() {\n this.microtaskQueued = false;\n const buffer = this.microtaskBuffer.splice(0);\n for (const pending of buffer){\n if (!pending.cancelled) {\n this.runTask(pending);\n }\n }\n }\n runTask(pending) {\n try {\n pending.task.run(this.clock.now());\n } catch (err) {\n pending.task.error(this.clock.now(), err);\n }\n }\n}\nlet RelativeScheduler = class RelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new RelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nclass VirtualScheduler {\n constructor(initialTime = TIME_ZERO){\n this.clock = new VirtualClock(initialTime);\n this.heap = new BinaryHeap();\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const time = timeAdd(this.clock.now(), delay);\n const pending = {\n task,\n time,\n cancelled: false,\n heapEntry: undefined\n };\n pending.heapEntry = this.heap.insert(pending, time);\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.heapEntry) ;\n }\n }\n };\n }\n relative(offset) {\n return new VirtualRelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n /** Advance time by a duration, executing all tasks that fall within range. */ advance(duration) {\n const target = timeAdd(this.clock.now(), duration);\n this.advanceTo(target);\n }\n /** Advance to an exact time, executing all tasks up to and including that time. */ advanceTo(time) {\n while(this.heap.size > 0){\n const next = this.heap.peek();\n if (next.priority > time) break;\n this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n this.clock.setTime(time);\n }\n /** Execute all pending tasks regardless of time. */ flush() {\n while(this.heap.size > 0){\n const next = this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n }\n /** Number of pending (non-cancelled) tasks. */ get pendingCount() {\n return this.heap.size;\n }\n}\nlet VirtualRelativeScheduler = class VirtualRelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new VirtualRelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nexport { BinaryHeap, DateClock, DefaultScheduler, PerformanceClock, VirtualClock, VirtualScheduler };\n//# sourceMappingURL=index.js.map\n","/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Wrap a raw millisecond value as a Duration. */ const toDuration = (ms)=>ms;\n/** Wrap a raw millisecond value as an Offset. */ const toOffset = (ms)=>ms;\n// --- Arithmetic ---\n/** Compute the Duration between two Time points. */ const timeDiff = (a, b)=>a - b;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n/** Advance a Time by an Offset. */ const timeShift = (t, o)=>t + o;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n/** Zero duration. */ const DURATION_ZERO = 0;\n/** Zero offset. */ const OFFSET_ZERO = 0;\n\n/**\n * Higher-Kinded Type encoding via URI-indexed Kind map.\n *\n * Uses module augmentation (Effect-TS / fp-ts style) so that each\n * package can register its own types without circular imports.\n */ // biome-ignore lint/suspicious/noEmptyInterface: open for module augmentation\n// --- Derived combinators ---\n/** Lift a binary function over two Applicative values. */ const liftA2 = (A)=>(f, fa1, fa2)=>A.ap(A.map((a1)=>(a2)=>f(a1, a2), fa1), fa2);\n/** Lift a ternary function over three Applicative values. */ const liftA3 = (A)=>(f, fa1, fa2, fa3)=>A.ap(A.ap(A.map((a1)=>(a2)=>(a3)=>f(a1, a2, a3), fa1), fa2), fa3);\n\n/**\n * Core runtime interfaces for the push/pull reactive system.\n *\n * These define the contracts that packages/core and packages/scheduler implement.\n * Denotational meanings are stated in JSDoc.\n */ // --- URI constants for HKT registration ---\nconst EventURI = \"Event\";\nconst BehaviorURI = \"Behavior\";\n\nexport { BehaviorURI, DURATION_ZERO, EventURI, OFFSET_ZERO, TIME_ZERO, liftA2, liftA3, timeAdd, timeDiff, timeShift, toDuration, toOffset, toTime };\n//# sourceMappingURL=index.js.map\n","/**\n * Marble testing DSL for Pulse.\n *\n * Marble notation:\n * - `-` : one time unit passes (no emission)\n * - `a` : value emission (looked up in values map)\n * - `|` : end (stream completes)\n * - `#` : error (uses provided error value)\n * - `(` : group start — multiple events at the same time\n * - `)` : group end\n *\n * Examples:\n * \"--a--b--c--|\" → emits a at t=2, b at t=5, c at t=8, ends at t=11\n * \"--(ab)--c--|\" → emits a,b both at t=2, c at t=8, ends at t=11\n * \"--a--#\" → emits a at t=2, errors at t=5\n *\n * The `timeUnit` parameter controls how many ms each `-` represents.\n * Default is 1ms per unit.\n */\n\nimport type { Time } from \"aeon-types\";\nimport { toTime } from \"aeon-types\";\n\n/** A parsed marble event. */\nexport type MarbleEntry<A, E> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/**\n * Parse a marble string into a sequence of timed entries.\n *\n * @param marble - The marble notation string\n * @param values - Map from single-character keys to values\n * @param error - The error value for `#`\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const parseMarble = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit = 1,\n): MarbleEntry<A, E>[] => {\n const entries: MarbleEntry<A, E>[] = [];\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n\n switch (ch) {\n case \"-\":\n if (!inGroup) time += timeUnit;\n break;\n\n case \"(\":\n inGroup = true;\n break;\n\n case \")\":\n inGroup = false;\n if (!inGroup) time += timeUnit;\n break;\n\n case \"|\":\n entries.push({ type: \"end\", time: toTime(time) });\n if (!inGroup) time += timeUnit;\n break;\n\n case \"#\":\n entries.push({ type: \"error\", time: toTime(time), error: error as E });\n if (!inGroup) time += timeUnit;\n break;\n\n case \" \":\n break;\n\n default: {\n const value = values[ch];\n if (value === undefined) {\n throw new Error(`Marble character '${ch}' not found in values map`);\n }\n entries.push({ type: \"event\", time: toTime(time), value });\n if (!inGroup) time += timeUnit;\n break;\n }\n }\n }\n\n return entries;\n};\n\n/**\n * Compute the total duration of a marble string (in time units).\n */\nexport const marbleDuration = (marble: string, timeUnit = 1): number => {\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n if (ch === \"(\") {\n inGroup = true;\n continue;\n }\n if (ch === \")\") {\n inGroup = false;\n time += timeUnit;\n continue;\n }\n if (ch === \" \") continue;\n if (!inGroup) time += timeUnit;\n }\n\n return time;\n};\n","/**\n * Test stream creation from marble notation.\n *\n * Creates a pulse Event that emits values according to a marble string,\n * scheduled on a VirtualScheduler.\n */\n\nimport type {\n Disposable,\n Duration,\n Event as PulseEvent,\n Scheduler,\n Sink,\n Source,\n Time,\n} from \"aeon-types\";\nimport { toDuration, toTime } from \"aeon-types\";\nimport { type MarbleEntry, parseMarble } from \"./marble.js\";\n\nclass MarbleSource<A, E> implements Source<A, E> {\n declare readonly entries: MarbleEntry<A, E>[];\n\n constructor(entries: MarbleEntry<A, E>[]) {\n this.entries = entries;\n }\n\n run(sink: Sink<A, E>, scheduler: Scheduler): Disposable {\n const disposables: Disposable[] = [];\n const currentTime = scheduler.currentTime() as number;\n\n for (const entry of this.entries) {\n const delay = toDuration((entry.time as number) - currentTime);\n\n switch (entry.type) {\n case \"event\": {\n const value = entry.value;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.event(t, value);\n },\n error(t: Time, err: unknown) {\n sink.error(t, err as E);\n },\n dispose() {},\n }),\n );\n break;\n }\n case \"error\": {\n const error = entry.error;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.error(t, error);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n case \"end\": {\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.end(t);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n }\n }\n\n return {\n dispose() {\n for (const d of disposables) d.dispose();\n },\n };\n }\n}\n\n/**\n * Create a pulse Event from a marble string.\n *\n * Events are scheduled on the provided scheduler. Use with VirtualScheduler\n * and advance/flush to control time.\n *\n * @param marble - Marble notation string\n * @param values - Map from single-character keys to values\n * @param error - Error value for `#` in the marble string\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const testEvent = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit?: number,\n): PulseEvent<A, E> => {\n const entries = parseMarble<A, E>(marble, values, error, timeUnit);\n return new MarbleSource(entries) as unknown as PulseEvent<A, E>;\n};\n","/**\n * Test collection helpers.\n *\n * Helpers for running a pulse Event and collecting its output\n * as a structured list of timed entries for assertion.\n */\n\nimport type { Disposable, Event as PulseEvent, Scheduler, Sink, Source, Time } from \"aeon-types\";\n\n/** A collected event entry (value, error, or end). */\nexport type CollectedEntry<A, E = never> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/** Result of collecting events from a stream. */\nexport interface CollectResult<A, E = never> {\n /** All collected entries in order. */\n readonly entries: CollectedEntry<A, E>[];\n /** Just the values (convenience accessor). */\n readonly values: A[];\n /** Whether the stream ended. */\n readonly ended: boolean;\n /** Whether the stream errored. */\n readonly errored: boolean;\n /** The error value, if any. */\n readonly error: E | undefined;\n /** Disposable to stop collection. */\n readonly disposable: Disposable;\n}\n\n/**\n * Subscribe to a pulse Event and collect all emissions.\n *\n * Returns a CollectResult whose `entries`, `values`, `ended`, etc.\n * update live as the scheduler advances. Use with VirtualScheduler:\n *\n * ```typescript\n * const scheduler = new VirtualScheduler();\n * const result = collectEvents(myEvent, scheduler);\n * scheduler.advanceTo(toTime(100));\n * expect(result.values).toEqual([1, 2, 3]);\n * expect(result.ended).toBe(true);\n * result.disposable.dispose();\n * ```\n */\nexport const collectEvents = <A, E = never>(\n event: PulseEvent<A, E>,\n scheduler: Scheduler,\n): CollectResult<A, E> => {\n const entries: CollectedEntry<A, E>[] = [];\n const values: A[] = [];\n const result: CollectResult<A, E> = {\n entries,\n values,\n ended: false,\n errored: false,\n error: undefined,\n disposable: { dispose() {} },\n };\n\n const sink: Sink<A, E> = {\n event(time: Time, value: A) {\n entries.push({ type: \"event\", time, value });\n values.push(value);\n },\n error(time: Time, err: E) {\n entries.push({ type: \"error\", time, error: err });\n (result as { errored: boolean }).errored = true;\n (result as { error: E | undefined }).error = err;\n },\n end(time: Time) {\n entries.push({ type: \"end\", time });\n (result as { ended: boolean }).ended = true;\n },\n };\n\n const source = event as unknown as Source<A, E>;\n const disposable = source.run(sink, scheduler);\n (result as { disposable: Disposable }).disposable = disposable;\n\n return result;\n};\n\n/**\n * Collect all values from a synchronous Event.\n *\n * For events backed by synchronous sources (fromArray, now, empty),\n * all values are available immediately without advancing time.\n */\nexport const collectSync = <A>(event: PulseEvent<A, never>, scheduler: Scheduler): A[] => {\n const result = collectEvents(event, scheduler);\n result.disposable.dispose();\n return result.values;\n};\n","/**\n * Assertion helpers for marble-based stream testing.\n */\n\nimport type { Time } from \"aeon-types\";\nimport type { CollectedEntry } from \"./collect.js\";\nimport type { MarbleEntry } from \"./marble.js\";\n\n/**\n * Compare collected entries against expected marble entries.\n *\n * Returns `{ pass: true }` if they match, or `{ pass: false, message }`\n * with a human-readable diff if they don't.\n *\n * This is framework-agnostic — use it with any assertion library:\n * ```typescript\n * const check = assertEvents(result.entries, expected);\n * if (!check.pass) throw new Error(check.message);\n * ```\n */\nexport const assertEvents = <A, E>(\n actual: readonly CollectedEntry<A, E>[],\n expected: readonly MarbleEntry<A, E>[],\n): { pass: true } | { pass: false; message: string } => {\n if (actual.length !== expected.length) {\n return {\n pass: false,\n message:\n `Expected ${expected.length} entries, got ${actual.length}.\\n` +\n ` actual: ${formatEntries(actual)}\\n` +\n ` expected: ${formatEntries(expected)}`,\n };\n }\n\n for (let i = 0; i < actual.length; i++) {\n const a = actual[i]!;\n const e = expected[i]!;\n\n if (a.type !== e.type) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected type '${e.type}', got '${a.type}'.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if ((a.time as number) !== (e.time as number)) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected time ${e.time as number}, got ${a.time as number}.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if (a.type === \"event\" && e.type === \"event\") {\n if (!Object.is(a.value, e.value)) {\n return {\n pass: false,\n message: `Entry ${i}: expected value ${JSON.stringify(e.value)}, got ${JSON.stringify(a.value)}.`,\n };\n }\n }\n\n if (a.type === \"error\" && e.type === \"error\") {\n if (!Object.is(a.error, e.error)) {\n return {\n pass: false,\n message: `Entry ${i}: expected error ${JSON.stringify(e.error)}, got ${JSON.stringify(a.error)}.`,\n };\n }\n }\n }\n\n return { pass: true };\n};\n\nconst formatEntry = (\n e: CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>,\n): string => {\n switch (e.type) {\n case \"event\":\n return `event(${e.time as number}, ${JSON.stringify(e.value)})`;\n case \"error\":\n return `error(${e.time as number}, ${JSON.stringify(e.error)})`;\n case \"end\":\n return `end(${e.time as number})`;\n }\n};\n\nconst formatEntries = (\n entries: readonly (CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>)[],\n): string => `[${entries.map(formatEntry).join(\", \")}]`;\n"],"names":["toTime","ms","timeAdd","t","d","TIME_ZERO","toDuration","parseMarble","marble","values","error","timeUnit","entries","time","inGroup","i","length","ch","push","type","value","undefined","Error","marbleDuration","MarbleSource","run","sink","scheduler","disposables","currentTime","entry","delay","scheduleTask","event","err","dispose","end","testEvent","collectEvents","result","ended","errored","disposable","source","collectSync","assertEvents","actual","expected","pass","message","formatEntries","a","e","formatEntry","Object","is","JSON","stringify","map","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../scheduler/dist/index.js","../../types/dist/index.js","../src/marble.ts","../src/testEvent.ts","../src/collect.ts","../src/assert.ts"],"sourcesContent":["/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n\n/** Clock using performance.now() — high-resolution, monotonic. */ class PerformanceClock {\n now() {\n return toTime(performance.now());\n }\n}\n/** Clock using Date.now() — fallback for environments without performance API. */ class DateClock {\n now() {\n return toTime(Date.now());\n }\n}\n/** Manually advanceable clock for deterministic testing. */ class VirtualClock {\n constructor(initialTime = toTime(0)){\n this.time = initialTime;\n }\n now() {\n return this.time;\n }\n setTime(time) {\n this.time = time;\n }\n advance(ms) {\n this.time = toTime(this.time + ms);\n }\n}\n\n/**\n * Binary min-heap for the scheduler's timer queue.\n *\n * Keyed on a numeric priority (scheduled time). Uses a pre-allocated\n * backing array to minimize per-node allocation.\n */ class BinaryHeap {\n constructor(initialCapacity = 64){\n this.items = [];\n // Pre-allocate hint (V8 will grow as needed)\n if (initialCapacity > 0) {\n this.items.length = 0;\n }\n }\n get size() {\n return this.items.length;\n }\n peek() {\n return this.items[0];\n }\n insert(value, priority) {\n const entry = {\n value,\n priority,\n index: this.items.length\n };\n this.items.push(entry);\n this.siftUp(entry.index);\n return entry;\n }\n extractMin() {\n if (this.items.length === 0) return undefined;\n const min = this.items[0];\n this.removeAt(0);\n return min;\n }\n remove(entry) {\n if (entry.index < 0 || entry.index >= this.items.length) return false;\n if (this.items[entry.index] !== entry) return false;\n this.removeAt(entry.index);\n return true;\n }\n removeAt(index) {\n const last = this.items.length - 1;\n if (index === last) {\n this.items.pop();\n return;\n }\n const moved = this.items[last];\n this.items[index] = moved;\n moved.index = index;\n this.items.pop();\n // Restore heap property\n const parent = index - 1 >>> 1;\n if (index > 0 && moved.priority < this.items[parent].priority) {\n this.siftUp(index);\n } else {\n this.siftDown(index);\n }\n }\n siftUp(index) {\n const item = this.items[index];\n while(index > 0){\n const parentIndex = index - 1 >>> 1;\n const parent = this.items[parentIndex];\n if (item.priority >= parent.priority) break;\n this.items[index] = parent;\n parent.index = index;\n index = parentIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n siftDown(index) {\n const item = this.items[index];\n const halfLength = this.items.length >>> 1;\n while(index < halfLength){\n let childIndex = 2 * index + 1;\n let child = this.items[childIndex];\n const rightIndex = childIndex + 1;\n if (rightIndex < this.items.length && this.items[rightIndex].priority < child.priority) {\n childIndex = rightIndex;\n child = this.items[rightIndex];\n }\n if (item.priority <= child.priority) break;\n this.items[index] = child;\n child.index = index;\n index = childIndex;\n }\n this.items[index] = item;\n item.index = index;\n }\n}\n\nclass DefaultScheduler {\n constructor(clock = new PerformanceClock()){\n this.clock = clock;\n this.heap = new BinaryHeap();\n this.microtaskQueued = false;\n this.microtaskBuffer = [];\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const now = this.clock.now();\n const time = timeAdd(now, delay);\n const delayMs = delay;\n const pending = {\n task,\n time,\n cancelled: false,\n timerId: undefined,\n heapEntry: undefined\n };\n if (delayMs <= 0) {\n // Schedule as microtask\n this.microtaskBuffer.push(pending);\n if (!this.microtaskQueued) {\n this.microtaskQueued = true;\n queueMicrotask(()=>this.flushMicrotasks());\n }\n } else {\n // Schedule as setTimeout\n pending.heapEntry = this.heap.insert(pending, time);\n pending.timerId = setTimeout(()=>{\n if (!pending.cancelled) {\n if (pending.heapEntry) {\n this.heap.remove(pending.heapEntry);\n }\n this.runTask(pending);\n }\n }, delayMs);\n }\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.timerId !== undefined) {\n clearTimeout(pending.timerId);\n }\n }\n }\n };\n }\n relative(offset) {\n return new RelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n flushMicrotasks() {\n this.microtaskQueued = false;\n const buffer = this.microtaskBuffer.splice(0);\n for (const pending of buffer){\n if (!pending.cancelled) {\n this.runTask(pending);\n }\n }\n }\n runTask(pending) {\n try {\n pending.task.run(this.clock.now());\n } catch (err) {\n pending.task.error(this.clock.now(), err);\n }\n }\n}\nlet RelativeScheduler = class RelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new RelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nclass VirtualScheduler {\n constructor(initialTime = TIME_ZERO){\n this.clock = new VirtualClock(initialTime);\n this.heap = new BinaryHeap();\n }\n currentTime() {\n return this.clock.now();\n }\n scheduleTask(delay, task) {\n const time = timeAdd(this.clock.now(), delay);\n const pending = {\n task,\n time,\n cancelled: false,\n heapEntry: undefined\n };\n pending.heapEntry = this.heap.insert(pending, time);\n return {\n task,\n time,\n dispose () {\n if (!pending.cancelled) {\n pending.cancelled = true;\n if (pending.heapEntry) ;\n }\n }\n };\n }\n relative(offset) {\n return new VirtualRelativeScheduler(offset, this);\n }\n cancelTask(st) {\n st.dispose();\n }\n /** Advance time by a duration, executing all tasks that fall within range. */ advance(duration) {\n const target = timeAdd(this.clock.now(), duration);\n this.advanceTo(target);\n }\n /** Advance to an exact time, executing all tasks up to and including that time. */ advanceTo(time) {\n while(this.heap.size > 0){\n const next = this.heap.peek();\n if (next.priority > time) break;\n this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n this.clock.setTime(time);\n }\n /** Execute all pending tasks regardless of time. */ flush() {\n while(this.heap.size > 0){\n const next = this.heap.extractMin();\n const pending = next.value;\n if (!pending.cancelled) {\n this.clock.setTime(pending.time);\n try {\n pending.task.run(pending.time);\n } catch (err) {\n pending.task.error(pending.time, err);\n }\n }\n }\n }\n /** Number of pending (non-cancelled) tasks. */ get pendingCount() {\n return this.heap.size;\n }\n}\nlet VirtualRelativeScheduler = class VirtualRelativeScheduler {\n constructor(offset, parent){\n this.offset = offset;\n this.parent = parent;\n }\n currentTime() {\n return toTime(this.parent.currentTime() + this.offset);\n }\n scheduleTask(delay, task) {\n return this.parent.scheduleTask(delay, task);\n }\n relative(offset) {\n return new VirtualRelativeScheduler(toTime(this.offset + offset), this.parent);\n }\n cancelTask(st) {\n this.parent.cancelTask(st);\n }\n};\n\nexport { BinaryHeap, DateClock, DefaultScheduler, PerformanceClock, VirtualClock, VirtualScheduler };\n//# sourceMappingURL=index.js.map\n","/**\n * Branded types for temporal values.\n *\n * Branding prevents accidental mixing of Time, Duration, and Offset\n * at the type level while remaining plain numbers at runtime.\n */ // --- Constructors ---\n/** Wrap a raw millisecond value as a Time. */ const toTime = (ms)=>ms;\n/** Wrap a raw millisecond value as a Duration. */ const toDuration = (ms)=>ms;\n/** Wrap a raw millisecond value as an Offset. */ const toOffset = (ms)=>ms;\n// --- Arithmetic ---\n/** Compute the Duration between two Time points. */ const timeDiff = (a, b)=>a - b;\n/** Advance a Time by a Duration. */ const timeAdd = (t, d)=>t + d;\n/** Advance a Time by an Offset. */ const timeShift = (t, o)=>t + o;\n// --- Constants ---\n/** Time zero — the epoch. */ const TIME_ZERO = 0;\n/** Zero duration. */ const DURATION_ZERO = 0;\n/** Zero offset. */ const OFFSET_ZERO = 0;\n\nexport { DURATION_ZERO, OFFSET_ZERO, TIME_ZERO, timeAdd, timeDiff, timeShift, toDuration, toOffset, toTime };\n//# sourceMappingURL=index.js.map\n","/**\n * Marble testing DSL for Pulse.\n *\n * Marble notation:\n * - `-` : one time unit passes (no emission)\n * - `a` : value emission (looked up in values map)\n * - `|` : end (stream completes)\n * - `#` : error (uses provided error value)\n * - `(` : group start — multiple events at the same time\n * - `)` : group end\n *\n * Examples:\n * \"--a--b--c--|\" → emits a at t=2, b at t=5, c at t=8, ends at t=11\n * \"--(ab)--c--|\" → emits a,b both at t=2, c at t=8, ends at t=11\n * \"--a--#\" → emits a at t=2, errors at t=5\n *\n * The `timeUnit` parameter controls how many ms each `-` represents.\n * Default is 1ms per unit.\n */\n\nimport type { Time } from \"aeon-types\";\nimport { toTime } from \"aeon-types\";\n\n/** A parsed marble event. */\nexport type MarbleEntry<A, E> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/**\n * Parse a marble string into a sequence of timed entries.\n *\n * @param marble - The marble notation string\n * @param values - Map from single-character keys to values\n * @param error - The error value for `#`\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const parseMarble = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit = 1,\n): MarbleEntry<A, E>[] => {\n const entries: MarbleEntry<A, E>[] = [];\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n\n switch (ch) {\n case \"-\":\n if (!inGroup) time += timeUnit;\n break;\n\n case \"(\":\n inGroup = true;\n break;\n\n case \")\":\n inGroup = false;\n if (!inGroup) time += timeUnit;\n break;\n\n case \"|\":\n entries.push({ type: \"end\", time: toTime(time) });\n if (!inGroup) time += timeUnit;\n break;\n\n case \"#\":\n entries.push({ type: \"error\", time: toTime(time), error: error as E });\n if (!inGroup) time += timeUnit;\n break;\n\n case \" \":\n break;\n\n default: {\n const value = values[ch];\n if (value === undefined) {\n throw new Error(`Marble character '${ch}' not found in values map`);\n }\n entries.push({ type: \"event\", time: toTime(time), value });\n if (!inGroup) time += timeUnit;\n break;\n }\n }\n }\n\n return entries;\n};\n\n/**\n * Compute the total duration of a marble string (in time units).\n */\nexport const marbleDuration = (marble: string, timeUnit = 1): number => {\n let time = 0;\n let inGroup = false;\n\n for (let i = 0; i < marble.length; i++) {\n const ch = marble[i]!;\n if (ch === \"(\") {\n inGroup = true;\n continue;\n }\n if (ch === \")\") {\n inGroup = false;\n time += timeUnit;\n continue;\n }\n if (ch === \" \") continue;\n if (!inGroup) time += timeUnit;\n }\n\n return time;\n};\n","/**\n * Test stream creation from marble notation.\n *\n * Creates a pulse Event that emits values according to a marble string,\n * scheduled on a VirtualScheduler.\n */\n\nimport type {\n Disposable,\n Duration,\n Event as PulseEvent,\n Scheduler,\n Sink,\n Source,\n Time,\n} from \"aeon-types\";\nimport { toDuration, toTime } from \"aeon-types\";\nimport { type MarbleEntry, parseMarble } from \"./marble.js\";\n\nclass MarbleSource<A, E> implements Source<A, E> {\n declare readonly entries: MarbleEntry<A, E>[];\n\n constructor(entries: MarbleEntry<A, E>[]) {\n this.entries = entries;\n }\n\n run(sink: Sink<A, E>, scheduler: Scheduler): Disposable {\n const disposables: Disposable[] = [];\n const currentTime = scheduler.currentTime() as number;\n\n for (const entry of this.entries) {\n const delay = toDuration((entry.time as number) - currentTime);\n\n switch (entry.type) {\n case \"event\": {\n const value = entry.value;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.event(t, value);\n },\n error(t: Time, err: unknown) {\n sink.error(t, err as E);\n },\n dispose() {},\n }),\n );\n break;\n }\n case \"error\": {\n const error = entry.error;\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.error(t, error);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n case \"end\": {\n disposables.push(\n scheduler.scheduleTask(delay, {\n run(t: Time) {\n sink.end(t);\n },\n error() {},\n dispose() {},\n }),\n );\n break;\n }\n }\n }\n\n return {\n dispose() {\n for (const d of disposables) d.dispose();\n },\n };\n }\n}\n\n/**\n * Create a pulse Event from a marble string.\n *\n * Events are scheduled on the provided scheduler. Use with VirtualScheduler\n * and advance/flush to control time.\n *\n * @param marble - Marble notation string\n * @param values - Map from single-character keys to values\n * @param error - Error value for `#` in the marble string\n * @param timeUnit - Milliseconds per time unit (default: 1)\n */\nexport const testEvent = <A, E = never>(\n marble: string,\n values: Record<string, A>,\n error?: E,\n timeUnit?: number,\n): PulseEvent<A, E> => {\n const entries = parseMarble<A, E>(marble, values, error, timeUnit);\n return new MarbleSource(entries) as unknown as PulseEvent<A, E>;\n};\n","/**\n * Test collection helpers.\n *\n * Helpers for running a pulse Event and collecting its output\n * as a structured list of timed entries for assertion.\n */\n\nimport type { Disposable, Event as PulseEvent, Scheduler, Sink, Source, Time } from \"aeon-types\";\n\n/** A collected event entry (value, error, or end). */\nexport type CollectedEntry<A, E = never> =\n | { readonly type: \"event\"; readonly time: Time; readonly value: A }\n | { readonly type: \"error\"; readonly time: Time; readonly error: E }\n | { readonly type: \"end\"; readonly time: Time };\n\n/** Result of collecting events from a stream. */\nexport interface CollectResult<A, E = never> {\n /** All collected entries in order. */\n readonly entries: CollectedEntry<A, E>[];\n /** Just the values (convenience accessor). */\n readonly values: A[];\n /** Whether the stream ended. */\n readonly ended: boolean;\n /** Whether the stream errored. */\n readonly errored: boolean;\n /** The error value, if any. */\n readonly error: E | undefined;\n /** Disposable to stop collection. */\n readonly disposable: Disposable;\n}\n\n/**\n * Subscribe to a pulse Event and collect all emissions.\n *\n * Returns a CollectResult whose `entries`, `values`, `ended`, etc.\n * update live as the scheduler advances. Use with VirtualScheduler:\n *\n * ```typescript\n * const scheduler = new VirtualScheduler();\n * const result = collectEvents(myEvent, scheduler);\n * scheduler.advanceTo(toTime(100));\n * expect(result.values).toEqual([1, 2, 3]);\n * expect(result.ended).toBe(true);\n * result.disposable.dispose();\n * ```\n */\nexport const collectEvents = <A, E = never>(\n event: PulseEvent<A, E>,\n scheduler: Scheduler,\n): CollectResult<A, E> => {\n const entries: CollectedEntry<A, E>[] = [];\n const values: A[] = [];\n const result: CollectResult<A, E> = {\n entries,\n values,\n ended: false,\n errored: false,\n error: undefined,\n disposable: { dispose() {} },\n };\n\n const sink: Sink<A, E> = {\n event(time: Time, value: A) {\n entries.push({ type: \"event\", time, value });\n values.push(value);\n },\n error(time: Time, err: E) {\n entries.push({ type: \"error\", time, error: err });\n (result as { errored: boolean }).errored = true;\n (result as { error: E | undefined }).error = err;\n },\n end(time: Time) {\n entries.push({ type: \"end\", time });\n (result as { ended: boolean }).ended = true;\n },\n };\n\n const source = event as unknown as Source<A, E>;\n const disposable = source.run(sink, scheduler);\n (result as { disposable: Disposable }).disposable = disposable;\n\n return result;\n};\n\n/**\n * Collect all values from a synchronous Event.\n *\n * For events backed by synchronous sources (fromArray, now, empty),\n * all values are available immediately without advancing time.\n */\nexport const collectSync = <A>(event: PulseEvent<A, never>, scheduler: Scheduler): A[] => {\n const result = collectEvents(event, scheduler);\n result.disposable.dispose();\n return result.values;\n};\n","/**\n * Assertion helpers for marble-based stream testing.\n */\n\nimport type { Time } from \"aeon-types\";\nimport type { CollectedEntry } from \"./collect.js\";\nimport type { MarbleEntry } from \"./marble.js\";\n\n/**\n * Compare collected entries against expected marble entries.\n *\n * Returns `{ pass: true }` if they match, or `{ pass: false, message }`\n * with a human-readable diff if they don't.\n *\n * This is framework-agnostic — use it with any assertion library:\n * ```typescript\n * const check = assertEvents(result.entries, expected);\n * if (!check.pass) throw new Error(check.message);\n * ```\n */\nexport const assertEvents = <A, E>(\n actual: readonly CollectedEntry<A, E>[],\n expected: readonly MarbleEntry<A, E>[],\n): { pass: true } | { pass: false; message: string } => {\n if (actual.length !== expected.length) {\n return {\n pass: false,\n message:\n `Expected ${expected.length} entries, got ${actual.length}.\\n` +\n ` actual: ${formatEntries(actual)}\\n` +\n ` expected: ${formatEntries(expected)}`,\n };\n }\n\n for (let i = 0; i < actual.length; i++) {\n const a = actual[i]!;\n const e = expected[i]!;\n\n if (a.type !== e.type) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected type '${e.type}', got '${a.type}'.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if ((a.time as number) !== (e.time as number)) {\n return {\n pass: false,\n message:\n `Entry ${i}: expected time ${e.time as number}, got ${a.time as number}.\\n` +\n ` actual: ${formatEntry(a)}\\n` +\n ` expected: ${formatEntry(e)}`,\n };\n }\n\n if (a.type === \"event\" && e.type === \"event\") {\n if (!Object.is(a.value, e.value)) {\n return {\n pass: false,\n message: `Entry ${i}: expected value ${JSON.stringify(e.value)}, got ${JSON.stringify(a.value)}.`,\n };\n }\n }\n\n if (a.type === \"error\" && e.type === \"error\") {\n if (!Object.is(a.error, e.error)) {\n return {\n pass: false,\n message: `Entry ${i}: expected error ${JSON.stringify(e.error)}, got ${JSON.stringify(a.error)}.`,\n };\n }\n }\n }\n\n return { pass: true };\n};\n\nconst formatEntry = (\n e: CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>,\n): string => {\n switch (e.type) {\n case \"event\":\n return `event(${e.time as number}, ${JSON.stringify(e.value)})`;\n case \"error\":\n return `error(${e.time as number}, ${JSON.stringify(e.error)})`;\n case \"end\":\n return `end(${e.time as number})`;\n }\n};\n\nconst formatEntries = (\n entries: readonly (CollectedEntry<unknown, unknown> | MarbleEntry<unknown, unknown>)[],\n): string => `[${entries.map(formatEntry).join(\", \")}]`;\n"],"names":["toTime","ms","timeAdd","t","d","TIME_ZERO","toDuration","parseMarble","marble","values","error","timeUnit","entries","time","inGroup","i","length","ch","push","type","value","undefined","Error","marbleDuration","MarbleSource","run","sink","scheduler","disposables","currentTime","entry","delay","scheduleTask","event","err","dispose","end","testEvent","collectEvents","result","ended","errored","disposable","source","collectSync","assertEvents","actual","expected","pass","message","formatEntries","a","e","formatEntry","Object","is","JSON","stringify","map","join"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aeon-test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Marble testing utilities for Aeon reactive streams",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Josh Burgess",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"dist"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"aeon-
|
|
44
|
-
"aeon-
|
|
45
|
-
"aeon-scheduler": "0.
|
|
43
|
+
"aeon-core": "0.2.0",
|
|
44
|
+
"aeon-types": "0.2.0",
|
|
45
|
+
"aeon-scheduler": "0.2.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"typescript": "^5.7.2"
|