@noego/testing 0.1.0 → 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/README.md +94 -0
- package/dist/index.cjs +610 -641
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +237 -227
- package/dist/index.d.ts +237 -227
- package/dist/index.js +601 -622
- package/dist/index.js.map +1 -1
- package/package.json +9 -6
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/time/manual_clock.ts","../src/time/manual_scheduler.ts","../src/identity/sequence_id_generator.ts","../src/identity/seeded_random_source.ts","../src/network/scripted_fetch_client.ts","../src/process/scripted_process_runner.ts","../src/storage/memory_key_value_store.ts","../src/storage/memory_object_store.ts","../src/events/recording_event_bus.ts","../src/observability/recording_sinks.ts","../src/contracts/clock.contract.ts","../src/contracts/key_value_store.contract.ts","../src/contracts/object_store.contract.ts","../src/contracts/event_bus.contract.ts","../src/leak/leak_detector.ts"],"sourcesContent":["import { Clock, Duration, Instant } from \"@noego/runtime\";\n\n/**\n * Deterministic clock. Time never moves unless the test moves it.\n * Defaults to a fixed instant so snapshots are stable.\n */\nexport class ManualClock extends Clock {\n static readonly defaultStart = Instant.ofEpochMilliseconds(Date.UTC(2020, 0, 1));\n\n private current: Instant;\n\n constructor(start: Instant = ManualClock.defaultStart) {\n super();\n this.current = start;\n }\n\n override now(): Instant {\n return this.current;\n }\n\n set(instant: Instant): void {\n if (instant.isBefore(this.current)) {\n throw new RangeError(`ManualClock cannot move backwards: ${this.current.toString()} -> ${instant.toString()}`);\n }\n this.current = instant;\n }\n\n advanceBy(duration: Duration): void {\n this.current = this.current.plus(duration);\n }\n}\n","import { Duration, Instant, Scheduler, brandId } from \"@noego/runtime\";\nimport type { ScheduleInput, ScheduledTaskHandle, ScheduledTaskId } from \"@noego/runtime\";\nimport type { ManualClock } from \"./manual_clock.js\";\n\nexport interface PendingTask {\n readonly id: ScheduledTaskId;\n readonly label: string;\n readonly deadline: Instant;\n}\n\ninterface InternalTask {\n readonly id: ScheduledTaskId;\n readonly label: string;\n readonly deadline: Instant;\n readonly sequence: number;\n readonly task: () => void | Promise<void>;\n cancelled: boolean;\n}\n\n/**\n * Deterministic scheduler driven by a ManualClock.\n *\n * Locked semantics:\n * - deadlines are (scheduledAt + delay);\n * - equal deadlines run in insertion order;\n * - cancelled tasks never execute;\n * - callbacks scheduled by callbacks are ordered deterministically (insertion\n * sequence is global and monotonic);\n * - async callbacks settle before advancing continues;\n * - runUntilIdle fails with a bounded diagnostic on infinite reschedule loops;\n * - assertNoPendingTasks fails teardown when tasks remain, unless allowed;\n * - CHOSEN MODEL: advancing time RUNS every task whose deadline falls inside\n * the advanced window, in deadline order. advanceBy(Duration.zero) runs\n * tasks already due.\n */\nexport class ManualScheduler extends Scheduler {\n private readonly tasks: InternalTask[] = [];\n private nextSequence = 0;\n private nextId = 0;\n\n constructor(private readonly clock: ManualClock) {\n super();\n }\n\n override schedule(input: ScheduleInput): ScheduledTaskHandle {\n const id = brandId(`task-${++this.nextId}`, \"ScheduledTask\");\n const internal: InternalTask = {\n id,\n label: input.label,\n deadline: this.clock.now().plus(input.delay),\n sequence: this.nextSequence++,\n task: input.task,\n cancelled: false,\n };\n this.tasks.push(internal);\n return {\n id,\n cancel: () => {\n internal.cancelled = true;\n },\n };\n }\n\n pending(): PendingTask[] {\n return this.live()\n .sort(compareTasks)\n .map(({ id, label, deadline }) => ({ id, label, deadline }));\n }\n\n /** Advance the clock, running every task due inside the window in order. */\n async advanceBy(duration: Duration): Promise<void> {\n const target = this.clock.now().plus(duration);\n for (;;) {\n const next = this.nextDue(target);\n if (next === null) break;\n if (next.deadline.isAfter(this.clock.now())) {\n this.clock.set(next.deadline);\n }\n await this.execute(next);\n }\n if (target.isAfter(this.clock.now())) {\n this.clock.set(target);\n }\n }\n\n /** Advance to the earliest pending deadline and run exactly that task. */\n async runNext(): Promise<void> {\n const next = this.live().sort(compareTasks)[0];\n if (next === undefined) {\n throw new Error(\"ManualScheduler.runNext: no pending tasks\");\n }\n if (next.deadline.isAfter(this.clock.now())) {\n this.clock.set(next.deadline);\n }\n await this.execute(next);\n }\n\n /** Run tasks (advancing time as needed) until none remain. */\n async runUntilIdle(options?: { maxTasks?: number }): Promise<void> {\n const maxTasks = options?.maxTasks ?? 10_000;\n let executed = 0;\n while (this.live().length > 0) {\n if (executed >= maxTasks) {\n const labels = this.pending().slice(0, 5).map((t) => t.label).join(\", \");\n throw new Error(\n `ManualScheduler.runUntilIdle: still not idle after ${maxTasks} tasks — probable infinite reschedule loop (next: ${labels})`,\n );\n }\n await this.runNext();\n executed += 1;\n }\n }\n\n /** Teardown assertion: fails when pending tasks remain, unless allowed. */\n assertNoPendingTasks(options?: { allow?: boolean }): void {\n if (options?.allow === true) return;\n const remaining = this.pending();\n if (remaining.length > 0) {\n const labels = remaining.map((t) => `${t.label} @ ${t.deadline.toString()}`).join(\"; \");\n throw new Error(`ManualScheduler: ${remaining.length} pending task(s) at teardown: ${labels}`);\n }\n }\n\n private live(): InternalTask[] {\n // Executed tasks are removed eagerly in execute(), so presence in the\n // list plus not-cancelled means pending.\n return this.tasks.filter((t) => !t.cancelled);\n }\n\n private nextDue(target: Instant): InternalTask | null {\n const candidates = this.live()\n .filter((t) => !t.deadline.isAfter(target))\n .sort(compareTasks);\n return candidates[0] ?? null;\n }\n\n private async execute(task: InternalTask): Promise<void> {\n this.remove(task);\n await task.task();\n }\n\n private remove(task: InternalTask): void {\n const index = this.tasks.indexOf(task);\n this.tasks.splice(index, 1);\n }\n}\n\nfunction compareTasks(a: InternalTask, b: InternalTask): number {\n const byDeadline = a.deadline.compareTo(b.deadline);\n return byDeadline !== 0 ? byDeadline : a.sequence - b.sequence;\n}\n","import { IdGenerator, brandId } from \"@noego/runtime\";\nimport type { BrandedId } from \"@noego/runtime\";\n\n/**\n * Deterministic ID generator: \"<brand>-1\", \"<brand>-2\", … per brand.\n * Fixed IDs keep snapshots stable; isolation makes collisions impossible.\n */\nexport class SequenceIdGenerator extends IdGenerator {\n private readonly counters = new Map<string, number>();\n\n override next<TBrand extends string>(brand: TBrand): BrandedId<TBrand> {\n const current = (this.counters.get(brand) ?? 0) + 1;\n this.counters.set(brand, current);\n return brandId(`${brand.toLowerCase()}-${current}`, brand);\n }\n}\n","import { RandomSource } from \"@noego/runtime\";\n\n/**\n * Deterministic random source (mulberry32). The same seed always yields the\n * same sequence.\n */\nexport class SeededRandomSource extends RandomSource {\n private state: number;\n\n constructor(seed: number = 1) {\n super();\n if (!Number.isInteger(seed)) {\n throw new RangeError(`SeededRandomSource seed must be an integer, got ${seed}`);\n }\n this.state = seed >>> 0;\n }\n\n private nextUint32(): number {\n this.state = (this.state + 0x6d2b79f5) >>> 0;\n let t = this.state;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return (t ^ (t >>> 14)) >>> 0;\n }\n\n override bytes(length: number): Uint8Array {\n if (!Number.isInteger(length) || length < 0) {\n throw new RangeError(`bytes length must be a non-negative integer, got ${length}`);\n }\n const out = new Uint8Array(length);\n for (let i = 0; i < length; i += 1) {\n out[i] = this.nextUint32() & 0xff;\n }\n return out;\n }\n\n override integer(minInclusive: number, maxExclusive: number): number {\n if (maxExclusive <= minInclusive) {\n throw new RangeError(`integer range is empty: [${minInclusive}, ${maxExclusive})`);\n }\n const span = maxExclusive - minInclusive;\n return minInclusive + (this.nextUint32() % span);\n }\n}\n","import { FetchClient, FetchError } from \"@noego/runtime\";\nimport type { FetchRequest, FetchResponse } from \"@noego/runtime\";\n\nexport interface FetchScriptEntry {\n /** Human-readable description used in failure diagnostics. */\n readonly describe: string;\n /** Typed predicate the incoming request must satisfy. */\n readonly matches: (request: FetchRequest) => boolean;\n /** Either respond or fail with a normalized FetchError. */\n readonly respond: (request: FetchRequest) => FetchResponse | FetchError;\n}\n\n/**\n * Scripted FetchClient. Scripts are consumed strictly in order; an unexpected\n * or mismatched call fails immediately with a diagnostic.\n */\nexport class ScriptedFetchClient extends FetchClient {\n private readonly script: FetchScriptEntry[];\n private cursor = 0;\n readonly executed: FetchRequest[] = [];\n\n constructor(script: readonly FetchScriptEntry[]) {\n super();\n this.script = [...script];\n }\n\n override execute(request: FetchRequest): Promise<FetchResponse> {\n const entry = this.script[this.cursor];\n if (entry === undefined) {\n return Promise.reject(\n new Error(`ScriptedFetchClient: unexpected call #${this.cursor + 1} (${request.method} ${request.url}); script is exhausted`),\n );\n }\n if (!entry.matches(request)) {\n return Promise.reject(\n new Error(`ScriptedFetchClient: call #${this.cursor + 1} (${request.method} ${request.url}) does not match script entry \"${entry.describe}\"`),\n );\n }\n this.cursor += 1;\n this.executed.push(request);\n const outcome = entry.respond(request);\n return outcome instanceof FetchError ? Promise.reject(outcome) : Promise.resolve(outcome);\n }\n\n /** Teardown assertion: every scripted call must have been consumed. */\n assertScriptConsumed(): void {\n if (this.cursor < this.script.length) {\n const remaining = this.script.slice(this.cursor).map((e) => e.describe).join(\", \");\n throw new Error(`ScriptedFetchClient: ${this.script.length - this.cursor} unconsumed script entr(ies): ${remaining}`);\n }\n }\n}\n","import { ProcessRunner } from \"@noego/runtime\";\nimport type { ProcessCommand, ProcessEvent, ProcessHandle, ProcessResult, SpawnCommand } from \"@noego/runtime\";\n\n/** Spawn handle with test-only deterministic delivery control. */\nexport interface ScriptedSpawnHandle extends ProcessHandle {\n /** Deliver all scripted events now (deterministic, test-controlled). */\n flush(): void;\n}\n\nexport interface ProcessScriptEntry {\n readonly describe: string;\n readonly matches: (command: ProcessCommand) => boolean;\n /** Events emitted (for spawn) / folded into the result (for run). */\n readonly events: readonly ProcessEvent[];\n}\n\n/**\n * Scripted ProcessRunner. Scripts are consumed in order; unexpected commands\n * fail immediately. Spawned handles emit their declared events when\n * flush() is called, keeping delivery under test control.\n */\nexport class ScriptedProcessRunner extends ProcessRunner {\n private readonly script: ProcessScriptEntry[];\n private cursor = 0;\n private readonly openHandles = new Set<ScriptedProcessHandle>();\n\n constructor(script: readonly ProcessScriptEntry[]) {\n super();\n this.script = [...script];\n }\n\n private consume(command: ProcessCommand, kind: string): ProcessScriptEntry {\n const entry = this.script[this.cursor];\n if (entry === undefined) {\n throw new Error(`ScriptedProcessRunner: unexpected ${kind} of \"${command.executable}\"; script is exhausted`);\n }\n if (!entry.matches(command)) {\n throw new Error(`ScriptedProcessRunner: ${kind} of \"${command.executable}\" does not match script entry \"${entry.describe}\"`);\n }\n this.cursor += 1;\n return entry;\n }\n\n override run(command: ProcessCommand): Promise<ProcessResult> {\n try {\n const entry = this.consume(command, \"run\");\n return Promise.resolve(foldEvents(entry.events));\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n override spawn(command: SpawnCommand): ScriptedSpawnHandle {\n const entry = this.consume(command, \"spawn\");\n const handle = new ScriptedProcessHandle(command.label, entry.events, () => {\n this.openHandles.delete(handle);\n });\n this.openHandles.add(handle);\n return handle;\n }\n\n /** Teardown assertion: no spawned process may remain open. */\n assertNoOpenHandles(): void {\n if (this.openHandles.size > 0) {\n const labels = [...this.openHandles].map((h) => h.label).join(\", \");\n throw new Error(`ScriptedProcessRunner: ${this.openHandles.size} open process handle(s) at teardown: ${labels}`);\n }\n }\n}\n\nclass ScriptedProcessHandle implements ScriptedSpawnHandle {\n private readonly listeners: Array<(event: ProcessEvent) => void> = [];\n private readonly resultPromise: Promise<ProcessResult>;\n private resolveResult!: (result: ProcessResult) => void;\n private flushed = false;\n private killed = false;\n\n constructor(\n public readonly label: string,\n private readonly events: readonly ProcessEvent[],\n private readonly onClosed: () => void,\n ) {\n this.resultPromise = new Promise((resolve) => {\n this.resolveResult = resolve;\n });\n }\n\n onEvent(listener: (event: ProcessEvent) => void): void {\n this.listeners.push(listener);\n }\n\n /** Deliver all scripted events now (deterministic, test-controlled). */\n flush(): void {\n if (this.flushed || this.killed) return;\n this.flushed = true;\n for (const event of this.events) {\n for (const listener of this.listeners) listener(event);\n if (event.kind === \"exit\") {\n this.resolveResult(foldEvents(this.events));\n this.onClosed();\n }\n }\n }\n\n kill(signal: string = \"SIGTERM\"): void {\n if (this.flushed || this.killed) return;\n this.killed = true;\n const exit: ProcessEvent = { kind: \"exit\", exitCode: null, signal };\n for (const listener of this.listeners) listener(exit);\n this.resolveResult({ exitCode: null, signal, stdout: \"\", stderr: \"\" });\n this.onClosed();\n }\n\n wait(): Promise<ProcessResult> {\n return this.resultPromise;\n }\n}\n\nfunction foldEvents(events: readonly ProcessEvent[]): ProcessResult {\n let stdout = \"\";\n let stderr = \"\";\n let exitCode: number | null = null;\n let signal: string | null = null;\n for (const event of events) {\n if (event.kind === \"stdout\") stdout += event.chunk;\n else if (event.kind === \"stderr\") stderr += event.chunk;\n else {\n exitCode = event.exitCode;\n signal = event.signal;\n }\n }\n return { exitCode, signal, stdout, stderr };\n}\n","import { KeyValueStore } from \"@noego/runtime\";\nimport type { Clock, Codec, Instant, Key, PutOptions } from \"@noego/runtime\";\n\ninterface Entry {\n readonly encoded: string;\n readonly expiresAt: Instant | null;\n}\n\n/**\n * Deterministic in-memory KeyValueStore with clock-driven TTL semantics.\n * Passes the same contract as KV/Redis adapters.\n */\nexport class MemoryKeyValueStore extends KeyValueStore {\n private readonly entries = new Map<string, Entry>();\n\n constructor(private readonly clock: Clock) {\n super();\n }\n\n override get<T>(key: Key, codec: Codec<T>): Promise<T | null> {\n const entry = this.entries.get(key);\n if (entry === undefined) return Promise.resolve(null);\n if (entry.expiresAt !== null && !this.clock.now().isBefore(entry.expiresAt)) {\n this.entries.delete(key);\n return Promise.resolve(null);\n }\n return Promise.resolve(codec.decode(entry.encoded));\n }\n\n override put<T>(key: Key, value: T, codec: Codec<T>, options?: PutOptions): Promise<void> {\n const expiresAt = options?.timeToLive !== undefined ? this.clock.now().plus(options.timeToLive) : null;\n this.entries.set(key, { encoded: codec.encode(value), expiresAt });\n return Promise.resolve();\n }\n\n override delete(key: Key): Promise<void> {\n this.entries.delete(key);\n return Promise.resolve();\n }\n}\n","import { ObjectStore, brandId } from \"@noego/runtime\";\nimport type { ObjectKey, ObjectVersion, StoredObject, StoredObjectInput } from \"@noego/runtime\";\n\n/**\n * Deterministic in-memory ObjectStore with monotonically increasing versions.\n * Passes the same contract as R2/S3 adapters.\n */\nexport class MemoryObjectStore extends ObjectStore {\n private readonly objects = new Map<string, StoredObject>();\n private nextVersion = 0;\n\n override read(key: ObjectKey): Promise<StoredObject | null> {\n return Promise.resolve(this.objects.get(key) ?? null);\n }\n\n override write(key: ObjectKey, object: StoredObjectInput): Promise<ObjectVersion> {\n const version = brandId(`v${++this.nextVersion}`, \"ObjectVersion\");\n this.objects.set(key, {\n bytes: object.bytes.slice(),\n contentType: object.contentType,\n metadata: object.metadata !== undefined ? new Map(object.metadata) : undefined,\n version,\n });\n return Promise.resolve(version);\n }\n\n override delete(key: ObjectKey): Promise<void> {\n this.objects.delete(key);\n return Promise.resolve();\n }\n}\n","import { EventBus } from \"@noego/runtime\";\nimport type { DomainEvent, EventHandler, Subscription } from \"@noego/runtime\";\n\nexport type DeliveryMode = \"immediate\" | \"queued\";\n\n/**\n * Recording EventBus. Records exact publication order. Delivery modes:\n * - immediate (default): handlers run during publish;\n * - queued: events buffer until deliverQueued() is called.\n * Active subscriptions at teardown fail assertNoActiveSubscriptions().\n */\nexport class RecordingEventBus<TEvent extends DomainEvent> extends EventBus<TEvent> {\n readonly published: TEvent[] = [];\n private readonly handlers = new Set<EventHandler<TEvent>>();\n private readonly queue: TEvent[] = [];\n\n constructor(private readonly mode: DeliveryMode = \"immediate\") {\n super();\n }\n\n override async publish(event: TEvent): Promise<void> {\n this.published.push(event);\n if (this.mode === \"queued\") {\n this.queue.push(event);\n return;\n }\n await this.deliver(event);\n }\n\n override subscribe(handler: EventHandler<TEvent>): Subscription {\n this.handlers.add(handler);\n const bus = this;\n const subscription = {\n active: true,\n unsubscribe(): void {\n if (!subscription.active) return;\n subscription.active = false;\n bus.handlers.delete(handler);\n },\n };\n return subscription;\n }\n\n /** Deliver buffered events (queued mode) in publication order. */\n async deliverQueued(): Promise<void> {\n while (this.queue.length > 0) {\n const event = this.queue.shift() as TEvent;\n await this.deliver(event);\n }\n }\n\n get activeSubscriptionCount(): number {\n return this.handlers.size;\n }\n\n assertNoActiveSubscriptions(): void {\n if (this.handlers.size > 0) {\n throw new Error(`RecordingEventBus: ${this.handlers.size} active subscription(s) at teardown`);\n }\n }\n\n private async deliver(event: TEvent): Promise<void> {\n for (const handler of [...this.handlers]) {\n await handler(event);\n }\n }\n}\n","import { LogSink, TelemetrySink, TraceSink } from \"@noego/runtime\";\nimport type { LogEnvelope, TelemetryEnvelope, TraceEnvelope } from \"@noego/runtime\";\n\n/** In-memory LogSink recording envelopes in emission order. */\nexport class RecordingLogSink extends LogSink {\n readonly envelopes: LogEnvelope[] = [];\n\n override emit(envelope: LogEnvelope): void {\n this.envelopes.push(envelope);\n }\n}\n\n/** In-memory TraceSink recording envelopes in emission order. */\nexport class RecordingTraceSink extends TraceSink {\n readonly envelopes: TraceEnvelope[] = [];\n\n override emit(envelope: TraceEnvelope): void {\n this.envelopes.push(envelope);\n }\n}\n\n/** In-memory TelemetrySink recording envelopes in emission order. */\nexport class RecordingTelemetrySink extends TelemetrySink {\n readonly envelopes: TelemetryEnvelope[] = [];\n\n override emit(envelope: TelemetryEnvelope): void {\n this.envelopes.push(envelope);\n }\n}\n\n/** LogSink that drops everything (for suites that assert nothing about logs). */\nexport class NoopLogSink extends LogSink {\n override emit(_envelope: LogEnvelope): void {\n /* intentionally empty */\n }\n}\n","import type { Clock } from \"@noego/runtime\";\n\n/**\n * Behavioral contract every Clock implementation must satisfy.\n * Runs inside the caller's jest context.\n */\nexport function runClockContract(name: string, setup: () => { clock: Clock }): void {\n describe(`Clock contract: ${name}`, () => {\n it(\"returns a monotonically non-decreasing instant across consecutive reads\", () => {\n const { clock } = setup();\n const first = clock.now();\n const second = clock.now();\n expect(second.isBefore(first)).toBe(false);\n });\n\n it(\"nowMs agrees with now()\", () => {\n const { clock } = setup();\n const instant = clock.now();\n const ms = clock.nowMs();\n expect(ms).toBeGreaterThanOrEqual(instant.epochMilliseconds);\n });\n });\n}\n","import { Duration, jsonCodec, storageKey } from \"@noego/runtime\";\nimport type { KeyValueStore } from \"@noego/runtime\";\n\nexport interface KeyValueStoreContractContext {\n store: KeyValueStore;\n /** Move the store's time source forward (real adapters may wait/mock). */\n advanceBy: (duration: Duration) => Promise<void>;\n /** Whether the implementation supports TTL expiry. */\n supportsTtl: boolean;\n}\n\n/**\n * Behavioral contract every KeyValueStore implementation must satisfy —\n * memory, KV, and Redis adapters alike.\n */\nexport function runKeyValueStoreContract(name: string, setup: () => KeyValueStoreContractContext): void {\n const codec = jsonCodec<{ n: number }>();\n\n describe(`KeyValueStore contract: ${name}`, () => {\n it(\"returns null for a missing key\", async () => {\n const { store } = setup();\n await expect(store.get(storageKey(\"missing\"), codec)).resolves.toBeNull();\n });\n\n it(\"round-trips a stored value\", async () => {\n const { store } = setup();\n const key = storageKey(\"k1\");\n await store.put(key, { n: 42 }, codec);\n await expect(store.get(key, codec)).resolves.toEqual({ n: 42 });\n });\n\n it(\"overwrites an existing value\", async () => {\n const { store } = setup();\n const key = storageKey(\"k1\");\n await store.put(key, { n: 1 }, codec);\n await store.put(key, { n: 2 }, codec);\n await expect(store.get(key, codec)).resolves.toEqual({ n: 2 });\n });\n\n it(\"deletes a value and tolerates deleting a missing key\", async () => {\n const { store } = setup();\n const key = storageKey(\"k1\");\n await store.put(key, { n: 1 }, codec);\n await store.delete(key);\n await expect(store.get(key, codec)).resolves.toBeNull();\n await expect(store.delete(storageKey(\"missing\"))).resolves.toBeUndefined();\n });\n\n it(\"expires values after their TTL when TTL is supported\", async () => {\n const context = setup();\n if (!context.supportsTtl) return;\n const key = storageKey(\"expiring\");\n await context.store.put(key, { n: 7 }, codec, { timeToLive: Duration.ofSeconds(30) });\n await expect(context.store.get(key, codec)).resolves.toEqual({ n: 7 });\n await context.advanceBy(Duration.ofSeconds(31));\n await expect(context.store.get(key, codec)).resolves.toBeNull();\n });\n\n it(\"keeps values without a TTL alive as time passes\", async () => {\n const context = setup();\n const key = storageKey(\"durable\");\n await context.store.put(key, { n: 9 }, codec);\n await context.advanceBy(Duration.ofMinutes(60));\n await expect(context.store.get(key, codec)).resolves.toEqual({ n: 9 });\n });\n });\n}\n","import { objectKey } from \"@noego/runtime\";\nimport type { ObjectStore } from \"@noego/runtime\";\n\n/**\n * Behavioral contract every ObjectStore implementation must satisfy —\n * memory, R2, S3, and filesystem adapters alike.\n */\nexport function runObjectStoreContract(name: string, setup: () => { store: ObjectStore }): void {\n const bytes = (...values: number[]) => new Uint8Array(values);\n\n describe(`ObjectStore contract: ${name}`, () => {\n it(\"returns null for a missing object\", async () => {\n const { store } = setup();\n await expect(store.read(objectKey(\"missing\"))).resolves.toBeNull();\n });\n\n it(\"round-trips bytes, content type, and metadata\", async () => {\n const { store } = setup();\n const key = objectKey(\"docs/a.bin\");\n await store.write(key, {\n bytes: bytes(1, 2, 3),\n contentType: \"application/octet-stream\",\n metadata: new Map([[\"owner\", \"alice\"]]),\n });\n const stored = await store.read(key);\n expect(stored).not.toBeNull();\n expect([...stored!.bytes]).toEqual([1, 2, 3]);\n expect(stored!.contentType).toBe(\"application/octet-stream\");\n expect(stored!.metadata?.get(\"owner\")).toBe(\"alice\");\n });\n\n it(\"issues a new version on every write\", async () => {\n const { store } = setup();\n const key = objectKey(\"docs/a.bin\");\n const v1 = await store.write(key, { bytes: bytes(1), contentType: \"text/plain\" });\n const v2 = await store.write(key, { bytes: bytes(2), contentType: \"text/plain\" });\n expect(v1).not.toBe(v2);\n const stored = await store.read(key);\n expect(stored!.version).toBe(v2);\n expect([...stored!.bytes]).toEqual([2]);\n });\n\n it(\"deletes an object and tolerates deleting a missing key\", async () => {\n const { store } = setup();\n const key = objectKey(\"docs/a.bin\");\n await store.write(key, { bytes: bytes(1), contentType: \"text/plain\" });\n await store.delete(key);\n await expect(store.read(key)).resolves.toBeNull();\n await expect(store.delete(objectKey(\"missing\"))).resolves.toBeUndefined();\n });\n\n it(\"does not expose caller mutations of written bytes\", async () => {\n const { store } = setup();\n const key = objectKey(\"docs/mutable.bin\");\n const input = bytes(9);\n await store.write(key, { bytes: input, contentType: \"text/plain\" });\n input[0] = 0;\n const stored = await store.read(key);\n expect([...stored!.bytes]).toEqual([9]);\n });\n });\n}\n","import type { DomainEvent, EventBus } from \"@noego/runtime\";\n\nexport interface TestEvent extends DomainEvent {\n readonly type: \"test-event\";\n readonly value: number;\n}\n\nexport const testEvent = (value: number): TestEvent => ({ type: \"test-event\", value });\n\nexport interface EventBusContractContext {\n bus: EventBus<TestEvent>;\n /** Force delivery of any buffered events (no-op for immediate buses). */\n deliver: () => Promise<void>;\n}\n\n/**\n * Behavioral contract every EventBus implementation must satisfy.\n */\nexport function runEventBusContract(name: string, setup: () => EventBusContractContext): void {\n describe(`EventBus contract: ${name}`, () => {\n it(\"delivers published events to subscribers in order\", async () => {\n const { bus, deliver } = setup();\n const seen: number[] = [];\n const subscription = bus.subscribe((event) => {\n seen.push(event.value);\n });\n await bus.publish(testEvent(1));\n await bus.publish(testEvent(2));\n await deliver();\n expect(seen).toEqual([1, 2]);\n subscription.unsubscribe();\n });\n\n it(\"stops delivering after unsubscribe\", async () => {\n const { bus, deliver } = setup();\n const seen: number[] = [];\n const subscription = bus.subscribe((event) => {\n seen.push(event.value);\n });\n await bus.publish(testEvent(1));\n await deliver();\n subscription.unsubscribe();\n await bus.publish(testEvent(2));\n await deliver();\n expect(seen).toEqual([1]);\n });\n\n it(\"supports multiple subscribers\", async () => {\n const { bus, deliver } = setup();\n const a: number[] = [];\n const b: number[] = [];\n const sa = bus.subscribe((event) => {\n a.push(event.value);\n });\n const sb = bus.subscribe((event) => {\n b.push(event.value);\n });\n await bus.publish(testEvent(5));\n await deliver();\n expect(a).toEqual([5]);\n expect(b).toEqual([5]);\n sa.unsubscribe();\n sb.unsubscribe();\n });\n\n it(\"marks subscriptions inactive after unsubscribe and tolerates double unsubscribe\", async () => {\n const { bus, deliver } = setup();\n const seen: number[] = [];\n const subscription = bus.subscribe((event) => {\n seen.push(event.value);\n });\n await bus.publish(testEvent(1));\n await deliver();\n expect(seen).toEqual([1]);\n expect(subscription.active).toBe(true);\n subscription.unsubscribe();\n expect(subscription.active).toBe(false);\n subscription.unsubscribe();\n expect(subscription.active).toBe(false);\n });\n });\n}\n","export interface LeakCheck {\n /** Which resource family this check guards (timers, subscriptions, …). */\n readonly name: string;\n /** Returns a description per leaked resource; empty means clean. */\n readonly check: () => readonly string[];\n}\n\n/**\n * Aggregates leak checks and fails teardown with one clear report listing\n * every leaked resource across all registered checks.\n */\nexport class LeakDetector {\n private readonly checks: LeakCheck[] = [];\n\n register(check: LeakCheck): void {\n this.checks.push(check);\n }\n\n findLeaks(): string[] {\n const leaks: string[] = [];\n for (const { name, check } of this.checks) {\n for (const description of check()) {\n leaks.push(`[${name}] ${description}`);\n }\n }\n return leaks;\n }\n\n assertNoLeaks(): void {\n const leaks = this.findLeaks();\n if (leaks.length > 0) {\n throw new Error(`LeakDetector: ${leaks.length} leaked resource(s):\\n${leaks.join(\"\\n\")}`);\n }\n }\n}\n"],"mappings":"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","names":["brandId","brandId","Duration"]}
|
|
1
|
+
{"version":3,"sources":["../src/builder.ts","../src/errors.ts","../src/descriptors.ts","../src/method_state.ts"],"sourcesContent":["/**\n * `testIoc` — the canonical shared real-IoC test composition builder.\n *\n * Persistent immutable: every fluent call returns a new derived builder\n * sharing the ordered write log structurally. Non-conflicting writes are\n * order-insensitive; the last write to the same effective identity wins on\n * that derived branch. `.build()` is non-consuming and creates fresh runtime,\n * watch, and expectation state (spec 15, PBR-01..15).\n */\n\nimport {\n createContainer,\n flattenModule,\n LoadAs,\n SCOPED_CONTAINER,\n type ApplicationModule,\n type IContainer,\n} from '@noego/ioc';\n\nimport { ENV_REGISTRY, isDescriptor, type MethodDescriptor } from './descriptors';\nimport { createMethodDecorator, WatchRegistry, tokenLabel } from './method_state';\nimport { InvalidDescriptorError, MissingIocSeamError, UnknownTokenKeyError } from './errors';\n\nconst COMPONENT_OPTIONS_KEY = Symbol.for('ioc:component:options');\n\ntype Token = unknown;\ntype ClassLike = new (...args: any[]) => any;\n\n/** Config maps accept plain objects (string keys) or Maps (exact tokens). */\nexport type ConfigMap<V> = Record<string, V> | ReadonlyMap<Token, V>;\n\nexport type MethodsConfig = ConfigMap<Record<string, MethodDescriptor> | ReadonlyMap<string, MethodDescriptor>>;\n\nexport type UseInput = ApplicationModule | TestIocBuilder;\n\ntype Write =\n | { op: 'use'; module: ApplicationModule }\n | { op: 'classes'; key: Token; byName: boolean; implementation: ClassLike }\n | { op: 'functions'; key: Token; byName: boolean; factory: (...args: any[]) => any }\n | { op: 'values'; key: Token; byName: boolean; value: unknown }\n | { op: 'methods'; key: Token; byName: boolean; methods: ReadonlyMap<string, MethodDescriptor> };\n\nexport interface TestEnvironment {\n readonly root: IContainer;\n get<T>(token: unknown, params?: any[]): Promise<T> | T;\n instance<T>(cls: new (...args: any[]) => T, params?: any[]): Promise<T> | T;\n extend(): IContainer;\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: WatchRegistry;\n}\n\nfunction* configEntries<V>(config: ConfigMap<V>): Iterable<[Token, boolean, V]> {\n if (config instanceof Map) {\n for (const [key, value] of config) yield [key, false, value];\n } else {\n for (const key of [...Object.getOwnPropertyNames(config), ...Object.getOwnPropertySymbols(config)]) {\n yield [key, typeof key === 'string', (config as any)[key]];\n }\n }\n}\n\nfunction componentScope(cls: any): LoadAs | undefined {\n const options =\n typeof Reflect !== 'undefined' && (Reflect as any).getMetadata\n ? (Reflect as any).getMetadata(COMPONENT_OPTIONS_KEY, cls)\n : undefined;\n return options?.scope;\n}\n\nfunction lifetimeToLoadAs(lifetime: string): LoadAs {\n if (lifetime === 'singleton') return LoadAs.Singleton;\n if (lifetime === 'scoped') return LoadAs.Scoped;\n return LoadAs.Transient;\n}\n\nexport class TestIocBuilder {\n private constructor(private readonly log: readonly Write[]) {\n Object.freeze(this);\n }\n\n /** @internal */\n static create(inputs: readonly UseInput[]): TestIocBuilder {\n return new TestIocBuilder([]).useAll(inputs);\n }\n\n /** @internal — read by .use(builderPreset) */\n get writes(): readonly Write[] {\n return this.log;\n }\n\n private derive(writes: Write[]): TestIocBuilder {\n return new TestIocBuilder([...this.log, ...writes]);\n }\n\n private useAll(inputs: readonly UseInput[]): TestIocBuilder {\n let builder: TestIocBuilder = this;\n for (const input of inputs) builder = builder.use(input);\n return builder;\n }\n\n /** Apply a reusable composition preset: an ApplicationModule or a builder. */\n use(preset: UseInput): TestIocBuilder {\n if (preset instanceof TestIocBuilder) {\n return this.derive([...preset.writes]);\n }\n return this.derive([{ op: 'use', module: preset }]);\n }\n\n /** Replace the implementation for IoC class tokens in the built environment. */\n classes(config: ConfigMap<ClassLike>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, implementation] of configEntries(config)) {\n if (typeof implementation !== 'function' || !implementation.prototype) {\n throw new InvalidDescriptorError(\n `.classes() value for \"${tokenLabel(key)}\" must be a class constructor.`,\n );\n }\n writes.push({ op: 'classes', key, byName, implementation });\n }\n return this.derive(writes);\n }\n\n /** Replace IoC factory/provider registrations. */\n functions(config: ConfigMap<(...args: any[]) => any>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, factory] of configEntries(config)) {\n if (typeof factory !== 'function') {\n throw new InvalidDescriptorError(`.functions() value for \"${tokenLabel(key)}\" must be a function.`);\n }\n writes.push({ op: 'functions', key, byName, factory });\n }\n return this.derive(writes);\n }\n\n /** Provide/replace IoC value registrations. */\n values(config: ConfigMap<unknown>): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, value] of configEntries(config)) {\n writes.push({ op: 'values', key, byName, value });\n }\n return this.derive(writes);\n }\n\n /** Install method behavior/observation descriptors on IoC-managed instances. */\n methods(config: MethodsConfig): TestIocBuilder {\n const writes: Write[] = [];\n for (const [key, byName, methodsInput] of configEntries(config)) {\n const methods = new Map<string, MethodDescriptor>();\n const entries =\n methodsInput instanceof Map\n ? methodsInput.entries()\n : Object.entries(methodsInput as Record<string, MethodDescriptor>);\n for (const [name, descriptor] of entries) {\n if (typeof descriptor !== 'function' && !isDescriptor(descriptor)) {\n throw new InvalidDescriptorError(\n `.methods() entry ${tokenLabel(key)}.${name} must be a test.* descriptor or a raw wrapper function.`,\n );\n }\n methods.set(name, descriptor);\n }\n writes.push({ op: 'methods', key, byName, methods });\n }\n return this.derive(writes);\n }\n\n /** Materialize a fresh, isolated real-IoC environment. Non-consuming. */\n async build(): Promise<TestEnvironment> {\n const root = createContainer();\n if (typeof (root as any).setInstanceDecorator !== 'function') {\n throw new MissingIocSeamError();\n }\n\n // ---- Materialize the ordered log: last write wins per effective identity\n // Known tokens (for resolving string keys of classes/functions/values)\n const knownByName = new Map<string, Token>();\n const knownLifetimes = new Map<Token, LoadAs>();\n const note = (token: Token, loadAs: LoadAs | undefined) => {\n if (typeof token === 'function' && (token as any).name) knownByName.set((token as any).name, token);\n else if (typeof token === 'string') knownByName.set(token, token);\n if (loadAs !== undefined) knownLifetimes.set(token, loadAs);\n };\n\n type Effective =\n | { space: 'class'; token: Token; implementation: ClassLike; loadAs?: LoadAs }\n | { space: 'factory'; token: Token; factory: (...args: any[]) => any; loadAs?: LoadAs; deps?: Token[] }\n | { space: 'value'; token: Token; value: unknown };\n\n // ordered map: identity → latest effective write (Map preserves first-write\n // position which is fine — later writes replace content, LWW)\n const effective = new Map<Token, Effective>();\n const methodWrites: { key: Token; byName: boolean; methods: Map<string, MethodDescriptor> }[] = [];\n\n const resolveKey = (key: Token, byName: boolean, space: string): Token => {\n if (!byName) return key;\n const known = knownByName.get(key as string);\n if (known !== undefined) return known;\n if (space === 'values' || space === 'functions') return key; // string tokens are first-class\n throw new UnknownTokenKeyError(key as string, space, [...knownByName.keys()]);\n };\n\n for (const write of this.log) {\n switch (write.op) {\n case 'use': {\n for (const reg of flattenModule(write.module)) {\n const loadAs = lifetimeToLoadAs(reg.lifetime);\n note(reg.token, loadAs);\n if (reg.kind === 'class') {\n note(reg.implementation as Token, loadAs);\n effective.set(reg.token, {\n space: 'class',\n token: reg.token,\n implementation: reg.implementation as ClassLike,\n loadAs,\n });\n knownLifetimes.set(reg.token, loadAs);\n knownLifetimes.set(reg.implementation as Token, loadAs);\n } else if (reg.kind === 'factory') {\n effective.set(reg.token, {\n space: 'factory',\n token: reg.token,\n factory: reg.implementation as (...args: any[]) => any,\n loadAs,\n deps: [...reg.dependencies],\n });\n } else {\n effective.set(reg.token, { space: 'value', token: reg.token, value: reg.implementation });\n knownLifetimes.set(reg.token, LoadAs.Singleton);\n }\n }\n break;\n }\n case 'classes': {\n const token = resolveKey(write.key, write.byName, 'classes');\n note(write.implementation, undefined);\n if (!write.byName) note(token, undefined);\n effective.set(token, {\n space: 'class',\n token,\n implementation: write.implementation,\n // preserve configured lifetime unless the replacement declares its own scope\n loadAs: componentScope(write.implementation) ?? knownLifetimes.get(token),\n });\n break;\n }\n case 'functions': {\n const token = resolveKey(write.key, write.byName, 'functions');\n const prior = effective.get(token);\n effective.set(token, {\n space: 'factory',\n token,\n factory: write.factory,\n // preserve configured lifetime unless the scenario overrides it\n loadAs: prior && prior.space === 'factory' ? prior.loadAs : knownLifetimes.get(token),\n deps: prior && prior.space === 'factory' ? prior.deps : undefined,\n });\n note(token, undefined);\n break;\n }\n case 'values': {\n const token = resolveKey(write.key, write.byName, 'values');\n effective.set(token, { space: 'value', token, value: write.value });\n note(token, LoadAs.Singleton);\n break;\n }\n case 'methods': {\n const token = write.byName && knownByName.has(write.key as string)\n ? knownByName.get(write.key as string)!\n : write.key;\n methodWrites.push({\n key: token,\n byName: write.byName && !knownByName.has(write.key as string),\n methods: new Map(write.methods),\n });\n break;\n }\n }\n }\n\n // ---- Apply effective registrations to the fresh root\n for (const entry of effective.values()) {\n if (entry.space === 'class') {\n this.registerClassBinding(root, entry.token, entry.implementation, entry.loadAs);\n } else if (entry.space === 'factory') {\n root.registerFunction(entry.token, entry.factory, {\n loadAs: entry.loadAs,\n param: entry.deps,\n } as any);\n } else {\n const value = entry.value;\n root.registerFunction(entry.token, () => value, { loadAs: LoadAs.Singleton } as any);\n }\n }\n\n // ---- Fresh watch/expectation state + the ioc decoration seam\n const registry = new WatchRegistry();\n // merge method writes: LWW per (entry key, method), deep-merge per token\n const mergedMethods = new Map<Token, { byName: boolean; methods: Map<string, MethodDescriptor> }>();\n for (const write of methodWrites) {\n const existing = mergedMethods.get(write.key);\n if (existing) {\n for (const [name, descriptor] of write.methods) existing.methods.set(name, descriptor);\n } else {\n mergedMethods.set(write.key, { byName: write.byName, methods: new Map(write.methods) });\n }\n }\n for (const [key, { byName, methods }] of mergedMethods) {\n registry.addEntry({ key, byName, methods });\n }\n (root as any).setInstanceDecorator(createMethodDecorator(registry));\n\n const env: TestEnvironment = {\n root: root as IContainer,\n get: (token, params) => root.get(token as any, params),\n instance: (cls, params) => root.instance(cls, params),\n extend: () => root.extend() as IContainer,\n verify: async () => registry.verify(),\n dispose: () => root.dispose(),\n [ENV_REGISTRY]: registry,\n };\n return env;\n }\n\n /**\n * Register a class binding. When token === implementation this is a plain\n * class registration. Otherwise an alias factory resolves the implementation\n * through real IoC, mirroring the implementation's effective lifetime so\n * lifetime validation (captive-lifetime checks) stays honest.\n */\n private registerClassBinding(\n root: IContainer,\n token: Token,\n implementation: ClassLike,\n configuredLoadAs?: LoadAs,\n ): void {\n const loadAs = configuredLoadAs ?? componentScope(implementation) ?? LoadAs.Transient;\n root.registerClass(implementation as any, configuredLoadAs !== undefined ? ({ loadAs } as any) : undefined);\n if (token === implementation) return;\n\n if (loadAs === LoadAs.Singleton) {\n root.registerFunction(token, () => root.get(implementation as any), {\n loadAs: LoadAs.Singleton,\n } as any);\n } else {\n root.registerFunction(token, (scope: IContainer) => scope.get(implementation as any), {\n loadAs,\n param: [SCOPED_CONTAINER],\n } as any);\n }\n }\n}\n\n/** Create a persistent immutable real-IoC test composition builder. */\nexport function testIoc(...inputs: UseInput[]): TestIocBuilder {\n return TestIocBuilder.create(inputs);\n}\n","/**\n * Diagnostics are first-class: every error names the real token/method and\n * what was expected vs what happened, never only internal wrapper machinery.\n */\n\nexport class TestingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/** Thrown at build() when the installed @noego/ioc lacks the decoration seam. */\nexport class MissingIocSeamError extends TestingError {\n constructor() {\n super(\n '@noego/testing requires an @noego/ioc version that provides ' +\n 'Container.setInstanceDecorator (>= 0.5.x with the instance-decoration seam). ' +\n 'Upgrade @noego/ioc.',\n );\n }\n}\n\n/** test.inspect() on a method that is not watched in this environment. */\nexport class UnwatchedInspectionError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Method \"${method}\" on ${token} is not watched in this environment. ` +\n 'Only watched methods are inspectable — install test.watch() or any ' +\n 'test.* behavior/expectation descriptor for it.',\n );\n }\n}\n\n/** A call arrived after a test.calls([...]) script was fully consumed. */\nexport class CallScriptExhaustedError extends TestingError {\n constructor(token: string, method: string, scriptLength: number, callIndex: number) {\n super(\n `Call #${callIndex} to ${token}.${method} exceeds its test.calls() script ` +\n `of ${scriptLength} ${scriptLength === 1 ? 'entry' : 'entries'}.`,\n );\n }\n}\n\n/** An exact expectation (once/times/never) was exceeded at call time. */\nexport class ExpectationOverflowError extends TestingError {\n constructor(token: string, method: string, expected: number, attempted: number) {\n super(\n expected === 0\n ? `${token}.${method} was expected never to be called, but it was invoked.`\n : `${token}.${method} was expected exactly ${expected} ` +\n `${expected === 1 ? 'call' : 'calls'}, but call #${attempted} arrived.`,\n );\n }\n}\n\n/** Aggregated under-count failures reported by env.verify(). */\nexport class VerificationError extends TestingError {\n constructor(failures: readonly { token: string; method: string; expected: number; actual: number }[]) {\n super(\n 'Exact method expectations were not satisfied:\\n' +\n failures\n .map(\n (f) =>\n ` - ${f.token}.${f.method}: expected exactly ${f.expected} ` +\n `${f.expected === 1 ? 'call' : 'calls'}, observed ${f.actual}`,\n )\n .join('\\n'),\n );\n }\n}\n\n/** .methods configured for a token whose resolved value has no such callable method. */\nexport class MethodNotCallableError extends TestingError {\n constructor(token: string, method: string) {\n super(\n `Cannot install a test.* descriptor on ${token}.${method}: the resolved ` +\n 'instance has no callable method with that name.',\n );\n }\n}\n\n/** .methods configured for a token that resolved to a non-object value. */\nexport class NonObjectMethodTargetError extends TestingError {\n constructor(token: string) {\n super(\n `.methods() is configured for ${token}, but that token resolved to a ` +\n 'non-object value. Method descriptors apply only to IoC-managed instances.',\n );\n }\n}\n\n/** A string configuration key could not be resolved to a known IoC token. */\nexport class UnknownTokenKeyError extends TestingError {\n constructor(key: string, space: string, known: readonly string[]) {\n super(\n `Unknown ${space} key \"${key}\" — it does not match any token known to this ` +\n 'builder. Pass the class/token itself via a Map, or include the ' +\n 'registration through .use(...). Known tokens: ' +\n (known.length ? known.join(', ') : '(none)'),\n );\n }\n}\n\n/** Invalid descriptor construction (e.g. test.times(-1)). */\nexport class InvalidDescriptorError extends TestingError {}\n","/**\n * Lowercase `test.*` descriptors — immutable frozen values.\n *\n * Descriptors carry NO mutable state (no cursors, no counters, no histories);\n * all mutable invocation state lives in the built environment, so one\n * descriptor value is safe to share across builders and repeated builds.\n */\n\nimport { InvalidDescriptorError } from './errors';\n\nexport const DESCRIPTOR = Symbol.for('noego:testing:descriptor');\n\n/** A raw custom method wrapper: (original) => replacement. NOT auto-watched. */\nexport type RawMethodWrapper = (\n original: (...args: any[]) => any,\n) => (...args: any[]) => any;\n\nexport interface ReturnsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'returns';\n readonly value: unknown;\n}\n\nexport interface ThrowsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'throws';\n readonly error: unknown;\n}\n\nexport interface OriginalDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'original';\n}\n\nexport interface CallsDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'calls';\n readonly script: readonly BehaviorDescriptor[];\n}\n\nexport type BehaviorDescriptor =\n | ReturnsDescriptor\n | ThrowsDescriptor\n | OriginalDescriptor\n | CallsDescriptor;\n\nexport interface WatchDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'watch';\n readonly wrapper?: RawMethodWrapper;\n}\n\nexport interface ExpectationDescriptor {\n readonly [DESCRIPTOR]: true;\n readonly kind: 'expect';\n /** Exact required call count; 0 for never(). */\n readonly expected: number;\n /** Behavior used for allowed calls; undefined = original effective behavior. */\n readonly behavior?: BehaviorDescriptor;\n}\n\n/** Everything installable through .methods({...}). */\nexport type MethodDescriptor =\n | BehaviorDescriptor\n | WatchDescriptor\n | ExpectationDescriptor\n | RawMethodWrapper;\n\nexport function isDescriptor(value: unknown): value is Exclude<MethodDescriptor, RawMethodWrapper> {\n return typeof value === 'object' && value !== null && (value as any)[DESCRIPTOR] === true;\n}\n\nfunction frozen<T extends object>(value: T): T {\n return Object.freeze(value);\n}\n\nfunction assertBehavior(value: unknown, where: string): asserts value is BehaviorDescriptor {\n if (!isDescriptor(value) || !['returns', 'throws', 'original', 'calls'].includes((value as any).kind)) {\n throw new InvalidDescriptorError(\n `${where} requires a behavior descriptor (test.returns/throws/original/calls).`,\n );\n }\n}\n\nexport const test = {\n /** Return the supplied value when the method is called. Auto-watches. */\n returns(value: unknown): ReturnsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'returns' as const, value });\n },\n\n /** Throw/reject with the supplied error. Auto-watches. */\n throws(error: unknown): ThrowsDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'throws' as const, error });\n },\n\n /** Invoke the original effective method. Auto-watches. */\n original(): OriginalDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'original' as const });\n },\n\n /**\n * Per-invocation behavior script: call 1 uses entry 1, and so on. A call\n * after exhaustion fails immediately. Unused entries do not fail verification.\n */\n calls(script: readonly BehaviorDescriptor[]): CallsDescriptor {\n if (!Array.isArray(script)) {\n throw new InvalidDescriptorError('test.calls() requires an array of behavior descriptors.');\n }\n script.forEach((entry, i) => assertBehavior(entry, `test.calls() entry #${i + 1}`));\n return frozen({ [DESCRIPTOR]: true as const, kind: 'calls' as const, script: Object.freeze([...script]) });\n },\n\n /**\n * Keep original behavior and record calls. With a raw wrapper argument, the\n * wrapper's behavior runs and is recorded.\n */\n watch(wrapper?: RawMethodWrapper): WatchDescriptor {\n if (wrapper !== undefined && typeof wrapper !== 'function') {\n throw new InvalidDescriptorError('test.watch() accepts only a raw wrapper function.');\n }\n return frozen({ [DESCRIPTOR]: true as const, kind: 'watch' as const, wrapper });\n },\n\n /** Require exactly one call; with no behavior, the original runs. */\n once(behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (behavior !== undefined) assertBehavior(behavior, 'test.once()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 1, behavior });\n },\n\n /** Require exactly `count` calls; with no behavior, the original runs. */\n times(count: number, behavior?: BehaviorDescriptor): ExpectationDescriptor {\n if (!Number.isInteger(count) || count < 0) {\n throw new InvalidDescriptorError(\n `test.times() requires a non-negative integer count, received ${String(count)}.`,\n );\n }\n if (behavior !== undefined) assertBehavior(behavior, 'test.times()');\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: count, behavior });\n },\n\n /** Require zero calls; the first invocation fails and skips the original. */\n never(): ExpectationDescriptor {\n return frozen({ [DESCRIPTOR]: true as const, kind: 'expect' as const, expected: 0 });\n },\n\n /** Read the recorded history for a watched method in one environment. */\n inspect(environment: unknown, token: unknown, method: string): MethodInspection {\n const registry = (environment as any)?.[ENV_REGISTRY];\n if (!registry) {\n throw new InvalidDescriptorError(\n 'test.inspect() requires a built @noego/testing environment as its first argument.',\n );\n }\n return registry.inspect(token, method);\n },\n};\n\nObject.freeze(test);\n\n/** Symbol under which a built environment exposes its watch registry. */\nexport const ENV_REGISTRY = Symbol.for('noego:testing:env-registry');\n\nexport interface RecordedCall {\n /** 1-based invocation index in this environment. */\n readonly index: number;\n readonly args: readonly unknown[];\n /** Present once the call returned (resolved value for async methods). */\n readonly result?: unknown;\n /** Present once the call threw/rejected. */\n readonly error?: unknown;\n /** True while an async outcome is still pending. */\n readonly pending: boolean;\n readonly timestamp: number;\n}\n\nexport interface MethodInspection {\n readonly count: number;\n readonly calls: readonly RecordedCall[];\n}\n","/**\n * Environment-owned method behavior/observation runtime.\n *\n * All mutable state (histories, expectation counters, calls-script cursors)\n * lives here, created fresh at every build(). Descriptors stay immutable.\n */\n\nimport {\n type BehaviorDescriptor,\n type ExpectationDescriptor,\n type MethodDescriptor,\n type MethodInspection,\n type RawMethodWrapper,\n type RecordedCall,\n isDescriptor,\n} from './descriptors';\nimport {\n CallScriptExhaustedError,\n ExpectationOverflowError,\n MethodNotCallableError,\n NonObjectMethodTargetError,\n UnwatchedInspectionError,\n VerificationError,\n} from './errors';\n\nconst CONTEXT_WRAPPED = Symbol.for('ioc:context-wrapped');\nconst CONTEXT_OWNER = Symbol.for('ioc:context-owner');\n\nexport function tokenLabel(token: unknown): string {\n if (typeof token === 'function') return (token as { name?: string }).name || '[anonymous class]';\n if (typeof token === 'symbol') return String(token);\n return String(token);\n}\n\ninterface MutableCall {\n index: number;\n args: readonly unknown[];\n result?: unknown;\n error?: unknown;\n pending: boolean;\n timestamp: number;\n}\n\nexport class MethodState {\n readonly calls: MutableCall[] = [];\n /** Actual invocation count (includes the call currently executing). */\n actual = 0;\n /** Cursor into a test.calls() script. */\n scriptCursor = 0;\n\n constructor(\n readonly tokenName: string,\n readonly method: string,\n readonly descriptor: Exclude<MethodDescriptor, RawMethodWrapper>,\n ) {}\n\n get expectation(): ExpectationDescriptor | undefined {\n return this.descriptor.kind === 'expect' ? this.descriptor : undefined;\n }\n\n inspection(): MethodInspection {\n return {\n count: this.calls.length,\n calls: this.calls.map((c) => ({ ...c, args: [...c.args] }) as RecordedCall),\n };\n }\n}\n\n/** One method-config entry: how it was keyed, and its per-method descriptors. */\nexport interface MethodConfigEntry {\n /** Exact token (class/symbol/string) or a name string matched lazily. */\n readonly key: unknown;\n readonly byName: boolean;\n readonly methods: ReadonlyMap<string, MethodDescriptor>;\n}\n\nexport class WatchRegistry {\n /** entry-identity → method → state. Entries share states across instances. */\n private readonly states = new Map<unknown, Map<string, MethodState>>();\n private readonly nameIndex = new Map<string, unknown>();\n private readonly entries: MethodConfigEntry[] = [];\n\n addEntry(entry: MethodConfigEntry): void {\n this.entries.push(entry);\n const byMethod = new Map<string, MethodState>();\n for (const [method, descriptor] of entry.methods) {\n if (typeof descriptor === 'function') continue; // raw wrapper: unwatched, no state\n byMethod.set(method, new MethodState(this.entryLabel(entry), method, descriptor));\n }\n this.states.set(entry.key, byMethod);\n if (entry.byName) this.nameIndex.set(entry.key as string, entry.key);\n else if (typeof entry.key === 'function' && (entry.key as any).name) {\n this.nameIndex.set((entry.key as any).name, entry.key);\n }\n }\n\n private entryLabel(entry: MethodConfigEntry): string {\n return entry.byName ? String(entry.key) : tokenLabel(entry.key);\n }\n\n /** Entries applying to a resolving token (exact identity or name match). */\n matchEntries(token: unknown): MethodConfigEntry[] {\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n return this.entries.filter(\n (entry) => entry.key === token || (entry.byName && name !== undefined && entry.key === name),\n );\n }\n\n state(entryKey: unknown, method: string): MethodState | undefined {\n return this.states.get(entryKey)?.get(method);\n }\n\n inspect(token: unknown, method: string): MethodInspection {\n const label = tokenLabel(token);\n const keys: unknown[] = [token];\n const name = typeof token === 'function' ? (token as any).name : typeof token === 'string' ? token : undefined;\n if (name !== undefined && this.nameIndex.has(name)) keys.push(this.nameIndex.get(name));\n for (const key of keys) {\n const state = this.states.get(key)?.get(method);\n if (state) return state.inspection();\n }\n throw new UnwatchedInspectionError(label, method);\n }\n\n /** Repeatable snapshot check of all exact expectations. */\n verify(): void {\n const failures: { token: string; method: string; expected: number; actual: number }[] = [];\n for (const byMethod of this.states.values()) {\n for (const state of byMethod.values()) {\n const expectation = state.expectation;\n if (!expectation) continue;\n if (state.actual !== expectation.expected) {\n failures.push({\n token: state.tokenName,\n method: state.method,\n expected: expectation.expected,\n actual: state.actual,\n });\n }\n }\n }\n if (failures.length) throw new VerificationError(failures);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is Promise<unknown> {\n return !!value && typeof (value as any).then === 'function';\n}\n\nfunction recordOutcome(call: MutableCall, outcome: unknown, threw: boolean): unknown {\n if (!threw && isPromiseLike(outcome)) {\n call.pending = true;\n outcome.then(\n (value) => {\n call.result = value;\n call.pending = false;\n },\n (error) => {\n call.error = error;\n call.pending = false;\n },\n );\n return outcome;\n }\n call.pending = false;\n if (threw) call.error = outcome;\n else call.result = outcome;\n return outcome;\n}\n\n/**\n * Build the ioc instance decorator for one environment.\n *\n * Identity-stable (WeakMap), preserves sync/async call shape and `this`\n * binding, forwards the ioc context symbols by delegating to the underlying\n * (context-wrapped) target for everything unconfigured.\n */\nexport function createMethodDecorator(registry: WatchRegistry): (instance: any, token: any) => any {\n const wrappers = new WeakMap<object, any>();\n\n return function decorate(instance: any, token: any): any {\n const entries = registry.matchEntries(token);\n if (entries.length === 0) return instance;\n\n if (instance === null || (typeof instance !== 'object' && typeof instance !== 'function')) {\n throw new NonObjectMethodTargetError(tokenLabel(token));\n }\n\n if (wrappers.has(instance)) return wrappers.get(instance);\n\n // Effective per-method config: entries merge in write order, LWW per method\n const effective = new Map<string, { entryKey: unknown; descriptor: MethodDescriptor }>();\n for (const entry of entries) {\n for (const [method, descriptor] of entry.methods) {\n effective.set(method, { entryKey: entry.key, descriptor });\n }\n }\n\n // Validate configured methods exist and are callable\n for (const method of effective.keys()) {\n if (typeof instance[method] !== 'function') {\n throw new MethodNotCallableError(tokenLabel(token), method);\n }\n }\n\n const methodCache = new Map<string, (...args: any[]) => any>();\n\n const proxy = new Proxy(instance, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && effective.has(prop)) {\n let wrapped = methodCache.get(prop);\n if (!wrapped) {\n const { entryKey, descriptor } = effective.get(prop)!;\n wrapped = buildMethodWrapper(target, prop, descriptor, registry.state(entryKey, prop));\n methodCache.set(prop, wrapped);\n }\n return wrapped;\n }\n // Everything else (including ioc context symbols) delegates to the\n // underlying — usually context-wrapped — target.\n if (prop === CONTEXT_WRAPPED || prop === CONTEXT_OWNER) return (target as any)[prop];\n return Reflect.get(target, prop, receiver);\n },\n });\n\n wrappers.set(instance, proxy);\n return proxy;\n };\n}\n\nfunction buildMethodWrapper(\n target: any,\n method: string,\n descriptor: MethodDescriptor,\n state: MethodState | undefined,\n): (...args: any[]) => any {\n // The original effective method, read through the underlying target so the\n // ioc context proxy still owns context entry for real invocations.\n const callOriginal = (self: any, args: any[]) => {\n const fn = target[method];\n return fn.apply(self === undefined ? target : self, args);\n };\n\n // Raw custom wrapper: installed as-is, NOT watched, no recording.\n if (typeof descriptor === 'function') {\n const replacement = descriptor((...args: any[]) => callOriginal(target, args));\n return function (this: any, ...args: any[]) {\n return replacement.apply(this, args);\n };\n }\n\n if (!isDescriptor(descriptor)) {\n // Should be unreachable: builder validates descriptors on write.\n throw new TypeError(`Invalid method descriptor for ${method}`);\n }\n\n return function (this: any, ...args: any[]) {\n if (!state) throw new TypeError(`Missing method state for ${method}`);\n\n const expectation = state.expectation;\n const attempted = state.actual + 1;\n\n // Overflow fails immediately and never runs behavior/original\n if (expectation && attempted > expectation.expected) {\n state.actual = attempted;\n throw new ExpectationOverflowError(state.tokenName, method, expectation.expected, attempted);\n }\n\n state.actual = attempted;\n const call: MutableCall = {\n index: attempted,\n args: [...args],\n pending: false,\n timestamp: Date.now(),\n };\n state.calls.push(call);\n\n // Effective behavior for this invocation\n let behavior: BehaviorDescriptor | 'original-effective' | RawMethodWrapper;\n const base =\n descriptor.kind === 'expect'\n ? (descriptor.behavior ?? 'original-effective')\n : descriptor.kind === 'watch'\n ? (descriptor.wrapper ?? 'original-effective')\n : descriptor;\n\n if (base !== 'original-effective' && typeof base !== 'function' && base !== undefined && base.kind === 'calls') {\n if (state.scriptCursor >= base.script.length) {\n throw new CallScriptExhaustedError(state.tokenName, method, base.script.length, attempted);\n }\n behavior = base.script[state.scriptCursor]!;\n state.scriptCursor += 1;\n } else {\n behavior = base;\n }\n\n try {\n let outcome: unknown;\n if (behavior === 'original-effective') {\n outcome = callOriginal(this, args);\n } else if (typeof behavior === 'function') {\n // test.watch(rawWrapper): wrapper behavior runs and is recorded\n outcome = behavior((...inner: any[]) => callOriginal(this, inner)).apply(this, args);\n } else if (behavior.kind === 'returns') {\n outcome = behavior.value;\n } else if (behavior.kind === 'throws') {\n throw behavior.error;\n } else if (behavior.kind === 'original') {\n outcome = callOriginal(this, args);\n } else {\n throw new TypeError(`Nested test.calls() scripts are not supported (${method}).`);\n }\n return recordOutcome(call, outcome, false);\n } catch (error) {\n recordOutcome(call, error, true);\n throw error;\n }\n };\n}\n"],"mappings":";AAUA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;;;ACZA,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;AAGO,IAAM,sBAAN,cAAkC,aAAa;AAAA,EACpD,cAAc;AACZ;AAAA,MACE;AAAA,IAGF;AAAA,EACF;AACF;AAGO,IAAM,2BAAN,cAAuC,aAAa;AAAA,EACzD,YAAY,OAAe,QAAgB;AACzC;AAAA,MACE,WAAW,MAAM,QAAQ,KAAK;AAAA,IAGhC;AAAA,EACF;AACF;AAGO,IAAM,2BAAN,cAAuC,aAAa;AAAA,EACzD,YAAY,OAAe,QAAgB,cAAsB,WAAmB;AAClF;AAAA,MACE,SAAS,SAAS,OAAO,KAAK,IAAI,MAAM,uCAChC,YAAY,IAAI,iBAAiB,IAAI,UAAU,SAAS;AAAA,IAClE;AAAA,EACF;AACF;AAGO,IAAM,2BAAN,cAAuC,aAAa;AAAA,EACzD,YAAY,OAAe,QAAgB,UAAkB,WAAmB;AAC9E;AAAA,MACE,aAAa,IACT,GAAG,KAAK,IAAI,MAAM,0DAClB,GAAG,KAAK,IAAI,MAAM,yBAAyB,QAAQ,IAChD,aAAa,IAAI,SAAS,OAAO,eAAe,SAAS;AAAA,IAClE;AAAA,EACF;AACF;AAGO,IAAM,oBAAN,cAAgC,aAAa;AAAA,EAClD,YAAY,UAA0F;AACpG;AAAA,MACE,oDACE,SACG;AAAA,QACC,CAAC,MACC,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,sBAAsB,EAAE,QAAQ,IACvD,EAAE,aAAa,IAAI,SAAS,OAAO,cAAc,EAAE,MAAM;AAAA,MAChE,EACC,KAAK,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAGO,IAAM,yBAAN,cAAqC,aAAa;AAAA,EACvD,YAAY,OAAe,QAAgB;AACzC;AAAA,MACE,yCAAyC,KAAK,IAAI,MAAM;AAAA,IAE1D;AAAA,EACF;AACF;AAGO,IAAM,6BAAN,cAAyC,aAAa;AAAA,EAC3D,YAAY,OAAe;AACzB;AAAA,MACE,gCAAgC,KAAK;AAAA,IAEvC;AAAA,EACF;AACF;AAGO,IAAM,uBAAN,cAAmC,aAAa;AAAA,EACrD,YAAY,KAAa,OAAe,OAA0B;AAChE;AAAA,MACE,WAAW,KAAK,SAAS,GAAG,sKAGzB,MAAM,SAAS,MAAM,KAAK,IAAI,IAAI;AAAA,IACvC;AAAA,EACF;AACF;AAGO,IAAM,yBAAN,cAAqC,aAAa;AAAC;;;AC/FnD,IAAM,aAAa,uBAAO,IAAI,0BAA0B;AA0DxD,SAAS,aAAa,OAAsE;AACjG,SAAO,OAAO,UAAU,YAAY,UAAU,QAAS,MAAc,UAAU,MAAM;AACvF;AAEA,SAAS,OAAyB,OAAa;AAC7C,SAAO,OAAO,OAAO,KAAK;AAC5B;AAEA,SAAS,eAAe,OAAgB,OAAoD;AAC1F,MAAI,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,WAAW,UAAU,YAAY,OAAO,EAAE,SAAU,MAAc,IAAI,GAAG;AACrG,UAAM,IAAI;AAAA,MACR,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AACF;AAEO,IAAM,OAAO;AAAA;AAAA,EAElB,QAAQ,OAAmC;AACzC,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,WAAoB,MAAM,CAAC;AAAA,EAChF;AAAA;AAAA,EAGA,OAAO,OAAkC;AACvC,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,UAAmB,MAAM,CAAC;AAAA,EAC/E;AAAA;AAAA,EAGA,WAA+B;AAC7B,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,WAAoB,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAwD;AAC5D,QAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,YAAM,IAAI,uBAAuB,yDAAyD;AAAA,IAC5F;AACA,WAAO,QAAQ,CAAC,OAAO,MAAM,eAAe,OAAO,uBAAuB,IAAI,CAAC,EAAE,CAAC;AAClF,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,SAAkB,QAAQ,OAAO,OAAO,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;AAAA,EAC3G;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SAA6C;AACjD,QAAI,YAAY,UAAa,OAAO,YAAY,YAAY;AAC1D,YAAM,IAAI,uBAAuB,mDAAmD;AAAA,IACtF;AACA,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,SAAkB,QAAQ,CAAC;AAAA,EAChF;AAAA;AAAA,EAGA,KAAK,UAAsD;AACzD,QAAI,aAAa,OAAW,gBAAe,UAAU,aAAa;AAClE,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,UAAmB,UAAU,GAAG,SAAS,CAAC;AAAA,EAC/F;AAAA;AAAA,EAGA,MAAM,OAAe,UAAsD;AACzE,QAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GAAG;AACzC,YAAM,IAAI;AAAA,QACR,gEAAgE,OAAO,KAAK,CAAC;AAAA,MAC/E;AAAA,IACF;AACA,QAAI,aAAa,OAAW,gBAAe,UAAU,cAAc;AACnE,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,UAAmB,UAAU,OAAO,SAAS,CAAC;AAAA,EACnG;AAAA;AAAA,EAGA,QAA+B;AAC7B,WAAO,OAAO,EAAE,CAAC,UAAU,GAAG,MAAe,MAAM,UAAmB,UAAU,EAAE,CAAC;AAAA,EACrF;AAAA;AAAA,EAGA,QAAQ,aAAsB,OAAgB,QAAkC;AAC9E,UAAM,WAAY,cAAsB,YAAY;AACpD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO,SAAS,QAAQ,OAAO,MAAM;AAAA,EACvC;AACF;AAEA,OAAO,OAAO,IAAI;AAGX,IAAM,eAAe,uBAAO,IAAI,4BAA4B;;;ACvInE,IAAM,kBAAkB,uBAAO,IAAI,qBAAqB;AACxD,IAAM,gBAAgB,uBAAO,IAAI,mBAAmB;AAE7C,SAAS,WAAW,OAAwB;AACjD,MAAI,OAAO,UAAU,WAAY,QAAQ,MAA4B,QAAQ;AAC7E,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK;AAClD,SAAO,OAAO,KAAK;AACrB;AAWO,IAAM,cAAN,MAAkB;AAAA,EAOvB,YACW,WACA,QACA,YACT;AAHS;AACA;AACA;AAAA,EACR;AAAA,EAHQ;AAAA,EACA;AAAA,EACA;AAAA,EATF,QAAuB,CAAC;AAAA;AAAA,EAEjC,SAAS;AAAA;AAAA,EAET,eAAe;AAAA,EAQf,IAAI,cAAiD;AACnD,WAAO,KAAK,WAAW,SAAS,WAAW,KAAK,aAAa;AAAA,EAC/D;AAAA,EAEA,aAA+B;AAC7B,WAAO;AAAA,MACL,OAAO,KAAK,MAAM;AAAA,MAClB,OAAO,KAAK,MAAM,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,EAAkB;AAAA,IAC5E;AAAA,EACF;AACF;AAUO,IAAM,gBAAN,MAAoB;AAAA;AAAA,EAER,SAAS,oBAAI,IAAuC;AAAA,EACpD,YAAY,oBAAI,IAAqB;AAAA,EACrC,UAA+B,CAAC;AAAA,EAEjD,SAAS,OAAgC;AACvC,SAAK,QAAQ,KAAK,KAAK;AACvB,UAAM,WAAW,oBAAI,IAAyB;AAC9C,eAAW,CAAC,QAAQ,UAAU,KAAK,MAAM,SAAS;AAChD,UAAI,OAAO,eAAe,WAAY;AACtC,eAAS,IAAI,QAAQ,IAAI,YAAY,KAAK,WAAW,KAAK,GAAG,QAAQ,UAAU,CAAC;AAAA,IAClF;AACA,SAAK,OAAO,IAAI,MAAM,KAAK,QAAQ;AACnC,QAAI,MAAM,OAAQ,MAAK,UAAU,IAAI,MAAM,KAAe,MAAM,GAAG;AAAA,aAC1D,OAAO,MAAM,QAAQ,cAAe,MAAM,IAAY,MAAM;AACnE,WAAK,UAAU,IAAK,MAAM,IAAY,MAAM,MAAM,GAAG;AAAA,IACvD;AAAA,EACF;AAAA,EAEQ,WAAW,OAAkC;AACnD,WAAO,MAAM,SAAS,OAAO,MAAM,GAAG,IAAI,WAAW,MAAM,GAAG;AAAA,EAChE;AAAA;AAAA,EAGA,aAAa,OAAqC;AAChD,UAAM,OAAO,OAAO,UAAU,aAAc,MAAc,OAAO,OAAO,UAAU,WAAW,QAAQ;AACrG,WAAO,KAAK,QAAQ;AAAA,MAClB,CAAC,UAAU,MAAM,QAAQ,SAAU,MAAM,UAAU,SAAS,UAAa,MAAM,QAAQ;AAAA,IACzF;AAAA,EACF;AAAA,EAEA,MAAM,UAAmB,QAAyC;AAChE,WAAO,KAAK,OAAO,IAAI,QAAQ,GAAG,IAAI,MAAM;AAAA,EAC9C;AAAA,EAEA,QAAQ,OAAgB,QAAkC;AACxD,UAAM,QAAQ,WAAW,KAAK;AAC9B,UAAM,OAAkB,CAAC,KAAK;AAC9B,UAAM,OAAO,OAAO,UAAU,aAAc,MAAc,OAAO,OAAO,UAAU,WAAW,QAAQ;AACrG,QAAI,SAAS,UAAa,KAAK,UAAU,IAAI,IAAI,EAAG,MAAK,KAAK,KAAK,UAAU,IAAI,IAAI,CAAC;AACtF,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,OAAO,IAAI,GAAG,GAAG,IAAI,MAAM;AAC9C,UAAI,MAAO,QAAO,MAAM,WAAW;AAAA,IACrC;AACA,UAAM,IAAI,yBAAyB,OAAO,MAAM;AAAA,EAClD;AAAA;AAAA,EAGA,SAAe;AACb,UAAM,WAAkF,CAAC;AACzF,eAAW,YAAY,KAAK,OAAO,OAAO,GAAG;AAC3C,iBAAW,SAAS,SAAS,OAAO,GAAG;AACrC,cAAM,cAAc,MAAM;AAC1B,YAAI,CAAC,YAAa;AAClB,YAAI,MAAM,WAAW,YAAY,UAAU;AACzC,mBAAS,KAAK;AAAA,YACZ,OAAO,MAAM;AAAA,YACb,QAAQ,MAAM;AAAA,YACd,UAAU,YAAY;AAAA,YACtB,QAAQ,MAAM;AAAA,UAChB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,OAAQ,OAAM,IAAI,kBAAkB,QAAQ;AAAA,EAC3D;AACF;AAEA,SAAS,cAAc,OAA2C;AAChE,SAAO,CAAC,CAAC,SAAS,OAAQ,MAAc,SAAS;AACnD;AAEA,SAAS,cAAc,MAAmB,SAAkB,OAAyB;AACnF,MAAI,CAAC,SAAS,cAAc,OAAO,GAAG;AACpC,SAAK,UAAU;AACf,YAAQ;AAAA,MACN,CAAC,UAAU;AACT,aAAK,SAAS;AACd,aAAK,UAAU;AAAA,MACjB;AAAA,MACA,CAAC,UAAU;AACT,aAAK,QAAQ;AACb,aAAK,UAAU;AAAA,MACjB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,OAAK,UAAU;AACf,MAAI,MAAO,MAAK,QAAQ;AAAA,MACnB,MAAK,SAAS;AACnB,SAAO;AACT;AASO,SAAS,sBAAsB,UAA6D;AACjG,QAAM,WAAW,oBAAI,QAAqB;AAE1C,SAAO,SAAS,SAAS,UAAe,OAAiB;AACvD,UAAM,UAAU,SAAS,aAAa,KAAK;AAC3C,QAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAI,aAAa,QAAS,OAAO,aAAa,YAAY,OAAO,aAAa,YAAa;AACzF,YAAM,IAAI,2BAA2B,WAAW,KAAK,CAAC;AAAA,IACxD;AAEA,QAAI,SAAS,IAAI,QAAQ,EAAG,QAAO,SAAS,IAAI,QAAQ;AAGxD,UAAM,YAAY,oBAAI,IAAiE;AACvF,eAAW,SAAS,SAAS;AAC3B,iBAAW,CAAC,QAAQ,UAAU,KAAK,MAAM,SAAS;AAChD,kBAAU,IAAI,QAAQ,EAAE,UAAU,MAAM,KAAK,WAAW,CAAC;AAAA,MAC3D;AAAA,IACF;AAGA,eAAW,UAAU,UAAU,KAAK,GAAG;AACrC,UAAI,OAAO,SAAS,MAAM,MAAM,YAAY;AAC1C,cAAM,IAAI,uBAAuB,WAAW,KAAK,GAAG,MAAM;AAAA,MAC5D;AAAA,IACF;AAEA,UAAM,cAAc,oBAAI,IAAqC;AAE7D,UAAM,QAAQ,IAAI,MAAM,UAAU;AAAA,MAChC,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAI,OAAO,SAAS,YAAY,UAAU,IAAI,IAAI,GAAG;AACnD,cAAI,UAAU,YAAY,IAAI,IAAI;AAClC,cAAI,CAAC,SAAS;AACZ,kBAAM,EAAE,UAAU,WAAW,IAAI,UAAU,IAAI,IAAI;AACnD,sBAAU,mBAAmB,QAAQ,MAAM,YAAY,SAAS,MAAM,UAAU,IAAI,CAAC;AACrF,wBAAY,IAAI,MAAM,OAAO;AAAA,UAC/B;AACA,iBAAO;AAAA,QACT;AAGA,YAAI,SAAS,mBAAmB,SAAS,cAAe,QAAQ,OAAe,IAAI;AACnF,eAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAAA,MAC3C;AAAA,IACF,CAAC;AAED,aAAS,IAAI,UAAU,KAAK;AAC5B,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBACP,QACA,QACA,YACA,OACyB;AAGzB,QAAM,eAAe,CAAC,MAAW,SAAgB;AAC/C,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,GAAG,MAAM,SAAS,SAAY,SAAS,MAAM,IAAI;AAAA,EAC1D;AAGA,MAAI,OAAO,eAAe,YAAY;AACpC,UAAM,cAAc,WAAW,IAAI,SAAgB,aAAa,QAAQ,IAAI,CAAC;AAC7E,WAAO,YAAwB,MAAa;AAC1C,aAAO,YAAY,MAAM,MAAM,IAAI;AAAA,IACrC;AAAA,EACF;AAEA,MAAI,CAAC,aAAa,UAAU,GAAG;AAE7B,UAAM,IAAI,UAAU,iCAAiC,MAAM,EAAE;AAAA,EAC/D;AAEA,SAAO,YAAwB,MAAa;AAC1C,QAAI,CAAC,MAAO,OAAM,IAAI,UAAU,4BAA4B,MAAM,EAAE;AAEpE,UAAM,cAAc,MAAM;AAC1B,UAAM,YAAY,MAAM,SAAS;AAGjC,QAAI,eAAe,YAAY,YAAY,UAAU;AACnD,YAAM,SAAS;AACf,YAAM,IAAI,yBAAyB,MAAM,WAAW,QAAQ,YAAY,UAAU,SAAS;AAAA,IAC7F;AAEA,UAAM,SAAS;AACf,UAAM,OAAoB;AAAA,MACxB,OAAO;AAAA,MACP,MAAM,CAAC,GAAG,IAAI;AAAA,MACd,SAAS;AAAA,MACT,WAAW,KAAK,IAAI;AAAA,IACtB;AACA,UAAM,MAAM,KAAK,IAAI;AAGrB,QAAI;AACJ,UAAM,OACJ,WAAW,SAAS,WACf,WAAW,YAAY,uBACxB,WAAW,SAAS,UACjB,WAAW,WAAW,uBACvB;AAER,QAAI,SAAS,wBAAwB,OAAO,SAAS,cAAc,SAAS,UAAa,KAAK,SAAS,SAAS;AAC9G,UAAI,MAAM,gBAAgB,KAAK,OAAO,QAAQ;AAC5C,cAAM,IAAI,yBAAyB,MAAM,WAAW,QAAQ,KAAK,OAAO,QAAQ,SAAS;AAAA,MAC3F;AACA,iBAAW,KAAK,OAAO,MAAM,YAAY;AACzC,YAAM,gBAAgB;AAAA,IACxB,OAAO;AACL,iBAAW;AAAA,IACb;AAEA,QAAI;AACF,UAAI;AACJ,UAAI,aAAa,sBAAsB;AACrC,kBAAU,aAAa,MAAM,IAAI;AAAA,MACnC,WAAW,OAAO,aAAa,YAAY;AAEzC,kBAAU,SAAS,IAAI,UAAiB,aAAa,MAAM,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AAAA,MACrF,WAAW,SAAS,SAAS,WAAW;AACtC,kBAAU,SAAS;AAAA,MACrB,WAAW,SAAS,SAAS,UAAU;AACrC,cAAM,SAAS;AAAA,MACjB,WAAW,SAAS,SAAS,YAAY;AACvC,kBAAU,aAAa,MAAM,IAAI;AAAA,MACnC,OAAO;AACL,cAAM,IAAI,UAAU,kDAAkD,MAAM,IAAI;AAAA,MAClF;AACA,aAAO,cAAc,MAAM,SAAS,KAAK;AAAA,IAC3C,SAAS,OAAO;AACd,oBAAc,MAAM,OAAO,IAAI;AAC/B,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;AHvSA,IAAM,wBAAwB,uBAAO,IAAI,uBAAuB;AA6BhE,UAAU,cAAiB,QAAqD;AAC9E,MAAI,kBAAkB,KAAK;AACzB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAQ,OAAM,CAAC,KAAK,OAAO,KAAK;AAAA,EAC7D,OAAO;AACL,eAAW,OAAO,CAAC,GAAG,OAAO,oBAAoB,MAAM,GAAG,GAAG,OAAO,sBAAsB,MAAM,CAAC,GAAG;AAClG,YAAM,CAAC,KAAK,OAAO,QAAQ,UAAW,OAAe,GAAG,CAAC;AAAA,IAC3D;AAAA,EACF;AACF;AAEA,SAAS,eAAe,KAA8B;AACpD,QAAM,UACJ,OAAO,YAAY,eAAgB,QAAgB,cAC9C,QAAgB,YAAY,uBAAuB,GAAG,IACvD;AACN,SAAO,SAAS;AAClB;AAEA,SAAS,iBAAiB,UAA0B;AAClD,MAAI,aAAa,YAAa,QAAO,OAAO;AAC5C,MAAI,aAAa,SAAU,QAAO,OAAO;AACzC,SAAO,OAAO;AAChB;AAEO,IAAM,iBAAN,MAAM,gBAAe;AAAA,EAClB,YAA6B,KAAuB;AAAvB;AACnC,WAAO,OAAO,IAAI;AAAA,EACpB;AAAA,EAFqC;AAAA;AAAA,EAKrC,OAAO,OAAO,QAA6C;AACzD,WAAO,IAAI,gBAAe,CAAC,CAAC,EAAE,OAAO,MAAM;AAAA,EAC7C;AAAA;AAAA,EAGA,IAAI,SAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,OAAO,QAAiC;AAC9C,WAAO,IAAI,gBAAe,CAAC,GAAG,KAAK,KAAK,GAAG,MAAM,CAAC;AAAA,EACpD;AAAA,EAEQ,OAAO,QAA6C;AAC1D,QAAI,UAA0B;AAC9B,eAAW,SAAS,OAAQ,WAAU,QAAQ,IAAI,KAAK;AACvD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,QAAkC;AACpC,QAAI,kBAAkB,iBAAgB;AACpC,aAAO,KAAK,OAAO,CAAC,GAAG,OAAO,MAAM,CAAC;AAAA,IACvC;AACA,WAAO,KAAK,OAAO,CAAC,EAAE,IAAI,OAAO,QAAQ,OAAO,CAAC,CAAC;AAAA,EACpD;AAAA;AAAA,EAGA,QAAQ,QAA8C;AACpD,UAAM,SAAkB,CAAC;AACzB,eAAW,CAAC,KAAK,QAAQ,cAAc,KAAK,cAAc,MAAM,GAAG;AACjE,UAAI,OAAO,mBAAmB,cAAc,CAAC,eAAe,WAAW;AACrE,cAAM,IAAI;AAAA,UACR,yBAAyB,WAAW,GAAG,CAAC;AAAA,QAC1C;AAAA,MACF;AACA,aAAO,KAAK,EAAE,IAAI,WAAW,KAAK,QAAQ,eAAe,CAAC;AAAA,IAC5D;AACA,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA;AAAA,EAGA,UAAU,QAA4D;AACpE,UAAM,SAAkB,CAAC;AACzB,eAAW,CAAC,KAAK,QAAQ,OAAO,KAAK,cAAc,MAAM,GAAG;AAC1D,UAAI,OAAO,YAAY,YAAY;AACjC,cAAM,IAAI,uBAAuB,2BAA2B,WAAW,GAAG,CAAC,uBAAuB;AAAA,MACpG;AACA,aAAO,KAAK,EAAE,IAAI,aAAa,KAAK,QAAQ,QAAQ,CAAC;AAAA,IACvD;AACA,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA;AAAA,EAGA,OAAO,QAA4C;AACjD,UAAM,SAAkB,CAAC;AACzB,eAAW,CAAC,KAAK,QAAQ,KAAK,KAAK,cAAc,MAAM,GAAG;AACxD,aAAO,KAAK,EAAE,IAAI,UAAU,KAAK,QAAQ,MAAM,CAAC;AAAA,IAClD;AACA,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA;AAAA,EAGA,QAAQ,QAAuC;AAC7C,UAAM,SAAkB,CAAC;AACzB,eAAW,CAAC,KAAK,QAAQ,YAAY,KAAK,cAAc,MAAM,GAAG;AAC/D,YAAM,UAAU,oBAAI,IAA8B;AAClD,YAAM,UACJ,wBAAwB,MACpB,aAAa,QAAQ,IACrB,OAAO,QAAQ,YAAgD;AACrE,iBAAW,CAAC,MAAM,UAAU,KAAK,SAAS;AACxC,YAAI,OAAO,eAAe,cAAc,CAAC,aAAa,UAAU,GAAG;AACjE,gBAAM,IAAI;AAAA,YACR,oBAAoB,WAAW,GAAG,CAAC,IAAI,IAAI;AAAA,UAC7C;AAAA,QACF;AACA,gBAAQ,IAAI,MAAM,UAAU;AAAA,MAC9B;AACA,aAAO,KAAK,EAAE,IAAI,WAAW,KAAK,QAAQ,QAAQ,CAAC;AAAA,IACrD;AACA,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA;AAAA,EAGA,MAAM,QAAkC;AACtC,UAAM,OAAO,gBAAgB;AAC7B,QAAI,OAAQ,KAAa,yBAAyB,YAAY;AAC5D,YAAM,IAAI,oBAAoB;AAAA,IAChC;AAIA,UAAM,cAAc,oBAAI,IAAmB;AAC3C,UAAM,iBAAiB,oBAAI,IAAmB;AAC9C,UAAM,OAAO,CAAC,OAAc,WAA+B;AACzD,UAAI,OAAO,UAAU,cAAe,MAAc,KAAM,aAAY,IAAK,MAAc,MAAM,KAAK;AAAA,eACzF,OAAO,UAAU,SAAU,aAAY,IAAI,OAAO,KAAK;AAChE,UAAI,WAAW,OAAW,gBAAe,IAAI,OAAO,MAAM;AAAA,IAC5D;AASA,UAAM,YAAY,oBAAI,IAAsB;AAC5C,UAAM,eAA0F,CAAC;AAEjG,UAAM,aAAa,CAAC,KAAY,QAAiB,UAAyB;AACxE,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,QAAQ,YAAY,IAAI,GAAa;AAC3C,UAAI,UAAU,OAAW,QAAO;AAChC,UAAI,UAAU,YAAY,UAAU,YAAa,QAAO;AACxD,YAAM,IAAI,qBAAqB,KAAe,OAAO,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC;AAAA,IAC9E;AAEA,eAAW,SAAS,KAAK,KAAK;AAC5B,cAAQ,MAAM,IAAI;AAAA,QAChB,KAAK,OAAO;AACV,qBAAW,OAAO,cAAc,MAAM,MAAM,GAAG;AAC7C,kBAAM,SAAS,iBAAiB,IAAI,QAAQ;AAC5C,iBAAK,IAAI,OAAO,MAAM;AACtB,gBAAI,IAAI,SAAS,SAAS;AACxB,mBAAK,IAAI,gBAAyB,MAAM;AACxC,wBAAU,IAAI,IAAI,OAAO;AAAA,gBACvB,OAAO;AAAA,gBACP,OAAO,IAAI;AAAA,gBACX,gBAAgB,IAAI;AAAA,gBACpB;AAAA,cACF,CAAC;AACD,6BAAe,IAAI,IAAI,OAAO,MAAM;AACpC,6BAAe,IAAI,IAAI,gBAAyB,MAAM;AAAA,YACxD,WAAW,IAAI,SAAS,WAAW;AACjC,wBAAU,IAAI,IAAI,OAAO;AAAA,gBACvB,OAAO;AAAA,gBACP,OAAO,IAAI;AAAA,gBACX,SAAS,IAAI;AAAA,gBACb;AAAA,gBACA,MAAM,CAAC,GAAG,IAAI,YAAY;AAAA,cAC5B,CAAC;AAAA,YACH,OAAO;AACL,wBAAU,IAAI,IAAI,OAAO,EAAE,OAAO,SAAS,OAAO,IAAI,OAAO,OAAO,IAAI,eAAe,CAAC;AACxF,6BAAe,IAAI,IAAI,OAAO,OAAO,SAAS;AAAA,YAChD;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,WAAW;AACd,gBAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,QAAQ,SAAS;AAC3D,eAAK,MAAM,gBAAgB,MAAS;AACpC,cAAI,CAAC,MAAM,OAAQ,MAAK,OAAO,MAAS;AACxC,oBAAU,IAAI,OAAO;AAAA,YACnB,OAAO;AAAA,YACP;AAAA,YACA,gBAAgB,MAAM;AAAA;AAAA,YAEtB,QAAQ,eAAe,MAAM,cAAc,KAAK,eAAe,IAAI,KAAK;AAAA,UAC1E,CAAC;AACD;AAAA,QACF;AAAA,QACA,KAAK,aAAa;AAChB,gBAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,QAAQ,WAAW;AAC7D,gBAAM,QAAQ,UAAU,IAAI,KAAK;AACjC,oBAAU,IAAI,OAAO;AAAA,YACnB,OAAO;AAAA,YACP;AAAA,YACA,SAAS,MAAM;AAAA;AAAA,YAEf,QAAQ,SAAS,MAAM,UAAU,YAAY,MAAM,SAAS,eAAe,IAAI,KAAK;AAAA,YACpF,MAAM,SAAS,MAAM,UAAU,YAAY,MAAM,OAAO;AAAA,UAC1D,CAAC;AACD,eAAK,OAAO,MAAS;AACrB;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,gBAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,QAAQ,QAAQ;AAC1D,oBAAU,IAAI,OAAO,EAAE,OAAO,SAAS,OAAO,OAAO,MAAM,MAAM,CAAC;AAClE,eAAK,OAAO,OAAO,SAAS;AAC5B;AAAA,QACF;AAAA,QACA,KAAK,WAAW;AACd,gBAAM,QAAQ,MAAM,UAAU,YAAY,IAAI,MAAM,GAAa,IAC7D,YAAY,IAAI,MAAM,GAAa,IACnC,MAAM;AACV,uBAAa,KAAK;AAAA,YAChB,KAAK;AAAA,YACL,QAAQ,MAAM,UAAU,CAAC,YAAY,IAAI,MAAM,GAAa;AAAA,YAC5D,SAAS,IAAI,IAAI,MAAM,OAAO;AAAA,UAChC,CAAC;AACD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,eAAW,SAAS,UAAU,OAAO,GAAG;AACtC,UAAI,MAAM,UAAU,SAAS;AAC3B,aAAK,qBAAqB,MAAM,MAAM,OAAO,MAAM,gBAAgB,MAAM,MAAM;AAAA,MACjF,WAAW,MAAM,UAAU,WAAW;AACpC,aAAK,iBAAiB,MAAM,OAAO,MAAM,SAAS;AAAA,UAChD,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,QACf,CAAQ;AAAA,MACV,OAAO;AACL,cAAM,QAAQ,MAAM;AACpB,aAAK,iBAAiB,MAAM,OAAO,MAAM,OAAO,EAAE,QAAQ,OAAO,UAAU,CAAQ;AAAA,MACrF;AAAA,IACF;AAGA,UAAM,WAAW,IAAI,cAAc;AAEnC,UAAM,gBAAgB,oBAAI,IAAwE;AAClG,eAAW,SAAS,cAAc;AAChC,YAAM,WAAW,cAAc,IAAI,MAAM,GAAG;AAC5C,UAAI,UAAU;AACZ,mBAAW,CAAC,MAAM,UAAU,KAAK,MAAM,QAAS,UAAS,QAAQ,IAAI,MAAM,UAAU;AAAA,MACvF,OAAO;AACL,sBAAc,IAAI,MAAM,KAAK,EAAE,QAAQ,MAAM,QAAQ,SAAS,IAAI,IAAI,MAAM,OAAO,EAAE,CAAC;AAAA,MACxF;AAAA,IACF;AACA,eAAW,CAAC,KAAK,EAAE,QAAQ,QAAQ,CAAC,KAAK,eAAe;AACtD,eAAS,SAAS,EAAE,KAAK,QAAQ,QAAQ,CAAC;AAAA,IAC5C;AACA,IAAC,KAAa,qBAAqB,sBAAsB,QAAQ,CAAC;AAElE,UAAM,MAAuB;AAAA,MAC3B;AAAA,MACA,KAAK,CAAC,OAAO,WAAW,KAAK,IAAI,OAAc,MAAM;AAAA,MACrD,UAAU,CAAC,KAAK,WAAW,KAAK,SAAS,KAAK,MAAM;AAAA,MACpD,QAAQ,MAAM,KAAK,OAAO;AAAA,MAC1B,QAAQ,YAAY,SAAS,OAAO;AAAA,MACpC,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,CAAC,YAAY,GAAG;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,qBACN,MACA,OACA,gBACA,kBACM;AACN,UAAM,SAAS,oBAAoB,eAAe,cAAc,KAAK,OAAO;AAC5E,SAAK,cAAc,gBAAuB,qBAAqB,SAAa,EAAE,OAAO,IAAY,MAAS;AAC1G,QAAI,UAAU,eAAgB;AAE9B,QAAI,WAAW,OAAO,WAAW;AAC/B,WAAK,iBAAiB,OAAO,MAAM,KAAK,IAAI,cAAqB,GAAG;AAAA,QAClE,QAAQ,OAAO;AAAA,MACjB,CAAQ;AAAA,IACV,OAAO;AACL,WAAK,iBAAiB,OAAO,CAAC,UAAsB,MAAM,IAAI,cAAqB,GAAG;AAAA,QACpF;AAAA,QACA,OAAO,CAAC,gBAAgB;AAAA,MAC1B,CAAQ;AAAA,IACV;AAAA,EACF;AACF;AAGO,SAAS,WAAW,QAAoC;AAC7D,SAAO,eAAe,OAAO,MAAM;AACrC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noego/testing",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Canonical NoEgo shared testing owner: testIoc real-IoC composition and the lowercase test.* method behavior/watch/expectation language",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -30,9 +30,7 @@
|
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"@noego/runtime": "^0.1.0"
|
|
35
|
-
},
|
|
33
|
+
"dependencies": {},
|
|
36
34
|
"devDependencies": {
|
|
37
35
|
"@types/jest": "^29.5.14",
|
|
38
36
|
"@types/node": "^20.10.4",
|
|
@@ -40,6 +38,11 @@
|
|
|
40
38
|
"ts-jest": "^29.3.2",
|
|
41
39
|
"ts-node": "^10.9.2",
|
|
42
40
|
"tsup": "^8.5.0",
|
|
43
|
-
"typescript": "^5.8.3"
|
|
41
|
+
"typescript": "^5.8.3",
|
|
42
|
+
"@noego/ioc": "file:../ioc",
|
|
43
|
+
"reflect-metadata": "^0.2.2"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@noego/ioc": ">=0.5.0"
|
|
44
47
|
}
|
|
45
48
|
}
|