@lovelaces-io/storyteller 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Storyteller (`@lovelaces-io/storyteller`) is a lightweight TypeScript logging library with zero production dependencies. You report beats of work as they happen; it keeps them and emits them as one structured record when the work finishes. Records go to pluggable audiences.
6
6
 
7
- Version: 0.3.0 (pre-1.0, API may change). Dual output: ESM + CJS.
7
+ Version: 0.3.1 (pre-1.0, API may change). Dual output: ESM + CJS.
8
8
 
9
9
  ## Narrate your work
10
10
 
package/dist/index.cjs CHANGED
@@ -1209,10 +1209,9 @@ function dbAudience(insert) {
1209
1209
  return {
1210
1210
  name: "db",
1211
1211
  hears: ["story"],
1212
- accepts: (emission) => emission.kind === "story" && (emission.level === "Warning" || emission.level === "Error"),
1213
- hear: async (emission) => {
1214
- if (emission.kind !== "story") return;
1215
- await insert(emission);
1212
+ accepts: (event) => event.level === "Warning" || event.level === "Error",
1213
+ hear: async (event) => {
1214
+ await insert(event);
1216
1215
  }
1217
1216
  };
1218
1217
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/utils.ts","../src/audiences/consoleAudience.ts","../src/normalize.ts","../src/audiences/ndjsonAudience.ts","../src/formatting.ts","../src/storyteller.ts","../src/useStoryteller.ts","../src/audiences/dbAudience.ts","../src/report/writeStoryReport.ts"],"sourcesContent":["export * from \"./storyteller\";\nexport * from \"./normalize\";\nexport * from \"./formatting\";\nexport * from \"./useStoryteller\";\nexport * from \"./audiences/consoleAudience\";\nexport * from \"./audiences/dbAudience\";\nexport * from \"./audiences/ndjsonAudience\";\nexport * from \"./environment\";\nexport * from \"./report/writeStoryReport\";\nexport { ANSI, getLevelColor, formatOrigin, summarizeContext } from \"./utils\";\n","import type { StoryLevel } from \"./storyteller\";\n\n/**\n * Which default audience a storyteller registers.\n *\n * - `text` — colorized console output for a person watching\n * - `ndjson` — one JSON object per line for a program reading\n */\nexport type OutputFormat = \"text\" | \"ndjson\";\n\n/** Rank levels so a minimum threshold can be compared numerically */\nconst LEVEL_RANK: Record<StoryLevel, number> = {\n Information: 0,\n Warning: 1,\n Error: 2,\n};\n\n/** Accepted spellings for a level, in env vars and options alike */\nconst LEVEL_ALIASES: Record<string, StoryLevel> = {\n info: \"Information\",\n information: \"Information\",\n tell: \"Information\",\n warn: \"Warning\",\n warning: \"Warning\",\n oops: \"Error\",\n error: \"Error\",\n};\n\n/**\n * A level written any of the ways people and agents actually write it.\n * `report(\"...\", { level: \"warn\" })` should not be a type error.\n */\nexport type LevelInput =\n | StoryLevel\n | \"info\"\n | \"information\"\n | \"warn\"\n | \"warning\"\n | \"oops\"\n | \"error\";\n\n/**\n * Resolve any accepted level spelling to a stored level label.\n *\n * @param input - A level in any accepted spelling\n * @returns The canonical StoryLevel, defaulting to Information\n */\nexport function toStoryLevel(input?: LevelInput): StoryLevel {\n if (!input) return \"Information\";\n return LEVEL_ALIASES[String(input).toLowerCase()] ?? \"Information\";\n}\n\n/**\n * Read an environment variable, tolerating runtimes that have no environment at all.\n *\n * @param name - Variable name\n * @returns The trimmed value, or undefined when unset or unavailable\n */\nexport function readEnvironmentValue(name: string): string | undefined {\n try {\n const runtime = globalThis as {\n process?: { env?: Record<string, string | undefined> };\n };\n const value = runtime.process?.env?.[name];\n return typeof value === \"string\" && value.length ? value.trim() : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve the minimum level to deliver, from an explicit option then `STORYTELLER_LEVEL`.\n *\n * @param requested - Explicit level, if the caller set one\n * @returns The threshold level, defaulting to Information (deliver everything)\n */\nexport function resolveMinimumLevel(requested?: StoryLevel | string): StoryLevel {\n const value = requested ?? readEnvironmentValue(\"STORYTELLER_LEVEL\");\n if (!value) return \"Information\";\n return LEVEL_ALIASES[String(value).toLowerCase()] ?? \"Information\";\n}\n\n/**\n * Check whether a level clears the configured minimum.\n *\n * @param level - The emission's level\n * @param minimum - The configured threshold\n */\nexport function meetsLevel(level: StoryLevel, minimum: StoryLevel): boolean {\n return LEVEL_RANK[level] >= LEVEL_RANK[minimum];\n}\n\n/**\n * Resolve which default audience to register, from an explicit option then\n * `STORYTELLER_FORMAT`.\n *\n * Deliberately not inferred from whether stdout is a TTY: output that silently\n * changes shape when a process is piped is a debugging afternoon nobody asked for.\n *\n * @param requested - Explicit format, if the caller set one\n * @returns The format, defaulting to text\n */\nexport function resolveOutputFormat(requested?: OutputFormat): OutputFormat {\n const value = requested ?? readEnvironmentValue(\"STORYTELLER_FORMAT\");\n return value === \"ndjson\" ? \"ndjson\" : \"text\";\n}\n\n/**\n * Resolve whether to colorize, from an explicit option then `STORYTELLER_COLOR`.\n *\n * @param requested - Explicit choice, if the caller set one\n * @returns Whether colors should be used, defaulting to true\n */\nexport function resolveColors(requested?: boolean): boolean {\n if (requested !== undefined) return requested;\n\n const value = readEnvironmentValue(\"STORYTELLER_COLOR\");\n if (value === undefined) return true;\n\n return !(value === \"0\" || value.toLowerCase() === \"false\");\n}\n","import type { StoryError, StoryLevel, StoryOrigin } from \"./storyteller\";\nimport type { JsonValue } from \"./normalize\";\n\n/** ANSI escape codes for terminal colorization */\nexport const ANSI = {\n reset: \"\\x1b[0m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n red: \"\\x1b[38;2;250;128;114m\",\n grayLight: \"\\x1b[37m\",\n grayDark: \"\\x1b[90m\",\n};\n\n/** Map a story level to its corresponding ANSI terminal color */\nexport function getLevelColor(level: StoryLevel): string {\n if (level === \"Information\") return ANSI.green;\n if (level === \"Warning\") return ANSI.yellow;\n return ANSI.red;\n}\n\n/** Format an origin context into a human-readable path like \"app / page / component\" */\nexport function formatOrigin(origin?: StoryOrigin): string | undefined {\n if (!origin?.where) return;\n if (typeof origin.where === \"string\") return origin.where;\n if (typeof origin.where !== \"object\" || Array.isArray(origin.where)) {\n return String(origin.where);\n }\n const whereRecord: Record<string, JsonValue> = origin.where;\n\n // Show well-known keys first in a natural order, then any additional fields\n const priorityKeys = [\"app\", \"service\", \"page\", \"component\"];\n const priorityParts = priorityKeys\n .filter((key) => whereRecord[key] != null)\n .map((key) => String(whereRecord[key]));\n const extraParts = Object.entries(whereRecord)\n .filter(([key, value]) => !priorityKeys.includes(key) && value != null)\n .map(([_, value]) => String(value));\n\n const parts = [...priorityParts, ...extraParts];\n return parts.length ? parts.join(\" / \") : undefined;\n}\n\n/** Colorize JSON output, dimming the notes section for visual hierarchy */\nexport function colorizeJsonSections(\n json: string,\n colors: { base: string; notes: string; reset: string }\n): string[] {\n const lines = json.split(\"\\n\");\n let insideNotes = false;\n let bracketDepth = 0;\n\n return lines.map((line) => {\n if (!insideNotes && line.includes('\"notes\": [')) {\n insideNotes = true;\n bracketDepth = countBrackets(line);\n return `${colors.notes}${line}${colors.reset}`;\n }\n\n if (insideNotes) {\n const colored = `${colors.notes}${line}${colors.reset}`;\n bracketDepth += countBrackets(line);\n if (bracketDepth <= 0) insideNotes = false;\n return colored;\n }\n\n return `${colors.base}${line}${colors.reset}`;\n });\n}\n\n/** Count the net bracket depth change in a line (opening brackets minus closing brackets) */\nexport function countBrackets(line: string): number {\n const openCount = (line.match(/\\[/g) || []).length;\n const closeCount = (line.match(/\\]/g) || []).length;\n return openCount - closeCount;\n}\n\n/** How much of a single note's context to show on one console line */\nconst CONTEXT_LINE_LIMIT = 120;\n\n/**\n * Condense a note's context into a short inline summary for one-line output.\n * The full values are always available on the story record and the NDJSON stream,\n * so this can afford to be lossy in favor of staying readable.\n *\n * @param note - The note's context fields\n * @returns A brace-wrapped summary, or undefined when there is no context\n */\nexport function summarizeContext(note: {\n note?: string;\n what?: JsonValue;\n where?: JsonValue;\n error?: StoryError;\n}): string | undefined {\n const parts: string[] = [];\n\n appendContextParts(parts, note.what);\n appendContextParts(parts, note.where);\n\n if (note.error) {\n const errorLine = [note.error.name, note.error.message].filter(Boolean).join(\": \");\n // Skip it when the note text was derived from this error — repeating it reads as noise\n if (errorLine && errorLine !== note.note) parts.push(errorLine);\n }\n\n if (!parts.length) return undefined;\n\n const joined = parts.join(\" \");\n const text = joined.length > CONTEXT_LINE_LIMIT\n ? `${joined.slice(0, CONTEXT_LINE_LIMIT)}…`\n : joined;\n\n return `{${text}}`;\n}\n\n/** Flatten one context value into `key=value` fragments */\nfunction appendContextParts(parts: string[], value?: JsonValue) {\n if (value == null) return;\n\n if (typeof value !== \"object\") {\n parts.push(String(value));\n return;\n }\n\n if (Array.isArray(value)) {\n parts.push(`[${value.length}]`);\n return;\n }\n\n for (const [key, entry] of Object.entries(value)) {\n if (entry == null) continue;\n if (key.startsWith(\"@\")) continue;\n parts.push(`${key}=${typeof entry === \"object\" ? summarizeNested(entry) : String(entry)}`);\n }\n}\n\n/** Render a nested context value as a size hint rather than expanding it inline */\nfunction summarizeNested(value: JsonValue): string {\n if (Array.isArray(value)) return `[${value.length}]`;\n if (value && typeof value === \"object\") return `{${Object.keys(value).length}}`;\n return String(value);\n}\n","import type { AudienceMember, Emission, NoteEmission, StoryEvent, StoryLevel } from \"../storyteller\";\nimport { resolveColors } from \"../environment\";\nimport { ANSI, getLevelColor, formatOrigin, summarizeContext } from \"../utils\";\n\n/** Short level labels for compact live output */\nconst LEVEL_LABELS: Record<StoryLevel, string> = {\n Information: \"info\",\n Warning: \"warn\",\n Error: \"oops\",\n};\n\n/** Browser console styles by level, used for the grouped story header */\nconst LEVEL_STYLES: Record<StoryLevel, string> = {\n Information: \"color:#16a34a;font-weight:600\",\n Warning: \"color:#f59e0b;font-weight:600\",\n Error: \"color:#dc2626;font-weight:600\",\n};\n\nexport type ConsoleAudienceOptions = {\n /** Set false to strip ANSI colors from live note lines. Defaults to `STORYTELLER_COLOR`. */\n colors?: boolean;\n};\n\n/**\n * Create an audience that prints to the console: one compact line per note when\n * narration is live, and a color-coded grouped record when a story is told.\n *\n * Registered by default on every Storyteller instance. It listens for notes as well\n * as stories, so switching a storyteller to live narration shows something immediately\n * without registering anything extra.\n *\n * @param options - Rendering options for live note lines\n *\n * @example\n * ```ts\n * // Already included — but you can re-add after removing:\n * story.audience.add(consoleAudience());\n * ```\n */\nexport function consoleAudience(options: ConsoleAudienceOptions = {}): AudienceMember {\n const colors = resolveColors(options.colors);\n\n return {\n name: \"console\",\n hears: [\"note\", \"story\"],\n hear: (emission: Emission) => {\n if (emission.kind === \"note\") {\n printNote(emission, colors);\n return;\n }\n printStory(emission);\n },\n };\n}\n\n/**\n * Print a single beat as one line. Deliberately compact — at one emission per note,\n * a collapsed group and a pretty-printed payload per line is unreadable.\n */\nfunction printNote(note: NoteEmission, colors: boolean) {\n const time = readClockTime(note.timestamp);\n const label = LEVEL_LABELS[note.level];\n const origin = formatOrigin(note.origin);\n const context = summarizeContext(note);\n\n const head = colors\n ? `${getLevelColor(note.level)}${label}${ANSI.reset}`\n : label;\n\n const line = [\n colors ? `${ANSI.grayDark}${time}${ANSI.reset}` : time,\n head,\n origin ? (colors ? `${ANSI.grayDark}${origin}${ANSI.reset}` : origin) : undefined,\n note.note,\n context ? (colors ? `${ANSI.grayDark}${context}${ANSI.reset}` : context) : undefined,\n ]\n .filter(Boolean)\n .join(\" \");\n\n if (note.level === \"Information\") {\n console.log(line);\n } else if (note.level === \"Warning\") {\n console.warn(line);\n } else {\n console.error(line);\n }\n}\n\n/** Print a told story as a collapsed group with the full record inside */\nfunction printStory(event: StoryEvent) {\n const prefix = \"Storyteller\";\n const header = `${prefix}: ${event.title}`;\n\n console.groupCollapsed(`%c${header}`, LEVEL_STYLES[event.level]);\n\n const payload = JSON.stringify(event, null, 2);\n\n if (event.level === \"Information\") {\n console.log(header, payload);\n } else if (event.level === \"Warning\") {\n console.warn(header, payload);\n } else {\n console.error(header, payload);\n }\n\n console.groupEnd();\n}\n\n/** Extract HH:MM:SS from an ISO timestamp without paying for Intl on every note */\nfunction readClockTime(timestamp: string): string {\n const timePart = timestamp.slice(11, 19);\n return timePart.length === 8 ? timePart : timestamp;\n}\n","import type { StoryError } from \"./storyteller\";\n\n/** A value that survives JSON.stringify with no loss and no throwing */\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport type NormalizeOptions = {\n /** How many levels deep to descend before replacing the value with a truncation marker */\n maxDepth?: number;\n /** How many array entries to keep before truncating */\n maxArrayLength?: number;\n /** How many object properties to keep before truncating */\n maxProperties?: number;\n /** How many characters of a string to keep before truncating */\n maxStringLength?: number;\n /** Property names whose values are replaced with the redaction marker */\n redactKeys?: string[];\n /** Set false to keep secret-shaped values as-is */\n redact?: boolean;\n};\n\n/** Marker written in place of a value that matched a redacted key name */\nexport const REDACTED = \"[redacted]\";\n\n/**\n * Property names whose values are replaced with {@link REDACTED}.\n * Matching ignores case and separators, so `apiKey`, `api_key` and `API-KEY` all match.\n */\nexport const DEFAULT_REDACT_KEYS = [\n \"password\",\n \"passphrase\",\n \"token\",\n \"secret\",\n \"apiKey\",\n \"accessKey\",\n \"authorization\",\n \"auth\",\n \"cookie\",\n \"sessionId\",\n \"privateKey\",\n \"clientSecret\",\n \"refreshToken\",\n];\n\nconst DEFAULT_MAX_DEPTH = 6;\nconst DEFAULT_MAX_ARRAY_LENGTH = 100;\nconst DEFAULT_MAX_PROPERTIES = 100;\nconst DEFAULT_MAX_STRING_LENGTH = 8000;\n\n/** How far to follow an error's `cause` chain before stopping */\nconst MAX_CAUSE_DEPTH = 5;\n/** How many bytes of a binary value to include as a readable preview */\nconst BINARY_PREVIEW_BYTES = 16;\n\ntype ResolvedOptions = {\n maxDepth: number;\n maxArrayLength: number;\n maxProperties: number;\n maxStringLength: number;\n redactKeys: Set<string>;\n redact: boolean;\n};\n\n/**\n * Convert any value into a JSON-safe structure suitable for a story record.\n *\n * Handles the shapes real code actually holds — errors, dates, maps, sets, class\n * instances, binary buffers, circular references, throwing getters — and never throws,\n * so a hostile object logged by a caller cannot break the delivery pipeline.\n *\n * Data dropped for size is replaced with an explicit `@truncated` marker rather than\n * disappearing silently, so a consumer can tell the difference between \"this was empty\"\n * and \"this was too big\".\n *\n * @param input - Any value\n * @param options - Depth, size and redaction limits\n * @returns A value that JSON.stringify can always serialize\n *\n * @example\n * ```ts\n * normalizeValue({ user: new Map([[\"id\", 1]]), apiKey: \"sk-live-abc\" });\n * // { user: { \"@type\": \"Map\", entries: { id: 1 } }, apiKey: \"[redacted]\" }\n * ```\n */\nexport function normalizeValue(\n input: unknown,\n options: NormalizeOptions = {}\n): JsonValue {\n const resolved: ResolvedOptions = {\n maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,\n maxArrayLength: options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH,\n maxProperties: options.maxProperties ?? DEFAULT_MAX_PROPERTIES,\n maxStringLength: options.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH,\n redactKeys: new Set(\n (options.redactKeys ?? DEFAULT_REDACT_KEYS).map(normalizeKeyForMatching)\n ),\n redact: options.redact ?? true,\n };\n\n try {\n return normalizeUnknown(input, resolved, 0, \"$\", new Map());\n } catch (failure) {\n // The normalizer must never throw into the delivery pipeline\n return `[Unreadable: ${describeFailure(failure)}]`;\n }\n}\n\n/**\n * Convert an unknown thrown value into a serializable StoryError,\n * following the `cause` chain and collecting AggregateError members.\n *\n * @param rawError - Any thrown or rejected value\n * @param options - Depth, size and redaction limits applied to attached data\n * @returns A StoryError safe to store and serialize\n */\nexport function normalizeError(\n rawError: unknown,\n options: NormalizeOptions = {}\n): StoryError {\n const resolved: ResolvedOptions = {\n maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,\n maxArrayLength: options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH,\n maxProperties: options.maxProperties ?? DEFAULT_MAX_PROPERTIES,\n maxStringLength: options.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH,\n redactKeys: new Set(\n (options.redactKeys ?? DEFAULT_REDACT_KEYS).map(normalizeKeyForMatching)\n ),\n redact: options.redact ?? true,\n };\n\n return normalizeErrorInternal(rawError, resolved, 0);\n}\n\n/** Build a StoryError from any thrown value, bounded by the cause-chain depth */\nfunction normalizeErrorInternal(\n rawError: unknown,\n options: ResolvedOptions,\n causeDepth: number\n): StoryError {\n if (!(rawError instanceof Error)) {\n if (isPlainRecord(rawError)) {\n // Error-shaped objects from across a serialization boundary are common\n const record = rawError as Record<string, unknown>;\n const message = typeof record[\"message\"] === \"string\" ? record[\"message\"] : undefined;\n const name = typeof record[\"name\"] === \"string\" ? record[\"name\"] : undefined;\n if (message !== undefined || name !== undefined) {\n return {\n ...(name !== undefined ? { name } : {}),\n ...(message !== undefined ? { message } : {}),\n };\n }\n }\n return { message: safeStringify(rawError, options.maxStringLength) };\n }\n\n const normalized: StoryError = {\n name: rawError.name,\n message: rawError.message,\n };\n\n if (rawError.stack !== undefined) {\n normalized.stack = truncateString(rawError.stack, options.maxStringLength);\n }\n\n const cause = (rawError as { cause?: unknown }).cause;\n if (cause !== undefined) {\n if (causeDepth >= MAX_CAUSE_DEPTH) {\n normalized.cause = { \"@truncated\": { kind: \"causeChain\" } };\n } else if (cause instanceof Error) {\n normalized.cause = normalizeErrorInternal(cause, options, causeDepth + 1);\n } else {\n normalized.cause = normalizeUnknown(cause, options, 0, \"$.cause\", new Map());\n }\n }\n\n const aggregated = (rawError as { errors?: unknown }).errors;\n if (Array.isArray(aggregated)) {\n normalized.errors = aggregated\n .slice(0, options.maxArrayLength)\n .map((member) => normalizeErrorInternal(member, options, causeDepth + 1));\n }\n\n return normalized;\n}\n\n/** Recursively convert a value, tracking ancestors so cycles become readable markers */\nfunction normalizeUnknown(\n value: unknown,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n if (value === null) return null;\n\n const valueType = typeof value;\n\n if (valueType === \"string\") {\n return truncateString(value as string, options.maxStringLength);\n }\n\n if (valueType === \"number\") {\n // NaN and Infinity are not representable in JSON\n return Number.isFinite(value as number) ? (value as number) : String(value);\n }\n\n if (valueType === \"boolean\") return value as boolean;\n if (valueType === \"undefined\") return null;\n if (valueType === \"bigint\") return `${String(value)}n`;\n if (valueType === \"symbol\") return String(value as symbol);\n\n if (valueType === \"function\") {\n const name = (value as { name?: string }).name;\n return `[Function: ${name ? name : \"anonymous\"}]`;\n }\n\n const objectValue = value as object;\n\n const existingPath = ancestors.get(objectValue);\n if (existingPath !== undefined) {\n return `[Circular → ${existingPath}]`;\n }\n\n if (depth > options.maxDepth) {\n return { \"@truncated\": { kind: \"depth\", depth: options.maxDepth } };\n }\n\n const wellKnown = normalizeWellKnown(objectValue, options, depth, path, ancestors);\n if (wellKnown !== undefined) return wellKnown;\n\n ancestors.set(objectValue, path);\n try {\n if (Array.isArray(objectValue)) {\n return normalizeArray(objectValue, options, depth, path, ancestors);\n }\n return normalizeObject(objectValue, options, depth, path, ancestors);\n } finally {\n // Only ancestors count as cycles — the same object appearing twice in a\n // tree is repetition, not recursion, and should render both times\n ancestors.delete(objectValue);\n }\n}\n\n/**\n * Convert the built-in object types that need a dedicated shape.\n * Returns undefined when the value is an ordinary array or object.\n */\nfunction normalizeWellKnown(\n value: object,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue | undefined {\n if (value instanceof Error) {\n return normalizeErrorInternal(value, options, 0) as unknown as JsonValue;\n }\n\n if (value instanceof Date) {\n const time = value.getTime();\n return Number.isNaN(time) ? \"[Invalid Date]\" : value.toISOString();\n }\n\n if (value instanceof RegExp) return String(value);\n if (value instanceof URL) return value.href;\n\n if (value instanceof Map) {\n const entries: Record<string, JsonValue> = {};\n let index = 0;\n let omitted = 0;\n for (const [entryKey, entryValue] of value) {\n if (index >= options.maxProperties) {\n omitted += 1;\n continue;\n }\n const keyLabel = safeStringify(entryKey, options.maxStringLength);\n entries[keyLabel] = redactOrNormalize(\n keyLabel,\n entryValue,\n options,\n depth + 1,\n `${path}.${keyLabel}`,\n ancestors\n );\n index += 1;\n }\n return {\n \"@type\": \"Map\",\n entries,\n ...(omitted ? { \"@truncated\": { kind: \"mapEntries\", omitted } } : {}),\n };\n }\n\n if (value instanceof Set) {\n const values: JsonValue[] = [];\n let omitted = 0;\n for (const member of value) {\n if (values.length >= options.maxArrayLength) {\n omitted += 1;\n continue;\n }\n values.push(\n normalizeUnknown(member, options, depth + 1, `${path}[${values.length}]`, ancestors)\n );\n }\n return {\n \"@type\": \"Set\",\n values,\n ...(omitted ? { \"@truncated\": { kind: \"setValues\", omitted } } : {}),\n };\n }\n\n if (value instanceof WeakMap) return \"[WeakMap]\";\n if (value instanceof WeakSet) return \"[WeakSet]\";\n if (value instanceof Promise) return \"[Promise]\";\n\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {\n return describeBinary(value);\n }\n\n const converted = callToJson(value);\n if (converted !== undefined) {\n return normalizeUnknown(converted, options, depth, path, ancestors);\n }\n\n return undefined;\n}\n\n/** Call a value's toJSON() if it has one, returning undefined when it has none or it throws */\nfunction callToJson(value: object): unknown {\n let toJson: unknown;\n try {\n toJson = (value as { toJSON?: unknown }).toJSON;\n } catch {\n return undefined;\n }\n\n if (typeof toJson !== \"function\") return undefined;\n\n try {\n return (toJson as () => unknown).call(value);\n } catch (failure) {\n return `[Unreadable: ${describeFailure(failure)}]`;\n }\n}\n\n/** Describe a binary value by its size and leading bytes rather than dumping its contents */\nfunction describeBinary(value: ArrayBufferView | ArrayBuffer): JsonValue {\n const typeName = readConstructorName(value) ?? \"ArrayBuffer\";\n const byteLength = value.byteLength;\n\n let preview: string;\n try {\n const bytes =\n value instanceof ArrayBuffer\n ? new Uint8Array(value, 0, Math.min(BINARY_PREVIEW_BYTES, byteLength))\n : new Uint8Array(\n value.buffer,\n value.byteOffset,\n Math.min(BINARY_PREVIEW_BYTES, value.byteLength)\n );\n preview = [...bytes].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\" \");\n } catch {\n preview = \"\";\n }\n\n return {\n \"@type\": typeName,\n byteLength,\n ...(preview ? { preview } : {}),\n ...(byteLength > BINARY_PREVIEW_BYTES\n ? { \"@truncated\": { kind: \"bytes\", omitted: byteLength - BINARY_PREVIEW_BYTES } }\n : {}),\n };\n}\n\n/** Convert an array, keeping at most maxArrayLength entries */\nfunction normalizeArray(\n value: unknown[],\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n const kept: JsonValue[] = [];\n const limit = Math.min(value.length, options.maxArrayLength);\n\n for (let index = 0; index < limit; index += 1) {\n kept.push(\n normalizeUnknown(value[index], options, depth + 1, `${path}[${index}]`, ancestors)\n );\n }\n\n if (value.length > limit) {\n kept.push({ \"@truncated\": { kind: \"array\", omitted: value.length - limit } });\n }\n\n return kept;\n}\n\n/** Convert a plain object or class instance, tagging the class name when there is one */\nfunction normalizeObject(\n value: object,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n const result: Record<string, JsonValue> = {};\n\n const className = readConstructorName(value);\n if (className && className !== \"Object\") {\n result[\"@type\"] = className;\n }\n\n let keys: string[];\n try {\n keys = Object.keys(value);\n } catch {\n return `[Unreadable: keys could not be listed]`;\n }\n\n let kept = 0;\n let omitted = 0;\n for (const key of keys) {\n if (kept >= options.maxProperties) {\n omitted += 1;\n continue;\n }\n\n let propertyValue: unknown;\n try {\n propertyValue = (value as Record<string, unknown>)[key];\n } catch (failure) {\n // A getter that throws must not take the whole record down with it\n result[key] = `[Unreadable: ${describeFailure(failure)}]`;\n kept += 1;\n continue;\n }\n\n // JSON.stringify drops undefined properties; match that so records stay clean\n if (propertyValue === undefined) continue;\n\n result[key] = redactOrNormalize(\n key,\n propertyValue,\n options,\n depth + 1,\n `${path}.${key}`,\n ancestors\n );\n kept += 1;\n }\n\n // Only count properties dropped for the size limit — a property skipped because\n // its value was undefined is absent from JSON.stringify output too, not truncated\n if (omitted) {\n result[\"@truncated\"] = { kind: \"properties\", omitted };\n }\n\n return result;\n}\n\n/** Replace secret-shaped values with the redaction marker, otherwise normalize normally */\nfunction redactOrNormalize(\n key: string,\n value: unknown,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n if (options.redact && options.redactKeys.has(normalizeKeyForMatching(key))) {\n return REDACTED;\n }\n return normalizeUnknown(value, options, depth, path, ancestors);\n}\n\n/** Reduce a property name to letters and digits so casing and separators do not matter */\nfunction normalizeKeyForMatching(key: string): string {\n return key.replace(/[^a-zA-Z0-9]/g, \"\").toLowerCase();\n}\n\n/** Read a value's class name, tolerating null-prototype objects and hostile proxies */\nfunction readConstructorName(value: object): string | undefined {\n try {\n const prototype = Object.getPrototypeOf(value) as { constructor?: { name?: string } } | null;\n if (prototype === null) return undefined;\n const name = prototype.constructor?.name;\n return typeof name === \"string\" && name.length ? name : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Check whether a value is a non-array object with an ordinary prototype */\nfunction isPlainRecord(value: unknown): boolean {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Cut a string to length, marking inline how many characters were dropped */\nfunction truncateString(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength)}…[+${value.length - maxLength} chars]`;\n}\n\n/** Convert any value to a short string without risking a throw from a custom toString */\nfunction safeStringify(value: unknown, maxLength: number): string {\n try {\n return truncateString(String(value), maxLength);\n } catch {\n return \"[unstringifiable]\";\n }\n}\n\n/** Extract a readable message from a value thrown while normalizing */\nfunction describeFailure(failure: unknown): string {\n if (failure instanceof Error && failure.message) return failure.message;\n try {\n return String(failure);\n } catch {\n return \"unknown error\";\n }\n}\n","import type { AudienceMember, Emission, StoryLevel } from \"../storyteller\";\nimport { meetsLevel, resolveMinimumLevel } from \"../environment\";\nimport { normalizeValue } from \"../normalize\";\n\n/** Anything that can take a line of text — a Node stream, or your own sink */\nexport type LineWriter = {\n write: (chunk: string) => unknown;\n};\n\nexport type NdjsonAudienceOptions = {\n /** Where lines go. Defaults to stdout in Node, console.log elsewhere. */\n stream?: LineWriter;\n /** Register under a different name, e.g. to run two streams at once */\n name?: string;\n /** Minimum level to write. Defaults to `STORYTELLER_LEVEL`, then everything. */\n level?: StoryLevel;\n};\n\n/**\n * Create an audience that writes one JSON object per line — every note and every\n * story, nothing else on the channel.\n *\n * This is the format to give a program: a log shipper, `jq`, or an agent reading\n * another process's output. Each line parses on its own, and `storyId` plus\n * `sequence` let a reader group streamed notes back into their story.\n *\n * @param options - Stream, name and level threshold\n *\n * @example\n * ```ts\n * story.audience.remove(\"console\");\n * story.audience.add(ndjsonAudience({ stream: process.stderr }));\n * ```\n */\nexport function ndjsonAudience(options: NdjsonAudienceOptions = {}): AudienceMember {\n const writer = options.stream ?? createDefaultWriter();\n const minimumLevel = resolveMinimumLevel(options.level);\n\n return {\n name: options.name ?? \"ndjson\",\n hears: [\"note\", \"story\"],\n accepts: (emission: Emission) => meetsLevel(emission.level, minimumLevel),\n hear: (emission: Emission) => {\n writer.write(`${serializeEmission(emission)}\\n`);\n },\n };\n}\n\n/**\n * Serialize an emission to a single line, falling back to a normalized copy if the\n * emission somehow resists stringifying. An audience must not be able to throw.\n */\nfunction serializeEmission(emission: Emission): string {\n try {\n return JSON.stringify(emission);\n } catch {\n try {\n return JSON.stringify(normalizeValue(emission));\n } catch {\n return JSON.stringify({\n kind: emission.kind,\n level: emission.level,\n error: \"[Unserializable emission]\",\n });\n }\n }\n}\n\n/** Write to stdout where there is one, and fall back to the console everywhere else */\nfunction createDefaultWriter(): LineWriter {\n const runtime = globalThis as {\n process?: { stdout?: { write?: (chunk: string) => unknown } };\n };\n\n const write = runtime.process?.stdout?.write;\n if (typeof write === \"function\") {\n const stdout = runtime.process!.stdout!;\n return { write: (chunk: string) => write.call(stdout, chunk) };\n }\n\n // console.log adds its own newline, so hand it the line without one\n return { write: (chunk: string) => console.log(chunk.replace(/\\n$/, \"\")) };\n}\n","import type {\n StoryEventBase,\n StoryNote,\n ReportNote,\n StoryReport,\n FormattedReport,\n ReportOptions,\n} from \"./storyteller\";\nimport { ANSI, getLevelColor, formatOrigin, colorizeJsonSections } from \"./utils\";\n\n/**\n * Format a story event into a human-readable report with optional colors.\n *\n * @param story - The story event to format\n * @param options - Formatting options (timezone, locale, detail level, colors)\n * @returns A FormattedReport with both text (for display) and data (structured)\n *\n * @example\n * ```ts\n * const report = formatStory(event, { colors: false, detail: \"brief\" });\n * console.log(report.text);\n * ```\n */\nexport function formatStory(\n story: StoryEventBase,\n options: ReportOptions = {}\n): FormattedReport {\n const {\n timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,\n locale = \"en-US\",\n detail = \"normal\",\n noteLimit = 50,\n showData = true,\n colors = true,\n } = options;\n\n const dateTimeFormatter = new Intl.DateTimeFormat(locale, {\n timeZone: timezone,\n year: \"numeric\",\n month: \"short\",\n day: \"2-digit\",\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n\n const timeFormatter = new Intl.DateTimeFormat(locale, {\n timeZone: timezone,\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n\n // Notes are already sorted if coming from buildEvent; sort again for standalone use\n const orderedNotes = [...story.notes].sort(\n (noteA, noteB) => Date.parse(noteA.timestamp) - Date.parse(noteB.timestamp)\n );\n\n // Use pre-computed durationMs from the event if available, otherwise compute\n const durationMs = story.durationMs ?? calculateNoteDuration(orderedNotes);\n const duration = durationMs != null ? formatDuration(durationMs) : undefined;\n\n const slicedNotes = orderedNotes.slice(0, noteLimit);\n const reportNotes: ReportNote[] = slicedNotes.map((note) => ({\n timestamp: note.timestamp,\n when: timeFormatter.format(new Date(note.timestamp)),\n note: note.note,\n text: formatNoteText(note, detail),\n ...(note.who ? { who: note.who } : {}),\n ...(note.what ? { what: note.what } : {}),\n ...(note.where ? { where: note.where } : {}),\n ...(note.error ? { error: note.error } : {}),\n }));\n\n const data: StoryReport = {\n title: story.title,\n level: story.level,\n when: dateTimeFormatter.format(new Date(story.timestamp)),\n ...(durationMs != null ? { durationMs } : {}),\n ...(duration ? { duration } : {}),\n ...(story.origin ? { origin: story.origin } : {}),\n notes: reportNotes,\n ...(story.error ? { error: story.error } : {}),\n };\n\n const lines = buildReportText(story, data, reportNotes, orderedNotes, {\n colors,\n detail,\n showData,\n duration,\n });\n\n return { text: lines.join(\"\\n\"), data };\n}\n\n/** Build the human-readable text lines for a story report */\nfunction buildReportText(\n story: StoryEventBase,\n data: StoryReport,\n reportNotes: ReportNote[],\n orderedNotes: StoryNote[],\n options: { colors: boolean; detail: string; showData: boolean; duration?: string | undefined }\n): string[] {\n const levelColor = getLevelColor(story.level);\n const label = (text: string) =>\n options.colors ? `${levelColor}${text}${ANSI.reset}` : text;\n const originLabel = formatOrigin(story.origin);\n\n const lines: string[] = [];\n lines.push(`${label(\"Story\")}: ${story.title}`);\n lines.push(`${label(\"Level\")}: ${story.level}`);\n lines.push(`${label(\"Time\")}: ${data.when}${options.duration ? ` (${options.duration})` : \"\"}`);\n\n if (originLabel) {\n lines.push(`${label(\"Origin\")}: ${originLabel}`);\n }\n\n if (story.error) {\n const errorLine = [story.error.name, story.error.message]\n .filter(Boolean)\n .join(\": \");\n if (errorLine) lines.push(`${label(\"Error\")}: ${errorLine}`);\n }\n\n if (options.detail !== \"brief\" && reportNotes.length) {\n lines.push(`${label(\"Notes\")}:`);\n for (const note of reportNotes) {\n lines.push(` ${note.when} — ${note.text}`);\n }\n if (orderedNotes.length > reportNotes.length) {\n lines.push(` … (${orderedNotes.length - reportNotes.length} more)`);\n }\n }\n\n if (options.showData) {\n lines.push(`${label(\"Data\")}:`);\n const json = JSON.stringify(data, null, 2);\n if (options.colors) {\n const colored = colorizeJsonSections(json, {\n base: ANSI.grayLight,\n notes: ANSI.grayDark,\n reset: ANSI.reset,\n });\n lines.push(...colored);\n } else {\n lines.push(...json.split(\"\\n\"));\n }\n }\n\n return lines;\n}\n\n/** Calculate the duration between the first and last note in a sequence */\nfunction calculateNoteDuration(notes: StoryNote[]): number | undefined {\n if (notes.length <= 1) return undefined;\n\n const startTime = Date.parse(notes[0]!.timestamp);\n const endTime = Date.parse(notes[notes.length - 1]!.timestamp);\n\n return Number.isFinite(startTime) && Number.isFinite(endTime)\n ? Math.max(0, endTime - startTime)\n : undefined;\n}\n\n/** Convert milliseconds into a human-readable duration string */\nexport function formatDuration(milliseconds: number): string {\n if (milliseconds < 1000) return `${milliseconds}ms`;\n const seconds = milliseconds / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = Math.round(seconds % 60)\n .toString()\n .padStart(2, \"0\");\n return `${minutes}:${remainingSeconds}m`;\n}\n\n/** @deprecated Use formatStory instead */\nexport const summarizeStory = formatStory;\n\n/** Format a note's text with optional context details when detail level is \"full\" */\nfunction formatNoteText(\n note: StoryNote,\n verbosity: \"brief\" | \"normal\" | \"full\"\n): string {\n if (verbosity !== \"full\") return note.note;\n\n const details: string[] = [];\n const what = note.what;\n const where = note.where;\n\n if (typeof what === \"string\") {\n details.push(`what=${what}`);\n } else if (what) {\n for (const [key, value] of Object.entries(what)) {\n if (value != null) details.push(`${key}=${String(value)}`);\n }\n }\n if (typeof where === \"string\") {\n details.push(`where=${where}`);\n } else if (where) {\n for (const [key, value] of Object.entries(where)) {\n if (value != null) details.push(`${key}=${String(value)}`);\n }\n }\n if (note.error) {\n const errorLine = [note.error.name, note.error.message]\n .filter(Boolean)\n .join(\": \");\n if (errorLine) details.push(`error=${errorLine}`);\n }\n\n return details.length\n ? `${note.note} (${details.join(\" \")})`\n : note.note;\n}\n","import { consoleAudience } from \"./audiences/consoleAudience\";\nimport { ndjsonAudience } from \"./audiences/ndjsonAudience\";\nimport type { LevelInput, OutputFormat } from \"./environment\";\nimport {\n meetsLevel,\n resolveMinimumLevel,\n resolveOutputFormat,\n readEnvironmentValue,\n toStoryLevel,\n} from \"./environment\";\n\nexport type { LevelInput } from \"./environment\";\nimport { formatStory } from \"./formatting\";\nimport type { JsonValue } from \"./normalize\";\nimport { normalizeError, normalizeValue } from \"./normalize\";\n\n/** Human-readable level labels stored in story records */\nexport type StoryLevel = \"Information\" | \"Warning\" | \"Error\";\n\n/**\n * A stored context value. Always JSON-safe — whatever the caller passed in has\n * already been through the normalizer by the time it reaches a record.\n */\nexport type StoryContextValue = JsonValue;\n\n/** Context accepted from callers. Anything goes; the normalizer makes it storable. */\nexport type StoryContextInput = unknown;\n\nexport type StoryError = {\n name?: string;\n message?: string;\n stack?: string;\n cause?: JsonValue;\n /** Members of an AggregateError */\n errors?: StoryError[];\n};\n\n/** Origin as stored on a record */\nexport type StoryOrigin = {\n who?: StoryContextValue;\n what?: StoryContextValue;\n where?: StoryContextValue;\n};\n\n/** Origin as accepted from callers */\nexport type StoryOriginInput = {\n who?: StoryContextInput;\n what?: StoryContextInput;\n where?: StoryContextInput;\n};\n\nexport type StoryNote = {\n timestamp: string;\n /**\n * Position within the story, assigned when the note is taken. Gap-free from 0.\n * Optional so records written before sequencing existed still typecheck.\n */\n sequence?: number;\n note: string;\n /** Omitted when the note carries the story's default Information level */\n level?: StoryLevel;\n who?: StoryContextValue;\n what?: StoryContextValue;\n where?: StoryContextValue;\n error?: StoryError;\n};\n\nexport type StoryEventBase = {\n timestamp: string;\n level: StoryLevel;\n title: string;\n\n /**\n * Correlates every note emission with the story it belongs to. Always set on\n * events this library builds; optional so older stored records still typecheck.\n */\n storyId?: string;\n\n /**\n * The story this one is a chapter of. Absent on a top-level story.\n * Following this field reconstructs the tree of a nested run.\n */\n parentStoryId?: string;\n\n origin?: StoryOrigin;\n\n notes: StoryNote[];\n durationMs?: number;\n\n /**\n * How many emissions were dropped for back-pressure while this story was being\n * collected. Present only when something was actually lost, so the loss shows up\n * in the record instead of vanishing.\n */\n droppedEmissions?: number;\n\n error?: StoryError;\n};\n\nexport type ReportOptions = {\n timezone?: string;\n locale?: string;\n detail?: \"brief\" | \"normal\" | \"full\";\n noteLimit?: number;\n showData?: boolean;\n colors?: boolean;\n};\n\n/** @deprecated Use ReportOptions instead */\nexport type StorySummaryOptions = ReportOptions;\n\nexport type PreviewOptions = ReportOptions & {\n title?: string;\n level?: StoryLevel;\n error?: unknown;\n};\n\n/** @deprecated Use PreviewOptions instead */\nexport type StoryPreviewOptions = PreviewOptions;\n\nexport type ReportNote = {\n timestamp: string;\n when: string;\n note: string;\n text: string;\n who?: StoryContextValue;\n what?: StoryContextValue;\n where?: StoryContextValue;\n error?: StoryError;\n};\n\n/** @deprecated Use ReportNote instead */\nexport type StorySummaryNote = ReportNote;\n\nexport type StoryReport = {\n title: string;\n level: StoryLevel;\n when: string;\n durationMs?: number;\n duration?: string;\n origin?: StoryEventBase[\"origin\"];\n notes: ReportNote[];\n error?: StoryError;\n};\n\n/** @deprecated Use StoryReport instead */\nexport type StorySummaryData = StoryReport;\n\nexport type FormattedReport = {\n text: string;\n data: StoryReport;\n};\n\n/** @deprecated Use FormattedReport instead */\nexport type StorySummary = FormattedReport;\n\nexport type StoryEvent = StoryEventBase & {\n kind: \"story\";\n summarize: (options?: ReportOptions) => FormattedReport;\n};\n\n/** @deprecated Use StoryEvent — the story-shaped emission */\nexport type StoryEmission = StoryEvent;\n\n/** The two things an audience can hear */\nexport type EmissionKind = \"note\" | \"story\";\n\n/**\n * A single beat, delivered the moment it happens when narration is live.\n *\n * `storyId` and `sequence` are what make streaming lossless: a consumer holding\n * the beats of a story can order and group them back into the record that\n * collected narration would have produced.\n */\nexport type NoteEmission = StoryNote & {\n kind: \"note\";\n storyId: string;\n parentStoryId?: string;\n sequence: number;\n level: StoryLevel;\n origin?: StoryOrigin;\n};\n\nexport type Emission = NoteEmission | StoryEvent;\n\nexport type AudienceMember = {\n name: string;\n /**\n * Which emission kinds this audience wants. Defaults to `[\"story\"]`, so an\n * audience written before live narration existed keeps hearing only stories.\n */\n hears?: EmissionKind[];\n /**\n * Declared with method syntax deliberately. TypeScript checks method parameters\n * bivariantly, so an audience written as `hear: (event: StoryEvent) => void`\n * still compiles. That would be unsound if such an audience could receive a note\n * emission — it cannot, because `hears` defaults to stories only.\n */\n accepts?(emission: Emission): boolean;\n hear(emission: Emission): void | Promise<void>;\n};\n\n/**\n * How a storyteller narrates.\n *\n * - `collected` — beats are buffered and leave as one story record (the default)\n * - `live` — each beat is emitted as it happens, and the story still lands at the end\n *\n * Live narration adds emissions, it never removes them: a consumer that only wants\n * beats says so with `hears: [\"note\"]` rather than by silencing the record.\n */\nexport type Narration = \"collected\" | \"live\";\n\n/** @deprecated `both` is now the behavior of `live` — beats stream and the story still lands */\nexport type NarrationInput = Narration | \"both\";\n\nexport type NoteData = {\n who?: StoryContextInput;\n what?: StoryContextInput;\n where?: StoryContextInput;\n error?: unknown;\n /** Level for this beat alone. Defaults to Information. */\n level?: LevelInput;\n /** Emit this beat immediately even when narration is collected */\n live?: boolean;\n /** Deliver this beat only to the named audiences */\n to?: string[];\n};\n\nexport type FinishOptions = {\n /** Defaults to Information */\n level?: LevelInput;\n /** The error that ended the story, normalized onto the record */\n error?: unknown;\n};\n\nexport type ChapterOptions = {\n /** Merged over the parent's origin */\n origin?: StoryOriginInput;\n /** Defaults to the parent's setting */\n narration?: NarrationInput;\n /** Defaults to the parent's setting */\n level?: LevelInput;\n /** Defaults to the parent's handler */\n onAudienceError?: AudienceErrorHandler;\n /** Defaults to the parent's bound */\n maxInFlight?: number;\n};\n\nexport type StorytellerOptions = {\n origin?: StoryOriginInput;\n audiences?: AudienceMember[];\n /** Defaults to `STORYTELLER_NARRATION`, then `collected` */\n narration?: NarrationInput;\n /**\n * Which default audience to register: colorized text for a person, NDJSON for a\n * program. Defaults to `STORYTELLER_FORMAT`, then `text`.\n */\n format?: OutputFormat;\n /**\n * Share another storyteller's audience registry instead of creating one.\n * Audiences added to it later reach this storyteller too. When given, no\n * default audience is registered — the registry already has whatever it has.\n */\n audience?: AudienceRegistry;\n /** The story this one is a chapter of. Set by `chapter()`. */\n parentStoryId?: string;\n /**\n * Drop emissions below this level before they reach any audience.\n * Defaults to `STORYTELLER_LEVEL`, then Information (deliver everything).\n */\n level?: LevelInput;\n /**\n * Called when an audience throws or rejects. Without one, a single throttled\n * warning per audience goes to the console — a logging library that loses\n * records in silence is worse than one that complains.\n */\n onAudienceError?: AudienceErrorHandler;\n /**\n * Cap on deliveries in flight to a single audience at once. Live narration is\n * fire-and-forget, so a slow audience would otherwise grow an unbounded queue.\n * Past the cap, emissions are dropped and counted on the closing story.\n */\n maxInFlight?: number;\n};\n\n/** Called when an audience member throws or rejects while hearing an emission */\nexport type AudienceErrorHandler = (\n error: unknown,\n member: AudienceMember,\n emission: Emission\n) => void;\n\n/** Manages the set of audience members that receive story events */\nexport class AudienceRegistry {\n private members = new Map<string, AudienceMember>();\n\n /** Register an audience member, replacing any existing member with the same name */\n add(member: AudienceMember) {\n this.members.set(member.name, member);\n return this;\n }\n\n /** Remove an audience member by name */\n remove(name: string) {\n this.members.delete(name);\n return this;\n }\n\n /** Return all registered audience members */\n getAll() {\n return [...this.members.values()];\n }\n\n /** Return only the audience members matching the given names */\n getOnly(names: string[]) {\n return names.map((name) => this.members.get(name)).filter(Boolean) as AudienceMember[];\n }\n\n /** Check if an audience member is registered by name */\n has(name: string) {\n return this.members.has(name);\n }\n\n /** List the names of all registered audience members */\n names() {\n return [...this.members.keys()];\n }\n}\n\n/**\n * Collects timestamped notes and emits them as one structured story — and, when\n * narration is live, emits each note the moment it is taken.\n *\n * @example\n * ```ts\n * const story = new Storyteller({ origin: { who: \"api-server\" }, narration: \"live\" });\n * story.report(\"Request received\", { what: { path: \"/checkout\" } });\n * story.report(\"Validated cart\");\n * story.finish(\"Checkout started\");\n * ```\n */\nexport class Storyteller {\n public readonly audience: AudienceRegistry;\n\n private readonly origin?: StoryOrigin;\n private readonly parentStoryId?: string;\n private notes: StoryNote[] = [];\n private narration: Narration;\n private readonly minimumLevel: StoryLevel;\n private readonly onAudienceError: AudienceErrorHandler;\n private readonly maxInFlight: number;\n\n /** Deliveries currently awaiting each audience, keyed by audience name */\n private readonly inFlight = new Map<string, number>();\n /** Emissions dropped for back-pressure since the current story began */\n private droppedEmissions = 0;\n\n /** Identifies the story currently being collected; regenerated after each telling */\n private storyId = createStoryId();\n /** Position of the next note within the current story */\n private nextSequence = 0;\n\n constructor(options?: StorytellerOptions) {\n const normalizedOrigin = normalizeOrigin(options?.origin);\n if (normalizedOrigin) {\n this.origin = normalizedOrigin;\n }\n\n if (options?.parentStoryId !== undefined) {\n this.parentStoryId = options.parentStoryId;\n }\n\n this.narration = resolveNarration(options?.narration);\n this.minimumLevel = resolveMinimumLevel(options?.level);\n this.onAudienceError = options?.onAudienceError ?? reportAudienceErrorToConsole;\n this.maxInFlight = options?.maxInFlight ?? DEFAULT_MAX_IN_FLIGHT;\n\n if (options?.audience) {\n // A shared registry already holds its audiences, including any the caller\n // customized. Adding a default here would replace them by name.\n this.audience = options.audience;\n } else {\n this.audience = new AudienceRegistry();\n\n // Every storyteller gets a default audience. Which one depends on who is\n // reading: a person at a terminal, or a program parsing the stream.\n this.audience.add(\n resolveOutputFormat(options?.format) === \"ndjson\"\n ? ndjsonAudience({ level: this.minimumLevel })\n : consoleAudience()\n );\n }\n\n options?.audiences?.forEach((audience) => this.audience.add(audience));\n }\n\n /**\n * Switch between collected and live narration at runtime.\n * Takes effect on the next note; already-buffered notes are not replayed.\n *\n * @param narration - `collected` to buffer, `live` to emit each note as it happens\n * @returns `this` for chaining\n */\n narrate(narration: NarrationInput) {\n this.narration = resolveNarration(narration);\n return this;\n }\n\n /** The id of the story currently being collected */\n get currentStoryId() {\n return this.storyId;\n }\n\n /**\n * Start a chapter: a child storyteller whose stories are linked back to this\n * one by `parentStoryId`.\n *\n * Real work nests — an agent spawns subtasks, a batch runs per-item operations.\n * A chapter keeps each of those a complete story in its own right while leaving\n * the run reconstructable as a tree.\n *\n * The child shares this storyteller's audience registry, so audiences added\n * later reach it too, and inherits narration, level and delivery settings.\n * Its stories are separate records — a chapter is not folded into the parent's\n * notes.\n *\n * @param options - Origin to merge over the parent's, and any setting to override\n * @returns A child Storyteller\n *\n * @example\n * ```ts\n * for (const account of accounts) {\n * const chapter = story.chapter({ origin: { what: account.id } });\n * chapter.report(\"Fetching invoices\");\n * chapter.finish(`Synced ${account.id}`);\n * }\n * ```\n */\n chapter(options: ChapterOptions = {}): Storyteller {\n const mergedOrigin: StoryOriginInput = { ...this.origin, ...options.origin };\n\n return new Storyteller({\n audience: this.audience,\n // Captured now, so a parent that finishes first does not orphan its chapters\n parentStoryId: this.storyId,\n ...(Object.keys(mergedOrigin).length ? { origin: mergedOrigin } : {}),\n narration: options.narration ?? this.narration,\n level: options.level ?? this.minimumLevel,\n onAudienceError: options.onAudienceError ?? this.onAudienceError,\n maxInFlight: options.maxInFlight ?? this.maxInFlight,\n });\n }\n\n /**\n * Report a beat of the current story.\n *\n * In collected narration the beat is buffered and leaves with the story. In live\n * narration it is emitted the moment you call this, so whoever is tuned in sees\n * the work as it happens.\n *\n * Accepts anything, not just a string — pass an error, an API response, a Map, a\n * class instance — and the value is normalized into a storable shape with the note\n * text derived from it.\n *\n * @param input - What happened: a message, or any value to describe\n * @param data - Optional context: who did it, what was involved, where it happened, any error\n * @returns `this` for chaining\n *\n * @example\n * ```ts\n * story.report(\"Card charged\", { what: { amount: \"$42\" }, where: \"stripe\" });\n * story.report(await response.json());\n * ```\n */\n report(input: unknown, data: NoteData = {}) {\n const described = describeInput(input);\n const level = toStoryLevel(data.level);\n\n const note: StoryNote = {\n timestamp: new Date().toISOString(),\n sequence: this.nextSequence,\n note: described.text,\n ...(level !== \"Information\" ? { level } : {}),\n ...(data.who !== undefined ? { who: normalizeValue(data.who) } : {}),\n ...(data.what !== undefined\n ? { what: normalizeValue(data.what) }\n : described.what !== undefined\n ? { what: described.what }\n : {}),\n ...(data.where !== undefined ? { where: normalizeValue(data.where) } : {}),\n ...(data.error !== undefined\n ? { error: normalizeError(data.error) }\n : described.error !== undefined\n ? { error: described.error }\n : {}),\n };\n\n this.nextSequence += 1;\n this.notes.push(note);\n\n if (this.narration === \"live\" || data.live) {\n this.emitNote(note, level, data.to);\n }\n\n return this;\n }\n\n /** Clear all accumulated notes without emitting a story, and start a new story id */\n reset() {\n this.notes = [];\n this.startNewStory();\n return this;\n }\n\n /** Preview the current notes as a formatted report without emitting or clearing them */\n summarize(options: PreviewOptions = {}) {\n const {\n title = \"Story preview\",\n level = \"Information\",\n error,\n ...reportOptions\n } = options;\n const event: StoryEventBase = {\n timestamp: new Date().toISOString(),\n level,\n title,\n storyId: this.storyId,\n ...(this.parentStoryId !== undefined ? { parentStoryId: this.parentStoryId } : {}),\n ...(this.origin ? { origin: this.origin } : {}),\n notes: [...this.notes],\n ...(error !== undefined ? { error: normalizeError(error) } : {}),\n };\n\n return formatStory(event, reportOptions);\n }\n\n /**\n * Finish the story: emit everything collected so far as one record, and start fresh.\n *\n * @param title - What the story was about\n * @param options - Level, and the error that ended it\n * @returns A one-shot handle whose `.to()` overrides the audience list — call it\n * synchronously, delivery happens on the next microtask\n *\n * @example\n * ```ts\n * story.finish(\"Sync complete\");\n * story.finish(\"Sync failed\", { level: \"oops\", error }).to(\"db\");\n * ```\n */\n finish(title: string, options: FinishOptions = {}) {\n return this.createDelivery(toStoryLevel(options.level), title, options.error);\n }\n\n /** @deprecated Use `finish(title)`. Removed at 1.0. */\n tell(title: string) {\n warnDeprecated(\"tell\", \"finish\");\n return this.createDelivery(\"Information\", title);\n }\n\n /** @deprecated Use `finish(title, { level: \"warn\" })`. Removed at 1.0. */\n warn(title: string) {\n warnDeprecated(\"warn\", 'finish(title, { level: \"warn\" })');\n return this.createDelivery(\"Warning\", title);\n }\n\n /** @deprecated Use `finish(title, { level: \"oops\", error })`. Removed at 1.0. */\n oops(title: string, error?: unknown) {\n warnDeprecated(\"oops\", 'finish(title, { level: \"oops\", error })');\n return this.createDelivery(\"Error\", title, error);\n }\n\n /** @deprecated Use `report()`. Removed at 1.0. */\n note(input: unknown, data: NoteData = {}) {\n warnDeprecated(\"note\", \"report\");\n return this.report(input, data);\n }\n\n /** Emit a single note to the audiences listening for notes */\n private emitNote(note: StoryNote, level: StoryLevel, only?: string[]) {\n const emission: NoteEmission = {\n ...note,\n kind: \"note\",\n storyId: this.storyId,\n ...(this.parentStoryId !== undefined ? { parentStoryId: this.parentStoryId } : {}),\n sequence: note.sequence ?? 0,\n level,\n ...(this.origin ? { origin: this.origin } : {}),\n };\n\n void this.deliver(emission, only ? { only } : {});\n }\n\n /** Build a story event and schedule delivery, returning a handle to override the audience list */\n private createDelivery(level: StoryLevel, title: string, error?: unknown) {\n const event = this.buildEvent(level, title, error);\n\n let delivered = false;\n let defaultCancelled = false;\n\n // Delivery is microtask-scheduled so .to() can override synchronously\n queueMicrotask(() => {\n if (delivered || defaultCancelled) return;\n delivered = true;\n void this.deliver(event);\n });\n\n return {\n to: (...names: string[]) => {\n defaultCancelled = true;\n if (delivered) return;\n delivered = true;\n void this.deliver(event, { only: names });\n },\n };\n }\n\n /** Assemble the story event from current notes and start a fresh story */\n private buildEvent(level: StoryLevel, title: string, error?: unknown): StoryEvent {\n const now = new Date().toISOString();\n const storyId = this.storyId;\n // Read before startNewStory() zeroes it\n const droppedEmissions = this.droppedEmissions;\n\n // Sort notes chronologically so the record tells the story in order.\n // Sequence breaks ties: two notes can share a millisecond.\n const sortedNotes = [...this.notes].sort(\n (noteA, noteB) =>\n Date.parse(noteA.timestamp) - Date.parse(noteB.timestamp) ||\n (noteA.sequence ?? 0) - (noteB.sequence ?? 0)\n );\n\n this.notes = [];\n this.startNewStory();\n\n // Compute duration from first to last note\n const durationMs = calculateNoteDuration(sortedNotes).durationMs;\n\n const event: StoryEventBase = {\n timestamp: now,\n level,\n title,\n storyId,\n ...(this.parentStoryId !== undefined ? { parentStoryId: this.parentStoryId } : {}),\n ...(this.origin ? { origin: this.origin } : {}),\n notes: sortedNotes,\n ...(durationMs != null ? { durationMs } : {}),\n ...(droppedEmissions ? { droppedEmissions } : {}),\n ...(error !== undefined ? { error: normalizeError(error) } : {}),\n };\n\n const eventWithSummary = event as StoryEvent;\n Object.defineProperty(eventWithSummary, \"kind\", {\n value: \"story\",\n enumerable: true,\n });\n Object.defineProperty(eventWithSummary, \"summarize\", {\n value: (options?: ReportOptions) => formatStory(event, options),\n enumerable: false,\n });\n\n return eventWithSummary;\n }\n\n /** Begin a new story: fresh id, sequence back to zero */\n private startNewStory() {\n this.storyId = createStoryId();\n this.nextSequence = 0;\n this.droppedEmissions = 0;\n }\n\n /** Deliver an emission to the audience members listening for its kind */\n private async deliver(emission: Emission, options?: { only?: string[] }) {\n // Cheap exit before any audience work — level filtering costs one comparison\n if (!meetsLevel(emission.level, this.minimumLevel)) return;\n\n const targets = options?.only?.length\n ? this.audience.getOnly(options.only)\n : this.audience.getAll();\n\n await Promise.all(\n targets\n .filter((member) => hearsKind(member, emission.kind))\n .filter((member) => this.acceptsSafely(member, emission))\n .map((member) => this.hearSafely(member, emission))\n );\n }\n\n /** Run an audience's accepts() without letting a throw from it lose the emission */\n private acceptsSafely(member: AudienceMember, emission: Emission): boolean {\n if (!member.accepts) return true;\n\n try {\n return member.accepts(emission);\n } catch (error) {\n this.handleAudienceError(error, member, emission);\n return false;\n }\n }\n\n /**\n * Hand an emission to one audience, keeping its failures and its slowness\n * contained: a throw is reported rather than swallowed, and a backlog is dropped\n * rather than grown without limit.\n */\n private async hearSafely(member: AudienceMember, emission: Emission) {\n const pending = this.inFlight.get(member.name) ?? 0;\n if (pending >= this.maxInFlight) {\n this.droppedEmissions += 1;\n return;\n }\n\n this.inFlight.set(member.name, pending + 1);\n try {\n await member.hear(emission);\n } catch (error) {\n this.handleAudienceError(error, member, emission);\n } finally {\n const remaining = (this.inFlight.get(member.name) ?? 1) - 1;\n if (remaining > 0) this.inFlight.set(member.name, remaining);\n else this.inFlight.delete(member.name);\n }\n }\n\n /** Report an audience failure without ever letting it reach caller code */\n private handleAudienceError(error: unknown, member: AudienceMember, emission: Emission) {\n try {\n this.onAudienceError(error, member, emission);\n } catch {\n // A failing error handler must not escalate into a failing log call\n }\n }\n}\n\n/** Names already warned about, so a deprecation notice appears at most once per process */\nconst warnedDeprecations = new Set<string>();\n\n/**\n * Warn once about a deprecated method, and only when asked.\n *\n * Off by default on purpose: a logging library that spams its own deprecation\n * notices into a consumer's output has become the thing it was meant to fix.\n * Opt in with `STORYTELLER_DEPRECATION_WARNINGS=1`.\n */\nfunction warnDeprecated(oldName: string, replacement: string) {\n if (warnedDeprecations.has(oldName)) return;\n if (readEnvironmentValue(\"STORYTELLER_DEPRECATION_WARNINGS\") !== \"1\") return;\n\n warnedDeprecations.add(oldName);\n console.warn(\n `Storyteller: ${oldName}() is deprecated and will be removed at 1.0 — use ${replacement}.`\n );\n}\n\n/** Cap on simultaneous deliveries to one audience before emissions start being dropped */\nconst DEFAULT_MAX_IN_FLIGHT = 1000;\n\n/** How long to stay quiet after warning about a given audience, in milliseconds */\nconst AUDIENCE_ERROR_THROTTLE_MS = 5000;\n\n/** When an audience last had a failure reported, keyed by audience name */\nconst lastReportedAudienceError = new Map<string, number>();\n\n/**\n * Default audience-error behavior: one throttled warning per audience.\n * Loud enough to notice a broken audience, quiet enough not to become the problem.\n */\nfunction reportAudienceErrorToConsole(\n error: unknown,\n member: AudienceMember,\n emission: Emission\n) {\n const now = Date.now();\n const lastReported = lastReportedAudienceError.get(member.name);\n if (lastReported !== undefined && now - lastReported < AUDIENCE_ERROR_THROTTLE_MS) {\n return;\n }\n\n lastReportedAudienceError.set(member.name, now);\n const reason = error instanceof Error ? error.message : String(error);\n console.error(\n `Storyteller: audience \"${member.name}\" failed to hear a ${emission.kind} — ${reason}`\n );\n}\n\n/** Check whether an audience member listens for a given emission kind */\nfunction hearsKind(member: AudienceMember, kind: EmissionKind): boolean {\n // Audiences written before live narration existed only expect stories\n const kinds = member.hears ?? [\"story\"];\n return kinds.includes(kind);\n}\n\n/** Resolve the narration mode from an explicit value, the environment, then the default */\nfunction resolveNarration(requested?: NarrationInput): Narration {\n const value = requested ?? readEnvironmentValue(\"STORYTELLER_NARRATION\");\n if (value === \"live\" || value === \"both\") return \"live\";\n return \"collected\";\n}\n\n/** Generate an identifier for a story, falling back when crypto is unavailable */\nfunction createStoryId(): string {\n try {\n const runtimeCrypto = globalThis.crypto as { randomUUID?: () => string } | undefined;\n if (typeof runtimeCrypto?.randomUUID === \"function\") {\n return runtimeCrypto.randomUUID();\n }\n } catch {\n // fall through to the manual identifier\n }\n\n return `story-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Normalize each origin field so the origin on a record is as storable as the notes */\nfunction normalizeOrigin(origin?: StoryOriginInput): StoryOrigin | undefined {\n if (!origin) return undefined;\n\n const normalized: StoryOrigin = {\n ...(origin.who !== undefined ? { who: normalizeValue(origin.who) } : {}),\n ...(origin.what !== undefined ? { what: normalizeValue(origin.what) } : {}),\n ...(origin.where !== undefined ? { where: normalizeValue(origin.where) } : {}),\n };\n\n return Object.keys(normalized).length ? normalized : undefined;\n}\n\n/**\n * Derive note text from whatever the caller passed, along with the structured\n * remainder. A string is its own text; anything else is described and carried\n * along as context so nothing is lost.\n */\nfunction describeInput(input: unknown): {\n text: string;\n what?: JsonValue;\n error?: StoryError;\n} {\n if (typeof input === \"string\") return { text: input };\n\n if (input instanceof Error) {\n const error = normalizeError(input);\n const label = [error.name, error.message].filter(Boolean).join(\": \");\n return { text: label || \"Error\", error };\n }\n\n if (input === null) return { text: \"null\" };\n if (input === undefined) return { text: \"undefined\" };\n\n const normalized = normalizeValue(input);\n\n // A primitive still gets carried as structured data, not only stringified into\n // the text — otherwise `report(42)` would leave no way to read 42 back as a number\n if (typeof normalized !== \"object\" || normalized === null) {\n return { text: String(normalized), what: normalized };\n }\n\n if (Array.isArray(normalized)) {\n return { text: `Array(${normalized.length})`, what: normalized };\n }\n\n // Prefer a field that reads like a headline before falling back to the type name\n for (const key of [\"message\", \"title\", \"name\", \"summary\", \"event\"]) {\n const candidate = normalized[key];\n if (typeof candidate === \"string\" && candidate.length) {\n return { text: candidate, what: normalized };\n }\n }\n\n const typeName = normalized[\"@type\"];\n return {\n text: typeof typeName === \"string\" ? typeName : \"Object\",\n what: normalized,\n };\n}\n\n/** Calculate the duration between the first and last note in a sequence */\nfunction calculateNoteDuration(notes: StoryNote[]) {\n if (notes.length <= 1) {\n return {\n durationMs: undefined as number | undefined,\n };\n }\n\n const startTime = Date.parse(notes[0]!.timestamp);\n const endTime = Date.parse(notes[notes.length - 1]!.timestamp);\n\n return {\n durationMs: Number.isFinite(startTime) && Number.isFinite(endTime)\n ? Math.max(0, endTime - startTime)\n : undefined,\n };\n}\n","import type { NarrationInput, StoryOriginInput } from \"./storyteller\";\nimport { Storyteller } from \"./storyteller\";\n\nlet sharedInstance: Storyteller | undefined;\n\ntype StorytellerSharedOptions = {\n origin?: StoryOriginInput;\n narration?: NarrationInput;\n reset?: boolean;\n};\n\n/**\n * Get or create a shared Storyteller instance for cross-component logging.\n * First call creates the instance; subsequent calls return the same one.\n *\n * @param options.origin - Origin context for the shared instance\n * @param options.reset - Create a fresh instance (useful in tests)\n *\n * @example\n * ```ts\n * // Same instance everywhere in your app\n * const story = useStoryteller({ origin: { who: \"worker\" } });\n * ```\n */\nexport function useStoryteller(\n options: StorytellerSharedOptions = {}\n): Storyteller {\n if (!sharedInstance || options.reset) {\n sharedInstance = new Storyteller({\n ...(options.origin !== undefined ? { origin: options.origin } : {}),\n ...(options.narration !== undefined ? { narration: options.narration } : {}),\n });\n return sharedInstance;\n }\n\n return sharedInstance;\n}\n","import type { AudienceMember, Emission, StoryEvent } from \"../storyteller\";\n\n/**\n * Create an audience that stores warn and oops stories via your insert function.\n * Tell-level events are filtered out to reduce noise — only warnings and errors are persisted.\n *\n * Hears stories only. Live notes are not persisted: the story record already contains\n * every note, so storing both would double-write the same content.\n *\n * Note: if the insert function throws, the error is silently caught by the delivery\n * pipeline (Promise.allSettled). Wrap your insert with try/catch to handle failures.\n *\n * @param insert - Function that receives the story event and stores it\n *\n * @example\n * ```ts\n * story.audience.add(\n * dbAudience(async (event) => {\n * await db.insert(\"story_logs\", event);\n * })\n * );\n * ```\n */\nexport function dbAudience(insert: (event: StoryEvent) => Promise<void> | void): AudienceMember {\n return {\n name: \"db\",\n hears: [\"story\"],\n accepts: (emission: Emission) =>\n emission.kind === \"story\" &&\n (emission.level === \"Warning\" || emission.level === \"Error\"),\n hear: async (emission: Emission) => {\n if (emission.kind !== \"story\") return;\n await insert(emission);\n },\n };\n}\n","import type { StoryEventBase } from \"../storyteller\";\nimport { formatStory } from \"../formatting\";\nimport { ANSI, getLevelColor, formatOrigin, colorizeJsonSections } from \"../utils\";\n\nexport type StoryReportOptions = {\n timezone?: string;\n locale?: string;\n detail?: \"brief\" | \"normal\" | \"full\";\n noteLimit?: number;\n showData?: boolean;\n colors?: boolean;\n};\n\n/** Generate a formatted report from an array of story events, grouped by day */\nexport function writeStoryReport(\n stories: StoryEventBase[],\n options: StoryReportOptions = {}\n): string {\n const {\n timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,\n locale = \"en-US\",\n detail = \"normal\",\n noteLimit = 50,\n showData = true,\n colors = true,\n } = options;\n\n if (!stories.length) {\n return \"Storyteller Report\\n\\n(no stories)\\n\";\n }\n\n const sorted = [...stories].sort(\n (storyA, storyB) => Date.parse(storyA.timestamp) - Date.parse(storyB.timestamp)\n );\n\n const dateFormatter = new Intl.DateTimeFormat(locale, {\n timeZone: timezone,\n year: \"numeric\",\n month: \"short\",\n day: \"2-digit\",\n });\n\n const firstStory = sorted[0];\n const lastStory = sorted[sorted.length - 1];\n if (!firstStory || !lastStory) {\n return \"Storyteller Report\\n\\n(no stories)\\n\";\n }\n\n const lines: string[] = [];\n lines.push(`Storyteller Report (${timezone})`);\n lines.push(\n `Range: ${dateFormatter.format(new Date(firstStory.timestamp))} – ${dateFormatter.format(\n new Date(lastStory.timestamp)\n )}`\n );\n lines.push(\"\");\n\n const storiesByDay = new Map<string, StoryEventBase[]>();\n for (const story of sorted) {\n const dayKey = dateFormatter.format(new Date(story.timestamp));\n const dayEvents = storiesByDay.get(dayKey) ?? [];\n dayEvents.push(story);\n storiesByDay.set(dayKey, dayEvents);\n }\n\n for (const [day, dayStories] of storiesByDay) {\n lines.push(day);\n\n for (const story of dayStories) {\n const report = formatStory(story, {\n timezone,\n locale,\n detail,\n noteLimit,\n colors,\n });\n const { data } = report;\n const originLabel = formatOrigin(story.origin);\n const levelColor = getLevelColor(story.level);\n const label = (text: string) =>\n colors ? `${levelColor}${text}${ANSI.reset}` : text;\n\n const duration = data.duration ? ` (${data.duration})` : \"\";\n lines.push(`${label(\"Story\")}: ${story.title}`);\n lines.push(`${label(\"Level\")}: ${story.level}`);\n lines.push(`${label(\"Time\")}: ${data.when}${duration}`);\n\n if (originLabel) {\n lines.push(`${label(\"Origin\")}: ${originLabel}`);\n }\n\n if (data.error) {\n const errorLine = [\n data.error.name,\n data.error.message,\n ]\n .filter(Boolean)\n .join(\": \");\n if (errorLine) lines.push(`${label(\"Error\")}: ${errorLine}`);\n }\n\n if (detail !== \"brief\" && data.notes.length) {\n lines.push(` ${label(\"Notes\")}:`);\n\n for (const reportNote of data.notes) {\n lines.push(` ${reportNote.when} — ${reportNote.text}`);\n }\n\n if (story.notes.length > data.notes.length) {\n lines.push(\n ` … (${story.notes.length - data.notes.length} more)`\n );\n }\n }\n\n if (showData) {\n lines.push(`${label(\"Data\")}:`);\n const json = JSON.stringify(data, null, 2);\n if (colors) {\n const colored = colorizeJsonSections(json, {\n base: ANSI.grayLight,\n notes: ANSI.grayDark,\n reset: ANSI.reset,\n });\n lines.push(...colored);\n } else {\n lines.push(...json.split(\"\\n\"));\n }\n }\n\n lines.push(\"\");\n }\n }\n\n return lines.join(\"\\n\").trim() + \"\\n\";\n}\n"],"mappings":"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","names":["calculateNoteDuration"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/utils.ts","../src/audiences/consoleAudience.ts","../src/normalize.ts","../src/audiences/ndjsonAudience.ts","../src/formatting.ts","../src/storyteller.ts","../src/useStoryteller.ts","../src/audiences/dbAudience.ts","../src/report/writeStoryReport.ts"],"sourcesContent":["export * from \"./storyteller\";\nexport * from \"./normalize\";\nexport * from \"./formatting\";\nexport * from \"./useStoryteller\";\nexport * from \"./audiences/consoleAudience\";\nexport * from \"./audiences/dbAudience\";\nexport * from \"./audiences/ndjsonAudience\";\nexport * from \"./environment\";\nexport * from \"./report/writeStoryReport\";\nexport { ANSI, getLevelColor, formatOrigin, summarizeContext } from \"./utils\";\n","import type { StoryLevel } from \"./storyteller\";\n\n/**\n * Which default audience a storyteller registers.\n *\n * - `text` — colorized console output for a person watching\n * - `ndjson` — one JSON object per line for a program reading\n */\nexport type OutputFormat = \"text\" | \"ndjson\";\n\n/** Rank levels so a minimum threshold can be compared numerically */\nconst LEVEL_RANK: Record<StoryLevel, number> = {\n Information: 0,\n Warning: 1,\n Error: 2,\n};\n\n/** Accepted spellings for a level, in env vars and options alike */\nconst LEVEL_ALIASES: Record<string, StoryLevel> = {\n info: \"Information\",\n information: \"Information\",\n tell: \"Information\",\n warn: \"Warning\",\n warning: \"Warning\",\n oops: \"Error\",\n error: \"Error\",\n};\n\n/**\n * A level written any of the ways people and agents actually write it.\n * `report(\"...\", { level: \"warn\" })` should not be a type error.\n */\nexport type LevelInput =\n | StoryLevel\n | \"info\"\n | \"information\"\n | \"warn\"\n | \"warning\"\n | \"oops\"\n | \"error\";\n\n/**\n * Resolve any accepted level spelling to a stored level label.\n *\n * @param input - A level in any accepted spelling\n * @returns The canonical StoryLevel, defaulting to Information\n */\nexport function toStoryLevel(input?: LevelInput): StoryLevel {\n if (!input) return \"Information\";\n return LEVEL_ALIASES[String(input).toLowerCase()] ?? \"Information\";\n}\n\n/**\n * Read an environment variable, tolerating runtimes that have no environment at all.\n *\n * @param name - Variable name\n * @returns The trimmed value, or undefined when unset or unavailable\n */\nexport function readEnvironmentValue(name: string): string | undefined {\n try {\n const runtime = globalThis as {\n process?: { env?: Record<string, string | undefined> };\n };\n const value = runtime.process?.env?.[name];\n return typeof value === \"string\" && value.length ? value.trim() : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolve the minimum level to deliver, from an explicit option then `STORYTELLER_LEVEL`.\n *\n * @param requested - Explicit level, if the caller set one\n * @returns The threshold level, defaulting to Information (deliver everything)\n */\nexport function resolveMinimumLevel(requested?: StoryLevel | string): StoryLevel {\n const value = requested ?? readEnvironmentValue(\"STORYTELLER_LEVEL\");\n if (!value) return \"Information\";\n return LEVEL_ALIASES[String(value).toLowerCase()] ?? \"Information\";\n}\n\n/**\n * Check whether a level clears the configured minimum.\n *\n * @param level - The emission's level\n * @param minimum - The configured threshold\n */\nexport function meetsLevel(level: StoryLevel, minimum: StoryLevel): boolean {\n return LEVEL_RANK[level] >= LEVEL_RANK[minimum];\n}\n\n/**\n * Resolve which default audience to register, from an explicit option then\n * `STORYTELLER_FORMAT`.\n *\n * Deliberately not inferred from whether stdout is a TTY: output that silently\n * changes shape when a process is piped is a debugging afternoon nobody asked for.\n *\n * @param requested - Explicit format, if the caller set one\n * @returns The format, defaulting to text\n */\nexport function resolveOutputFormat(requested?: OutputFormat): OutputFormat {\n const value = requested ?? readEnvironmentValue(\"STORYTELLER_FORMAT\");\n return value === \"ndjson\" ? \"ndjson\" : \"text\";\n}\n\n/**\n * Resolve whether to colorize, from an explicit option then `STORYTELLER_COLOR`.\n *\n * @param requested - Explicit choice, if the caller set one\n * @returns Whether colors should be used, defaulting to true\n */\nexport function resolveColors(requested?: boolean): boolean {\n if (requested !== undefined) return requested;\n\n const value = readEnvironmentValue(\"STORYTELLER_COLOR\");\n if (value === undefined) return true;\n\n return !(value === \"0\" || value.toLowerCase() === \"false\");\n}\n","import type { StoryError, StoryLevel, StoryOrigin } from \"./storyteller\";\nimport type { JsonValue } from \"./normalize\";\n\n/** ANSI escape codes for terminal colorization */\nexport const ANSI = {\n reset: \"\\x1b[0m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n red: \"\\x1b[38;2;250;128;114m\",\n grayLight: \"\\x1b[37m\",\n grayDark: \"\\x1b[90m\",\n};\n\n/** Map a story level to its corresponding ANSI terminal color */\nexport function getLevelColor(level: StoryLevel): string {\n if (level === \"Information\") return ANSI.green;\n if (level === \"Warning\") return ANSI.yellow;\n return ANSI.red;\n}\n\n/** Format an origin context into a human-readable path like \"app / page / component\" */\nexport function formatOrigin(origin?: StoryOrigin): string | undefined {\n if (!origin?.where) return;\n if (typeof origin.where === \"string\") return origin.where;\n if (typeof origin.where !== \"object\" || Array.isArray(origin.where)) {\n return String(origin.where);\n }\n const whereRecord: Record<string, JsonValue> = origin.where;\n\n // Show well-known keys first in a natural order, then any additional fields\n const priorityKeys = [\"app\", \"service\", \"page\", \"component\"];\n const priorityParts = priorityKeys\n .filter((key) => whereRecord[key] != null)\n .map((key) => String(whereRecord[key]));\n const extraParts = Object.entries(whereRecord)\n .filter(([key, value]) => !priorityKeys.includes(key) && value != null)\n .map(([_, value]) => String(value));\n\n const parts = [...priorityParts, ...extraParts];\n return parts.length ? parts.join(\" / \") : undefined;\n}\n\n/** Colorize JSON output, dimming the notes section for visual hierarchy */\nexport function colorizeJsonSections(\n json: string,\n colors: { base: string; notes: string; reset: string }\n): string[] {\n const lines = json.split(\"\\n\");\n let insideNotes = false;\n let bracketDepth = 0;\n\n return lines.map((line) => {\n if (!insideNotes && line.includes('\"notes\": [')) {\n insideNotes = true;\n bracketDepth = countBrackets(line);\n return `${colors.notes}${line}${colors.reset}`;\n }\n\n if (insideNotes) {\n const colored = `${colors.notes}${line}${colors.reset}`;\n bracketDepth += countBrackets(line);\n if (bracketDepth <= 0) insideNotes = false;\n return colored;\n }\n\n return `${colors.base}${line}${colors.reset}`;\n });\n}\n\n/** Count the net bracket depth change in a line (opening brackets minus closing brackets) */\nexport function countBrackets(line: string): number {\n const openCount = (line.match(/\\[/g) || []).length;\n const closeCount = (line.match(/\\]/g) || []).length;\n return openCount - closeCount;\n}\n\n/** How much of a single note's context to show on one console line */\nconst CONTEXT_LINE_LIMIT = 120;\n\n/**\n * Condense a note's context into a short inline summary for one-line output.\n * The full values are always available on the story record and the NDJSON stream,\n * so this can afford to be lossy in favor of staying readable.\n *\n * @param note - The note's context fields\n * @returns A brace-wrapped summary, or undefined when there is no context\n */\nexport function summarizeContext(note: {\n note?: string;\n what?: JsonValue;\n where?: JsonValue;\n error?: StoryError;\n}): string | undefined {\n const parts: string[] = [];\n\n appendContextParts(parts, note.what);\n appendContextParts(parts, note.where);\n\n if (note.error) {\n const errorLine = [note.error.name, note.error.message].filter(Boolean).join(\": \");\n // Skip it when the note text was derived from this error — repeating it reads as noise\n if (errorLine && errorLine !== note.note) parts.push(errorLine);\n }\n\n if (!parts.length) return undefined;\n\n const joined = parts.join(\" \");\n const text = joined.length > CONTEXT_LINE_LIMIT\n ? `${joined.slice(0, CONTEXT_LINE_LIMIT)}…`\n : joined;\n\n return `{${text}}`;\n}\n\n/** Flatten one context value into `key=value` fragments */\nfunction appendContextParts(parts: string[], value?: JsonValue) {\n if (value == null) return;\n\n if (typeof value !== \"object\") {\n parts.push(String(value));\n return;\n }\n\n if (Array.isArray(value)) {\n parts.push(`[${value.length}]`);\n return;\n }\n\n for (const [key, entry] of Object.entries(value)) {\n if (entry == null) continue;\n if (key.startsWith(\"@\")) continue;\n parts.push(`${key}=${typeof entry === \"object\" ? summarizeNested(entry) : String(entry)}`);\n }\n}\n\n/** Render a nested context value as a size hint rather than expanding it inline */\nfunction summarizeNested(value: JsonValue): string {\n if (Array.isArray(value)) return `[${value.length}]`;\n if (value && typeof value === \"object\") return `{${Object.keys(value).length}}`;\n return String(value);\n}\n","import type { AudienceMember, Emission, EmissionKind, NoteEmission, StoryEvent, StoryLevel } from \"../storyteller\";\nimport { resolveColors } from \"../environment\";\nimport { ANSI, getLevelColor, formatOrigin, summarizeContext } from \"../utils\";\n\n/** Short level labels for compact live output */\nconst LEVEL_LABELS: Record<StoryLevel, string> = {\n Information: \"info\",\n Warning: \"warn\",\n Error: \"oops\",\n};\n\n/** Browser console styles by level, used for the grouped story header */\nconst LEVEL_STYLES: Record<StoryLevel, string> = {\n Information: \"color:#16a34a;font-weight:600\",\n Warning: \"color:#f59e0b;font-weight:600\",\n Error: \"color:#dc2626;font-weight:600\",\n};\n\nexport type ConsoleAudienceOptions = {\n /** Set false to strip ANSI colors from live note lines. Defaults to `STORYTELLER_COLOR`. */\n colors?: boolean;\n};\n\n/**\n * Create an audience that prints to the console: one compact line per note when\n * narration is live, and a color-coded grouped record when a story is told.\n *\n * Registered by default on every Storyteller instance. It listens for notes as well\n * as stories, so switching a storyteller to live narration shows something immediately\n * without registering anything extra.\n *\n * @param options - Rendering options for live note lines\n *\n * @example\n * ```ts\n * // Already included — but you can re-add after removing:\n * story.audience.add(consoleAudience());\n * ```\n */\nexport function consoleAudience(options: ConsoleAudienceOptions = {}): AudienceMember<EmissionKind> {\n const colors = resolveColors(options.colors);\n\n return {\n name: \"console\",\n hears: [\"note\", \"story\"],\n hear: (emission: Emission) => {\n if (emission.kind === \"note\") {\n printNote(emission, colors);\n return;\n }\n printStory(emission);\n },\n };\n}\n\n/**\n * Print a single beat as one line. Deliberately compact — at one emission per note,\n * a collapsed group and a pretty-printed payload per line is unreadable.\n */\nfunction printNote(note: NoteEmission, colors: boolean) {\n const time = readClockTime(note.timestamp);\n const label = LEVEL_LABELS[note.level];\n const origin = formatOrigin(note.origin);\n const context = summarizeContext(note);\n\n const head = colors\n ? `${getLevelColor(note.level)}${label}${ANSI.reset}`\n : label;\n\n const line = [\n colors ? `${ANSI.grayDark}${time}${ANSI.reset}` : time,\n head,\n origin ? (colors ? `${ANSI.grayDark}${origin}${ANSI.reset}` : origin) : undefined,\n note.note,\n context ? (colors ? `${ANSI.grayDark}${context}${ANSI.reset}` : context) : undefined,\n ]\n .filter(Boolean)\n .join(\" \");\n\n if (note.level === \"Information\") {\n console.log(line);\n } else if (note.level === \"Warning\") {\n console.warn(line);\n } else {\n console.error(line);\n }\n}\n\n/** Print a told story as a collapsed group with the full record inside */\nfunction printStory(event: StoryEvent) {\n const prefix = \"Storyteller\";\n const header = `${prefix}: ${event.title}`;\n\n console.groupCollapsed(`%c${header}`, LEVEL_STYLES[event.level]);\n\n const payload = JSON.stringify(event, null, 2);\n\n if (event.level === \"Information\") {\n console.log(header, payload);\n } else if (event.level === \"Warning\") {\n console.warn(header, payload);\n } else {\n console.error(header, payload);\n }\n\n console.groupEnd();\n}\n\n/** Extract HH:MM:SS from an ISO timestamp without paying for Intl on every note */\nfunction readClockTime(timestamp: string): string {\n const timePart = timestamp.slice(11, 19);\n return timePart.length === 8 ? timePart : timestamp;\n}\n","import type { StoryError } from \"./storyteller\";\n\n/** A value that survives JSON.stringify with no loss and no throwing */\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport type NormalizeOptions = {\n /** How many levels deep to descend before replacing the value with a truncation marker */\n maxDepth?: number;\n /** How many array entries to keep before truncating */\n maxArrayLength?: number;\n /** How many object properties to keep before truncating */\n maxProperties?: number;\n /** How many characters of a string to keep before truncating */\n maxStringLength?: number;\n /** Property names whose values are replaced with the redaction marker */\n redactKeys?: string[];\n /** Set false to keep secret-shaped values as-is */\n redact?: boolean;\n};\n\n/** Marker written in place of a value that matched a redacted key name */\nexport const REDACTED = \"[redacted]\";\n\n/**\n * Property names whose values are replaced with {@link REDACTED}.\n * Matching ignores case and separators, so `apiKey`, `api_key` and `API-KEY` all match.\n */\nexport const DEFAULT_REDACT_KEYS = [\n \"password\",\n \"passphrase\",\n \"token\",\n \"secret\",\n \"apiKey\",\n \"accessKey\",\n \"authorization\",\n \"auth\",\n \"cookie\",\n \"sessionId\",\n \"privateKey\",\n \"clientSecret\",\n \"refreshToken\",\n];\n\nconst DEFAULT_MAX_DEPTH = 6;\nconst DEFAULT_MAX_ARRAY_LENGTH = 100;\nconst DEFAULT_MAX_PROPERTIES = 100;\nconst DEFAULT_MAX_STRING_LENGTH = 8000;\n\n/** How far to follow an error's `cause` chain before stopping */\nconst MAX_CAUSE_DEPTH = 5;\n/** How many bytes of a binary value to include as a readable preview */\nconst BINARY_PREVIEW_BYTES = 16;\n\ntype ResolvedOptions = {\n maxDepth: number;\n maxArrayLength: number;\n maxProperties: number;\n maxStringLength: number;\n redactKeys: Set<string>;\n redact: boolean;\n};\n\n/**\n * Convert any value into a JSON-safe structure suitable for a story record.\n *\n * Handles the shapes real code actually holds — errors, dates, maps, sets, class\n * instances, binary buffers, circular references, throwing getters — and never throws,\n * so a hostile object logged by a caller cannot break the delivery pipeline.\n *\n * Data dropped for size is replaced with an explicit `@truncated` marker rather than\n * disappearing silently, so a consumer can tell the difference between \"this was empty\"\n * and \"this was too big\".\n *\n * @param input - Any value\n * @param options - Depth, size and redaction limits\n * @returns A value that JSON.stringify can always serialize\n *\n * @example\n * ```ts\n * normalizeValue({ user: new Map([[\"id\", 1]]), apiKey: \"sk-live-abc\" });\n * // { user: { \"@type\": \"Map\", entries: { id: 1 } }, apiKey: \"[redacted]\" }\n * ```\n */\nexport function normalizeValue(\n input: unknown,\n options: NormalizeOptions = {}\n): JsonValue {\n const resolved: ResolvedOptions = {\n maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,\n maxArrayLength: options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH,\n maxProperties: options.maxProperties ?? DEFAULT_MAX_PROPERTIES,\n maxStringLength: options.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH,\n redactKeys: new Set(\n (options.redactKeys ?? DEFAULT_REDACT_KEYS).map(normalizeKeyForMatching)\n ),\n redact: options.redact ?? true,\n };\n\n try {\n return normalizeUnknown(input, resolved, 0, \"$\", new Map());\n } catch (failure) {\n // The normalizer must never throw into the delivery pipeline\n return `[Unreadable: ${describeFailure(failure)}]`;\n }\n}\n\n/**\n * Convert an unknown thrown value into a serializable StoryError,\n * following the `cause` chain and collecting AggregateError members.\n *\n * @param rawError - Any thrown or rejected value\n * @param options - Depth, size and redaction limits applied to attached data\n * @returns A StoryError safe to store and serialize\n */\nexport function normalizeError(\n rawError: unknown,\n options: NormalizeOptions = {}\n): StoryError {\n const resolved: ResolvedOptions = {\n maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,\n maxArrayLength: options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH,\n maxProperties: options.maxProperties ?? DEFAULT_MAX_PROPERTIES,\n maxStringLength: options.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH,\n redactKeys: new Set(\n (options.redactKeys ?? DEFAULT_REDACT_KEYS).map(normalizeKeyForMatching)\n ),\n redact: options.redact ?? true,\n };\n\n return normalizeErrorInternal(rawError, resolved, 0);\n}\n\n/** Build a StoryError from any thrown value, bounded by the cause-chain depth */\nfunction normalizeErrorInternal(\n rawError: unknown,\n options: ResolvedOptions,\n causeDepth: number\n): StoryError {\n if (!(rawError instanceof Error)) {\n if (isPlainRecord(rawError)) {\n // Error-shaped objects from across a serialization boundary are common\n const record = rawError as Record<string, unknown>;\n const message = typeof record[\"message\"] === \"string\" ? record[\"message\"] : undefined;\n const name = typeof record[\"name\"] === \"string\" ? record[\"name\"] : undefined;\n if (message !== undefined || name !== undefined) {\n return {\n ...(name !== undefined ? { name } : {}),\n ...(message !== undefined ? { message } : {}),\n };\n }\n }\n return { message: safeStringify(rawError, options.maxStringLength) };\n }\n\n const normalized: StoryError = {\n name: rawError.name,\n message: rawError.message,\n };\n\n if (rawError.stack !== undefined) {\n normalized.stack = truncateString(rawError.stack, options.maxStringLength);\n }\n\n const cause = (rawError as { cause?: unknown }).cause;\n if (cause !== undefined) {\n if (causeDepth >= MAX_CAUSE_DEPTH) {\n normalized.cause = { \"@truncated\": { kind: \"causeChain\" } };\n } else if (cause instanceof Error) {\n normalized.cause = normalizeErrorInternal(cause, options, causeDepth + 1);\n } else {\n normalized.cause = normalizeUnknown(cause, options, 0, \"$.cause\", new Map());\n }\n }\n\n const aggregated = (rawError as { errors?: unknown }).errors;\n if (Array.isArray(aggregated)) {\n normalized.errors = aggregated\n .slice(0, options.maxArrayLength)\n .map((member) => normalizeErrorInternal(member, options, causeDepth + 1));\n }\n\n return normalized;\n}\n\n/** Recursively convert a value, tracking ancestors so cycles become readable markers */\nfunction normalizeUnknown(\n value: unknown,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n if (value === null) return null;\n\n const valueType = typeof value;\n\n if (valueType === \"string\") {\n return truncateString(value as string, options.maxStringLength);\n }\n\n if (valueType === \"number\") {\n // NaN and Infinity are not representable in JSON\n return Number.isFinite(value as number) ? (value as number) : String(value);\n }\n\n if (valueType === \"boolean\") return value as boolean;\n if (valueType === \"undefined\") return null;\n if (valueType === \"bigint\") return `${String(value)}n`;\n if (valueType === \"symbol\") return String(value as symbol);\n\n if (valueType === \"function\") {\n const name = (value as { name?: string }).name;\n return `[Function: ${name ? name : \"anonymous\"}]`;\n }\n\n const objectValue = value as object;\n\n const existingPath = ancestors.get(objectValue);\n if (existingPath !== undefined) {\n return `[Circular → ${existingPath}]`;\n }\n\n if (depth > options.maxDepth) {\n return { \"@truncated\": { kind: \"depth\", depth: options.maxDepth } };\n }\n\n const wellKnown = normalizeWellKnown(objectValue, options, depth, path, ancestors);\n if (wellKnown !== undefined) return wellKnown;\n\n ancestors.set(objectValue, path);\n try {\n if (Array.isArray(objectValue)) {\n return normalizeArray(objectValue, options, depth, path, ancestors);\n }\n return normalizeObject(objectValue, options, depth, path, ancestors);\n } finally {\n // Only ancestors count as cycles — the same object appearing twice in a\n // tree is repetition, not recursion, and should render both times\n ancestors.delete(objectValue);\n }\n}\n\n/**\n * Convert the built-in object types that need a dedicated shape.\n * Returns undefined when the value is an ordinary array or object.\n */\nfunction normalizeWellKnown(\n value: object,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue | undefined {\n if (value instanceof Error) {\n return normalizeErrorInternal(value, options, 0) as unknown as JsonValue;\n }\n\n if (value instanceof Date) {\n const time = value.getTime();\n return Number.isNaN(time) ? \"[Invalid Date]\" : value.toISOString();\n }\n\n if (value instanceof RegExp) return String(value);\n if (value instanceof URL) return value.href;\n\n if (value instanceof Map) {\n const entries: Record<string, JsonValue> = {};\n let index = 0;\n let omitted = 0;\n for (const [entryKey, entryValue] of value) {\n if (index >= options.maxProperties) {\n omitted += 1;\n continue;\n }\n const keyLabel = safeStringify(entryKey, options.maxStringLength);\n entries[keyLabel] = redactOrNormalize(\n keyLabel,\n entryValue,\n options,\n depth + 1,\n `${path}.${keyLabel}`,\n ancestors\n );\n index += 1;\n }\n return {\n \"@type\": \"Map\",\n entries,\n ...(omitted ? { \"@truncated\": { kind: \"mapEntries\", omitted } } : {}),\n };\n }\n\n if (value instanceof Set) {\n const values: JsonValue[] = [];\n let omitted = 0;\n for (const member of value) {\n if (values.length >= options.maxArrayLength) {\n omitted += 1;\n continue;\n }\n values.push(\n normalizeUnknown(member, options, depth + 1, `${path}[${values.length}]`, ancestors)\n );\n }\n return {\n \"@type\": \"Set\",\n values,\n ...(omitted ? { \"@truncated\": { kind: \"setValues\", omitted } } : {}),\n };\n }\n\n if (value instanceof WeakMap) return \"[WeakMap]\";\n if (value instanceof WeakSet) return \"[WeakSet]\";\n if (value instanceof Promise) return \"[Promise]\";\n\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {\n return describeBinary(value);\n }\n\n const converted = callToJson(value);\n if (converted !== undefined) {\n return normalizeUnknown(converted, options, depth, path, ancestors);\n }\n\n return undefined;\n}\n\n/** Call a value's toJSON() if it has one, returning undefined when it has none or it throws */\nfunction callToJson(value: object): unknown {\n let toJson: unknown;\n try {\n toJson = (value as { toJSON?: unknown }).toJSON;\n } catch {\n return undefined;\n }\n\n if (typeof toJson !== \"function\") return undefined;\n\n try {\n return (toJson as () => unknown).call(value);\n } catch (failure) {\n return `[Unreadable: ${describeFailure(failure)}]`;\n }\n}\n\n/** Describe a binary value by its size and leading bytes rather than dumping its contents */\nfunction describeBinary(value: ArrayBufferView | ArrayBuffer): JsonValue {\n const typeName = readConstructorName(value) ?? \"ArrayBuffer\";\n const byteLength = value.byteLength;\n\n let preview: string;\n try {\n const bytes =\n value instanceof ArrayBuffer\n ? new Uint8Array(value, 0, Math.min(BINARY_PREVIEW_BYTES, byteLength))\n : new Uint8Array(\n value.buffer,\n value.byteOffset,\n Math.min(BINARY_PREVIEW_BYTES, value.byteLength)\n );\n preview = [...bytes].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\" \");\n } catch {\n preview = \"\";\n }\n\n return {\n \"@type\": typeName,\n byteLength,\n ...(preview ? { preview } : {}),\n ...(byteLength > BINARY_PREVIEW_BYTES\n ? { \"@truncated\": { kind: \"bytes\", omitted: byteLength - BINARY_PREVIEW_BYTES } }\n : {}),\n };\n}\n\n/** Convert an array, keeping at most maxArrayLength entries */\nfunction normalizeArray(\n value: unknown[],\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n const kept: JsonValue[] = [];\n const limit = Math.min(value.length, options.maxArrayLength);\n\n for (let index = 0; index < limit; index += 1) {\n kept.push(\n normalizeUnknown(value[index], options, depth + 1, `${path}[${index}]`, ancestors)\n );\n }\n\n if (value.length > limit) {\n kept.push({ \"@truncated\": { kind: \"array\", omitted: value.length - limit } });\n }\n\n return kept;\n}\n\n/** Convert a plain object or class instance, tagging the class name when there is one */\nfunction normalizeObject(\n value: object,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n const result: Record<string, JsonValue> = {};\n\n const className = readConstructorName(value);\n if (className && className !== \"Object\") {\n result[\"@type\"] = className;\n }\n\n let keys: string[];\n try {\n keys = Object.keys(value);\n } catch {\n return `[Unreadable: keys could not be listed]`;\n }\n\n let kept = 0;\n let omitted = 0;\n for (const key of keys) {\n if (kept >= options.maxProperties) {\n omitted += 1;\n continue;\n }\n\n let propertyValue: unknown;\n try {\n propertyValue = (value as Record<string, unknown>)[key];\n } catch (failure) {\n // A getter that throws must not take the whole record down with it\n result[key] = `[Unreadable: ${describeFailure(failure)}]`;\n kept += 1;\n continue;\n }\n\n // JSON.stringify drops undefined properties; match that so records stay clean\n if (propertyValue === undefined) continue;\n\n result[key] = redactOrNormalize(\n key,\n propertyValue,\n options,\n depth + 1,\n `${path}.${key}`,\n ancestors\n );\n kept += 1;\n }\n\n // Only count properties dropped for the size limit — a property skipped because\n // its value was undefined is absent from JSON.stringify output too, not truncated\n if (omitted) {\n result[\"@truncated\"] = { kind: \"properties\", omitted };\n }\n\n return result;\n}\n\n/** Replace secret-shaped values with the redaction marker, otherwise normalize normally */\nfunction redactOrNormalize(\n key: string,\n value: unknown,\n options: ResolvedOptions,\n depth: number,\n path: string,\n ancestors: Map<object, string>\n): JsonValue {\n if (options.redact && options.redactKeys.has(normalizeKeyForMatching(key))) {\n return REDACTED;\n }\n return normalizeUnknown(value, options, depth, path, ancestors);\n}\n\n/** Reduce a property name to letters and digits so casing and separators do not matter */\nfunction normalizeKeyForMatching(key: string): string {\n return key.replace(/[^a-zA-Z0-9]/g, \"\").toLowerCase();\n}\n\n/** Read a value's class name, tolerating null-prototype objects and hostile proxies */\nfunction readConstructorName(value: object): string | undefined {\n try {\n const prototype = Object.getPrototypeOf(value) as { constructor?: { name?: string } } | null;\n if (prototype === null) return undefined;\n const name = prototype.constructor?.name;\n return typeof name === \"string\" && name.length ? name : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Check whether a value is a non-array object with an ordinary prototype */\nfunction isPlainRecord(value: unknown): boolean {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Cut a string to length, marking inline how many characters were dropped */\nfunction truncateString(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength)}…[+${value.length - maxLength} chars]`;\n}\n\n/** Convert any value to a short string without risking a throw from a custom toString */\nfunction safeStringify(value: unknown, maxLength: number): string {\n try {\n return truncateString(String(value), maxLength);\n } catch {\n return \"[unstringifiable]\";\n }\n}\n\n/** Extract a readable message from a value thrown while normalizing */\nfunction describeFailure(failure: unknown): string {\n if (failure instanceof Error && failure.message) return failure.message;\n try {\n return String(failure);\n } catch {\n return \"unknown error\";\n }\n}\n","import type { AudienceMember, Emission, EmissionKind, StoryLevel } from \"../storyteller\";\nimport { meetsLevel, resolveMinimumLevel } from \"../environment\";\nimport { normalizeValue } from \"../normalize\";\n\n/** Anything that can take a line of text — a Node stream, or your own sink */\nexport type LineWriter = {\n write: (chunk: string) => unknown;\n};\n\nexport type NdjsonAudienceOptions = {\n /** Where lines go. Defaults to stdout in Node, console.log elsewhere. */\n stream?: LineWriter;\n /** Register under a different name, e.g. to run two streams at once */\n name?: string;\n /** Minimum level to write. Defaults to `STORYTELLER_LEVEL`, then everything. */\n level?: StoryLevel;\n};\n\n/**\n * Create an audience that writes one JSON object per line — every note and every\n * story, nothing else on the channel.\n *\n * This is the format to give a program: a log shipper, `jq`, or an agent reading\n * another process's output. Each line parses on its own, and `storyId` plus\n * `sequence` let a reader group streamed notes back into their story.\n *\n * @param options - Stream, name and level threshold\n *\n * @example\n * ```ts\n * story.audience.remove(\"console\");\n * story.audience.add(ndjsonAudience({ stream: process.stderr }));\n * ```\n */\nexport function ndjsonAudience(options: NdjsonAudienceOptions = {}): AudienceMember<EmissionKind> {\n const writer = options.stream ?? createDefaultWriter();\n const minimumLevel = resolveMinimumLevel(options.level);\n\n return {\n name: options.name ?? \"ndjson\",\n hears: [\"note\", \"story\"],\n accepts: (emission: Emission) => meetsLevel(emission.level, minimumLevel),\n hear: (emission: Emission) => {\n writer.write(`${serializeEmission(emission)}\\n`);\n },\n };\n}\n\n/**\n * Serialize an emission to a single line, falling back to a normalized copy if the\n * emission somehow resists stringifying. An audience must not be able to throw.\n */\nfunction serializeEmission(emission: Emission): string {\n try {\n return JSON.stringify(emission);\n } catch {\n try {\n return JSON.stringify(normalizeValue(emission));\n } catch {\n return JSON.stringify({\n kind: emission.kind,\n level: emission.level,\n error: \"[Unserializable emission]\",\n });\n }\n }\n}\n\n/** Write to stdout where there is one, and fall back to the console everywhere else */\nfunction createDefaultWriter(): LineWriter {\n const runtime = globalThis as {\n process?: { stdout?: { write?: (chunk: string) => unknown } };\n };\n\n const write = runtime.process?.stdout?.write;\n if (typeof write === \"function\") {\n const stdout = runtime.process!.stdout!;\n return { write: (chunk: string) => write.call(stdout, chunk) };\n }\n\n // console.log adds its own newline, so hand it the line without one\n return { write: (chunk: string) => console.log(chunk.replace(/\\n$/, \"\")) };\n}\n","import type {\n StoryEventBase,\n StoryNote,\n ReportNote,\n StoryReport,\n FormattedReport,\n ReportOptions,\n} from \"./storyteller\";\nimport { ANSI, getLevelColor, formatOrigin, colorizeJsonSections } from \"./utils\";\n\n/**\n * Format a story event into a human-readable report with optional colors.\n *\n * @param story - The story event to format\n * @param options - Formatting options (timezone, locale, detail level, colors)\n * @returns A FormattedReport with both text (for display) and data (structured)\n *\n * @example\n * ```ts\n * const report = formatStory(event, { colors: false, detail: \"brief\" });\n * console.log(report.text);\n * ```\n */\nexport function formatStory(\n story: StoryEventBase,\n options: ReportOptions = {}\n): FormattedReport {\n const {\n timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,\n locale = \"en-US\",\n detail = \"normal\",\n noteLimit = 50,\n showData = true,\n colors = true,\n } = options;\n\n const dateTimeFormatter = new Intl.DateTimeFormat(locale, {\n timeZone: timezone,\n year: \"numeric\",\n month: \"short\",\n day: \"2-digit\",\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n\n const timeFormatter = new Intl.DateTimeFormat(locale, {\n timeZone: timezone,\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n\n // Notes are already sorted if coming from buildEvent; sort again for standalone use\n const orderedNotes = [...story.notes].sort(\n (noteA, noteB) => Date.parse(noteA.timestamp) - Date.parse(noteB.timestamp)\n );\n\n // Use pre-computed durationMs from the event if available, otherwise compute\n const durationMs = story.durationMs ?? calculateNoteDuration(orderedNotes);\n const duration = durationMs != null ? formatDuration(durationMs) : undefined;\n\n const slicedNotes = orderedNotes.slice(0, noteLimit);\n const reportNotes: ReportNote[] = slicedNotes.map((note) => ({\n timestamp: note.timestamp,\n when: timeFormatter.format(new Date(note.timestamp)),\n note: note.note,\n text: formatNoteText(note, detail),\n ...(note.who ? { who: note.who } : {}),\n ...(note.what ? { what: note.what } : {}),\n ...(note.where ? { where: note.where } : {}),\n ...(note.error ? { error: note.error } : {}),\n }));\n\n const data: StoryReport = {\n title: story.title,\n level: story.level,\n when: dateTimeFormatter.format(new Date(story.timestamp)),\n ...(durationMs != null ? { durationMs } : {}),\n ...(duration ? { duration } : {}),\n ...(story.origin ? { origin: story.origin } : {}),\n notes: reportNotes,\n ...(story.error ? { error: story.error } : {}),\n };\n\n const lines = buildReportText(story, data, reportNotes, orderedNotes, {\n colors,\n detail,\n showData,\n duration,\n });\n\n return { text: lines.join(\"\\n\"), data };\n}\n\n/** Build the human-readable text lines for a story report */\nfunction buildReportText(\n story: StoryEventBase,\n data: StoryReport,\n reportNotes: ReportNote[],\n orderedNotes: StoryNote[],\n options: { colors: boolean; detail: string; showData: boolean; duration?: string | undefined }\n): string[] {\n const levelColor = getLevelColor(story.level);\n const label = (text: string) =>\n options.colors ? `${levelColor}${text}${ANSI.reset}` : text;\n const originLabel = formatOrigin(story.origin);\n\n const lines: string[] = [];\n lines.push(`${label(\"Story\")}: ${story.title}`);\n lines.push(`${label(\"Level\")}: ${story.level}`);\n lines.push(`${label(\"Time\")}: ${data.when}${options.duration ? ` (${options.duration})` : \"\"}`);\n\n if (originLabel) {\n lines.push(`${label(\"Origin\")}: ${originLabel}`);\n }\n\n if (story.error) {\n const errorLine = [story.error.name, story.error.message]\n .filter(Boolean)\n .join(\": \");\n if (errorLine) lines.push(`${label(\"Error\")}: ${errorLine}`);\n }\n\n if (options.detail !== \"brief\" && reportNotes.length) {\n lines.push(`${label(\"Notes\")}:`);\n for (const note of reportNotes) {\n lines.push(` ${note.when} — ${note.text}`);\n }\n if (orderedNotes.length > reportNotes.length) {\n lines.push(` … (${orderedNotes.length - reportNotes.length} more)`);\n }\n }\n\n if (options.showData) {\n lines.push(`${label(\"Data\")}:`);\n const json = JSON.stringify(data, null, 2);\n if (options.colors) {\n const colored = colorizeJsonSections(json, {\n base: ANSI.grayLight,\n notes: ANSI.grayDark,\n reset: ANSI.reset,\n });\n lines.push(...colored);\n } else {\n lines.push(...json.split(\"\\n\"));\n }\n }\n\n return lines;\n}\n\n/** Calculate the duration between the first and last note in a sequence */\nfunction calculateNoteDuration(notes: StoryNote[]): number | undefined {\n if (notes.length <= 1) return undefined;\n\n const startTime = Date.parse(notes[0]!.timestamp);\n const endTime = Date.parse(notes[notes.length - 1]!.timestamp);\n\n return Number.isFinite(startTime) && Number.isFinite(endTime)\n ? Math.max(0, endTime - startTime)\n : undefined;\n}\n\n/** Convert milliseconds into a human-readable duration string */\nexport function formatDuration(milliseconds: number): string {\n if (milliseconds < 1000) return `${milliseconds}ms`;\n const seconds = milliseconds / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = Math.round(seconds % 60)\n .toString()\n .padStart(2, \"0\");\n return `${minutes}:${remainingSeconds}m`;\n}\n\n/** @deprecated Use formatStory instead */\nexport const summarizeStory = formatStory;\n\n/** Format a note's text with optional context details when detail level is \"full\" */\nfunction formatNoteText(\n note: StoryNote,\n verbosity: \"brief\" | \"normal\" | \"full\"\n): string {\n if (verbosity !== \"full\") return note.note;\n\n const details: string[] = [];\n const what = note.what;\n const where = note.where;\n\n if (typeof what === \"string\") {\n details.push(`what=${what}`);\n } else if (what) {\n for (const [key, value] of Object.entries(what)) {\n if (value != null) details.push(`${key}=${String(value)}`);\n }\n }\n if (typeof where === \"string\") {\n details.push(`where=${where}`);\n } else if (where) {\n for (const [key, value] of Object.entries(where)) {\n if (value != null) details.push(`${key}=${String(value)}`);\n }\n }\n if (note.error) {\n const errorLine = [note.error.name, note.error.message]\n .filter(Boolean)\n .join(\": \");\n if (errorLine) details.push(`error=${errorLine}`);\n }\n\n return details.length\n ? `${note.note} (${details.join(\" \")})`\n : note.note;\n}\n","import { consoleAudience } from \"./audiences/consoleAudience\";\nimport { ndjsonAudience } from \"./audiences/ndjsonAudience\";\nimport type { LevelInput, OutputFormat } from \"./environment\";\nimport {\n meetsLevel,\n resolveMinimumLevel,\n resolveOutputFormat,\n readEnvironmentValue,\n toStoryLevel,\n} from \"./environment\";\n\nexport type { LevelInput } from \"./environment\";\nimport { formatStory } from \"./formatting\";\nimport type { JsonValue } from \"./normalize\";\nimport { normalizeError, normalizeValue } from \"./normalize\";\n\n/** Human-readable level labels stored in story records */\nexport type StoryLevel = \"Information\" | \"Warning\" | \"Error\";\n\n/**\n * A stored context value. Always JSON-safe — whatever the caller passed in has\n * already been through the normalizer by the time it reaches a record.\n */\nexport type StoryContextValue = JsonValue;\n\n/** Context accepted from callers. Anything goes; the normalizer makes it storable. */\nexport type StoryContextInput = unknown;\n\nexport type StoryError = {\n name?: string;\n message?: string;\n stack?: string;\n cause?: JsonValue;\n /** Members of an AggregateError */\n errors?: StoryError[];\n};\n\n/** Origin as stored on a record */\nexport type StoryOrigin = {\n who?: StoryContextValue;\n what?: StoryContextValue;\n where?: StoryContextValue;\n};\n\n/** Origin as accepted from callers */\nexport type StoryOriginInput = {\n who?: StoryContextInput;\n what?: StoryContextInput;\n where?: StoryContextInput;\n};\n\nexport type StoryNote = {\n timestamp: string;\n /**\n * Position within the story, assigned when the note is taken. Gap-free from 0.\n * Optional so records written before sequencing existed still typecheck.\n */\n sequence?: number;\n note: string;\n /** Omitted when the note carries the story's default Information level */\n level?: StoryLevel;\n who?: StoryContextValue;\n what?: StoryContextValue;\n where?: StoryContextValue;\n error?: StoryError;\n};\n\nexport type StoryEventBase = {\n timestamp: string;\n level: StoryLevel;\n title: string;\n\n /**\n * Correlates every note emission with the story it belongs to. Always set on\n * events this library builds; optional so older stored records still typecheck.\n */\n storyId?: string;\n\n /**\n * The story this one is a chapter of. Absent on a top-level story.\n * Following this field reconstructs the tree of a nested run.\n */\n parentStoryId?: string;\n\n origin?: StoryOrigin;\n\n notes: StoryNote[];\n durationMs?: number;\n\n /**\n * How many emissions were dropped for back-pressure while this story was being\n * collected. Present only when something was actually lost, so the loss shows up\n * in the record instead of vanishing.\n */\n droppedEmissions?: number;\n\n error?: StoryError;\n};\n\nexport type ReportOptions = {\n timezone?: string;\n locale?: string;\n detail?: \"brief\" | \"normal\" | \"full\";\n noteLimit?: number;\n showData?: boolean;\n colors?: boolean;\n};\n\n/** @deprecated Use ReportOptions instead */\nexport type StorySummaryOptions = ReportOptions;\n\nexport type PreviewOptions = ReportOptions & {\n title?: string;\n level?: StoryLevel;\n error?: unknown;\n};\n\n/** @deprecated Use PreviewOptions instead */\nexport type StoryPreviewOptions = PreviewOptions;\n\nexport type ReportNote = {\n timestamp: string;\n when: string;\n note: string;\n text: string;\n who?: StoryContextValue;\n what?: StoryContextValue;\n where?: StoryContextValue;\n error?: StoryError;\n};\n\n/** @deprecated Use ReportNote instead */\nexport type StorySummaryNote = ReportNote;\n\nexport type StoryReport = {\n title: string;\n level: StoryLevel;\n when: string;\n durationMs?: number;\n duration?: string;\n origin?: StoryEventBase[\"origin\"];\n notes: ReportNote[];\n error?: StoryError;\n};\n\n/** @deprecated Use StoryReport instead */\nexport type StorySummaryData = StoryReport;\n\nexport type FormattedReport = {\n text: string;\n data: StoryReport;\n};\n\n/** @deprecated Use FormattedReport instead */\nexport type StorySummary = FormattedReport;\n\nexport type StoryEvent = StoryEventBase & {\n kind: \"story\";\n summarize: (options?: ReportOptions) => FormattedReport;\n};\n\n/** @deprecated Use StoryEvent — the story-shaped emission */\nexport type StoryEmission = StoryEvent;\n\n/** The two things an audience can hear */\nexport type EmissionKind = \"note\" | \"story\";\n\n/**\n * A single beat, delivered the moment it happens when narration is live.\n *\n * `storyId` and `sequence` are what make streaming lossless: a consumer holding\n * the beats of a story can order and group them back into the record that\n * collected narration would have produced.\n */\nexport type NoteEmission = StoryNote & {\n kind: \"note\";\n storyId: string;\n parentStoryId?: string;\n sequence: number;\n level: StoryLevel;\n origin?: StoryOrigin;\n};\n\nexport type Emission = NoteEmission | StoryEvent;\n\n/** The emission type an audience receives, given the kinds it hears */\nexport type EmissionOf<Kind extends EmissionKind> = Kind extends \"note\"\n ? NoteEmission\n : StoryEvent;\n\n/**\n * An audience, typed by what it hears.\n *\n * With no `hears`, it hears stories only and `accepts`/`hear` receive a\n * `StoryEvent` — so an audience written before live narration existed compiles\n * unchanged, including one that hands the event to a helper typed for\n * `StoryEvent`. `hears: [\"note\"]` gives `NoteEmission`; `[\"note\", \"story\"]`\n * gives the union, and the code narrows on `kind`.\n */\nexport type AudienceMember<Kind extends EmissionKind = \"story\"> = {\n name: string;\n /** Which emission kinds this audience wants. Defaults to `[\"story\"]`. */\n hears?: Kind[];\n accepts?(emission: EmissionOf<Kind>): boolean;\n hear(emission: EmissionOf<Kind>): void | Promise<void>;\n};\n\n/** Any audience, whatever it hears — the shape the registry stores */\nexport type AnyAudienceMember = AudienceMember<EmissionKind>;\n\n/**\n * How a storyteller narrates.\n *\n * - `collected` — beats are buffered and leave as one story record (the default)\n * - `live` — each beat is emitted as it happens, and the story still lands at the end\n *\n * Live narration adds emissions, it never removes them: a consumer that only wants\n * beats says so with `hears: [\"note\"]` rather than by silencing the record.\n */\nexport type Narration = \"collected\" | \"live\";\n\n/** @deprecated `both` is now the behavior of `live` — beats stream and the story still lands */\nexport type NarrationInput = Narration | \"both\";\n\nexport type NoteData = {\n who?: StoryContextInput;\n what?: StoryContextInput;\n where?: StoryContextInput;\n error?: unknown;\n /** Level for this beat alone. Defaults to Information. */\n level?: LevelInput;\n /** Emit this beat immediately even when narration is collected */\n live?: boolean;\n /** Deliver this beat only to the named audiences */\n to?: string[];\n};\n\nexport type FinishOptions = {\n /** Defaults to Information */\n level?: LevelInput;\n /** The error that ended the story, normalized onto the record */\n error?: unknown;\n};\n\nexport type ChapterOptions = {\n /** Merged over the parent's origin */\n origin?: StoryOriginInput;\n /** Defaults to the parent's setting */\n narration?: NarrationInput;\n /** Defaults to the parent's setting */\n level?: LevelInput;\n /** Defaults to the parent's handler */\n onAudienceError?: AudienceErrorHandler;\n /** Defaults to the parent's bound */\n maxInFlight?: number;\n};\n\nexport type StorytellerOptions = {\n origin?: StoryOriginInput;\n audiences?: AnyAudienceMember[];\n /** Defaults to `STORYTELLER_NARRATION`, then `collected` */\n narration?: NarrationInput;\n /**\n * Which default audience to register: colorized text for a person, NDJSON for a\n * program. Defaults to `STORYTELLER_FORMAT`, then `text`.\n */\n format?: OutputFormat;\n /**\n * Share another storyteller's audience registry instead of creating one.\n * Audiences added to it later reach this storyteller too. When given, no\n * default audience is registered — the registry already has whatever it has.\n */\n audience?: AudienceRegistry;\n /** The story this one is a chapter of. Set by `chapter()`. */\n parentStoryId?: string;\n /**\n * Drop emissions below this level before they reach any audience.\n * Defaults to `STORYTELLER_LEVEL`, then Information (deliver everything).\n */\n level?: LevelInput;\n /**\n * Called when an audience throws or rejects. Without one, a single throttled\n * warning per audience goes to the console — a logging library that loses\n * records in silence is worse than one that complains.\n */\n onAudienceError?: AudienceErrorHandler;\n /**\n * Cap on deliveries in flight to a single audience at once. Live narration is\n * fire-and-forget, so a slow audience would otherwise grow an unbounded queue.\n * Past the cap, emissions are dropped and counted on the closing story.\n */\n maxInFlight?: number;\n};\n\n/** Called when an audience member throws or rejects while hearing an emission */\nexport type AudienceErrorHandler = (\n error: unknown,\n member: AnyAudienceMember,\n emission: Emission\n) => void;\n\n/** Manages the set of audience members that receive story events */\nexport class AudienceRegistry {\n private members = new Map<string, AnyAudienceMember>();\n\n /** Register an audience member, replacing any existing member with the same name */\n add<Kind extends EmissionKind = \"story\">(member: AudienceMember<Kind>) {\n // Delivery routes by `hears` at runtime, so a member typed for one kind is\n // only ever handed that kind; widening the stored type is safe here and\n // nowhere else.\n this.members.set(member.name, member as AnyAudienceMember);\n return this;\n }\n\n /** Remove an audience member by name */\n remove(name: string) {\n this.members.delete(name);\n return this;\n }\n\n /** Return all registered audience members */\n getAll() {\n return [...this.members.values()];\n }\n\n /** Return only the audience members matching the given names */\n getOnly(names: string[]) {\n return names.map((name) => this.members.get(name)).filter(Boolean) as AnyAudienceMember[];\n }\n\n /** Check if an audience member is registered by name */\n has(name: string) {\n return this.members.has(name);\n }\n\n /** List the names of all registered audience members */\n names() {\n return [...this.members.keys()];\n }\n}\n\n/**\n * Collects timestamped notes and emits them as one structured story — and, when\n * narration is live, emits each note the moment it is taken.\n *\n * @example\n * ```ts\n * const story = new Storyteller({ origin: { who: \"api-server\" }, narration: \"live\" });\n * story.report(\"Request received\", { what: { path: \"/checkout\" } });\n * story.report(\"Validated cart\");\n * story.finish(\"Checkout started\");\n * ```\n */\nexport class Storyteller {\n public readonly audience: AudienceRegistry;\n\n private readonly origin?: StoryOrigin;\n private readonly parentStoryId?: string;\n private notes: StoryNote[] = [];\n private narration: Narration;\n private readonly minimumLevel: StoryLevel;\n private readonly onAudienceError: AudienceErrorHandler;\n private readonly maxInFlight: number;\n\n /** Deliveries currently awaiting each audience, keyed by audience name */\n private readonly inFlight = new Map<string, number>();\n /** Emissions dropped for back-pressure since the current story began */\n private droppedEmissions = 0;\n\n /** Identifies the story currently being collected; regenerated after each telling */\n private storyId = createStoryId();\n /** Position of the next note within the current story */\n private nextSequence = 0;\n\n constructor(options?: StorytellerOptions) {\n const normalizedOrigin = normalizeOrigin(options?.origin);\n if (normalizedOrigin) {\n this.origin = normalizedOrigin;\n }\n\n if (options?.parentStoryId !== undefined) {\n this.parentStoryId = options.parentStoryId;\n }\n\n this.narration = resolveNarration(options?.narration);\n this.minimumLevel = resolveMinimumLevel(options?.level);\n this.onAudienceError = options?.onAudienceError ?? reportAudienceErrorToConsole;\n this.maxInFlight = options?.maxInFlight ?? DEFAULT_MAX_IN_FLIGHT;\n\n if (options?.audience) {\n // A shared registry already holds its audiences, including any the caller\n // customized. Adding a default here would replace them by name.\n this.audience = options.audience;\n } else {\n this.audience = new AudienceRegistry();\n\n // Every storyteller gets a default audience. Which one depends on who is\n // reading: a person at a terminal, or a program parsing the stream.\n this.audience.add(\n resolveOutputFormat(options?.format) === \"ndjson\"\n ? ndjsonAudience({ level: this.minimumLevel })\n : consoleAudience()\n );\n }\n\n options?.audiences?.forEach((audience) => this.audience.add(audience));\n }\n\n /**\n * Switch between collected and live narration at runtime.\n * Takes effect on the next note; already-buffered notes are not replayed.\n *\n * @param narration - `collected` to buffer, `live` to emit each note as it happens\n * @returns `this` for chaining\n */\n narrate(narration: NarrationInput) {\n this.narration = resolveNarration(narration);\n return this;\n }\n\n /** The id of the story currently being collected */\n get currentStoryId() {\n return this.storyId;\n }\n\n /**\n * Start a chapter: a child storyteller whose stories are linked back to this\n * one by `parentStoryId`.\n *\n * Real work nests — an agent spawns subtasks, a batch runs per-item operations.\n * A chapter keeps each of those a complete story in its own right while leaving\n * the run reconstructable as a tree.\n *\n * The child shares this storyteller's audience registry, so audiences added\n * later reach it too, and inherits narration, level and delivery settings.\n * Its stories are separate records — a chapter is not folded into the parent's\n * notes.\n *\n * @param options - Origin to merge over the parent's, and any setting to override\n * @returns A child Storyteller\n *\n * @example\n * ```ts\n * for (const account of accounts) {\n * const chapter = story.chapter({ origin: { what: account.id } });\n * chapter.report(\"Fetching invoices\");\n * chapter.finish(`Synced ${account.id}`);\n * }\n * ```\n */\n chapter(options: ChapterOptions = {}): Storyteller {\n const mergedOrigin: StoryOriginInput = { ...this.origin, ...options.origin };\n\n return new Storyteller({\n audience: this.audience,\n // Captured now, so a parent that finishes first does not orphan its chapters\n parentStoryId: this.storyId,\n ...(Object.keys(mergedOrigin).length ? { origin: mergedOrigin } : {}),\n narration: options.narration ?? this.narration,\n level: options.level ?? this.minimumLevel,\n onAudienceError: options.onAudienceError ?? this.onAudienceError,\n maxInFlight: options.maxInFlight ?? this.maxInFlight,\n });\n }\n\n /**\n * Report a beat of the current story.\n *\n * In collected narration the beat is buffered and leaves with the story. In live\n * narration it is emitted the moment you call this, so whoever is tuned in sees\n * the work as it happens.\n *\n * Accepts anything, not just a string — pass an error, an API response, a Map, a\n * class instance — and the value is normalized into a storable shape with the note\n * text derived from it.\n *\n * @param input - What happened: a message, or any value to describe\n * @param data - Optional context: who did it, what was involved, where it happened, any error\n * @returns `this` for chaining\n *\n * @example\n * ```ts\n * story.report(\"Card charged\", { what: { amount: \"$42\" }, where: \"stripe\" });\n * story.report(await response.json());\n * ```\n */\n report(input: unknown, data: NoteData = {}) {\n const described = describeInput(input);\n const level = toStoryLevel(data.level);\n\n const note: StoryNote = {\n timestamp: new Date().toISOString(),\n sequence: this.nextSequence,\n note: described.text,\n ...(level !== \"Information\" ? { level } : {}),\n ...(data.who !== undefined ? { who: normalizeValue(data.who) } : {}),\n ...(data.what !== undefined\n ? { what: normalizeValue(data.what) }\n : described.what !== undefined\n ? { what: described.what }\n : {}),\n ...(data.where !== undefined ? { where: normalizeValue(data.where) } : {}),\n ...(data.error !== undefined\n ? { error: normalizeError(data.error) }\n : described.error !== undefined\n ? { error: described.error }\n : {}),\n };\n\n this.nextSequence += 1;\n this.notes.push(note);\n\n if (this.narration === \"live\" || data.live) {\n this.emitNote(note, level, data.to);\n }\n\n return this;\n }\n\n /** Clear all accumulated notes without emitting a story, and start a new story id */\n reset() {\n this.notes = [];\n this.startNewStory();\n return this;\n }\n\n /** Preview the current notes as a formatted report without emitting or clearing them */\n summarize(options: PreviewOptions = {}) {\n const {\n title = \"Story preview\",\n level = \"Information\",\n error,\n ...reportOptions\n } = options;\n const event: StoryEventBase = {\n timestamp: new Date().toISOString(),\n level,\n title,\n storyId: this.storyId,\n ...(this.parentStoryId !== undefined ? { parentStoryId: this.parentStoryId } : {}),\n ...(this.origin ? { origin: this.origin } : {}),\n notes: [...this.notes],\n ...(error !== undefined ? { error: normalizeError(error) } : {}),\n };\n\n return formatStory(event, reportOptions);\n }\n\n /**\n * Finish the story: emit everything collected so far as one record, and start fresh.\n *\n * @param title - What the story was about\n * @param options - Level, and the error that ended it\n * @returns A one-shot handle whose `.to()` overrides the audience list — call it\n * synchronously, delivery happens on the next microtask\n *\n * @example\n * ```ts\n * story.finish(\"Sync complete\");\n * story.finish(\"Sync failed\", { level: \"oops\", error }).to(\"db\");\n * ```\n */\n finish(title: string, options: FinishOptions = {}) {\n return this.createDelivery(toStoryLevel(options.level), title, options.error);\n }\n\n /** @deprecated Use `finish(title)`. Removed at 1.0. */\n tell(title: string) {\n warnDeprecated(\"tell\", \"finish\");\n return this.createDelivery(\"Information\", title);\n }\n\n /** @deprecated Use `finish(title, { level: \"warn\" })`. Removed at 1.0. */\n warn(title: string) {\n warnDeprecated(\"warn\", 'finish(title, { level: \"warn\" })');\n return this.createDelivery(\"Warning\", title);\n }\n\n /** @deprecated Use `finish(title, { level: \"oops\", error })`. Removed at 1.0. */\n oops(title: string, error?: unknown) {\n warnDeprecated(\"oops\", 'finish(title, { level: \"oops\", error })');\n return this.createDelivery(\"Error\", title, error);\n }\n\n /** @deprecated Use `report()`. Removed at 1.0. */\n note(input: unknown, data: NoteData = {}) {\n warnDeprecated(\"note\", \"report\");\n return this.report(input, data);\n }\n\n /** Emit a single note to the audiences listening for notes */\n private emitNote(note: StoryNote, level: StoryLevel, only?: string[]) {\n const emission: NoteEmission = {\n ...note,\n kind: \"note\",\n storyId: this.storyId,\n ...(this.parentStoryId !== undefined ? { parentStoryId: this.parentStoryId } : {}),\n sequence: note.sequence ?? 0,\n level,\n ...(this.origin ? { origin: this.origin } : {}),\n };\n\n void this.deliver(emission, only ? { only } : {});\n }\n\n /** Build a story event and schedule delivery, returning a handle to override the audience list */\n private createDelivery(level: StoryLevel, title: string, error?: unknown) {\n const event = this.buildEvent(level, title, error);\n\n let delivered = false;\n let defaultCancelled = false;\n\n // Delivery is microtask-scheduled so .to() can override synchronously\n queueMicrotask(() => {\n if (delivered || defaultCancelled) return;\n delivered = true;\n void this.deliver(event);\n });\n\n return {\n to: (...names: string[]) => {\n defaultCancelled = true;\n if (delivered) return;\n delivered = true;\n void this.deliver(event, { only: names });\n },\n };\n }\n\n /** Assemble the story event from current notes and start a fresh story */\n private buildEvent(level: StoryLevel, title: string, error?: unknown): StoryEvent {\n const now = new Date().toISOString();\n const storyId = this.storyId;\n // Read before startNewStory() zeroes it\n const droppedEmissions = this.droppedEmissions;\n\n // Sort notes chronologically so the record tells the story in order.\n // Sequence breaks ties: two notes can share a millisecond.\n const sortedNotes = [...this.notes].sort(\n (noteA, noteB) =>\n Date.parse(noteA.timestamp) - Date.parse(noteB.timestamp) ||\n (noteA.sequence ?? 0) - (noteB.sequence ?? 0)\n );\n\n this.notes = [];\n this.startNewStory();\n\n // Compute duration from first to last note\n const durationMs = calculateNoteDuration(sortedNotes).durationMs;\n\n const event: StoryEventBase = {\n timestamp: now,\n level,\n title,\n storyId,\n ...(this.parentStoryId !== undefined ? { parentStoryId: this.parentStoryId } : {}),\n ...(this.origin ? { origin: this.origin } : {}),\n notes: sortedNotes,\n ...(durationMs != null ? { durationMs } : {}),\n ...(droppedEmissions ? { droppedEmissions } : {}),\n ...(error !== undefined ? { error: normalizeError(error) } : {}),\n };\n\n const eventWithSummary = event as StoryEvent;\n Object.defineProperty(eventWithSummary, \"kind\", {\n value: \"story\",\n enumerable: true,\n });\n Object.defineProperty(eventWithSummary, \"summarize\", {\n value: (options?: ReportOptions) => formatStory(event, options),\n enumerable: false,\n });\n\n return eventWithSummary;\n }\n\n /** Begin a new story: fresh id, sequence back to zero */\n private startNewStory() {\n this.storyId = createStoryId();\n this.nextSequence = 0;\n this.droppedEmissions = 0;\n }\n\n /** Deliver an emission to the audience members listening for its kind */\n private async deliver(emission: Emission, options?: { only?: string[] }) {\n // Cheap exit before any audience work — level filtering costs one comparison\n if (!meetsLevel(emission.level, this.minimumLevel)) return;\n\n const targets = options?.only?.length\n ? this.audience.getOnly(options.only)\n : this.audience.getAll();\n\n await Promise.all(\n targets\n .filter((member) => hearsKind(member, emission.kind))\n .filter((member) => this.acceptsSafely(member, emission))\n .map((member) => this.hearSafely(member, emission))\n );\n }\n\n /** Run an audience's accepts() without letting a throw from it lose the emission */\n private acceptsSafely(member: AnyAudienceMember, emission: Emission): boolean {\n if (!member.accepts) return true;\n\n try {\n return member.accepts(emission);\n } catch (error) {\n this.handleAudienceError(error, member, emission);\n return false;\n }\n }\n\n /**\n * Hand an emission to one audience, keeping its failures and its slowness\n * contained: a throw is reported rather than swallowed, and a backlog is dropped\n * rather than grown without limit.\n */\n private async hearSafely(member: AnyAudienceMember, emission: Emission) {\n const pending = this.inFlight.get(member.name) ?? 0;\n if (pending >= this.maxInFlight) {\n this.droppedEmissions += 1;\n return;\n }\n\n this.inFlight.set(member.name, pending + 1);\n try {\n await member.hear(emission);\n } catch (error) {\n this.handleAudienceError(error, member, emission);\n } finally {\n const remaining = (this.inFlight.get(member.name) ?? 1) - 1;\n if (remaining > 0) this.inFlight.set(member.name, remaining);\n else this.inFlight.delete(member.name);\n }\n }\n\n /** Report an audience failure without ever letting it reach caller code */\n private handleAudienceError(error: unknown, member: AnyAudienceMember, emission: Emission) {\n try {\n this.onAudienceError(error, member, emission);\n } catch {\n // A failing error handler must not escalate into a failing log call\n }\n }\n}\n\n/** Names already warned about, so a deprecation notice appears at most once per process */\nconst warnedDeprecations = new Set<string>();\n\n/**\n * Warn once about a deprecated method, and only when asked.\n *\n * Off by default on purpose: a logging library that spams its own deprecation\n * notices into a consumer's output has become the thing it was meant to fix.\n * Opt in with `STORYTELLER_DEPRECATION_WARNINGS=1`.\n */\nfunction warnDeprecated(oldName: string, replacement: string) {\n if (warnedDeprecations.has(oldName)) return;\n if (readEnvironmentValue(\"STORYTELLER_DEPRECATION_WARNINGS\") !== \"1\") return;\n\n warnedDeprecations.add(oldName);\n console.warn(\n `Storyteller: ${oldName}() is deprecated and will be removed at 1.0 — use ${replacement}.`\n );\n}\n\n/** Cap on simultaneous deliveries to one audience before emissions start being dropped */\nconst DEFAULT_MAX_IN_FLIGHT = 1000;\n\n/** How long to stay quiet after warning about a given audience, in milliseconds */\nconst AUDIENCE_ERROR_THROTTLE_MS = 5000;\n\n/** When an audience last had a failure reported, keyed by audience name */\nconst lastReportedAudienceError = new Map<string, number>();\n\n/**\n * Default audience-error behavior: one throttled warning per audience.\n * Loud enough to notice a broken audience, quiet enough not to become the problem.\n */\nfunction reportAudienceErrorToConsole(\n error: unknown,\n member: AnyAudienceMember,\n emission: Emission\n) {\n const now = Date.now();\n const lastReported = lastReportedAudienceError.get(member.name);\n if (lastReported !== undefined && now - lastReported < AUDIENCE_ERROR_THROTTLE_MS) {\n return;\n }\n\n lastReportedAudienceError.set(member.name, now);\n const reason = error instanceof Error ? error.message : String(error);\n console.error(\n `Storyteller: audience \"${member.name}\" failed to hear a ${emission.kind} — ${reason}`\n );\n}\n\n/** Check whether an audience member listens for a given emission kind */\nfunction hearsKind(member: AnyAudienceMember, kind: EmissionKind): boolean {\n // Audiences written before live narration existed only expect stories\n const kinds = member.hears ?? [\"story\"];\n return kinds.includes(kind);\n}\n\n/** Resolve the narration mode from an explicit value, the environment, then the default */\nfunction resolveNarration(requested?: NarrationInput): Narration {\n const value = requested ?? readEnvironmentValue(\"STORYTELLER_NARRATION\");\n if (value === \"live\" || value === \"both\") return \"live\";\n return \"collected\";\n}\n\n/** Generate an identifier for a story, falling back when crypto is unavailable */\nfunction createStoryId(): string {\n try {\n const runtimeCrypto = globalThis.crypto as { randomUUID?: () => string } | undefined;\n if (typeof runtimeCrypto?.randomUUID === \"function\") {\n return runtimeCrypto.randomUUID();\n }\n } catch {\n // fall through to the manual identifier\n }\n\n return `story-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Normalize each origin field so the origin on a record is as storable as the notes */\nfunction normalizeOrigin(origin?: StoryOriginInput): StoryOrigin | undefined {\n if (!origin) return undefined;\n\n const normalized: StoryOrigin = {\n ...(origin.who !== undefined ? { who: normalizeValue(origin.who) } : {}),\n ...(origin.what !== undefined ? { what: normalizeValue(origin.what) } : {}),\n ...(origin.where !== undefined ? { where: normalizeValue(origin.where) } : {}),\n };\n\n return Object.keys(normalized).length ? normalized : undefined;\n}\n\n/**\n * Derive note text from whatever the caller passed, along with the structured\n * remainder. A string is its own text; anything else is described and carried\n * along as context so nothing is lost.\n */\nfunction describeInput(input: unknown): {\n text: string;\n what?: JsonValue;\n error?: StoryError;\n} {\n if (typeof input === \"string\") return { text: input };\n\n if (input instanceof Error) {\n const error = normalizeError(input);\n const label = [error.name, error.message].filter(Boolean).join(\": \");\n return { text: label || \"Error\", error };\n }\n\n if (input === null) return { text: \"null\" };\n if (input === undefined) return { text: \"undefined\" };\n\n const normalized = normalizeValue(input);\n\n // A primitive still gets carried as structured data, not only stringified into\n // the text — otherwise `report(42)` would leave no way to read 42 back as a number\n if (typeof normalized !== \"object\" || normalized === null) {\n return { text: String(normalized), what: normalized };\n }\n\n if (Array.isArray(normalized)) {\n return { text: `Array(${normalized.length})`, what: normalized };\n }\n\n // Prefer a field that reads like a headline before falling back to the type name\n for (const key of [\"message\", \"title\", \"name\", \"summary\", \"event\"]) {\n const candidate = normalized[key];\n if (typeof candidate === \"string\" && candidate.length) {\n return { text: candidate, what: normalized };\n }\n }\n\n const typeName = normalized[\"@type\"];\n return {\n text: typeof typeName === \"string\" ? typeName : \"Object\",\n what: normalized,\n };\n}\n\n/** Calculate the duration between the first and last note in a sequence */\nfunction calculateNoteDuration(notes: StoryNote[]) {\n if (notes.length <= 1) {\n return {\n durationMs: undefined as number | undefined,\n };\n }\n\n const startTime = Date.parse(notes[0]!.timestamp);\n const endTime = Date.parse(notes[notes.length - 1]!.timestamp);\n\n return {\n durationMs: Number.isFinite(startTime) && Number.isFinite(endTime)\n ? Math.max(0, endTime - startTime)\n : undefined,\n };\n}\n","import type { NarrationInput, StoryOriginInput } from \"./storyteller\";\nimport { Storyteller } from \"./storyteller\";\n\nlet sharedInstance: Storyteller | undefined;\n\ntype StorytellerSharedOptions = {\n origin?: StoryOriginInput;\n narration?: NarrationInput;\n reset?: boolean;\n};\n\n/**\n * Get or create a shared Storyteller instance for cross-component logging.\n * First call creates the instance; subsequent calls return the same one.\n *\n * @param options.origin - Origin context for the shared instance\n * @param options.reset - Create a fresh instance (useful in tests)\n *\n * @example\n * ```ts\n * // Same instance everywhere in your app\n * const story = useStoryteller({ origin: { who: \"worker\" } });\n * ```\n */\nexport function useStoryteller(\n options: StorytellerSharedOptions = {}\n): Storyteller {\n if (!sharedInstance || options.reset) {\n sharedInstance = new Storyteller({\n ...(options.origin !== undefined ? { origin: options.origin } : {}),\n ...(options.narration !== undefined ? { narration: options.narration } : {}),\n });\n return sharedInstance;\n }\n\n return sharedInstance;\n}\n","import type { AudienceMember, StoryEvent } from \"../storyteller\";\n\n/**\n * Create an audience that stores warn and oops stories via your insert function.\n * Tell-level events are filtered out to reduce noise — only warnings and errors are persisted.\n *\n * Hears stories only. Live notes are not persisted: the story record already contains\n * every note, so storing both would double-write the same content.\n *\n * Note: if the insert function throws, the error is silently caught by the delivery\n * pipeline (Promise.allSettled). Wrap your insert with try/catch to handle failures.\n *\n * @param insert - Function that receives the story event and stores it\n *\n * @example\n * ```ts\n * story.audience.add(\n * dbAudience(async (event) => {\n * await db.insert(\"story_logs\", event);\n * })\n * );\n * ```\n */\nexport function dbAudience(insert: (event: StoryEvent) => Promise<void> | void): AudienceMember {\n return {\n name: \"db\",\n hears: [\"story\"],\n accepts: (event) => event.level === \"Warning\" || event.level === \"Error\",\n hear: async (event) => {\n await insert(event);\n },\n };\n}\n","import type { StoryEventBase } from \"../storyteller\";\nimport { formatStory } from \"../formatting\";\nimport { ANSI, getLevelColor, formatOrigin, colorizeJsonSections } from \"../utils\";\n\nexport type StoryReportOptions = {\n timezone?: string;\n locale?: string;\n detail?: \"brief\" | \"normal\" | \"full\";\n noteLimit?: number;\n showData?: boolean;\n colors?: boolean;\n};\n\n/** Generate a formatted report from an array of story events, grouped by day */\nexport function writeStoryReport(\n stories: StoryEventBase[],\n options: StoryReportOptions = {}\n): string {\n const {\n timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,\n locale = \"en-US\",\n detail = \"normal\",\n noteLimit = 50,\n showData = true,\n colors = true,\n } = options;\n\n if (!stories.length) {\n return \"Storyteller Report\\n\\n(no stories)\\n\";\n }\n\n const sorted = [...stories].sort(\n (storyA, storyB) => Date.parse(storyA.timestamp) - Date.parse(storyB.timestamp)\n );\n\n const dateFormatter = new Intl.DateTimeFormat(locale, {\n timeZone: timezone,\n year: \"numeric\",\n month: \"short\",\n day: \"2-digit\",\n });\n\n const firstStory = sorted[0];\n const lastStory = sorted[sorted.length - 1];\n if (!firstStory || !lastStory) {\n return \"Storyteller Report\\n\\n(no stories)\\n\";\n }\n\n const lines: string[] = [];\n lines.push(`Storyteller Report (${timezone})`);\n lines.push(\n `Range: ${dateFormatter.format(new Date(firstStory.timestamp))} – ${dateFormatter.format(\n new Date(lastStory.timestamp)\n )}`\n );\n lines.push(\"\");\n\n const storiesByDay = new Map<string, StoryEventBase[]>();\n for (const story of sorted) {\n const dayKey = dateFormatter.format(new Date(story.timestamp));\n const dayEvents = storiesByDay.get(dayKey) ?? [];\n dayEvents.push(story);\n storiesByDay.set(dayKey, dayEvents);\n }\n\n for (const [day, dayStories] of storiesByDay) {\n lines.push(day);\n\n for (const story of dayStories) {\n const report = formatStory(story, {\n timezone,\n locale,\n detail,\n noteLimit,\n colors,\n });\n const { data } = report;\n const originLabel = formatOrigin(story.origin);\n const levelColor = getLevelColor(story.level);\n const label = (text: string) =>\n colors ? `${levelColor}${text}${ANSI.reset}` : text;\n\n const duration = data.duration ? ` (${data.duration})` : \"\";\n lines.push(`${label(\"Story\")}: ${story.title}`);\n lines.push(`${label(\"Level\")}: ${story.level}`);\n lines.push(`${label(\"Time\")}: ${data.when}${duration}`);\n\n if (originLabel) {\n lines.push(`${label(\"Origin\")}: ${originLabel}`);\n }\n\n if (data.error) {\n const errorLine = [\n data.error.name,\n data.error.message,\n ]\n .filter(Boolean)\n .join(\": \");\n if (errorLine) lines.push(`${label(\"Error\")}: ${errorLine}`);\n }\n\n if (detail !== \"brief\" && data.notes.length) {\n lines.push(` ${label(\"Notes\")}:`);\n\n for (const reportNote of data.notes) {\n lines.push(` ${reportNote.when} — ${reportNote.text}`);\n }\n\n if (story.notes.length > data.notes.length) {\n lines.push(\n ` … (${story.notes.length - data.notes.length} more)`\n );\n }\n }\n\n if (showData) {\n lines.push(`${label(\"Data\")}:`);\n const json = JSON.stringify(data, null, 2);\n if (colors) {\n const colored = colorizeJsonSections(json, {\n base: ANSI.grayLight,\n notes: ANSI.grayDark,\n reset: ANSI.reset,\n });\n lines.push(...colored);\n } else {\n lines.push(...json.split(\"\\n\"));\n }\n }\n\n lines.push(\"\");\n }\n }\n\n return lines.join(\"\\n\").trim() + \"\\n\";\n}\n"],"mappings":"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","names":["calculateNoteDuration"]}