@demlik/tea 0.20.0 → 0.22.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 +11 -2
- package/dist/agent/index.d.ts +36 -23
- package/dist/agent/index.js +12 -10
- package/dist/{chunk-UHAQ4PI6.js → chunk-2A5XTKQQ.js} +205 -59
- package/dist/chunk-2A5XTKQQ.js.map +1 -0
- package/dist/{chunk-JAKM53JF.js → chunk-7K7SEYCH.js} +16 -15
- package/dist/chunk-7K7SEYCH.js.map +1 -0
- package/dist/{chunk-RKVUVALV.js → chunk-A3QPMX5M.js} +31 -9
- package/dist/chunk-A3QPMX5M.js.map +1 -0
- package/dist/{chunk-DVNXEB7V.js → chunk-BVFS3ICC.js} +3 -3
- package/dist/chunk-BVFS3ICC.js.map +1 -0
- package/dist/{chunk-FTCYY2DO.js → chunk-CPE5ZQXQ.js} +3 -3
- package/dist/{chunk-FTCYY2DO.js.map → chunk-CPE5ZQXQ.js.map} +1 -1
- package/dist/chunk-DEAPNMKO.js +11 -0
- package/dist/chunk-DEAPNMKO.js.map +1 -0
- package/dist/{chunk-XWIE5V7R.js → chunk-ETRNSJCR.js} +4 -4
- package/dist/{chunk-XWIE5V7R.js.map → chunk-ETRNSJCR.js.map} +1 -1
- package/dist/chunk-FXWDDNGD.js +98 -0
- package/dist/chunk-FXWDDNGD.js.map +1 -0
- package/dist/chunk-GFGOQIEC.js +10 -0
- package/dist/chunk-GFGOQIEC.js.map +1 -0
- package/dist/{chunk-2R62Q5UK.js → chunk-IAHEYPYJ.js} +3 -3
- package/dist/{chunk-2R62Q5UK.js.map → chunk-IAHEYPYJ.js.map} +1 -1
- package/dist/{chunk-NGYFBD5G.js → chunk-J5JNLXO7.js} +3 -3
- package/dist/{chunk-NGYFBD5G.js.map → chunk-J5JNLXO7.js.map} +1 -1
- package/dist/{chunk-72CN3VPY.js → chunk-NFOGYDRS.js} +5 -5
- package/dist/{chunk-72CN3VPY.js.map → chunk-NFOGYDRS.js.map} +1 -1
- package/dist/{chunk-A73IJ27X.js → chunk-R2YNDRGL.js} +3 -3
- package/dist/{chunk-A73IJ27X.js.map → chunk-R2YNDRGL.js.map} +1 -1
- package/dist/{chunk-5HVKDHY6.js → chunk-R5NXOPL2.js} +3 -3
- package/dist/{chunk-5HVKDHY6.js.map → chunk-R5NXOPL2.js.map} +1 -1
- package/dist/chunk-RMBZ7YH7.js +52 -0
- package/dist/chunk-RMBZ7YH7.js.map +1 -0
- package/dist/{chunk-AIXC33SE.js → chunk-SHXUSTHM.js} +62 -15
- package/dist/chunk-SHXUSTHM.js.map +1 -0
- package/dist/{chunk-TAKPC6SD.js → chunk-UGTC33VH.js} +4 -4
- package/dist/{chunk-TAKPC6SD.js.map → chunk-UGTC33VH.js.map} +1 -1
- package/dist/{chunk-EA47MI5Q.js → chunk-VH5MYJ2K.js} +4 -4
- package/dist/{chunk-EA47MI5Q.js.map → chunk-VH5MYJ2K.js.map} +1 -1
- package/dist/{chunk-2774I242.js → chunk-WZEDIUWR.js} +21 -20
- package/dist/chunk-WZEDIUWR.js.map +1 -0
- package/dist/{chunk-TGJLXZAU.js → chunk-ZRRET3RP.js} +11 -11
- package/dist/chunk-ZRRET3RP.js.map +1 -0
- package/dist/{core-CtvAyPpH.d.ts → core-DNDAEhO2.d.ts} +9 -12
- package/dist/devtools/index.d.ts +4 -4
- package/dist/do/index.d.ts +103 -38
- package/dist/do/index.js +40 -21
- package/dist/do/index.js.map +1 -1
- package/dist/{drive-loop-Dw1sGSsW.d.ts → drive-loop-DlOEv49c.d.ts} +2 -2
- package/dist/{durable-effects-Ump5lSdO.d.ts → durable-effects-wLWEdy9H.d.ts} +1 -1
- package/dist/effect/index.d.ts +121 -28
- package/dist/effect/index.js +45 -31
- package/dist/effect/index.js.map +1 -1
- package/dist/extension/index.d.ts +3 -3
- package/dist/extension/index.js +2 -2
- package/dist/extension/index.js.map +1 -1
- package/dist/flow/index.d.ts +17 -13
- package/dist/flow/index.js +15 -12
- package/dist/flow/index.js.map +1 -1
- package/dist/idempotency/index.d.ts +3 -3
- package/dist/idempotency/index.js +3 -3
- package/dist/{index-CrcoVfd_.d.ts → index-9P1PaXIV.d.ts} +2 -2
- package/dist/{index-D3NY3VCN.d.ts → index-BBLI2WWN.d.ts} +1 -1
- package/dist/{index-Dl71V7kt.d.ts → index-C1WhBZ6a.d.ts} +4 -5
- package/dist/{index-CKtEKGyp.d.ts → index-C92SgNWz.d.ts} +3 -3
- package/dist/{index-CZZ3HI-Z.d.ts → index-CAsUE_d1.d.ts} +4 -4
- package/dist/{index-EIgfl83J.d.ts → index-CO34esWT.d.ts} +2 -2
- package/dist/{index-Bk2hBUdK.d.ts → index-COeX9Bmz.d.ts} +11 -9
- package/dist/{index-CVU7SK3a.d.ts → index-Cg0QXnzf.d.ts} +2 -2
- package/dist/index.d.ts +9 -9
- package/dist/index.js +2 -2
- package/dist/jev/index.d.ts +15 -15
- package/dist/jev/index.js +5 -5
- package/dist/jev/index.js.map +1 -1
- package/dist/laws-DTtCDDlU.d.ts +135 -0
- package/dist/loop-DReKGV1X.d.ts +70 -0
- package/dist/{machine-Dtre2TmN.d.ts → machine-C0WhOAyB.d.ts} +7 -7
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +2 -2
- package/dist/mem/index.d.ts +4 -3
- package/dist/mem/index.js +2 -2
- package/dist/mem/index.js.map +1 -1
- package/dist/node/index.d.ts +57 -9
- package/dist/node/index.js +175 -68
- package/dist/node/index.js.map +1 -1
- package/dist/otel/index.d.ts +28 -12
- package/dist/otel/index.js +25 -21
- package/dist/otel/index.js.map +1 -1
- package/dist/paginate/index.d.ts +4 -4
- package/dist/paginate/index.js +5 -5
- package/dist/parity/index.d.ts +11 -15
- package/dist/parity/index.js +4 -9
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/index.d.ts +279 -67
- package/dist/pbt/index.js +299 -58
- package/dist/pbt/index.js.map +1 -1
- package/dist/persistence/index.d.ts +10 -12
- package/dist/persistence/index.js +4 -4
- package/dist/promise/index.d.ts +118 -21
- package/dist/promise/index.js +6 -4
- package/dist/react/index.d.ts +34 -21
- package/dist/react/index.js +271 -42
- package/dist/react/index.js.map +1 -1
- package/dist/resilience/index.d.ts +6 -6
- package/dist/resilience/index.js +5 -5
- package/dist/{run-fiBe7pOG.d.ts → run-Cnw4qXj2.d.ts} +105 -23
- package/dist/{runtime-types-DEluQgGO.d.ts → runtime-types-CP-ykwgf.d.ts} +157 -29
- package/dist/testing/effect/index.d.ts +162 -9
- package/dist/testing/effect/index.js +110 -10
- package/dist/testing/effect/index.js.map +1 -1
- package/dist/testing/index.d.ts +6 -8
- package/dist/testing/index.js +4 -24
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/promise/index.d.ts +4 -4
- package/dist/testing/promise/index.js +5 -5
- package/dist/timing/index.d.ts +4 -4
- package/dist/timing/index.js +4 -4
- package/dist/{types-CXNzlbAg.d.ts → types-S53WDy_h.d.ts} +1 -1
- package/dist/work-queue/index.d.ts +4 -4
- package/dist/work-queue/index.js +2 -2
- package/package.json +19 -17
- package/dist/chunk-2774I242.js.map +0 -1
- package/dist/chunk-AIXC33SE.js.map +0 -1
- package/dist/chunk-DVNXEB7V.js.map +0 -1
- package/dist/chunk-HX3C6LDK.js +0 -34
- package/dist/chunk-HX3C6LDK.js.map +0 -1
- package/dist/chunk-JAKM53JF.js.map +0 -1
- package/dist/chunk-RKVUVALV.js.map +0 -1
- package/dist/chunk-TGJLXZAU.js.map +0 -1
- package/dist/chunk-UHAQ4PI6.js.map +0 -1
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/internal/journal/index.ts","../../src/node/index.ts"],"names":["handle"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * internal/journal — an append-only, ordered record log for `@demlik/tea`.\n * Internal since #49 — not published on any subpath; `fileJournal` on\n * `@demlik/tea/node` is the one public route.\n *\n * The log a \"durable, replayable state machine\" replays from. tea already ships\n * the log's neighbours — `fileStore`/`memoryStore` (`Store<S>`: snapshot with\n * whole-load / whole-save), `recorder` (JSONL debug traces), `durable-effects`\n * (`foldLedger` over an owed-effect ledger) — but not the log itself, so every\n * consumer hand-rolled it. This is that log, domain-blind: a `Journal<R>` over\n * an opaque record `R`, with no pipeline/ledger vocabulary of its own.\n *\n * Three operations carry the contract:\n * - `append(stream, record)` — durable before it returns, assigning a\n * per-stream total-order `seq` (1, 2, 3, …). Concurrent appenders to one\n * stream converge on one order; the lock's only job is an honest order key.\n * - `list(stream)` — every entry of a stream in `seq` order.\n * - `changes(listener)` — a subscription over appends made through this\n * instance after subscribing.\n *\n * `withLock(stream, fx)` runs `fx` with exclusive access to a stream, so a\n * read-decide-append sequence is atomic against other writers. It is\n * re-entrant for the same stream: an `append` inside `fx` does not deadlock on\n * the lock `fx` already holds.\n *\n * The substrate is a `JournalBackend<R>` — how a stream is read, how one record\n * is appended durably, and how a stream is locked across processes.\n * `makeJournal` wires the domain-blind orchestration (per-stream serialisation,\n * `seq` assignment, `changes` fan-out) over any backend. `memoryJournal` is the\n * in-process backend that mirrors `memoryStore` for tests; the Node file\n * backend (`fileJournal`) homes in `@demlik/tea/node` beside `fileStore`.\n *\n * Interface shape follows Effect `unstable/eventlog` `EventJournal`\n * (`entries`/`write`/`changes`/`withLock`) as prior art, not a dependency.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n/** A per-stream total-order key. Starts at 1 and increments by one per append. */\nexport type Seq = number;\n\n/** One appended record, tagged with its stream and its per-stream `seq`. */\nexport interface JournalEntry<R> {\n readonly stream: string;\n readonly seq: Seq;\n readonly record: R;\n}\n\n/**\n * An append-only, ordered record log over an opaque record `R`.\n *\n * Domain-blind: it knows nothing of what a record means. Ordering, durability\n * and the honest order key are its whole contract.\n */\nexport interface Journal<R> {\n /**\n * Append `record` to `stream`, durable before the promise resolves, and\n * resolve with the `seq` it was assigned — the next integer in the stream's\n * total order.\n */\n append(stream: string, record: R): Promise<{ seq: Seq }>;\n /** Every entry of `stream`, in `seq` order. Empty for an unwritten stream. */\n list(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Subscribe to appends made through this instance after this call. Returns an\n * unsubscribe. Cross-process appends are not delivered — a cross-process\n * subscription is a polling concern this log leaves to its consumer.\n */\n changes(listener: (entry: JournalEntry<R>) => void): () => void;\n /**\n * Run `fx` with exclusive access to `stream` — the in-process mutex against\n * other callers here, the backend lock against other processes. Re-entrant\n * for the same stream, so an `append` (or nested `withLock`) inside `fx` runs\n * under the lock this call already holds rather than deadlocking on it.\n */\n withLock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * The substrate a `Journal<R>` is built over. Everything host-specific lives\n * here; `makeJournal` owns the host-blind rest.\n *\n * `append` is always invoked under `lock` for the same stream, so a backend\n * assigns `seq` from `read().length + 1` without racing itself.\n */\nexport interface JournalBackend<R> {\n /** Every entry of `stream` in `seq` order; empty for an unwritten stream. */\n read(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Persist `record` as the next entry of `stream`, durable before resolving,\n * and resolve with the entry it wrote. Invoked only while `lock` holds the\n * stream, so `seq` is safely the current length plus one.\n */\n append(stream: string, record: R): Promise<JournalEntry<R>>;\n /** Run `fx` while holding `stream`'s cross-process lock. */\n lock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * A promise-chained mutex: each `run` waits for the prior one to settle, so the\n * bodies never interleave. The order key is honest because assignment happens\n * inside a body, never across the `await` that another body could slip through.\n */\nclass AsyncLock {\n private tail: Promise<unknown> = Promise.resolve();\n\n run<A>(fx: () => Promise<A>): Promise<A> {\n const result = this.tail.then(fx, fx);\n this.tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n}\n\n/**\n * Build a `Journal<R>` over `backend`. Owns the host-blind half: per-stream\n * serialisation, re-entrant exclusion, and the `changes` fan-out. `append` and\n * `withLock` both route through `exclusive`, which holds the in-process mutex\n * and the backend lock for one stream and is re-entrant for that stream within\n * a single call chain.\n */\nexport function makeJournal<R>(backend: JournalBackend<R>): Journal<R> {\n const mutexes = new Map<string, AsyncLock>();\n // The streams this async call chain holds. Kept in AsyncLocalStorage, not an\n // instance-wide Set, because re-entrancy is a property of the CALL CHAIN, not\n // of the instance: a genuine re-entrant append (nested inside an `fx` that\n // already holds the stream) and an INDEPENDENT top-level append that merely\n // arrives while another chain holds the lock are indistinguishable to a\n // shared Set — so the shared Set waves the independent call through inline,\n // past the mutex, to run concurrently with the holder. That is a lost record:\n // in `fileJournal` the two both read the same length and compute the same\n // `seq`, and the second `rename` clobbers the first. A per-chain store only\n // ever reports the streams THIS chain entered, so an independent call sees an\n // empty (or unrelated) store and correctly queues on the mutex.\n const heldStreams = new AsyncLocalStorage<ReadonlySet<string>>();\n const listeners = new Set<(entry: JournalEntry<R>) => void>();\n\n const mutexFor = (stream: string): AsyncLock => {\n let mutex = mutexes.get(stream);\n if (!mutex) {\n mutex = new AsyncLock();\n mutexes.set(stream, mutex);\n }\n return mutex;\n };\n\n const exclusive = <A>(stream: string, fx: () => Promise<A>): Promise<A> => {\n const held = heldStreams.getStore();\n // This call chain already holds this stream's lock: run inline, or a\n // re-entrant append/withLock would block forever on a lock it owns.\n if (held?.has(stream)) return fx();\n return mutexFor(stream).run(() =>\n backend.lock(stream, () => {\n // Scope the augmented held-set to `fx` and its async continuations. The\n // store is torn down when `fx` settles — no finally clause to leak it.\n const nested = new Set(held).add(stream);\n return heldStreams.run(nested, fx);\n }),\n );\n };\n\n return {\n append(stream, record) {\n return exclusive(stream, async () => {\n const entry = await backend.append(stream, record);\n for (const listener of listeners) listener(entry);\n return { seq: entry.seq };\n });\n },\n list(stream) {\n return Promise.resolve(backend.read(stream));\n },\n changes(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n withLock(stream, fx) {\n return exclusive(stream, fx);\n },\n };\n}\n\n/**\n * An in-process `Journal<R>` — the standard test substrate, mirroring\n * `memoryStore` from `@demlik/tea/mem`. Reference semantics: a record handed to\n * `append` is stored by reference, exactly as `memoryStore` stores state. No\n * cross-process lock is needed — a single process owns the whole log — so the\n * backend lock is identity and `makeJournal`'s in-process mutex is the only\n * ordering guarantee.\n */\nexport function memoryJournal<R>(): Journal<R> {\n const streams = new Map<string, JournalEntry<R>[]>();\n\n return makeJournal<R>({\n async read(stream) {\n return (streams.get(stream) ?? []).slice();\n },\n async append(stream, record) {\n const log = streams.get(stream) ?? [];\n const entry: JournalEntry<R> = { stream, seq: log.length + 1, record };\n log.push(entry);\n streams.set(stream, log);\n return entry;\n },\n async lock(_stream, fx) {\n return fx();\n },\n });\n}\n\n// The remote-journal + per-remote-cursor half of the journal feature (#31): the\n// remote a client pushes to and pulls from, the local cursor that resumes sync,\n// and the sync client that joins a local `Journal` to a remote so two local\n// journals meet in one order. It builds ON `memoryJournal` above.\nexport * from \"./remote\";\n","/**\n * @packageDocumentation\n * @demlik/tea/node — Node host adapter for `@demlik/tea`.\n *\n * Every host adapter ships two halves (mirrors `@demlik/tea/do`):\n *\n * 1. `fileStore<S>(path, parse)` — a `Store<S>` over a JSON file on disk.\n * Atomic save (temp + rename). Absent file → `load()` resolves `null`\n * (fresh boot). Structurally malformed JSON throws at `load()` (an infra\n * error), matching `doStore`; a shape mismatch is NOT a throw — the\n * substrate calls `migrate`, which returns `refuse(reason)`. Both make\n * `ready` reject with `StoreRefusedError`, and the file is left as is.\n * Use for resumable scripts / local repro of prod machines. Ephemeral\n * CLIs can skip persistence with `memoryStore` from `@demlik/tea/mem`.\n *\n * 2. `nodeSubscribe<M, Ctx>({ ws })` — the runners for the three node-native\n * Sub types this package owns, handed to `run` as its `subscribe`:\n * - `node_ws` — deps `{ key, url }`. Opens a `ws` WebSocket; routes\n * open/message/close/error into Msgs through the `ws`\n * handlers. Registers the live socket in\n * `ctx.wsRegistry` under `key` so a Cmd handler can\n * write to it (see `sendToWebSocket`). Cleanup closes\n * the socket for ANY non-CLOSED state.\n * - `node_timer` — deps `{ delayMs, msg, repeat? }`. setTimeout /\n * setInterval; cleanup clears it.\n * - `node_signal` — deps `{ signal, msg }`. A process signal\n * (SIGINT/SIGTERM/…) → Msg; cleanup detaches the\n * listener.\n *\n * Unlike tea-do — whose `alarm()` / `webSocketMessage()` are DO lifecycle\n * methods the handler can't observe from the outside, hence its registries —\n * a node process is continuously alive: each runner holds its resource in\n * closure and the returned cleanup closes it. The engine runs that cleanup the\n * instant the Sub's `deps` go null, and a changed `deps` value stops the old\n * runner before starting the new one — end-then-start choreography for free\n * (Rule 9, [invariant 4]). A lifecycle-bound resource (a socket, a timer)\n * becomes impossible to leak from a reducer cell, because no reducer cell owns\n * its teardown.\n *\n * `ctx.wsRegistry` is the one shared surface: `node_ws` populates it, keyed by\n * the Sub's `deps.key`; a Cmd interpreter reads it to write to the open\n * socket. Same shape as tea-do's `wsRegistry`.\n */\n\nimport {\n mkdir,\n open,\n readFile,\n rename,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport type WebSocket from \"ws\";\nimport type { RawData } from \"ws\";\nimport type {\n DeletableStore,\n Dispose,\n FencedStore,\n Migrated,\n Sub,\n} from \"../index\";\nimport { StoreConflictError } from \"../index\";\nimport {\n type Journal,\n type JournalEntry,\n makeJournal,\n type Seq,\n} from \"../internal/journal\";\nimport { dispatchIfPresent } from \"../subs/types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileStore — `Store<S>` over a JSON file.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Persist a machine's state to a JSON file — pass the `path` and a `parse` that\n * validates what comes back, get a `Store<S>` you hand to `run` so the next run\n * resumes where this one stopped.\n *\n * `parse` is REQUIRED because the file is a real serialization boundary — the\n * bytes that come back through `JSON.parse` are structurally `unknown`, and the\n * caller (who owns `S`) is the only party that can validate the shape.\n * `parse` returns `S` for a shape it recognizes, `null` when nothing was saved\n * (the substrate boots `init` with `loaded = null`), and `refuse(reason)` for\n * saved bytes it cannot read (`ready` rejects with `StoreRefusedError`).\n * `parse` must NOT throw per the `Store<S>.migrate` contract.\n *\n * Pass `{ fenced: true }` to get a `FencedStore<S>` instead — see\n * {@link FileStoreOptions}.\n *\n * Either form is a `DeletableStore<S>`: `delete()` removes the state file and\n * its `<path>.fence` version stamp, so a host that forgets a run needs no\n * knowledge of the layout on disk.\n */\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n): DeletableStore<S>;\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n options: FileStoreOptions & { readonly fenced: true },\n): FencedStore<S> & DeletableStore<S>;\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n options?: FileStoreOptions,\n): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>);\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n options: FileStoreOptions = {},\n): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>) {\n const base: DeletableStore<S> = {\n async load(): Promise<unknown> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n // JSON.parse throws on structural malformation — propagate (infra error),\n // matching `doStore`. The decoded value is intentionally returned as\n // `unknown`; the substrate's `migrate` callback is the boundary parse.\n return JSON.parse(raw);\n },\n async save(state: S): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n // Write to a pid-scoped temp then atomically rename. The rename gives\n // torn-write safety (a reader sees the old file or the new one, never a\n // partial) and survives process crash / Ctrl-C — the only failure a local\n // resumable run resumes from. fsync (file + dir) would add power-loss\n // durability the use case doesn't need; the substrate serializes saves\n // through run()'s queue, so there's never a second concurrent writer\n // WITHIN one run. Between two runs there is nothing here refusing one —\n // that is what `{ fenced: true }` below adds (#143).\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, JSON.stringify(state), \"utf8\");\n await rename(tmp, path);\n },\n migrate(raw: unknown): Migrated<S> {\n return parse(raw);\n },\n async delete(): Promise<void> {\n await removeStateFiles(path);\n },\n };\n return options.fenced === true ? fenceFileStore(base, path) : base;\n}\n\n/**\n * Remove a `fileStore`'s state file and its `.fence` stamp; an absent one is\n * already deleted. The unfenced store removes the stamp too, because a file\n * written fenced can be opened unfenced and a stale stamp would outlive it.\n *\n * Stamp FIRST, state second. A crash between the two leaves the bytes at\n * version `0`, which a live fenced run reads as a conflict — the safe\n * direction. The reverse leaves the old version beside no state, and a run\n * still holding that version would swap cleanly onto a deleted run.\n */\nasync function removeStateFiles(path: string): Promise<void> {\n for (const file of [`${path}.fence`, path]) {\n await unlink(file).catch((err: NodeJS.ErrnoException) => {\n if (err.code !== \"ENOENT\") throw err;\n });\n }\n}\n\n/** Options for {@link fileStore}. */\nexport interface FileStoreOptions {\n /**\n * Refuse a second live writer (#143). With `{ fenced: true }` the returned\n * store is a `FencedStore<S>`: it carries a version stamp beside the state\n * file, and `run` compare-and-swaps against it on every save. A process that\n * boots reads the current version and takes the fence; the older live writer\n * that started from the same version is refused with a `StoreConflictError`\n * at its next save. Omit it and the store is exactly as it was — the last\n * writer wins, and single-writer is the caller's precondition to keep.\n */\n readonly fenced?: true;\n}\n\n/**\n * The fencing half of {@link fileStore}. The state file itself is unchanged, so\n * a file written unfenced resumes fenced and back again; the version lives in a\n * sidecar `<path>.fence` holding a decimal integer, and an absent stamp is `0`.\n *\n * The compare-and-swap is taken under the same `wx` lock file `fileJournal`\n * uses, because `open(…, \"wx\")` is the one primitive that is atomic ACROSS\n * PROCESSES. Without it the read-compare-write is a TOCTOU race and two writers\n * both pass their own check. Rename-over-tmp still does the torn-write work for\n * each of the two files; the lock does the exclusion no rename could.\n */\nfunction fenceFileStore<S>(\n base: DeletableStore<S>,\n path: string,\n): FencedStore<S> & DeletableStore<S> {\n const fencePath = `${path}.fence`;\n const lockPath = `${path}.fence.lock`;\n\n async function readVersion(): Promise<number> {\n let raw: string;\n try {\n raw = await readFile(fencePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return 0;\n throw err;\n }\n const version = Number.parseInt(raw.trim(), 10);\n // A stamp we cannot read is not a version we may assume: defaulting it to 0\n // would let a fresh writer win a fence against a live one.\n if (!Number.isSafeInteger(version) || version < 0) {\n throw new Error(\n `fileStore: version stamp at \"${fencePath}\" is not an integer: ${raw}`,\n );\n }\n return version;\n }\n\n return {\n ...base,\n fenced: true,\n async loadFenced(): Promise<{ raw: unknown; version: number }> {\n // Version FIRST, bytes second: a writer landing between the two reads\n // leaves us with a version OLDER than the bytes, so our next save is\n // refused. Reading bytes first could hand back a version newer than them,\n // and that save would silently win.\n const version = await readVersion();\n return { raw: await base.load(), version };\n },\n async saveFenced(state: S, expectedVersion: number): Promise<number> {\n await mkdir(dirname(path), { recursive: true });\n await acquireFileLock(dirname(path), lockPath);\n try {\n const actual = await readVersion();\n if (actual !== expectedVersion) {\n throw new StoreConflictError(expectedVersion, actual);\n }\n const next = expectedVersion + 1;\n // State before stamp. A crash between the two leaves a stamp BEHIND the\n // bytes, which reads as a conflict for the next writer — the safe\n // direction. The reverse advertises a write that never landed.\n await base.save(state);\n const tmp = `${fencePath}.${process.pid}.tmp`;\n await writeFile(tmp, String(next), \"utf8\");\n await rename(tmp, fencePath);\n return next;\n } finally {\n await unlink(lockPath).catch(() => {});\n }\n },\n async delete(): Promise<void> {\n // Under the swap's own lock, so a delete never lands between another\n // writer's state write and its stamp write.\n await acquireFileLock(dirname(path), lockPath);\n try {\n await base.delete();\n } finally {\n await unlink(lockPath).catch(() => {});\n }\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileJournal — a `Journal<R>` over one on-disk JSONL file per stream.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst LOCK_SPIN_MS = 10;\n\n/**\n * The Node file substrate for the journal (`src/internal/journal/`), beside\n * `fileStore`. A\n * stream is one JSONL file under `dir` (`<encoded-stream>.jsonl`), one\n * `{\"seq\":n,\"record\":…}` object per line — readable and greppable on disk, no\n * SQLite. `parse` is REQUIRED for the same reason `fileStore` requires it: the\n * bytes read back through `JSON.parse` are structurally `unknown`, and the\n * caller who owns `R` is the only party that can validate a record's shape.\n *\n * `append` rewrites the whole stream file via a pid-scoped temp + `rename`,\n * exactly as `fileStore.save` does — a reader sees the old file or the new one,\n * never a torn line, and the write survives a crash mid-append. The append is\n * O(n) in the stream's length; a durable record log a machine replays is read\n * whole anyway, so the atomic-rewrite guarantee is worth the cost.\n *\n * The ordering key is taken under a `wx` lock file (`<file>.lock`) holding the\n * writer's PID: a second writer racing the same stream blocks on the lock, so\n * `seq` is a single total order across processes. A crashed writer's lock is\n * stolen once its PID proves dead (`process.kill(pid, 0)` → `ESRCH`), so a\n * crash never wedges the stream. This is the lock phoenix spike #6673 proved:\n * its only job is an honest order key, not the win.\n *\n * `changes` fires for appends made through THIS instance; a cross-process\n * append lands on disk but is not broadcast here (that is a polling concern the\n * log leaves to its consumer, and the remote-journal work downstream).\n *\n * The journal carries no stability promise, and this factory travels with it\n * rather than with `./node`'s `stable` stamp (MAINTAINING.md).\n *\n * @experimental\n */\nexport function fileJournal<R>(\n dir: string,\n parse: (raw: unknown) => R,\n): Journal<R> {\n const streamPath = (stream: string): string =>\n join(dir, `${encodeURIComponent(stream)}.jsonl`);\n\n const readEntries = async (stream: string): Promise<JournalEntry<R>[]> => {\n const path = streamPath(stream);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n return raw\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => {\n const row = JSON.parse(line) as { seq: Seq; record: unknown };\n return { stream, seq: row.seq, record: parse(row.record) };\n });\n };\n\n return makeJournal<R>({\n read: readEntries,\n async append(stream, record) {\n const entries = await readEntries(stream);\n const entry: JournalEntry<R> = {\n stream,\n seq: entries.length + 1,\n record,\n };\n const path = streamPath(stream);\n const lines = [...entries, entry]\n .map((e) => JSON.stringify({ seq: e.seq, record: e.record }))\n .join(\"\\n\");\n await mkdir(dir, { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, `${lines}\\n`, \"utf8\");\n await rename(tmp, path);\n return entry;\n },\n async lock(stream, fx) {\n const lockPath = `${streamPath(stream)}.lock`;\n await acquireFileLock(dir, lockPath);\n try {\n return await fx();\n } finally {\n await unlink(lockPath).catch(() => {});\n }\n },\n });\n}\n\n/**\n * Acquire an exclusive lock by creating `lockPath` with `wx` (O_CREAT|O_EXCL) —\n * the one primitive that is atomic across processes. On contention, steal the\n * lock only once its holder's PID proves dead; otherwise spin. `process.kill(pid,\n * 0)` sends no signal — it tests existence: it throws `ESRCH` for a dead PID,\n * and `EPERM` for a live one this process may not signal (still alive, so hold\n * off). A lock that vanishes between the failed create and the read is already\n * free, so retry.\n */\nasync function acquireFileLock(dir: string, lockPath: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n try {\n await handle.writeFile(String(process.pid));\n } finally {\n await handle.close();\n }\n return;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n if (await stealIfStale(lockPath)) continue;\n await new Promise((resolve) => setTimeout(resolve, LOCK_SPIN_MS));\n }\n }\n}\n\nasync function stealIfStale(lockPath: string): Promise<boolean> {\n let holder: string;\n try {\n holder = await readFile(lockPath, \"utf8\");\n } catch (err) {\n // Vanished between the failed create and this read — already free.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return true;\n throw err;\n }\n const pid = Number(holder.trim());\n if (!Number.isInteger(pid) || pid <= 0) return false;\n if (isProcessAlive(pid)) return false;\n await unlink(lockPath).catch(() => {});\n return true;\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NodeSub — the three node-native subscription variants.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The `deps` of a `node_ws` Sub: which socket (`key`) and where it connects\n * (`url`). Both are plain data, so the engine derives the Sub's id from them —\n * the same `{ key, url }` across transitions is the same socket (kept open),\n * and a changed one closes it and opens a fresh one.\n *\n * `key` is the author's name for the socket: the Cmd side addresses it through\n * {@link sendToWebSocket} by that key, since the derived Sub id is not\n * something a Cmd handler can know. It must be unique among the machine's live\n * `node_ws` Subs.\n */\nexport type NodeWsDeps = { readonly key: string; readonly url: string };\n\n/**\n * A node WebSocket sub. Its callbacks are not data, so they live on the runner\n * side — {@link NodeWsHandlers}, handed to {@link nodeSubscribe}.\n */\nexport type NodeWsSub = Sub<\"node_ws\", NodeWsDeps>;\n\n/**\n * The `deps` of a `node_timer` Sub. One-shot `setTimeout` by default;\n * `repeat: true` → `setInterval`.\n */\nexport type NodeTimerDeps<M> = {\n readonly delayMs: number;\n readonly msg: M;\n readonly repeat?: boolean;\n};\n\n/**\n * A node timer sub. Cleanup clears the timer — a timer keyed to a phase is\n * cancelled the moment the machine leaves that phase, and a changed\n * `delayMs` / `msg` / `repeat` is a new id, so the timer restarts. For a plain\n * one-shot, the engine's built-in `timer` Sub needs no runner at all.\n */\nexport type NodeTimerSub<M> = Sub<\"node_timer\", NodeTimerDeps<M>>;\n\n/** The `deps` of a `node_signal` Sub: `signal` (SIGINT/SIGTERM/…) → `msg`. */\nexport type NodeSignalDeps<M> = {\n readonly signal: NodeJS.Signals;\n readonly msg: M;\n};\n\n/**\n * A process-signal sub. Cleanup detaches the listener. Lets graceful shutdown\n * be a real machine transition rather than an out-of-band `process.on` that the\n * reducer can't see.\n */\nexport type NodeSignalSub<M> = Sub<\"node_signal\", NodeSignalDeps<M>>;\n\n/**\n * The union of the Sub types the Node adapter ships runners for: WebSocket,\n * timer and process signal.\n */\nexport type NodeSub<M> = NodeWsSub | NodeTimerSub<M> | NodeSignalSub<M>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Ctx — what the node Subs read / populate.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** The live `node_ws` sockets, keyed by each Sub's `deps.key`. */\nexport type NodeWsRegistry = Map<string, WebSocket>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ws — loaded on first use, never at module load.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * `ws` is an OPTIONAL peer: the `node_ws` Sub needs it, `fileStore` and\n * `fileJournal` — everything the tutorial uses — do not. A static\n * `import … from \"ws\"` puts the dependency on the module's load path, so a\n * consumer who installed only the tutorial's three packages could not import\n * this door at all (`ERR_MODULE_NOT_FOUND`). The type import above is erased,\n * and the value arrives through this loader at the moment a socket is actually\n * opened — so the cost of an optional peer is paid by the feature that needs\n * it, not by the door it happens to share.\n *\n * `createRequire` rather than `await import(\"ws\")` because a runner's\n * contract is synchronous — it must return its cleanup on the same tick the\n * engine starts it, and a deferred socket would give cleanup nothing to close.\n */\ntype NodeWsConstructor = new (url: string) => WebSocket;\n\nlet cachedWs: NodeWsConstructor | undefined;\n\nfunction loadWebSocket(): NodeWsConstructor {\n if (cachedWs) return cachedWs;\n let mod: unknown;\n try {\n mod = createRequire(import.meta.url)(\"ws\");\n } catch (cause) {\n throw new Error(\n 'The `node_ws` Sub needs the optional peer dependency \"ws\". Install it: `npm install ws`.',\n { cause },\n );\n }\n const ctor = (mod as { default?: NodeWsConstructor }).default ?? mod;\n cachedWs = ctor as NodeWsConstructor;\n return cachedWs;\n}\n\n/**\n * `WebSocket.OPEN`, inlined. Reading it off the class would drag `ws` onto the\n * load path of `sendToWebSocket`, which is a Cmd-side helper a caller can reach\n * without ever opening a socket. The value is fixed by the WHATWG WebSocket\n * spec, not by `ws`.\n */\nconst WS_OPEN = 1;\n\n/** `WebSocket.CLOSED`, inlined for the same reason as {@link WS_OPEN}. */\nconst WS_CLOSED = 3;\n\n/**\n * The Ctx fields the `node_ws` runner depends on. A consumer's Ctx must extend\n * this so the runner can register the live socket and Cmd handlers can reach\n * it.\n *\n * Deliberately NOT parameterized by Msg: the registry holds raw `WebSocket`s,\n * and the Msg-typed callbacks live in the {@link NodeWsHandlers} handed to\n * `nodeSubscribe`. Trip-wire: if a future node Sub holds a Msg-typed callback\n * on ctx, this becomes `NodeSubscribeCtx<M>`.\n */\nexport interface NodeSubscribeCtx {\n wsRegistry: NodeWsRegistry;\n}\n\n/**\n * Write a frame to the `node_ws` socket whose `deps.key` is `key`. No-op\n * (returns `false`) when the socket is absent, not OPEN, or `send()` throws.\n * The Cmd-side companion to the `node_ws` Sub: the Sub owns the socket's\n * lifetime; this writes to it.\n *\n * The contract is a boolean no-op, never a throw: the socket can transition out\n * of OPEN between the `readyState` check and `send()` (or `ws` can throw on an\n * errored underlying connection), and a Cmd interpreter must not crash on that.\n */\nexport function sendToWebSocket(\n ctx: NodeSubscribeCtx,\n key: string,\n data: string,\n): boolean {\n const socket = ctx.wsRegistry.get(key);\n if (!socket || socket.readyState !== WS_OPEN) return false;\n try {\n socket.send(data);\n return true;\n } catch {\n return false;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// nodeSubscribe — the runner table.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * What a `node_ws` socket's events become. Functions cannot be `deps`, so they\n * live here, on the runner side; each receives the running Sub so one handler\n * table can branch on `sub.deps` (its `key` or `url`) when a machine runs more\n * than one socket.\n *\n * Only `onMessage` returns `M | null` (null drops the frame) because inbound\n * frames are the boundary that needs a parse-and-maybe-discard seam. `onOpen`/\n * `onClose`/`onError` always dispatch — a spurious one is dropped in the reducer\n * (the cell ignores it), not here. An omitted one dispatches nothing.\n */\nexport interface NodeWsHandlers<M> {\n onMessage(data: string, sub: NodeWsSub): M | null;\n onOpen?(sub: NodeWsSub): M;\n onClose?(code: number, reason: string, sub: NodeWsSub): M;\n onError?(message: string, sub: NodeWsSub): M;\n}\n\n/** The options of {@link nodeSubscribe}: the `node_ws` handlers, when used. */\nexport interface NodeSubscribeOpts<M> {\n readonly ws: NodeWsHandlers<M>;\n}\n\n/** The `node_timer` + `node_signal` runners — what every `nodeSubscribe` returns. */\nexport interface NodeSubscribeBase<M> {\n node_timer: (\n sub: NodeTimerSub<M>,\n ctx: unknown,\n dispatch: (msg: M) => void,\n ) => Dispose;\n node_signal: (\n sub: NodeSignalSub<M>,\n ctx: unknown,\n dispatch: (msg: M) => void,\n ) => Dispose;\n}\n\n/** The full runner table, with `node_ws` — `nodeSubscribe({ ws })`. */\nexport interface NodeSubscribeWithWs<M, Ctx extends NodeSubscribeCtx>\n extends NodeSubscribeBase<M> {\n node_ws: (sub: NodeWsSub, ctx: Ctx, dispatch: (msg: M) => void) => Dispose;\n}\n\n/**\n * Build the `subscribe` runners for the node Sub types, to hand to `run`:\n * `run(machine, { subscribe: nodeSubscribe<M, Ctx>({ ws: { onMessage } }), ctx })`.\n *\n * `node_timer` and `node_signal` read everything off `sub.deps`, so they need\n * no options. `node_ws` needs its {@link NodeWsHandlers}; called without them,\n * the table has no `node_ws` runner, so a machine that declares `NodeWsSub`\n * fails to typecheck at `run` rather than opening a socket nobody listens to.\n * `Ctx` must extend {@link NodeSubscribeCtx} so the `node_ws` runner can\n * register its socket.\n */\nexport function nodeSubscribe<M>(): NodeSubscribeBase<M>;\nexport function nodeSubscribe<M, Ctx extends NodeSubscribeCtx>(\n opts: NodeSubscribeOpts<M>,\n): NodeSubscribeWithWs<M, Ctx>;\nexport function nodeSubscribe<M, Ctx extends NodeSubscribeCtx>(\n opts?: NodeSubscribeOpts<M>,\n): NodeSubscribeBase<M> | NodeSubscribeWithWs<M, Ctx> {\n const base: NodeSubscribeBase<M> = {\n node_timer: (sub, _ctx, dispatch) => {\n // Two reconcile lifecycles under one type: a one-shot (`setTimeout`)\n // whose `deps` should go null on the state after it fires, vs a\n // repeating (`setInterval`) one the state keeps on. The `repeat` flag\n // picks the install + cleanup.\n const { delayMs, msg, repeat } = sub.deps;\n if (repeat) {\n const handle = setInterval(() => dispatch(msg), delayMs);\n return () => clearInterval(handle);\n }\n const handle = setTimeout(() => dispatch(msg), delayMs);\n return () => clearTimeout(handle);\n },\n\n node_signal: (sub, _ctx, dispatch) => {\n const { signal, msg } = sub.deps;\n const handler = () => dispatch(msg);\n process.on(signal, handler);\n return () => {\n process.off(signal, handler);\n };\n },\n };\n if (opts === undefined) return base;\n return { ...base, node_ws: nodeWsRunner<M, Ctx>(opts.ws) };\n}\n\nfunction nodeWsRunner<M, Ctx extends NodeSubscribeCtx>(\n handlers: NodeWsHandlers<M>,\n): NodeSubscribeWithWs<M, Ctx>[\"node_ws\"] {\n return (sub, ctx, dispatch) => {\n const { key, url } = sub.deps;\n // Two live sockets under one key would make `sendToWebSocket` ambiguous,\n // and the first one's cleanup would unregister the second. The engine\n // stops a Sub before it starts its replacement, so a key still held here\n // is two concurrent Subs sharing it — an authoring bug, refused loudly.\n if (ctx.wsRegistry.has(key)) {\n throw new Error(\n `node_ws: a live socket is already registered under key \"${key}\" — each running node_ws Sub needs its own \\`deps.key\\`.`,\n );\n }\n const socket = new (loadWebSocket())(url);\n ctx.wsRegistry.set(key, socket);\n\n socket.on(\"open\", () => {\n if (handlers.onOpen) dispatch(handlers.onOpen(sub));\n });\n socket.on(\"message\", (data: RawData) => {\n dispatchIfPresent(dispatch, handlers.onMessage(data.toString(), sub));\n });\n socket.on(\"close\", (code: number, reason: Buffer) => {\n if (handlers.onClose)\n dispatch(handlers.onClose(code, reason.toString(), sub));\n });\n socket.on(\"error\", (err: Error) => {\n if (handlers.onError) dispatch(handlers.onError(err.message, sub));\n });\n\n return () => {\n ctx.wsRegistry.delete(key);\n socket.removeAllListeners();\n // Swallow teardown-induced errors: terminate()-ing a CONNECTING socket\n // emits an 'error' (\"closed before the connection was established\"),\n // and with our listeners gone that would be an unhandled 'error' that\n // crashes the process. A no-op listener absorbs it.\n socket.on(\"error\", () => {});\n // Close for ANY non-closed state: a graceful 1000 when OPEN, a hard\n // terminate() otherwise (CONNECTING/CLOSING) so a socket caught mid-\n // handshake can't dangle. This is the teardown the hand-driven Cmd-pair\n // approach (`cleanup_browser` closing the ws only `if OPEN`) could not\n // guarantee.\n if (socket.readyState === WS_OPEN) {\n socket.close(1000, \"tea-node sub cleanup\");\n } else if (socket.readyState !== WS_CLOSED) {\n socket.terminate();\n }\n };\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/internal/journal/index.ts","../../src/node/file-lock.ts","../../src/node/index.ts"],"names":["readFile","mkdir","unlink","handle"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * internal/journal — an append-only, ordered record log for `@demlik/tea`.\n * Internal since #49 — not published on any subpath; `fileJournal` on\n * `@demlik/tea/node` is the one public route.\n *\n * The log a \"durable, replayable state machine\" replays from. tea already ships\n * the log's neighbours — `fileStore`/`memoryStore` (`Store<S>`: snapshot with\n * whole-load / whole-save), `recorder` (JSONL debug traces), `durable-effects`\n * (`foldLedger` over an owed-effect ledger) — but not the log itself, so every\n * consumer hand-rolled it. This is that log, domain-blind: a `Journal<R>` over\n * an opaque record `R`, with no pipeline/ledger vocabulary of its own.\n *\n * Three operations carry the contract:\n * - `append(stream, record)` — durable before it returns, assigning a\n * per-stream total-order `seq` (1, 2, 3, …). Concurrent appenders to one\n * stream converge on one order; the lock's only job is an honest order key.\n * - `list(stream)` — every entry of a stream in `seq` order.\n * - `changes(listener)` — a subscription over appends made through this\n * instance after subscribing.\n *\n * `withLock(stream, fx)` runs `fx` with exclusive access to a stream, so a\n * read-decide-append sequence is atomic against other writers. It is\n * re-entrant for the same stream: an `append` inside `fx` does not deadlock on\n * the lock `fx` already holds.\n *\n * The substrate is a `JournalBackend<R>` — how a stream is read, how one record\n * is appended durably, and how a stream is locked across processes.\n * `makeJournal` wires the domain-blind orchestration (per-stream serialisation,\n * `seq` assignment, `changes` fan-out) over any backend. `memoryJournal` is the\n * in-process backend that mirrors `memoryStore` for tests; the Node file\n * backend (`fileJournal`) homes in `@demlik/tea/node` beside `fileStore`.\n *\n * Interface shape follows Effect `unstable/eventlog` `EventJournal`\n * (`entries`/`write`/`changes`/`withLock`) as prior art, not a dependency.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n/** A per-stream total-order key. Starts at 1 and increments by one per append. */\nexport type Seq = number;\n\n/** One appended record, tagged with its stream and its per-stream `seq`. */\nexport interface JournalEntry<R> {\n readonly stream: string;\n readonly seq: Seq;\n readonly record: R;\n}\n\n/**\n * An append-only, ordered record log over an opaque record `R`.\n *\n * Domain-blind: it knows nothing of what a record means. Ordering, durability\n * and the honest order key are its whole contract.\n */\nexport interface Journal<R> {\n /**\n * Append `record` to `stream`, durable before the promise resolves, and\n * resolve with the `seq` it was assigned — the next integer in the stream's\n * total order.\n */\n append(stream: string, record: R): Promise<{ seq: Seq }>;\n /** Every entry of `stream`, in `seq` order. Empty for an unwritten stream. */\n list(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Subscribe to appends made through this instance after this call. Returns an\n * unsubscribe. Cross-process appends are not delivered — a cross-process\n * subscription is a polling concern this log leaves to its consumer.\n */\n changes(listener: (entry: JournalEntry<R>) => void): () => void;\n /**\n * Run `fx` with exclusive access to `stream` — the in-process mutex against\n * other callers here, the backend lock against other processes. Re-entrant\n * for the same stream, so an `append` (or nested `withLock`) inside `fx` runs\n * under the lock this call already holds rather than deadlocking on it.\n */\n withLock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * The substrate a `Journal<R>` is built over. Everything host-specific lives\n * here; `makeJournal` owns the host-blind rest.\n *\n * `append` is always invoked under `lock` for the same stream, so a backend\n * assigns `seq` from `read().length + 1` without racing itself.\n */\nexport interface JournalBackend<R> {\n /** Every entry of `stream` in `seq` order; empty for an unwritten stream. */\n read(stream: string): Promise<ReadonlyArray<JournalEntry<R>>>;\n /**\n * Persist `record` as the next entry of `stream`, durable before resolving,\n * and resolve with the entry it wrote. Invoked only while `lock` holds the\n * stream, so `seq` is safely the current length plus one.\n */\n append(stream: string, record: R): Promise<JournalEntry<R>>;\n /** Run `fx` while holding `stream`'s cross-process lock. */\n lock<A>(stream: string, fx: () => Promise<A>): Promise<A>;\n}\n\n/**\n * A promise-chained mutex: each `run` waits for the prior one to settle, so the\n * bodies never interleave. The order key is honest because assignment happens\n * inside a body, never across the `await` that another body could slip through.\n */\nclass AsyncLock {\n private tail: Promise<unknown> = Promise.resolve();\n\n run<A>(fx: () => Promise<A>): Promise<A> {\n const result = this.tail.then(fx, fx);\n this.tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n}\n\n/**\n * Build a `Journal<R>` over `backend`. Owns the host-blind half: per-stream\n * serialisation, re-entrant exclusion, and the `changes` fan-out. `append` and\n * `withLock` both route through `exclusive`, which holds the in-process mutex\n * and the backend lock for one stream and is re-entrant for that stream within\n * a single call chain.\n */\nexport function makeJournal<R>(backend: JournalBackend<R>): Journal<R> {\n const mutexes = new Map<string, AsyncLock>();\n // The streams this async call chain holds. Kept in AsyncLocalStorage, not an\n // instance-wide Set, because re-entrancy is a property of the CALL CHAIN, not\n // of the instance: a genuine re-entrant append (nested inside an `fx` that\n // already holds the stream) and an INDEPENDENT top-level append that merely\n // arrives while another chain holds the lock are indistinguishable to a\n // shared Set — so the shared Set waves the independent call through inline,\n // past the mutex, to run concurrently with the holder. That is a lost record:\n // in `fileJournal` the two both read the same length and compute the same\n // `seq`, and the second `rename` clobbers the first. A per-chain store only\n // ever reports the streams THIS chain entered, so an independent call sees an\n // empty (or unrelated) store and correctly queues on the mutex.\n const heldStreams = new AsyncLocalStorage<ReadonlySet<string>>();\n const listeners = new Set<(entry: JournalEntry<R>) => void>();\n\n const mutexFor = (stream: string): AsyncLock => {\n let mutex = mutexes.get(stream);\n if (!mutex) {\n mutex = new AsyncLock();\n mutexes.set(stream, mutex);\n }\n return mutex;\n };\n\n const exclusive = <A>(stream: string, fx: () => Promise<A>): Promise<A> => {\n const held = heldStreams.getStore();\n // This call chain already holds this stream's lock: run inline, or a\n // re-entrant append/withLock would block forever on a lock it owns.\n if (held?.has(stream)) return fx();\n return mutexFor(stream).run(() =>\n backend.lock(stream, () => {\n // Scope the augmented held-set to `fx` and its async continuations. The\n // store is torn down when `fx` settles — no finally clause to leak it.\n const nested = new Set(held).add(stream);\n return heldStreams.run(nested, fx);\n }),\n );\n };\n\n return {\n append(stream, record) {\n return exclusive(stream, async () => {\n const entry = await backend.append(stream, record);\n for (const listener of listeners) listener(entry);\n return { seq: entry.seq };\n });\n },\n list(stream) {\n return Promise.resolve(backend.read(stream));\n },\n changes(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n withLock(stream, fx) {\n return exclusive(stream, fx);\n },\n };\n}\n\n/**\n * An in-process `Journal<R>` — the standard test substrate, mirroring\n * `memoryStore` from `@demlik/tea/mem`. Reference semantics: a record handed to\n * `append` is stored by reference, exactly as `memoryStore` stores state. No\n * cross-process lock is needed — a single process owns the whole log — so the\n * backend lock is identity and `makeJournal`'s in-process mutex is the only\n * ordering guarantee.\n */\nexport function memoryJournal<R>(): Journal<R> {\n const streams = new Map<string, JournalEntry<R>[]>();\n\n return makeJournal<R>({\n async read(stream) {\n return (streams.get(stream) ?? []).slice();\n },\n async append(stream, record) {\n const log = streams.get(stream) ?? [];\n const entry: JournalEntry<R> = { stream, seq: log.length + 1, record };\n log.push(entry);\n streams.set(stream, log);\n return entry;\n },\n async lock(_stream, fx) {\n return fx();\n },\n });\n}\n\n// The remote-journal + per-remote-cursor half of the journal feature (#31): the\n// remote a client pushes to and pulls from, the local cursor that resumes sync,\n// and the sync client that joins a local `Journal` to a remote so two local\n// journals meet in one order. It builds ON `memoryJournal` above.\nexport * from \"./remote\";\n","/**\n * The cross-process lock under the fenced `fileStore` and `fileJournal`: a file\n * created with `wx` (O_CREAT|O_EXCL), the one primitive atomic across processes.\n *\n * A lock file left behind is judged by what it can prove about its holder. A\n * holder proven gone is stolen; anything else is waited on, up to a limit that\n * ends in a {@link FileLockTimeoutError}. A wrong steal silently loses writes and\n * a loud failure costs one clear step, so every case that proves nothing waits.\n */\n\nimport { mkdir, open, readFile, stat, unlink } from \"node:fs/promises\";\nimport { hostname, uptime } from \"node:os\";\n\n/** How long the waits in this module last. Only a test passes other values. */\nexport interface FileLockLimits {\n /** How long a writer waits for the lock before it gives up. */\n readonly waitMs: number;\n /**\n * How old an empty lock must be to count as a crash, not as a writer between\n * its create and its write. Below `waitMs`, so a crash is stolen within a wait.\n */\n readonly graceMs: number;\n}\n\nconst DEFAULT_LIMITS: FileLockLimits = { waitMs: 10_000, graceMs: 2_000 };\n\nconst SPIN_MS = 10;\n\n/**\n * How far apart two boot times may read and still be one boot. A boot time here\n * is derived from the clock, so one boot reads slightly differently from two\n * moments. Too small a tolerance would rob a live holder; too large sends a very\n * quick reboot to the loud failure. It cannot see a clock step: one larger than\n * this during a hold reads as an earlier boot, and that live holder is stolen.\n */\nconst BOOT_TOLERANCE_MS = 60_000;\n\n/** What a lock file says about who holds it. */\ntype LockHolder =\n /** Nothing yet: a writer between create and write, or a crash in that gap. */\n | { readonly kind: \"unwritten\" }\n /** The format older versions wrote: a bare process id. */\n | { readonly kind: \"pid-only\"; readonly pid: number }\n | {\n readonly kind: \"stamped\";\n readonly host: string;\n readonly bootedAtMs: number;\n readonly pid: number;\n }\n | { readonly kind: \"unreadable\" };\n\n/** Where this process runs. Read per attempt, because the clock can step. */\ninterface ThisMachine {\n readonly host: string;\n readonly bootedAtMs: number;\n}\n\n/**\n * Why a writer could not take a lock:\n *\n * - `another-host`: the lock names another host, which may still be writing.\n * - `live-holder`: its process is alive on this machine, or its old-format id\n * names a live process here.\n * - `unwritten`: the lock is empty and too young to count as a crash.\n * - `unreadable`: its contents are not a lock this version can read.\n * - `contended`: no one holder was to blame; other writers kept taking it.\n */\nexport type FileLockBlock =\n | \"another-host\"\n | \"live-holder\"\n | \"unwritten\"\n | \"unreadable\"\n | \"contended\";\n\nconst BLOCK_EXPLAINED: Readonly<Record<FileLockBlock, string>> = {\n \"another-host\":\n \"it was written on another host, which may still be writing, so it is not stolen. If that host is gone, delete the file and try again.\",\n \"live-holder\":\n \"the process it names is alive. If that process is not a writer here, delete the file and try again.\",\n unwritten: \"it is empty and too young to count as a crash.\",\n unreadable:\n \"its contents are not a lock this version can read. If no writer holds it, delete the file and try again.\",\n contended: \"other writers kept taking it.\",\n};\n\n/**\n * A write gave up waiting for a lock file. `lockPath` names the file to clear\n * and `blockedBy` says why it could not be taken. The write did not happen.\n */\nexport class FileLockTimeoutError extends Error {\n override readonly name = \"FileLockTimeoutError\";\n readonly _tag = \"file_lock_timeout\" as const;\n constructor(\n readonly lockPath: string,\n readonly waitedMs: number,\n readonly blockedBy: FileLockBlock,\n ) {\n super(\n `@demlik/tea: gave up on lock \"${lockPath}\" after ${Math.round(waitedMs)} ms: ${BLOCK_EXPLAINED[blockedBy]}`,\n );\n }\n}\n\nfunction thisMachine(): ThisMachine {\n return {\n host: hostname(),\n bootedAtMs: Math.round(Date.now() - uptime() * 1000),\n };\n}\n\nfunction isValidPid(pid: unknown): pid is number {\n return typeof pid === \"number\" && Number.isSafeInteger(pid) && pid > 0;\n}\n\nfunction parseStamp(body: string): LockHolder {\n let raw: unknown;\n try {\n raw = JSON.parse(body);\n } catch {\n return { kind: \"unreadable\" };\n }\n if (typeof raw !== \"object\" || raw === null) return { kind: \"unreadable\" };\n const { host, bootedAtMs, pid } = raw as Record<string, unknown>;\n if (\n typeof host !== \"string\" ||\n typeof bootedAtMs !== \"number\" ||\n !Number.isFinite(bootedAtMs) ||\n !isValidPid(pid)\n ) {\n return { kind: \"unreadable\" };\n }\n return { kind: \"stamped\", host, bootedAtMs, pid };\n}\n\nfunction parseHolder(text: string): LockHolder {\n const body = text.trim();\n if (body === \"\") return { kind: \"unwritten\" };\n if (/^\\d+$/.test(body)) {\n const pid = Number(body);\n return isValidPid(pid) ? { kind: \"pid-only\", pid } : { kind: \"unreadable\" };\n }\n return parseStamp(body);\n}\n\n/**\n * `process.kill(pid, 0)` sends no signal, it tests existence: it throws `ESRCH`\n * for a dead process and `EPERM` for a live one this process may not signal.\n */\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/** `stale` is a holder proven gone, and nothing else may be stolen. */\nfunction judgeHolder(\n holder: LockHolder,\n ageMs: number,\n here: ThisMachine,\n graceMs: number,\n): \"stale\" | FileLockBlock {\n switch (holder.kind) {\n case \"unwritten\":\n return ageMs > graceMs ? \"stale\" : \"unwritten\";\n case \"unreadable\":\n return \"unreadable\";\n case \"pid-only\":\n return isProcessAlive(holder.pid) ? \"live-holder\" : \"stale\";\n case \"stamped\":\n if (holder.host !== here.host) return \"another-host\";\n // A process id means nothing across a boot, whoever holds it now.\n if (Math.abs(holder.bootedAtMs - here.bootedAtMs) > BOOT_TOLERANCE_MS) {\n return \"stale\";\n }\n // One process can hold its own lock from another store, so this process's\n // own id is a live holder like any other.\n return isProcessAlive(holder.pid) ? \"live-holder\" : \"stale\";\n }\n}\n\nasync function ageOf(path: string): Promise<number | \"vanished\"> {\n try {\n return Date.now() - (await stat(path)).mtimeMs;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return \"vanished\";\n throw err;\n }\n}\n\n/**\n * Read, then age: a writer landing between the two can only make the lock read\n * younger than it is, which holds off a steal.\n */\nasync function inspectLock(\n lockPath: string,\n limits: FileLockLimits,\n): Promise<\"vanished\" | \"stale\" | FileLockBlock> {\n let text: string;\n try {\n text = await readFile(lockPath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return \"vanished\";\n throw err;\n }\n const ageMs = await ageOf(lockPath);\n if (ageMs === \"vanished\") return \"vanished\";\n return judgeHolder(parseHolder(text), ageMs, thisMachine(), limits.graceMs);\n}\n\nasync function createExclusive(path: string, body: string): Promise<boolean> {\n try {\n const handle = await open(path, \"wx\");\n try {\n if (body !== \"\") await handle.writeFile(body);\n } finally {\n await handle.close();\n }\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"EEXIST\") return false;\n throw err;\n }\n}\n\nconst removeIfPresent = (path: string): Promise<void> =>\n unlink(path).catch((err: NodeJS.ErrnoException) => {\n if (err.code !== \"ENOENT\") throw err;\n });\n\n/**\n * Remove a stale lock, or report that this attempt did not.\n *\n * Two writers can both read one dead holder. If each then removed the file, the\n * slower one could remove the lock the faster one just took, and both would\n * hold it. So stealers take turns under a guard file, and each judges the lock\n * again inside it. A guard old enough to be a crash is cleared for the next try.\n */\nasync function stealIfStillStale(\n lockPath: string,\n limits: FileLockLimits,\n): Promise<boolean> {\n const guardPath = `${lockPath}.steal`;\n if (!(await createExclusive(guardPath, \"\"))) {\n const age = await ageOf(guardPath);\n if (age !== \"vanished\" && age > limits.graceMs) {\n await removeIfPresent(guardPath);\n }\n return false;\n }\n try {\n const verdict = await inspectLock(lockPath, limits);\n if (verdict === \"stale\") await removeIfPresent(lockPath);\n return verdict === \"stale\" || verdict === \"vanished\";\n } finally {\n await removeIfPresent(guardPath);\n }\n}\n\nasync function acquireFileLock(\n dir: string,\n lockPath: string,\n limits: FileLockLimits,\n): Promise<void> {\n await mkdir(dir, { recursive: true });\n const startedAt = performance.now();\n let blockedBy: FileLockBlock = \"contended\";\n for (;;) {\n const here = thisMachine();\n const stamp = JSON.stringify({\n host: here.host,\n bootedAtMs: here.bootedAtMs,\n pid: process.pid,\n });\n if (await createExclusive(lockPath, stamp)) return;\n\n const verdict = await inspectLock(lockPath, limits);\n if (verdict !== \"stale\" && verdict !== \"vanished\") blockedBy = verdict;\n const freed =\n verdict === \"vanished\" ||\n (verdict === \"stale\" && (await stealIfStillStale(lockPath, limits)));\n\n const waitedMs = performance.now() - startedAt;\n if (waitedMs >= limits.waitMs) {\n throw new FileLockTimeoutError(lockPath, waitedMs, blockedBy);\n }\n if (!freed) await new Promise((resolve) => setTimeout(resolve, SPIN_MS));\n }\n}\n\n/** Run `fx` holding the lock at `lockPath`, and release it afterwards. */\nexport async function withFileLock<A>(\n dir: string,\n lockPath: string,\n fx: () => Promise<A>,\n limits: FileLockLimits = DEFAULT_LIMITS,\n): Promise<A> {\n await acquireFileLock(dir, lockPath, limits);\n try {\n return await fx();\n } finally {\n await unlink(lockPath).catch(() => {});\n }\n}\n","/**\n * @packageDocumentation\n * @demlik/tea/node — Node host adapter for `@demlik/tea`.\n *\n * Every host adapter ships two halves (mirrors `@demlik/tea/do`):\n *\n * 1. `fileStore<S>(path, parse)` — a `Store<S>` over a JSON file on disk.\n * Atomic save (temp + rename). Absent file → `load()` resolves `null`\n * (fresh boot). Structurally malformed JSON throws at `load()` (an infra\n * error), matching `doStore`; a shape mismatch is NOT a throw — the\n * substrate calls `migrate`, which returns `refuse(reason)`. Both make\n * `ready` reject with `StoreRefusedError`, and the file is left as is.\n * Use for resumable scripts / local repro of prod machines. Ephemeral\n * CLIs can skip persistence with `memoryStore` from `@demlik/tea/mem`.\n *\n * 2. `nodeSubscribe<M, Ctx>({ ws })` — the runners for the three node-native\n * Sub types this package owns, handed to `run` as its `subscribe`:\n * - `node_ws` — deps `{ key, url }`. Opens a `ws` WebSocket; routes\n * open/message/close/error into Msgs through the `ws`\n * handlers. Registers the live socket in\n * `ctx.wsRegistry` under `key` so a Cmd handler can\n * write to it (see `sendToWebSocket`). Cleanup closes\n * the socket for ANY non-CLOSED state.\n * - `node_timer` — deps `{ delayMs, msg, repeat? }`. setTimeout /\n * setInterval; cleanup clears it.\n * - `node_signal` — deps `{ signal, msg }`. A process signal\n * (SIGINT/SIGTERM/…) → Msg; cleanup detaches the\n * listener.\n *\n * Unlike tea-do — whose `alarm()` / `webSocketMessage()` are DO lifecycle\n * methods the handler can't observe from the outside, hence its registries —\n * a node process is continuously alive: each runner holds its resource in\n * closure and the returned cleanup closes it. The engine runs that cleanup the\n * instant the Sub's `deps` go null, and a changed `deps` value stops the old\n * runner before starting the new one — end-then-start choreography for free\n * (Rule 9, [invariant 4]). A lifecycle-bound resource (a socket, a timer)\n * becomes impossible to leak from a reducer cell, because no reducer cell owns\n * its teardown.\n *\n * `ctx.wsRegistry` is the one shared surface: `node_ws` populates it, keyed by\n * the Sub's `deps.key`; a Cmd interpreter reads it to write to the open\n * socket. Same shape as tea-do's `wsRegistry`.\n */\n\nimport { mkdir, readFile, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport type WebSocket from \"ws\";\nimport type { RawData } from \"ws\";\nimport type {\n DeletableStore,\n Dispose,\n FencedStore,\n Migrated,\n Sub,\n} from \"../index\";\nimport { StoreConflictError } from \"../index\";\nimport {\n type Journal,\n type JournalEntry,\n makeJournal,\n type Seq,\n} from \"../internal/journal\";\nimport { dispatchIfPresent } from \"../subs/types\";\nimport { withFileLock } from \"./file-lock\";\n\nexport { type FileLockBlock, FileLockTimeoutError } from \"./file-lock\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileStore — `Store<S>` over a JSON file.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Persist a machine's state to a JSON file — pass the `path` and a `parse` that\n * validates what comes back, get a `Store<S>` you hand to `run` so the next run\n * resumes where this one stopped.\n *\n * `parse` is REQUIRED because the file is a real serialization boundary — the\n * bytes that come back through `JSON.parse` are structurally `unknown`, and the\n * caller (who owns `S`) is the only party that can validate the shape.\n * `parse` returns `S` for a shape it recognizes, `null` when nothing was saved\n * (the substrate boots `init` with `loaded = null`), and `refuse(reason)` for\n * saved bytes it cannot read (`ready` rejects with `StoreRefusedError`).\n * `parse` must NOT throw per the `Store<S>.migrate` contract.\n *\n * Pass `{ fenced: true }` to get a `FencedStore<S>` instead — see\n * {@link FileStoreOptions}.\n *\n * Either form is a `DeletableStore<S>`: `delete()` removes the state file and\n * its `<path>.fence` version stamp, so a host that forgets a run needs no\n * knowledge of the layout on disk.\n */\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n): DeletableStore<S>;\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n options: FileStoreOptions & { readonly fenced: true },\n): FencedStore<S> & DeletableStore<S>;\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n options?: FileStoreOptions,\n): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>);\nexport function fileStore<S>(\n path: string,\n parse: (raw: unknown) => Migrated<S>,\n options: FileStoreOptions = {},\n): DeletableStore<S> | (FencedStore<S> & DeletableStore<S>) {\n const base: DeletableStore<S> = {\n async load(): Promise<unknown> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n // JSON.parse throws on structural malformation — propagate (infra error),\n // matching `doStore`. The decoded value is intentionally returned as\n // `unknown`; the substrate's `migrate` callback is the boundary parse.\n return JSON.parse(raw);\n },\n async save(state: S): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n // Write to a pid-scoped temp then atomically rename. The rename gives\n // torn-write safety (a reader sees the old file or the new one, never a\n // partial) and survives process crash / Ctrl-C — the only failure a local\n // resumable run resumes from. fsync (file + dir) would add power-loss\n // durability the use case doesn't need; the substrate serializes saves\n // through run()'s queue, so there's never a second concurrent writer\n // WITHIN one run. Between two runs there is nothing here refusing one —\n // that is what `{ fenced: true }` below adds (#143).\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, JSON.stringify(state), \"utf8\");\n await rename(tmp, path);\n },\n migrate(raw: unknown): Migrated<S> {\n return parse(raw);\n },\n async delete(): Promise<void> {\n await removeStateFiles(path);\n },\n };\n return options.fenced === true ? fenceFileStore(base, path) : base;\n}\n\n/**\n * Remove a `fileStore`'s state file and its `.fence` stamp; an absent one is\n * already deleted. The unfenced store removes the stamp too, because a file\n * written fenced can be opened unfenced and a stale stamp would outlive it.\n *\n * Stamp FIRST, state second. A crash between the two leaves the bytes at\n * version `0`, which a live fenced run reads as a conflict — the safe\n * direction. The reverse leaves the old version beside no state, and a run\n * still holding that version would swap cleanly onto a deleted run.\n */\nasync function removeStateFiles(path: string): Promise<void> {\n for (const file of [`${path}.fence`, path]) {\n await unlink(file).catch((err: NodeJS.ErrnoException) => {\n if (err.code !== \"ENOENT\") throw err;\n });\n }\n}\n\n/** Options for {@link fileStore}. */\nexport interface FileStoreOptions {\n /**\n * Refuse a second live writer (#143). With `{ fenced: true }` the returned\n * store is a `FencedStore<S>`: it carries a version stamp beside the state\n * file, and `run` compare-and-swaps against it on every save. A process that\n * boots reads the current version and takes the fence; the older live writer\n * that started from the same version is refused with a `StoreConflictError`\n * at its next save. Omit it and the store is exactly as it was — the last\n * writer wins, and single-writer is the caller's precondition to keep.\n *\n * A run on a fenced store must be given an `onError`, or it does not compile\n * (see `StoreAndOnError`). A lost fence can surface with no caller to reject\n * at, under `\"stop-save\"` or `\"follow-up\"`, and the default sink would rethrow\n * it and end the process. Treat a `StoreConflictError` there as this run having\n * moved to another process: stop that run, and leave the others alone.\n *\n * Each save and `delete()` takes a lock file, `<path>.fence.lock`, and a\n * writer that dies holding it leaves it behind. The next write steals a lock\n * whose holder is proven gone: an empty one older than a short grace period,\n * one from an earlier boot of this machine, or one whose process is dead.\n *\n * Any other lock is waited on for up to 10 seconds, and then the write rejects\n * with a {@link FileLockTimeoutError} naming the file. That includes a lock\n * from another host, which is never stolen because that host may be live on a\n * shared volume. So a folder restored onto a new machine can need one clear\n * step: once the old machine is known to be gone, delete the file. Hosts are\n * told apart by `os.hostname()`, so give two live hosts on one volume two\n * names.\n */\n readonly fenced?: true;\n}\n\n/**\n * The fencing half of {@link fileStore}. The state file itself is unchanged, so\n * a file written unfenced resumes fenced and back again; the version lives in a\n * sidecar `<path>.fence` holding a decimal integer, and an absent stamp is `0`.\n *\n * The compare-and-swap is taken under the same `wx` lock file `fileJournal`\n * uses, because `open(…, \"wx\")` is the one primitive that is atomic ACROSS\n * PROCESSES. Without it the read-compare-write is a TOCTOU race and two writers\n * both pass their own check. Rename-over-tmp still does the torn-write work for\n * each of the two files; the lock does the exclusion no rename could.\n */\nfunction fenceFileStore<S>(\n base: DeletableStore<S>,\n path: string,\n): FencedStore<S> & DeletableStore<S> {\n const fencePath = `${path}.fence`;\n const lockPath = `${path}.fence.lock`;\n\n async function readVersion(): Promise<number> {\n let raw: string;\n try {\n raw = await readFile(fencePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return 0;\n throw err;\n }\n const version = Number.parseInt(raw.trim(), 10);\n // A stamp we cannot read is not a version we may assume: defaulting it to 0\n // would let a fresh writer win a fence against a live one.\n if (!Number.isSafeInteger(version) || version < 0) {\n throw new Error(\n `fileStore: version stamp at \"${fencePath}\" is not an integer: ${raw}`,\n );\n }\n return version;\n }\n\n return {\n ...base,\n fenced: true,\n async loadFenced(): Promise<{ raw: unknown; version: number }> {\n // Version FIRST, bytes second: a writer landing between the two reads\n // leaves us with a version OLDER than the bytes, so our next save is\n // refused. Reading bytes first could hand back a version newer than them,\n // and that save would silently win.\n const version = await readVersion();\n return { raw: await base.load(), version };\n },\n async saveFenced(state: S, expectedVersion: number): Promise<number> {\n return withFileLock(dirname(path), lockPath, async () => {\n const actual = await readVersion();\n if (actual !== expectedVersion) {\n throw new StoreConflictError(expectedVersion, actual);\n }\n const next = expectedVersion + 1;\n // State before stamp. A crash between the two leaves a stamp BEHIND the\n // bytes, which reads as a conflict for the next writer — the safe\n // direction. The reverse advertises a write that never landed.\n await base.save(state);\n const tmp = `${fencePath}.${process.pid}.tmp`;\n await writeFile(tmp, String(next), \"utf8\");\n await rename(tmp, fencePath);\n return next;\n });\n },\n async delete(): Promise<void> {\n // Under the swap's own lock, so a delete never lands between another\n // writer's state write and its stamp write.\n await withFileLock(dirname(path), lockPath, () => base.delete());\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// fileJournal — a `Journal<R>` over one on-disk JSONL file per stream.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The Node file substrate for the journal (`src/internal/journal/`), beside\n * `fileStore`. A\n * stream is one JSONL file under `dir` (`<encoded-stream>.jsonl`), one\n * `{\"seq\":n,\"record\":…}` object per line — readable and greppable on disk, no\n * SQLite. `parse` is REQUIRED for the same reason `fileStore` requires it: the\n * bytes read back through `JSON.parse` are structurally `unknown`, and the\n * caller who owns `R` is the only party that can validate a record's shape.\n *\n * `append` rewrites the whole stream file via a pid-scoped temp + `rename`,\n * exactly as `fileStore.save` does — a reader sees the old file or the new one,\n * never a torn line, and the write survives a crash mid-append. The append is\n * O(n) in the stream's length; a durable record log a machine replays is read\n * whole anyway, so the atomic-rewrite guarantee is worth the cost.\n *\n * The ordering key is taken under a `wx` lock file (`<file>.lock`) naming the\n * writer's host, boot and PID: a second writer racing the same stream blocks on\n * the lock, so `seq` is a single total order across processes. This is the lock\n * phoenix spike #6673 proved: its only job is an honest order key, not the win.\n *\n * A leftover lock behaves as it does on a fenced {@link fileStore}: one whose\n * holder is proven gone is stolen, so a crash on this machine does not wedge the\n * stream. Any other lock, including one from another host, is waited on for up\n * to 10 seconds, and then the append rejects with a\n * {@link FileLockTimeoutError} naming the file. See\n * {@link FileStoreOptions.fenced} for the cases and the clear step.\n *\n * `changes` fires for appends made through THIS instance; a cross-process\n * append lands on disk but is not broadcast here (that is a polling concern the\n * log leaves to its consumer, and the remote-journal work downstream).\n *\n * The journal carries no stability promise, and this factory travels with it\n * rather than with `./node`'s `stable` stamp (MAINTAINING.md).\n *\n * @experimental\n */\nexport function fileJournal<R>(\n dir: string,\n parse: (raw: unknown) => R,\n): Journal<R> {\n const streamPath = (stream: string): string =>\n join(dir, `${encodeURIComponent(stream)}.jsonl`);\n\n const readEntries = async (stream: string): Promise<JournalEntry<R>[]> => {\n const path = streamPath(stream);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n return raw\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => {\n const row = JSON.parse(line) as { seq: Seq; record: unknown };\n return { stream, seq: row.seq, record: parse(row.record) };\n });\n };\n\n return makeJournal<R>({\n read: readEntries,\n async append(stream, record) {\n const entries = await readEntries(stream);\n const entry: JournalEntry<R> = {\n stream,\n seq: entries.length + 1,\n record,\n };\n const path = streamPath(stream);\n const lines = [...entries, entry]\n .map((e) => JSON.stringify({ seq: e.seq, record: e.record }))\n .join(\"\\n\");\n await mkdir(dir, { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n await writeFile(tmp, `${lines}\\n`, \"utf8\");\n await rename(tmp, path);\n return entry;\n },\n async lock(stream, fx) {\n return withFileLock(dir, `${streamPath(stream)}.lock`, fx);\n },\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NodeSub — the three node-native subscription variants.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The `deps` of a `node_ws` Sub: which socket (`key`) and where it connects\n * (`url`). Both are plain data, so the engine derives the Sub's id from them —\n * the same `{ key, url }` across transitions is the same socket (kept open),\n * and a changed one closes it and opens a fresh one.\n *\n * `key` is the author's name for the socket: the Cmd side addresses it through\n * {@link sendToWebSocket} by that key, since the derived Sub id is not\n * something a Cmd handler can know. It must be unique among the machine's live\n * `node_ws` Subs.\n */\nexport type NodeWsDeps = { readonly key: string; readonly url: string };\n\n/**\n * A node WebSocket sub. Its callbacks are not data, so they live on the runner\n * side — {@link NodeWsHandlers}, handed to {@link nodeSubscribe}.\n */\nexport type NodeWsSub = Sub<\"node_ws\", NodeWsDeps>;\n\n/**\n * The `deps` of a `node_timer` Sub. One-shot `setTimeout` by default;\n * `repeat: true` → `setInterval`.\n */\nexport type NodeTimerDeps<M> = {\n readonly delayMs: number;\n readonly msg: M;\n readonly repeat?: boolean;\n};\n\n/**\n * A node timer sub. Cleanup clears the timer — a timer keyed to a phase is\n * cancelled the moment the machine leaves that phase, and a changed\n * `delayMs` / `msg` / `repeat` is a new id, so the timer restarts. For a plain\n * one-shot, the engine's built-in `timer` Sub needs no runner at all.\n */\nexport type NodeTimerSub<M> = Sub<\"node_timer\", NodeTimerDeps<M>>;\n\n/** The `deps` of a `node_signal` Sub: `signal` (SIGINT/SIGTERM/…) → `msg`. */\nexport type NodeSignalDeps<M> = {\n readonly signal: NodeJS.Signals;\n readonly msg: M;\n};\n\n/**\n * A process-signal sub. Cleanup detaches the listener. Lets graceful shutdown\n * be a real machine transition rather than an out-of-band `process.on` that the\n * reducer can't see.\n */\nexport type NodeSignalSub<M> = Sub<\"node_signal\", NodeSignalDeps<M>>;\n\n/**\n * The union of the Sub types the Node adapter ships runners for: WebSocket,\n * timer and process signal.\n */\nexport type NodeSub<M> = NodeWsSub | NodeTimerSub<M> | NodeSignalSub<M>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Ctx — what the node Subs read / populate.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** The live `node_ws` sockets, keyed by each Sub's `deps.key`. */\nexport type NodeWsRegistry = Map<string, WebSocket>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ws — loaded on first use, never at module load.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * `ws` is an OPTIONAL peer: the `node_ws` Sub needs it, `fileStore` and\n * `fileJournal` — everything the tutorial uses — do not. A static\n * `import … from \"ws\"` puts the dependency on the module's load path, so a\n * consumer who installed only the tutorial's three packages could not import\n * this subpath at all (`ERR_MODULE_NOT_FOUND`). The type import above is erased,\n * and the value arrives through this loader at the moment a socket is actually\n * opened — so the cost of an optional peer is paid by the feature that needs\n * it, not by the subpath it happens to share.\n *\n * `createRequire` rather than `await import(\"ws\")` because a runner's\n * contract is synchronous — it must return its cleanup on the same tick the\n * engine starts it, and a deferred socket would give cleanup nothing to close.\n */\ntype NodeWsConstructor = new (url: string) => WebSocket;\n\nlet cachedWs: NodeWsConstructor | undefined;\n\nfunction loadWebSocket(): NodeWsConstructor {\n if (cachedWs) return cachedWs;\n let mod: unknown;\n try {\n mod = createRequire(import.meta.url)(\"ws\");\n } catch (cause) {\n throw new Error(\n 'The `node_ws` Sub needs the optional peer dependency \"ws\". Install it: `npm install ws`.',\n { cause },\n );\n }\n const ctor = (mod as { default?: NodeWsConstructor }).default ?? mod;\n cachedWs = ctor as NodeWsConstructor;\n return cachedWs;\n}\n\n/**\n * `WebSocket.OPEN`, inlined. Reading it off the class would drag `ws` onto the\n * load path of `sendToWebSocket`, which is a Cmd-side helper a caller can reach\n * without ever opening a socket. The value is fixed by the WHATWG WebSocket\n * spec, not by `ws`.\n */\nconst WS_OPEN = 1;\n\n/** `WebSocket.CLOSED`, inlined for the same reason as {@link WS_OPEN}. */\nconst WS_CLOSED = 3;\n\n/**\n * The Ctx fields the `node_ws` runner depends on. A consumer's Ctx must extend\n * this so the runner can register the live socket and Cmd handlers can reach\n * it.\n *\n * Deliberately NOT parameterized by Msg: the registry holds raw `WebSocket`s,\n * and the Msg-typed callbacks live in the {@link NodeWsHandlers} handed to\n * `nodeSubscribe`. Trip-wire: if a future node Sub holds a Msg-typed callback\n * on ctx, this becomes `NodeSubscribeCtx<M>`.\n */\nexport interface NodeSubscribeCtx {\n wsRegistry: NodeWsRegistry;\n}\n\n/**\n * Write a frame to the `node_ws` socket whose `deps.key` is `key`. No-op\n * (returns `false`) when the socket is absent, not OPEN, or `send()` throws.\n * The Cmd-side companion to the `node_ws` Sub: the Sub owns the socket's\n * lifetime; this writes to it.\n *\n * The contract is a boolean no-op, never a throw: the socket can transition out\n * of OPEN between the `readyState` check and `send()` (or `ws` can throw on an\n * errored underlying connection), and a Cmd interpreter must not crash on that.\n */\nexport function sendToWebSocket(\n ctx: NodeSubscribeCtx,\n key: string,\n data: string,\n): boolean {\n const socket = ctx.wsRegistry.get(key);\n if (!socket || socket.readyState !== WS_OPEN) return false;\n try {\n socket.send(data);\n return true;\n } catch {\n return false;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// nodeSubscribe — the runner table.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * What a `node_ws` socket's events become. Functions cannot be `deps`, so they\n * live here, on the runner side; each receives the running Sub so one handler\n * table can branch on `sub.deps` (its `key` or `url`) when a machine runs more\n * than one socket.\n *\n * Only `onMessage` returns `M | null` (null drops the frame) because inbound\n * frames are the boundary that needs a parse-and-maybe-discard seam. `onOpen`/\n * `onClose`/`onError` always dispatch — a spurious one is dropped in the reducer\n * (the cell ignores it), not here. An omitted one dispatches nothing.\n */\nexport interface NodeWsHandlers<M> {\n onMessage(data: string, sub: NodeWsSub): M | null;\n onOpen?(sub: NodeWsSub): M;\n onClose?(code: number, reason: string, sub: NodeWsSub): M;\n onError?(message: string, sub: NodeWsSub): M;\n}\n\n/** The options of {@link nodeSubscribe}: the `node_ws` handlers, when used. */\nexport interface NodeSubscribeOpts<M> {\n readonly ws: NodeWsHandlers<M>;\n}\n\n/** The `node_timer` + `node_signal` runners — what every `nodeSubscribe` returns. */\nexport interface NodeSubscribeBase<M> {\n node_timer: (\n sub: NodeTimerSub<M>,\n ctx: unknown,\n dispatch: (msg: M) => void,\n ) => Dispose;\n node_signal: (\n sub: NodeSignalSub<M>,\n ctx: unknown,\n dispatch: (msg: M) => void,\n ) => Dispose;\n}\n\n/** The full runner table, with `node_ws` — `nodeSubscribe({ ws })`. */\nexport interface NodeSubscribeWithWs<M, Ctx extends NodeSubscribeCtx>\n extends NodeSubscribeBase<M> {\n node_ws: (sub: NodeWsSub, ctx: Ctx, dispatch: (msg: M) => void) => Dispose;\n}\n\n/**\n * Build the `subscribe` runners for the node Sub types, to hand to `run`:\n * `run(machine, { subscribe: nodeSubscribe<M, Ctx>({ ws: { onMessage } }), ctx })`.\n *\n * `node_timer` and `node_signal` read everything off `sub.deps`, so they need\n * no options. `node_ws` needs its {@link NodeWsHandlers}; called without them,\n * the table has no `node_ws` runner, so a machine that declares `NodeWsSub`\n * fails to typecheck at `run` rather than opening a socket nobody listens to.\n * `Ctx` must extend {@link NodeSubscribeCtx} so the `node_ws` runner can\n * register its socket.\n */\nexport function nodeSubscribe<M>(): NodeSubscribeBase<M>;\nexport function nodeSubscribe<M, Ctx extends NodeSubscribeCtx>(\n opts: NodeSubscribeOpts<M>,\n): NodeSubscribeWithWs<M, Ctx>;\nexport function nodeSubscribe<M, Ctx extends NodeSubscribeCtx>(\n opts?: NodeSubscribeOpts<M>,\n): NodeSubscribeBase<M> | NodeSubscribeWithWs<M, Ctx> {\n const base: NodeSubscribeBase<M> = {\n node_timer: (sub, _ctx, dispatch) => {\n // Two reconcile lifecycles under one type: a one-shot (`setTimeout`)\n // whose `deps` should go null on the state after it fires, vs a\n // repeating (`setInterval`) one the state keeps on. The `repeat` flag\n // picks the install + cleanup.\n const { delayMs, msg, repeat } = sub.deps;\n if (repeat) {\n const handle = setInterval(() => dispatch(msg), delayMs);\n return () => clearInterval(handle);\n }\n const handle = setTimeout(() => dispatch(msg), delayMs);\n return () => clearTimeout(handle);\n },\n\n node_signal: (sub, _ctx, dispatch) => {\n const { signal, msg } = sub.deps;\n const handler = () => dispatch(msg);\n process.on(signal, handler);\n return () => {\n process.off(signal, handler);\n };\n },\n };\n if (opts === undefined) return base;\n return { ...base, node_ws: nodeWsRunner<M, Ctx>(opts.ws) };\n}\n\nfunction nodeWsRunner<M, Ctx extends NodeSubscribeCtx>(\n handlers: NodeWsHandlers<M>,\n): NodeSubscribeWithWs<M, Ctx>[\"node_ws\"] {\n return (sub, ctx, dispatch) => {\n const { key, url } = sub.deps;\n // Two live sockets under one key would make `sendToWebSocket` ambiguous,\n // and the first one's cleanup would unregister the second. The engine\n // stops a Sub before it starts its replacement, so a key still held here\n // is two concurrent Subs sharing it — an authoring bug, refused loudly.\n if (ctx.wsRegistry.has(key)) {\n throw new Error(\n `node_ws: a live socket is already registered under key \"${key}\" — each running node_ws Sub needs its own \\`deps.key\\`.`,\n );\n }\n const socket = new (loadWebSocket())(url);\n ctx.wsRegistry.set(key, socket);\n\n socket.on(\"open\", () => {\n if (handlers.onOpen) dispatch(handlers.onOpen(sub));\n });\n socket.on(\"message\", (data: RawData) => {\n dispatchIfPresent(dispatch, handlers.onMessage(data.toString(), sub));\n });\n socket.on(\"close\", (code: number, reason: Buffer) => {\n if (handlers.onClose)\n dispatch(handlers.onClose(code, reason.toString(), sub));\n });\n socket.on(\"error\", (err: Error) => {\n if (handlers.onError) dispatch(handlers.onError(err.message, sub));\n });\n\n return () => {\n ctx.wsRegistry.delete(key);\n socket.removeAllListeners();\n // Swallow teardown-induced errors: terminate()-ing a CONNECTING socket\n // emits an 'error' (\"closed before the connection was established\"),\n // and with our listeners gone that would be an unhandled 'error' that\n // crashes the process. A no-op listener absorbs it.\n socket.on(\"error\", () => {});\n // Close for ANY non-closed state: a graceful 1000 when OPEN, a hard\n // terminate() otherwise (CONNECTING/CLOSING) so a socket caught mid-\n // handshake can't dangle. This is the teardown the hand-driven Cmd-pair\n // approach (`cleanup_browser` closing the ws only `if OPEN`) could not\n // guarantee.\n if (socket.readyState === WS_OPEN) {\n socket.close(1000, \"tea-node sub cleanup\");\n } else if (socket.readyState !== WS_CLOSED) {\n socket.terminate();\n }\n };\n };\n}\n"]}
|
package/dist/otel/index.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { Tracer } from '@opentelemetry/api';
|
|
2
|
-
import { A as AgentEvent } from '../machine-
|
|
3
|
-
import '../core-
|
|
1
|
+
import { Tracer, Context } from '@opentelemetry/api';
|
|
2
|
+
import { A as AgentEvent } from '../machine-C0WhOAyB.js';
|
|
3
|
+
import '../core-DNDAEhO2.js';
|
|
4
4
|
import '@standard-schema/spec';
|
|
5
|
-
import '../index-
|
|
6
|
-
import '../index-
|
|
7
|
-
import '../index-
|
|
8
|
-
import '../types-
|
|
9
|
-
import '../index-
|
|
10
|
-
import '../runtime-types-
|
|
11
|
-
import '../index-
|
|
5
|
+
import '../index-CAsUE_d1.js';
|
|
6
|
+
import '../index-COeX9Bmz.js';
|
|
7
|
+
import '../index-Cg0QXnzf.js';
|
|
8
|
+
import '../types-S53WDy_h.js';
|
|
9
|
+
import '../index-BBLI2WWN.js';
|
|
10
|
+
import '../runtime-types-CP-ykwgf.js';
|
|
11
|
+
import '../index-C92SgNWz.js';
|
|
12
12
|
import '../retry-backoff/index.js';
|
|
13
13
|
import '../protocol-ClRZBhHQ.js';
|
|
14
|
-
import '../index-
|
|
14
|
+
import '../index-9P1PaXIV.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @packageDocumentation
|
|
@@ -39,6 +39,8 @@ import '../index-CrcoVfd_.js';
|
|
|
39
39
|
* `Store` writes into the same trace as the one that was killed. Spans the
|
|
40
40
|
* killed process never ended are never exported; OpenTelemetry exports a span
|
|
41
41
|
* when it ends. What it did end is in the trace beside the resumed leg.
|
|
42
|
+
* This holds only on the default path: a run started under a host's `parent`
|
|
43
|
+
* is in the host's trace, and keeping it one trace across a kill is the host's.
|
|
42
44
|
*
|
|
43
45
|
* ## Time
|
|
44
46
|
*
|
|
@@ -74,9 +76,23 @@ interface TraceAgentOptions {
|
|
|
74
76
|
readonly name?: string;
|
|
75
77
|
/**
|
|
76
78
|
* The trace id a run writes into, from its `runId`. Omit → {@link runTraceId}.
|
|
77
|
-
* Must return 32 lowercase hex characters, not all zero.
|
|
79
|
+
* Must return 32 lowercase hex characters, not all zero. Not called when
|
|
80
|
+
* {@link TraceAgentOptions.parent | parent} is given.
|
|
78
81
|
*/
|
|
79
82
|
readonly traceIdOf?: (runId: string) => string;
|
|
83
|
+
/**
|
|
84
|
+
* The context every run span starts under — a span the host already has
|
|
85
|
+
* open, as `trace.setSpan(context.active(), span)`. The run then lands in the
|
|
86
|
+
* host's trace as that span's child, and its brain and tool spans follow.
|
|
87
|
+
*
|
|
88
|
+
* `parent` wins over `traceIdOf`: when both are given, the run is in the
|
|
89
|
+
* parent's trace and `traceIdOf` is never called. With a `parent`, the run's
|
|
90
|
+
* trace is the host's, so keeping one trace across a kill is the host's job
|
|
91
|
+
* too — a resumed run's spans go wherever the resuming host's `parent`
|
|
92
|
+
* points. Omit → each run hangs off a detached parent in the trace
|
|
93
|
+
* `traceIdOf(runId)` names, which a resumed process derives again.
|
|
94
|
+
*/
|
|
95
|
+
readonly parent?: Context;
|
|
80
96
|
}
|
|
81
97
|
/** Anything that delivers an agent's events by type — a runtime built with `events: agentEvents()`. */
|
|
82
98
|
interface AgentEventSource<R> {
|
package/dist/otel/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { AGENT_EVENT_TYPES } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
1
|
+
import { AGENT_EVENT_TYPES } from '../chunk-WZEDIUWR.js';
|
|
2
|
+
import '../chunk-VH5MYJ2K.js';
|
|
3
|
+
import '../chunk-IAHEYPYJ.js';
|
|
4
4
|
import '../chunk-QNGYQMZB.js';
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-NFOGYDRS.js';
|
|
6
|
+
import '../chunk-UGTC33VH.js';
|
|
7
7
|
import '../chunk-VBDU4JL6.js';
|
|
8
8
|
import '../chunk-4LPY67E4.js';
|
|
9
9
|
import '../chunk-TXN2KFHF.js';
|
|
10
|
-
import '../chunk-
|
|
11
|
-
import '../chunk-
|
|
12
|
-
import '../chunk-
|
|
13
|
-
import '../chunk-
|
|
14
|
-
import '../chunk-
|
|
15
|
-
import
|
|
10
|
+
import '../chunk-7K7SEYCH.js';
|
|
11
|
+
import '../chunk-GFGOQIEC.js';
|
|
12
|
+
import '../chunk-2A5XTKQQ.js';
|
|
13
|
+
import '../chunk-R2YNDRGL.js';
|
|
14
|
+
import '../chunk-DEAPNMKO.js';
|
|
15
|
+
import '../chunk-BVFS3ICC.js';
|
|
16
|
+
import '../chunk-A3QPMX5M.js';
|
|
17
|
+
import { SpanKind, trace, SpanStatusCode, ROOT_CONTEXT, TraceFlags } from '@opentelemetry/api';
|
|
16
18
|
|
|
17
19
|
// src/otel/sha256.ts
|
|
18
20
|
function sha256Hex(message) {
|
|
@@ -151,6 +153,8 @@ function agentSpans(opts) {
|
|
|
151
153
|
const mask = opts.mask ?? maskBase64DataUris;
|
|
152
154
|
const name = opts.name ?? "agent";
|
|
153
155
|
const traceIdOf = opts.traceIdOf ?? runTraceId;
|
|
156
|
+
const { parent } = opts;
|
|
157
|
+
const parentOf = (runId) => parent ?? detachedParent(traceIdOf(runId), runId);
|
|
154
158
|
const runs = /* @__PURE__ */ new Map();
|
|
155
159
|
const exported = (value) => {
|
|
156
160
|
const masked = mask({ data: json(value) });
|
|
@@ -159,12 +163,7 @@ function agentSpans(opts) {
|
|
|
159
163
|
const openRun = (runId, at) => {
|
|
160
164
|
const known = runs.get(runId);
|
|
161
165
|
if (known !== void 0) return known;
|
|
162
|
-
const
|
|
163
|
-
traceId: traceIdOf(runId),
|
|
164
|
-
spanId: detachedSpanId(runId),
|
|
165
|
-
traceFlags: TraceFlags.SAMPLED,
|
|
166
|
-
isRemote: true
|
|
167
|
-
});
|
|
166
|
+
const parent2 = parentOf(runId);
|
|
168
167
|
const span = startSpan(
|
|
169
168
|
tracer,
|
|
170
169
|
`invoke_agent ${name}`,
|
|
@@ -180,11 +179,11 @@ function agentSpans(opts) {
|
|
|
180
179
|
"tea.run.id": runId
|
|
181
180
|
}
|
|
182
181
|
},
|
|
183
|
-
|
|
182
|
+
parent2
|
|
184
183
|
);
|
|
185
184
|
const opened = {
|
|
186
185
|
span,
|
|
187
|
-
context: trace.setSpan(
|
|
186
|
+
context: trace.setSpan(parent2, span),
|
|
188
187
|
brain: null,
|
|
189
188
|
tools: /* @__PURE__ */ new Map(),
|
|
190
189
|
lastAt: at
|
|
@@ -322,8 +321,13 @@ function startSpan(tracer, spanName, at, options, context) {
|
|
|
322
321
|
function endSpan(span, at) {
|
|
323
322
|
span.end(spanTime(at));
|
|
324
323
|
}
|
|
325
|
-
function
|
|
326
|
-
return
|
|
324
|
+
function detachedParent(traceId, runId) {
|
|
325
|
+
return trace.setSpanContext(ROOT_CONTEXT, {
|
|
326
|
+
traceId,
|
|
327
|
+
spanId: sha256Hex(runId).slice(32, 48),
|
|
328
|
+
traceFlags: TraceFlags.SAMPLED,
|
|
329
|
+
isRemote: true
|
|
330
|
+
});
|
|
327
331
|
}
|
|
328
332
|
function openChildren(run) {
|
|
329
333
|
return [...run.brain === null ? [] : [run.brain], ...run.tools.values()];
|
package/dist/otel/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/otel/sha256.ts","../../src/otel/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * SHA-256 of a string's UTF-8 bytes, as 64 lowercase hex characters (FIPS\n * 180-4). Synchronous and dependency-free, because the trace id has to exist\n * the moment a run's first event arrives, on every host tea runs on — and the\n * one digest every host shares, `crypto.subtle.digest`, is async.\n */\nexport function sha256Hex(message: string): string {\n const bytes = new TextEncoder().encode(message);\n // The message, a 0x80 byte, zero padding, and the bit length as a 64-bit\n // big-endian integer, filling a whole number of 64-byte blocks.\n const blocks = new Uint8Array(Math.ceil((bytes.length + 9) / 64) * 64);\n blocks.set(bytes);\n blocks[bytes.length] = 0x80;\n const view = new DataView(blocks.buffer);\n const bits = bytes.length * 8;\n view.setUint32(blocks.length - 8, Math.floor(bits / 0x100000000));\n view.setUint32(blocks.length - 4, bits >>> 0);\n\n const h = [...H0];\n const w = new Array<number>(64).fill(0);\n for (let offset = 0; offset < blocks.length; offset += 64) {\n for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4);\n for (let i = 16; i < 64; i++) {\n const w15 = w[i - 15] ?? 0;\n const w2 = w[i - 2] ?? 0;\n const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);\n const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);\n w[i] = ((w[i - 16] ?? 0) + s0 + (w[i - 7] ?? 0) + s1) | 0;\n }\n let [a, b, c, d, e, f, g, hh] = h as [\n number,\n number,\n number,\n number,\n number,\n number,\n number,\n number,\n ];\n for (let i = 0; i < 64; i++) {\n const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (hh + s1 + ch + (K[i] ?? 0) + (w[i] ?? 0)) | 0;\n const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (s0 + maj) | 0;\n hh = g;\n g = f;\n f = e;\n e = (d + t1) | 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) | 0;\n }\n const round = [a, b, c, d, e, f, g, hh];\n for (let i = 0; i < 8; i++) h[i] = ((h[i] ?? 0) + (round[i] ?? 0)) | 0;\n }\n return h.map((word) => (word >>> 0).toString(16).padStart(8, \"0\")).join(\"\");\n}\n\nfunction rotr(x: number, n: number): number {\n return (x >>> n) | (x << (32 - n));\n}\n\nconst H0: readonly number[] = [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n 0x1f83d9ab, 0x5be0cd19,\n];\n\nconst K: readonly number[] = [\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,\n 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\n 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\n 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\n 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n];\n","/**\n * @packageDocumentation\n * @demlik/tea/otel — an agent run as OpenTelemetry spans.\n *\n * `traceAgent(runtime, { tracer })` listens to an agent runtime's semantic\n * events and writes one span tree per run: a run span, a brain-call span per\n * turn and a tool span per tool call, the brain and tool spans children of the\n * run. Nothing here touches the reducer: the spans are a projection of the\n * `AgentEvent` stream, the way `transcript` is.\n *\n * The attributes follow the OpenTelemetry GenAI semantic conventions\n * (`gen_ai.*`), plus Langfuse's observation attributes (`langfuse.*`), so\n * Langfuse renders the run as an agent with generations and tool calls without\n * tea depending on any Langfuse package. Any OTel backend reads the same spans.\n *\n * `@opentelemetry/api` is an optional peer. Only this entry imports it.\n *\n * ## One trace across a kill\n *\n * The trace id is derived from the run's `runId` — the SHA-256 of it, cut to\n * 32 hex characters, which is exactly Langfuse's `createTraceId(runId)` — and\n * the run span is parented to a detached span context carrying it. The id\n * lives on the durable Model, so a process that resumes the run from its\n * `Store` writes into the same trace as the one that was killed. Spans the\n * killed process never ended are never exported; OpenTelemetry exports a span\n * when it ends. What it did end is in the trace beside the resumed leg.\n *\n * ## Time\n *\n * Every span starts and ends at the `at` of the event that opened or closed\n * it, and `at` is read as **epoch milliseconds** — the runtime's `clock`\n * contract. It reaches OpenTelemetry as an exact `HrTime`, never a bare\n * number, so a small clock value (a logical or test clock) stays the instant\n * it names instead of being rebased onto process start.\n */\n\nimport {\n type Attributes,\n type Context,\n type HrTime,\n ROOT_CONTEXT,\n type Span,\n SpanKind,\n type SpanOptions,\n SpanStatusCode,\n TraceFlags,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport {\n AGENT_EVENT_TYPES,\n type AgentEndedStatus,\n type AgentEvent,\n type ToolFailure,\n type TurnUsage,\n} from \"../agent/index\";\nimport { sha256Hex } from \"./sha256\";\n\n/**\n * A mask over one exported input or output — the same shape as\n * `LangfuseSpanProcessor`'s `mask`, so one function serves both. `data` is the\n * value already serialized to JSON; return what should be exported instead.\n */\nexport type SpanMask = (params: { readonly data: unknown }) => unknown;\n\n/** How {@link traceAgent} writes spans. */\nexport interface TraceAgentOptions {\n /** The tracer the spans are started on — `provider.getTracer(\"…\")`. */\n readonly tracer: Tracer;\n /**\n * Applied to every exported input and output. Omit → {@link maskBase64DataUris},\n * which keeps a screenshot a tool returned from being exported as megabytes\n * of base64.\n */\n readonly mask?: SpanMask;\n /**\n * The agent's name, on `gen_ai.agent.name`, the run span's name and\n * Langfuse's trace name. Omit → `\"agent\"`.\n */\n readonly name?: string;\n /**\n * The trace id a run writes into, from its `runId`. Omit → {@link runTraceId}.\n * Must return 32 lowercase hex characters, not all zero.\n */\n readonly traceIdOf?: (runId: string) => string;\n}\n\n/** Anything that delivers an agent's events by type — a runtime built with `events: agentEvents()`. */\nexport interface AgentEventSource<R> {\n on<K extends AgentEvent<R>[\"type\"]>(\n type: K,\n handler: (event: Extract<AgentEvent<R>, { type: K }>) => void,\n ): () => void;\n}\n\n/**\n * Trace an agent runtime into OpenTelemetry. Subscribes to every\n * `AgentEvent` type on `runtime` and returns a cleanup that detaches the\n * subscriptions and ends any span still open, at the `at` of the last event\n * its run folded.\n *\n * The runtime must project events — built with\n * `run(machine, { events: agentEvents() })`. For `defineAgent`, which takes an\n * `onEvent` listener instead of handing out its runtime, wire\n * {@link agentSpans}'s `onEvent` there.\n *\n * Each span is timed by its events' `at`, read as epoch milliseconds.\n *\n * @example\n * ```ts\n * import { type AgentEventSource, traceAgent } from \"@demlik/tea/otel\";\n * import type { TracerProvider } from \"@opentelemetry/api\";\n *\n * declare const provider: TracerProvider;\n * // Built with `run(machine, { ...wired, ctx, events: agentEvents() }).ready`.\n * declare const runtime: AgentEventSource<string>;\n *\n * const stop = traceAgent(runtime, { tracer: provider.getTracer(\"audit\") });\n * ```\n */\nexport function traceAgent<R>(\n runtime: AgentEventSource<R>,\n opts: TraceAgentOptions,\n): () => void {\n const spans = agentSpans<R>(opts);\n const offs = AGENT_EVENT_TYPES.map((type) => runtime.on(type, spans.onEvent));\n return () => {\n for (const off of offs) off();\n spans.end();\n };\n}\n\n/** The span writer {@link traceAgent} subscribes, for wiring to an `onEvent` listener. */\nexport interface AgentSpans<R> {\n /** Fold one event into the span tree. */\n readonly onEvent: (event: AgentEvent<R>) => void;\n /**\n * End every span still open — the leg this process ran, when it stops\n * before its run does. Each carries `tea.run.detached: true`.\n *\n * With `at`, every open span ends at `at`, used as given. Without it, each\n * run's spans end at the `at` of the last event that run folded — its own\n * clock, never wall time.\n */\n readonly end: (at?: number) => void;\n}\n\n/**\n * The span writer behind {@link traceAgent}, as a listener. Use it where the\n * events arrive through a callback rather than a runtime:\n *\n * @example\n * ```ts\n * import { defineAgent } from \"@demlik/tea/agent\";\n * import { agentSpans } from \"@demlik/tea/otel\";\n * import { trace } from \"@opentelemetry/api\";\n *\n * const agent = defineAgent({\n * model: async () => ({ content: \"done\", toolCalls: [] }),\n * tools: [],\n * instructions: \"Summarise the page.\",\n * });\n *\n * const spans = agentSpans({ tracer: trace.getTracer(\"audit\") });\n * await agent.run(\"https://example.com\", { onEvent: spans.onEvent });\n * ```\n */\nexport function agentSpans<R>(opts: TraceAgentOptions): AgentSpans<R> {\n const { tracer } = opts;\n const mask = opts.mask ?? maskBase64DataUris;\n const name = opts.name ?? \"agent\";\n const traceIdOf = opts.traceIdOf ?? runTraceId;\n const runs = new Map<string, OpenRun>();\n\n const exported = (value: unknown): string => {\n const masked = mask({ data: json(value) });\n return typeof masked === \"string\" ? masked : json(masked);\n };\n\n const openRun = (runId: string, at: number): OpenRun => {\n const known = runs.get(runId);\n if (known !== undefined) return known;\n const parent = trace.setSpanContext(ROOT_CONTEXT, {\n traceId: traceIdOf(runId),\n spanId: detachedSpanId(runId),\n traceFlags: TraceFlags.SAMPLED,\n isRemote: true,\n });\n const span = startSpan(\n tracer,\n `invoke_agent ${name}`,\n at,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n \"gen_ai.operation.name\": \"invoke_agent\",\n \"gen_ai.agent.name\": name,\n \"gen_ai.conversation.id\": runId,\n \"langfuse.observation.type\": \"agent\",\n \"langfuse.trace.name\": name,\n \"tea.run.id\": runId,\n },\n },\n parent,\n );\n const opened: OpenRun = {\n span,\n context: trace.setSpan(parent, span),\n brain: null,\n tools: new Map(),\n lastAt: at,\n };\n runs.set(runId, opened);\n return opened;\n };\n\n const onEvent = (event: AgentEvent<R>): void => {\n const run = openRun(event.runId, event.at);\n run.lastAt = event.at;\n switch (event.type) {\n case \"BrainStarted\": {\n // A boot in the same process re-issues the call already open.\n if (run.brain !== null) return;\n const model = event.model;\n run.brain = startSpan(\n tracer,\n model === null ? \"chat\" : `chat ${model}`,\n event.at,\n {\n kind: SpanKind.CLIENT,\n attributes: {\n \"gen_ai.operation.name\": \"chat\",\n ...(model === null\n ? {}\n : {\n \"gen_ai.request.model\": model,\n \"langfuse.observation.model.name\": model,\n }),\n \"langfuse.observation.type\": \"generation\",\n \"langfuse.observation.input\": exported(event.payload),\n \"tea.agent.turn\": event.turn,\n \"tea.agent.purpose\": event.purpose,\n },\n },\n run.context,\n );\n return;\n }\n case \"TurnSettled\": {\n const brain =\n run.brain ??\n startSpan(\n tracer,\n \"chat\",\n event.at,\n { kind: SpanKind.CLIENT },\n run.context,\n );\n brain.setAttributes({\n \"langfuse.observation.output\": exported(event.turn),\n ...usageAttributes(event.usage),\n });\n endSpan(brain, event.at);\n run.brain = null;\n return;\n }\n case \"ToolStarted\": {\n if (run.tools.has(event.callId)) return;\n run.tools.set(\n event.callId,\n startSpan(\n tracer,\n `execute_tool ${event.name}`,\n event.at,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n \"gen_ai.operation.name\": \"execute_tool\",\n \"gen_ai.tool.name\": event.name,\n \"gen_ai.tool.call.id\": event.callId,\n \"langfuse.observation.type\": \"tool\",\n \"langfuse.observation.input\": exported(event.args),\n },\n },\n run.context,\n ),\n );\n return;\n }\n case \"ToolSettled\": {\n const span = run.tools.get(event.callId);\n if (span === undefined) return;\n span.setAttribute(\n \"langfuse.observation.output\",\n exported(event.result),\n );\n endSpan(span, event.at);\n run.tools.delete(event.callId);\n return;\n }\n case \"ToolFailed\": {\n const span = run.tools.get(event.callId);\n if (span === undefined) return;\n span.setAttribute(\n \"langfuse.observation.output\",\n exported(event.failure),\n );\n failed(span, event.failure.reason, errorType(event.failure));\n endSpan(span, event.at);\n run.tools.delete(event.callId);\n return;\n }\n case \"RunDone\":\n closeRun(run, event.status, event.at, exported);\n runs.delete(event.runId);\n return;\n }\n };\n\n const end = (at?: number): void => {\n for (const run of runs.values()) {\n const endAt = at ?? run.lastAt;\n for (const span of [...openChildren(run), run.span]) {\n span.setAttribute(\"tea.run.detached\", true);\n endSpan(span, endAt);\n }\n }\n runs.clear();\n };\n\n return { onEvent, end };\n}\n\n/**\n * The default {@link SpanMask}: every base64 `data:` URI in a string becomes\n * `[base64 data URI stripped before export]`. Anything that is not a string\n * passes through untouched.\n */\nexport const maskBase64DataUris: SpanMask = ({ data }) =>\n typeof data === \"string\"\n ? data.replace(BASE64_DATA_URI, STRIPPED_DATA_URI)\n : data;\n\n/**\n * The trace id a run's spans are written into: the SHA-256 of `runId` as\n * lowercase hex, cut to 32 characters. It equals Langfuse's\n * `createTraceId(runId)`, so a trace can be looked up by the run it traced.\n */\nexport function runTraceId(runId: string): string {\n return sha256Hex(runId).slice(0, 32);\n}\n\n// ---------------------------------------------------------------------------\n\nconst BASE64_DATA_URI = /data:[^;]+;base64,[A-Za-z0-9+/=]+/g;\nconst STRIPPED_DATA_URI = \"[base64 data URI stripped before export]\";\n\ninterface OpenRun {\n readonly span: Span;\n readonly context: Context;\n brain: Span | null;\n readonly tools: Map<string, Span>;\n /** The `at` of the last event this run folded — where a detach ends it. */\n lastAt: number;\n}\n\n// The one place a run's clock becomes OpenTelemetry time. `at` is epoch\n// milliseconds. A bare number is ambiguous to the SDK: one no bigger than\n// `performance.now()` is read as milliseconds since process start and rebased\n// onto `performance.timeOrigin`, so a small clock value would silently move by\n// however long the process has been up (#367). An `HrTime` is taken as-is.\nfunction spanTime(at: number): HrTime {\n const seconds = Math.floor(at / 1_000);\n const nanos = Math.round((at - seconds * 1_000) * 1_000_000);\n return nanos === 1_000_000_000 ? [seconds + 1, 0] : [seconds, nanos];\n}\n\n// A span opened at `at`. The options carry no `startTime`, so no call site\n// can hand OpenTelemetry a raw clock value.\nfunction startSpan(\n tracer: Tracer,\n spanName: string,\n at: number,\n options: Omit<SpanOptions, \"startTime\">,\n context: Context,\n): Span {\n return tracer.startSpan(\n spanName,\n { ...options, startTime: spanTime(at) },\n context,\n );\n}\n\n// The one place a span ends. Every `Span#end` in this module goes through it,\n// which `span-end.test.ts` holds, so no end reaches OpenTelemetry off the\n// run's clock.\nfunction endSpan(span: Span, at: number): void {\n span.end(spanTime(at));\n}\n\n// The detached parent every run span hangs off. Derived from the run rather\n// than a constant so two runs never share a phantom parent; it is never\n// exported itself, only named as a parent.\nfunction detachedSpanId(runId: string): string {\n return sha256Hex(runId).slice(32, 48);\n}\n\nfunction openChildren(run: OpenRun): readonly Span[] {\n return [...(run.brain === null ? [] : [run.brain]), ...run.tools.values()];\n}\n\n// End the run span and whatever it still holds open, with the ending the run\n// reached. A child still open when a run fails or is cancelled did not\n// finish, and says so.\nfunction closeRun(\n run: OpenRun,\n status: AgentEndedStatus,\n at: number,\n exported: (value: unknown) => string,\n): void {\n switch (status.kind) {\n case \"done\":\n // Nothing is still open on a run that finished, unless a settle never\n // reached the events — end it rather than leave it unexported.\n for (const span of openChildren(run)) endSpan(span, at);\n run.span.setAttribute(\n \"langfuse.observation.output\",\n exported(status.output),\n );\n break;\n case \"failed\": {\n const reason = status.failure.reason;\n for (const span of openChildren(run)) {\n failed(\n span,\n `run failed (${reason}) before this settled`,\n \"run_failed\",\n );\n endSpan(span, at);\n }\n run.span.setAttribute(\n \"langfuse.observation.output\",\n exported(status.failure),\n );\n failed(run.span, `run failed: ${reason}`, reason);\n break;\n }\n case \"cancelled\":\n for (const span of openChildren(run)) {\n cancelled(span);\n endSpan(span, at);\n }\n cancelled(run.span);\n break;\n }\n endSpan(run.span, at);\n}\n\nfunction failed(span: Span, message: string, type: string): void {\n span.setStatus({ code: SpanStatusCode.ERROR, message });\n span.setAttributes({\n \"error.type\": type,\n \"langfuse.observation.level\": \"ERROR\",\n \"langfuse.observation.status_message\": message,\n });\n}\n\n// Cancelled is not an error — the caller asked — so the status stays unset\n// and Langfuse reads it as a warning.\nfunction cancelled(span: Span): void {\n span.setAttributes({\n \"langfuse.observation.level\": \"WARNING\",\n \"langfuse.observation.status_message\": \"cancelled\",\n });\n}\n\nfunction errorType(failure: ToolFailure): string {\n return failure._tag ?? \"tool_error\";\n}\n\n// The token usage the provider reported for the turn (#332), as the GenAI\n// usage attributes Langfuse reads into the generation's usage. A turn with no\n// reported usage contributes nothing, rather than a zero that reads as free.\nfunction usageAttributes(usage: TurnUsage | undefined): Attributes {\n if (usage === undefined) return {};\n return {\n \"gen_ai.usage.input_tokens\": usage.inputTokens,\n \"gen_ai.usage.output_tokens\": usage.outputTokens,\n ...(usage.cachedInputTokens === undefined\n ? {}\n : { \"gen_ai.usage.cache_read.input_tokens\": usage.cachedInputTokens }),\n ...(usage.reasoningTokens === undefined\n ? {}\n : { \"gen_ai.usage.reasoning.output_tokens\": usage.reasoningTokens }),\n };\n}\n\nfunction json(value: unknown): string {\n try {\n return JSON.stringify(value) ?? \"null\";\n } catch {\n return JSON.stringify(String(value));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/otel/sha256.ts","../../src/otel/index.ts"],"names":["parent"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * SHA-256 of a string's UTF-8 bytes, as 64 lowercase hex characters (FIPS\n * 180-4). Synchronous and dependency-free, because the trace id has to exist\n * the moment a run's first event arrives, on every host tea runs on — and the\n * one digest every host shares, `crypto.subtle.digest`, is async.\n */\nexport function sha256Hex(message: string): string {\n const bytes = new TextEncoder().encode(message);\n // The message, a 0x80 byte, zero padding, and the bit length as a 64-bit\n // big-endian integer, filling a whole number of 64-byte blocks.\n const blocks = new Uint8Array(Math.ceil((bytes.length + 9) / 64) * 64);\n blocks.set(bytes);\n blocks[bytes.length] = 0x80;\n const view = new DataView(blocks.buffer);\n const bits = bytes.length * 8;\n view.setUint32(blocks.length - 8, Math.floor(bits / 0x100000000));\n view.setUint32(blocks.length - 4, bits >>> 0);\n\n const h = [...H0];\n const w = new Array<number>(64).fill(0);\n for (let offset = 0; offset < blocks.length; offset += 64) {\n for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4);\n for (let i = 16; i < 64; i++) {\n const w15 = w[i - 15] ?? 0;\n const w2 = w[i - 2] ?? 0;\n const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);\n const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);\n w[i] = ((w[i - 16] ?? 0) + s0 + (w[i - 7] ?? 0) + s1) | 0;\n }\n let [a, b, c, d, e, f, g, hh] = h as [\n number,\n number,\n number,\n number,\n number,\n number,\n number,\n number,\n ];\n for (let i = 0; i < 64; i++) {\n const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (hh + s1 + ch + (K[i] ?? 0) + (w[i] ?? 0)) | 0;\n const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (s0 + maj) | 0;\n hh = g;\n g = f;\n f = e;\n e = (d + t1) | 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) | 0;\n }\n const round = [a, b, c, d, e, f, g, hh];\n for (let i = 0; i < 8; i++) h[i] = ((h[i] ?? 0) + (round[i] ?? 0)) | 0;\n }\n return h.map((word) => (word >>> 0).toString(16).padStart(8, \"0\")).join(\"\");\n}\n\nfunction rotr(x: number, n: number): number {\n return (x >>> n) | (x << (32 - n));\n}\n\nconst H0: readonly number[] = [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n 0x1f83d9ab, 0x5be0cd19,\n];\n\nconst K: readonly number[] = [\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,\n 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\n 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\n 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\n 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n];\n","/**\n * @packageDocumentation\n * @demlik/tea/otel — an agent run as OpenTelemetry spans.\n *\n * `traceAgent(runtime, { tracer })` listens to an agent runtime's semantic\n * events and writes one span tree per run: a run span, a brain-call span per\n * turn and a tool span per tool call, the brain and tool spans children of the\n * run. Nothing here touches the reducer: the spans are a projection of the\n * `AgentEvent` stream, the way `transcript` is.\n *\n * The attributes follow the OpenTelemetry GenAI semantic conventions\n * (`gen_ai.*`), plus Langfuse's observation attributes (`langfuse.*`), so\n * Langfuse renders the run as an agent with generations and tool calls without\n * tea depending on any Langfuse package. Any OTel backend reads the same spans.\n *\n * `@opentelemetry/api` is an optional peer. Only this entry imports it.\n *\n * ## One trace across a kill\n *\n * The trace id is derived from the run's `runId` — the SHA-256 of it, cut to\n * 32 hex characters, which is exactly Langfuse's `createTraceId(runId)` — and\n * the run span is parented to a detached span context carrying it. The id\n * lives on the durable Model, so a process that resumes the run from its\n * `Store` writes into the same trace as the one that was killed. Spans the\n * killed process never ended are never exported; OpenTelemetry exports a span\n * when it ends. What it did end is in the trace beside the resumed leg.\n * This holds only on the default path: a run started under a host's `parent`\n * is in the host's trace, and keeping it one trace across a kill is the host's.\n *\n * ## Time\n *\n * Every span starts and ends at the `at` of the event that opened or closed\n * it, and `at` is read as **epoch milliseconds** — the runtime's `clock`\n * contract. It reaches OpenTelemetry as an exact `HrTime`, never a bare\n * number, so a small clock value (a logical or test clock) stays the instant\n * it names instead of being rebased onto process start.\n */\n\nimport {\n type Attributes,\n type Context,\n type HrTime,\n ROOT_CONTEXT,\n type Span,\n SpanKind,\n type SpanOptions,\n SpanStatusCode,\n TraceFlags,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport {\n AGENT_EVENT_TYPES,\n type AgentEndedStatus,\n type AgentEvent,\n type ToolFailure,\n type TurnUsage,\n} from \"../agent/index\";\nimport { sha256Hex } from \"./sha256\";\n\n/**\n * A mask over one exported input or output — the same shape as\n * `LangfuseSpanProcessor`'s `mask`, so one function serves both. `data` is the\n * value already serialized to JSON; return what should be exported instead.\n */\nexport type SpanMask = (params: { readonly data: unknown }) => unknown;\n\n/** How {@link traceAgent} writes spans. */\nexport interface TraceAgentOptions {\n /** The tracer the spans are started on — `provider.getTracer(\"…\")`. */\n readonly tracer: Tracer;\n /**\n * Applied to every exported input and output. Omit → {@link maskBase64DataUris},\n * which keeps a screenshot a tool returned from being exported as megabytes\n * of base64.\n */\n readonly mask?: SpanMask;\n /**\n * The agent's name, on `gen_ai.agent.name`, the run span's name and\n * Langfuse's trace name. Omit → `\"agent\"`.\n */\n readonly name?: string;\n /**\n * The trace id a run writes into, from its `runId`. Omit → {@link runTraceId}.\n * Must return 32 lowercase hex characters, not all zero. Not called when\n * {@link TraceAgentOptions.parent | parent} is given.\n */\n readonly traceIdOf?: (runId: string) => string;\n /**\n * The context every run span starts under — a span the host already has\n * open, as `trace.setSpan(context.active(), span)`. The run then lands in the\n * host's trace as that span's child, and its brain and tool spans follow.\n *\n * `parent` wins over `traceIdOf`: when both are given, the run is in the\n * parent's trace and `traceIdOf` is never called. With a `parent`, the run's\n * trace is the host's, so keeping one trace across a kill is the host's job\n * too — a resumed run's spans go wherever the resuming host's `parent`\n * points. Omit → each run hangs off a detached parent in the trace\n * `traceIdOf(runId)` names, which a resumed process derives again.\n */\n readonly parent?: Context;\n}\n\n/** Anything that delivers an agent's events by type — a runtime built with `events: agentEvents()`. */\nexport interface AgentEventSource<R> {\n on<K extends AgentEvent<R>[\"type\"]>(\n type: K,\n handler: (event: Extract<AgentEvent<R>, { type: K }>) => void,\n ): () => void;\n}\n\n/**\n * Trace an agent runtime into OpenTelemetry. Subscribes to every\n * `AgentEvent` type on `runtime` and returns a cleanup that detaches the\n * subscriptions and ends any span still open, at the `at` of the last event\n * its run folded.\n *\n * The runtime must project events — built with\n * `run(machine, { events: agentEvents() })`. For `defineAgent`, which takes an\n * `onEvent` listener instead of handing out its runtime, wire\n * {@link agentSpans}'s `onEvent` there.\n *\n * Each span is timed by its events' `at`, read as epoch milliseconds.\n *\n * @example\n * ```ts\n * import { type AgentEventSource, traceAgent } from \"@demlik/tea/otel\";\n * import type { TracerProvider } from \"@opentelemetry/api\";\n *\n * declare const provider: TracerProvider;\n * // Built with `run(machine, { ...wired, ctx, events: agentEvents() }).ready`.\n * declare const runtime: AgentEventSource<string>;\n *\n * const stop = traceAgent(runtime, { tracer: provider.getTracer(\"audit\") });\n * ```\n */\nexport function traceAgent<R>(\n runtime: AgentEventSource<R>,\n opts: TraceAgentOptions,\n): () => void {\n const spans = agentSpans<R>(opts);\n const offs = AGENT_EVENT_TYPES.map((type) => runtime.on(type, spans.onEvent));\n return () => {\n for (const off of offs) off();\n spans.end();\n };\n}\n\n/** The span writer {@link traceAgent} subscribes, for wiring to an `onEvent` listener. */\nexport interface AgentSpans<R> {\n /** Fold one event into the span tree. */\n readonly onEvent: (event: AgentEvent<R>) => void;\n /**\n * End every span still open — the leg this process ran, when it stops\n * before its run does. Each carries `tea.run.detached: true`.\n *\n * With `at`, every open span ends at `at`, used as given. Without it, each\n * run's spans end at the `at` of the last event that run folded — its own\n * clock, never wall time.\n */\n readonly end: (at?: number) => void;\n}\n\n/**\n * The span writer behind {@link traceAgent}, as a listener. Use it where the\n * events arrive through a callback rather than a runtime:\n *\n * @example\n * ```ts\n * import { defineAgent } from \"@demlik/tea/agent\";\n * import { agentSpans } from \"@demlik/tea/otel\";\n * import { trace } from \"@opentelemetry/api\";\n *\n * const agent = defineAgent({\n * model: async () => ({ content: \"done\", toolCalls: [] }),\n * tools: [],\n * instructions: \"Summarise the page.\",\n * });\n *\n * const spans = agentSpans({ tracer: trace.getTracer(\"audit\") });\n * await agent.run(\"https://example.com\", { onEvent: spans.onEvent });\n * ```\n */\nexport function agentSpans<R>(opts: TraceAgentOptions): AgentSpans<R> {\n const { tracer } = opts;\n const mask = opts.mask ?? maskBase64DataUris;\n const name = opts.name ?? \"agent\";\n const traceIdOf = opts.traceIdOf ?? runTraceId;\n const { parent } = opts;\n const parentOf = (runId: string): Context =>\n parent ?? detachedParent(traceIdOf(runId), runId);\n const runs = new Map<string, OpenRun>();\n\n const exported = (value: unknown): string => {\n const masked = mask({ data: json(value) });\n return typeof masked === \"string\" ? masked : json(masked);\n };\n\n const openRun = (runId: string, at: number): OpenRun => {\n const known = runs.get(runId);\n if (known !== undefined) return known;\n const parent = parentOf(runId);\n const span = startSpan(\n tracer,\n `invoke_agent ${name}`,\n at,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n \"gen_ai.operation.name\": \"invoke_agent\",\n \"gen_ai.agent.name\": name,\n \"gen_ai.conversation.id\": runId,\n \"langfuse.observation.type\": \"agent\",\n \"langfuse.trace.name\": name,\n \"tea.run.id\": runId,\n },\n },\n parent,\n );\n const opened: OpenRun = {\n span,\n context: trace.setSpan(parent, span),\n brain: null,\n tools: new Map(),\n lastAt: at,\n };\n runs.set(runId, opened);\n return opened;\n };\n\n const onEvent = (event: AgentEvent<R>): void => {\n const run = openRun(event.runId, event.at);\n run.lastAt = event.at;\n switch (event.type) {\n case \"BrainStarted\": {\n // A boot in the same process re-issues the call already open.\n if (run.brain !== null) return;\n const model = event.model;\n run.brain = startSpan(\n tracer,\n model === null ? \"chat\" : `chat ${model}`,\n event.at,\n {\n kind: SpanKind.CLIENT,\n attributes: {\n \"gen_ai.operation.name\": \"chat\",\n ...(model === null\n ? {}\n : {\n \"gen_ai.request.model\": model,\n \"langfuse.observation.model.name\": model,\n }),\n \"langfuse.observation.type\": \"generation\",\n \"langfuse.observation.input\": exported(event.payload),\n \"tea.agent.turn\": event.turn,\n \"tea.agent.purpose\": event.purpose,\n },\n },\n run.context,\n );\n return;\n }\n case \"TurnSettled\": {\n const brain =\n run.brain ??\n startSpan(\n tracer,\n \"chat\",\n event.at,\n { kind: SpanKind.CLIENT },\n run.context,\n );\n brain.setAttributes({\n \"langfuse.observation.output\": exported(event.turn),\n ...usageAttributes(event.usage),\n });\n endSpan(brain, event.at);\n run.brain = null;\n return;\n }\n case \"ToolStarted\": {\n if (run.tools.has(event.callId)) return;\n run.tools.set(\n event.callId,\n startSpan(\n tracer,\n `execute_tool ${event.name}`,\n event.at,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n \"gen_ai.operation.name\": \"execute_tool\",\n \"gen_ai.tool.name\": event.name,\n \"gen_ai.tool.call.id\": event.callId,\n \"langfuse.observation.type\": \"tool\",\n \"langfuse.observation.input\": exported(event.args),\n },\n },\n run.context,\n ),\n );\n return;\n }\n case \"ToolSettled\": {\n const span = run.tools.get(event.callId);\n if (span === undefined) return;\n span.setAttribute(\n \"langfuse.observation.output\",\n exported(event.result),\n );\n endSpan(span, event.at);\n run.tools.delete(event.callId);\n return;\n }\n case \"ToolFailed\": {\n const span = run.tools.get(event.callId);\n if (span === undefined) return;\n span.setAttribute(\n \"langfuse.observation.output\",\n exported(event.failure),\n );\n failed(span, event.failure.reason, errorType(event.failure));\n endSpan(span, event.at);\n run.tools.delete(event.callId);\n return;\n }\n case \"RunDone\":\n closeRun(run, event.status, event.at, exported);\n runs.delete(event.runId);\n return;\n }\n };\n\n const end = (at?: number): void => {\n for (const run of runs.values()) {\n const endAt = at ?? run.lastAt;\n for (const span of [...openChildren(run), run.span]) {\n span.setAttribute(\"tea.run.detached\", true);\n endSpan(span, endAt);\n }\n }\n runs.clear();\n };\n\n return { onEvent, end };\n}\n\n/**\n * The default {@link SpanMask}: every base64 `data:` URI in a string becomes\n * `[base64 data URI stripped before export]`. Anything that is not a string\n * passes through untouched.\n */\nexport const maskBase64DataUris: SpanMask = ({ data }) =>\n typeof data === \"string\"\n ? data.replace(BASE64_DATA_URI, STRIPPED_DATA_URI)\n : data;\n\n/**\n * The trace id a run's spans are written into: the SHA-256 of `runId` as\n * lowercase hex, cut to 32 characters. It equals Langfuse's\n * `createTraceId(runId)`, so a trace can be looked up by the run it traced.\n */\nexport function runTraceId(runId: string): string {\n return sha256Hex(runId).slice(0, 32);\n}\n\n// ---------------------------------------------------------------------------\n\nconst BASE64_DATA_URI = /data:[^;]+;base64,[A-Za-z0-9+/=]+/g;\nconst STRIPPED_DATA_URI = \"[base64 data URI stripped before export]\";\n\ninterface OpenRun {\n readonly span: Span;\n readonly context: Context;\n brain: Span | null;\n readonly tools: Map<string, Span>;\n /** The `at` of the last event this run folded — where a detach ends it. */\n lastAt: number;\n}\n\n// The one place a run's clock becomes OpenTelemetry time. `at` is epoch\n// milliseconds. A bare number is ambiguous to the SDK: one no bigger than\n// `performance.now()` is read as milliseconds since process start and rebased\n// onto `performance.timeOrigin`, so a small clock value would silently move by\n// however long the process has been up (#367). An `HrTime` is taken as-is.\nfunction spanTime(at: number): HrTime {\n const seconds = Math.floor(at / 1_000);\n const nanos = Math.round((at - seconds * 1_000) * 1_000_000);\n return nanos === 1_000_000_000 ? [seconds + 1, 0] : [seconds, nanos];\n}\n\n// A span opened at `at`. The options carry no `startTime`, so no call site\n// can hand OpenTelemetry a raw clock value.\nfunction startSpan(\n tracer: Tracer,\n spanName: string,\n at: number,\n options: Omit<SpanOptions, \"startTime\">,\n context: Context,\n): Span {\n return tracer.startSpan(\n spanName,\n { ...options, startTime: spanTime(at) },\n context,\n );\n}\n\n// The one place a span ends. Every `Span#end` in this module goes through it,\n// which `span-end.test.ts` holds, so no end reaches OpenTelemetry off the\n// run's clock.\nfunction endSpan(span: Span, at: number): void {\n span.end(spanTime(at));\n}\n\n// The detached parent a run span hangs off when the host gives none. Its span\n// id is derived from the run rather than a constant so two runs never share a\n// phantom parent; it is never exported itself, only named as a parent.\nfunction detachedParent(traceId: string, runId: string): Context {\n return trace.setSpanContext(ROOT_CONTEXT, {\n traceId,\n spanId: sha256Hex(runId).slice(32, 48),\n traceFlags: TraceFlags.SAMPLED,\n isRemote: true,\n });\n}\n\nfunction openChildren(run: OpenRun): readonly Span[] {\n return [...(run.brain === null ? [] : [run.brain]), ...run.tools.values()];\n}\n\n// End the run span and whatever it still holds open, with the ending the run\n// reached. A child still open when a run fails or is cancelled did not\n// finish, and says so.\nfunction closeRun(\n run: OpenRun,\n status: AgentEndedStatus,\n at: number,\n exported: (value: unknown) => string,\n): void {\n switch (status.kind) {\n case \"done\":\n // Nothing is still open on a run that finished, unless a settle never\n // reached the events — end it rather than leave it unexported.\n for (const span of openChildren(run)) endSpan(span, at);\n run.span.setAttribute(\n \"langfuse.observation.output\",\n exported(status.output),\n );\n break;\n case \"failed\": {\n const reason = status.failure.reason;\n for (const span of openChildren(run)) {\n failed(\n span,\n `run failed (${reason}) before this settled`,\n \"run_failed\",\n );\n endSpan(span, at);\n }\n run.span.setAttribute(\n \"langfuse.observation.output\",\n exported(status.failure),\n );\n failed(run.span, `run failed: ${reason}`, reason);\n break;\n }\n case \"cancelled\":\n for (const span of openChildren(run)) {\n cancelled(span);\n endSpan(span, at);\n }\n cancelled(run.span);\n break;\n }\n endSpan(run.span, at);\n}\n\nfunction failed(span: Span, message: string, type: string): void {\n span.setStatus({ code: SpanStatusCode.ERROR, message });\n span.setAttributes({\n \"error.type\": type,\n \"langfuse.observation.level\": \"ERROR\",\n \"langfuse.observation.status_message\": message,\n });\n}\n\n// Cancelled is not an error — the caller asked — so the status stays unset\n// and Langfuse reads it as a warning.\nfunction cancelled(span: Span): void {\n span.setAttributes({\n \"langfuse.observation.level\": \"WARNING\",\n \"langfuse.observation.status_message\": \"cancelled\",\n });\n}\n\nfunction errorType(failure: ToolFailure): string {\n return failure._tag ?? \"tool_error\";\n}\n\n// The token usage the provider reported for the turn (#332), as the GenAI\n// usage attributes Langfuse reads into the generation's usage. A turn with no\n// reported usage contributes nothing, rather than a zero that reads as free.\nfunction usageAttributes(usage: TurnUsage | undefined): Attributes {\n if (usage === undefined) return {};\n return {\n \"gen_ai.usage.input_tokens\": usage.inputTokens,\n \"gen_ai.usage.output_tokens\": usage.outputTokens,\n ...(usage.cachedInputTokens === undefined\n ? {}\n : { \"gen_ai.usage.cache_read.input_tokens\": usage.cachedInputTokens }),\n ...(usage.reasoningTokens === undefined\n ? {}\n : { \"gen_ai.usage.reasoning.output_tokens\": usage.reasoningTokens }),\n };\n}\n\nfunction json(value: unknown): string {\n try {\n return JSON.stringify(value) ?? \"null\";\n } catch {\n return JSON.stringify(String(value));\n }\n}\n"]}
|