@lovelaces-io/storyteller 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +42 -1
- package/README.md +30 -0
- package/dist/index.cjs +454 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -515
- package/dist/index.d.ts +55 -515
- package/dist/index.js +440 -17
- package/dist/index.js.map +1 -1
- package/dist/store/file.cjs +513 -0
- package/dist/store/file.cjs.map +1 -0
- package/dist/store/file.d.cts +29 -0
- package/dist/store/file.d.ts +29 -0
- package/dist/store/file.js +478 -0
- package/dist/store/file.js.map +1 -0
- package/dist/stories-BgN4BSoZ.d.cts +744 -0
- package/dist/stories-BgN4BSoZ.d.ts +744 -0
- package/llms.txt +11 -1
- package/package.json +16 -9
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACWA,IAAM,aAAyC;AAAA,EAC7C,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO;AACT;AAGA,IAAM,gBAA4C;AAAA,EAChD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AACT;AAqBO,SAAS,aAAa,OAAgC;AAC3D,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,cAAc,OAAO,KAAK,EAAE,YAAY,CAAC,KAAK;AACvD;AAQO,SAAS,qBAAqB,MAAkC;AACrE,MAAI;AACF,UAAM,UAAU;AAGhB,UAAM,QAAQ,QAAQ,SAAS,MAAM,IAAI;AACzC,WAAO,OAAO,UAAU,YAAY,MAAM,SAAS,MAAM,KAAK,IAAI;AAAA,EACpE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,oBAAoB,WAA6C;AAC/E,QAAM,QAAQ,aAAa,qBAAqB,mBAAmB;AACnE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,cAAc,OAAO,KAAK,EAAE,YAAY,CAAC,KAAK;AACvD;AAQO,SAAS,WAAW,OAAmB,SAA8B;AAC1E,SAAO,WAAW,KAAK,KAAK,WAAW,OAAO;AAChD;AAYO,SAAS,oBAAoB,WAAwC;AAC1E,QAAM,QAAQ,aAAa,qBAAqB,oBAAoB;AACpE,SAAO,UAAU,WAAW,WAAW;AACzC;AAQO,SAAS,cAAc,WAA8B;AAC1D,MAAI,cAAc,OAAW,QAAO;AAEpC,QAAM,QAAQ,qBAAqB,mBAAmB;AACtD,MAAI,UAAU,OAAW,QAAO;AAEhC,SAAO,EAAE,UAAU,OAAO,MAAM,YAAY,MAAM;AACpD;;;ACpHO,IAAM,OAAO;AAAA,EAClB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,WAAW;AAAA,EACX,UAAU;AACZ;AAGO,SAAS,cAAc,OAA2B;AACvD,MAAI,UAAU,cAAe,QAAO,KAAK;AACzC,MAAI,UAAU,UAAW,QAAO,KAAK;AACrC,SAAO,KAAK;AACd;AAGO,SAAS,aAAa,QAA0C;AACrE,MAAI,CAAC,QAAQ,MAAO;AACpB,MAAI,OAAO,OAAO,UAAU,SAAU,QAAO,OAAO;AACpD,MAAI,OAAO,OAAO,UAAU,YAAY,MAAM,QAAQ,OAAO,KAAK,GAAG;AACnE,WAAO,OAAO,OAAO,KAAK;AAAA,EAC5B;AACA,QAAM,cAAyC,OAAO;AAGtD,QAAM,eAAe,CAAC,OAAO,WAAW,QAAQ,WAAW;AAC3D,QAAM,gBAAgB,aACnB,OAAO,CAAC,QAAQ,YAAY,GAAG,KAAK,IAAI,EACxC,IAAI,CAAC,QAAQ,OAAO,YAAY,GAAG,CAAC,CAAC;AACxC,QAAM,aAAa,OAAO,QAAQ,WAAW,EAC1C,OAAO,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,aAAa,SAAS,GAAG,KAAK,SAAS,IAAI,EACrE,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM,OAAO,KAAK,CAAC;AAEpC,QAAM,QAAQ,CAAC,GAAG,eAAe,GAAG,UAAU;AAC9C,SAAO,MAAM,SAAS,MAAM,KAAK,KAAK,IAAI;AAC5C;AAGO,SAAS,qBACd,MACA,QACU;AACV,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,MAAI,cAAc;AAClB,MAAI,eAAe;AAEnB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,QAAI,CAAC,eAAe,KAAK,SAAS,YAAY,GAAG;AAC/C,oBAAc;AACd,qBAAe,cAAc,IAAI;AACjC,aAAO,GAAG,OAAO,KAAK,GAAG,IAAI,GAAG,OAAO,KAAK;AAAA,IAC9C;AAEA,QAAI,aAAa;AACf,YAAM,UAAU,GAAG,OAAO,KAAK,GAAG,IAAI,GAAG,OAAO,KAAK;AACrD,sBAAgB,cAAc,IAAI;AAClC,UAAI,gBAAgB,EAAG,eAAc;AACrC,aAAO;AAAA,IACT;AAEA,WAAO,GAAG,OAAO,IAAI,GAAG,IAAI,GAAG,OAAO,KAAK;AAAA,EAC7C,CAAC;AACH;AAGO,SAAS,cAAc,MAAsB;AAClD,QAAM,aAAa,KAAK,MAAM,KAAK,KAAK,CAAC,GAAG;AAC5C,QAAM,cAAc,KAAK,MAAM,KAAK,KAAK,CAAC,GAAG;AAC7C,SAAO,YAAY;AACrB;AAGA,IAAM,qBAAqB;AAUpB,SAAS,iBAAiB,MAKV;AACrB,QAAM,QAAkB,CAAC;AAEzB,qBAAmB,OAAO,KAAK,IAAI;AACnC,qBAAmB,OAAO,KAAK,KAAK;AAEpC,MAAI,KAAK,OAAO;AACd,UAAM,YAAY,CAAC,KAAK,MAAM,MAAM,KAAK,MAAM,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAEjF,QAAI,aAAa,cAAc,KAAK,KAAM,OAAM,KAAK,SAAS;AAAA,EAChE;AAEA,MAAI,CAAC,MAAM,OAAQ,QAAO;AAE1B,QAAM,SAAS,MAAM,KAAK,GAAG;AAC7B,QAAM,OAAO,OAAO,SAAS,qBACzB,GAAG,OAAO,MAAM,GAAG,kBAAkB,CAAC,WACtC;AAEJ,SAAO,IAAI,IAAI;AACjB;AAGA,SAAS,mBAAmB,OAAiB,OAAmB;AAC9D,MAAI,SAAS,KAAM;AAEnB,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,KAAK,OAAO,KAAK,CAAC;AACxB;AAAA,EACF;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,KAAK,IAAI,MAAM,MAAM,GAAG;AAC9B;AAAA,EACF;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,SAAS,KAAM;AACnB,QAAI,IAAI,WAAW,GAAG,EAAG;AACzB,UAAM,KAAK,GAAG,GAAG,IAAI,OAAO,UAAU,WAAW,gBAAgB,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE;AAAA,EAC3F;AACF;AAGA,SAAS,gBAAgB,OAA0B;AACjD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,IAAI,MAAM,MAAM;AACjD,MAAI,SAAS,OAAO,UAAU,SAAU,QAAO,IAAI,OAAO,KAAK,KAAK,EAAE,MAAM;AAC5E,SAAO,OAAO,KAAK;AACrB;;;ACvIA,IAAM,eAA2C;AAAA,EAC/C,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO;AACT;AAGA,IAAM,eAA2C;AAAA,EAC/C,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO;AACT;AAuBO,SAAS,gBAAgB,UAAkC,CAAC,GAAmB;AACpF,QAAM,SAAS,cAAc,QAAQ,MAAM;AAE3C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,CAAC,QAAQ,OAAO;AAAA,IACvB,MAAM,CAAC,aAAuB;AAC5B,UAAI,SAAS,SAAS,QAAQ;AAC5B,kBAAU,UAAU,MAAM;AAC1B;AAAA,MACF;AACA,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF;AACF;AAMA,SAAS,UAAU,MAAoB,QAAiB;AACtD,QAAM,OAAO,cAAc,KAAK,SAAS;AACzC,QAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,QAAM,SAAS,aAAa,KAAK,MAAM;AACvC,QAAM,UAAU,iBAAiB,IAAI;AAErC,QAAM,OAAO,SACT,GAAG,cAAc,KAAK,KAAK,CAAC,GAAG,KAAK,GAAG,KAAK,KAAK,KACjD;AAEJ,QAAM,OAAO;AAAA,IACX,SAAS,GAAG,KAAK,QAAQ,GAAG,IAAI,GAAG,KAAK,KAAK,KAAK;AAAA,IAClD;AAAA,IACA,SAAU,SAAS,GAAG,KAAK,QAAQ,GAAG,MAAM,GAAG,KAAK,KAAK,KAAK,SAAU;AAAA,IACxE,KAAK;AAAA,IACL,UAAW,SAAS,GAAG,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAK,KAAK,KAAK,UAAW;AAAA,EAC7E,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AAEZ,MAAI,KAAK,UAAU,eAAe;AAChC,YAAQ,IAAI,IAAI;AAAA,EAClB,WAAW,KAAK,UAAU,WAAW;AACnC,YAAQ,KAAK,IAAI;AAAA,EACnB,OAAO;AACL,YAAQ,MAAM,IAAI;AAAA,EACpB;AACF;AAGA,SAAS,WAAW,OAAmB;AACrC,QAAM,SAAS;AACf,QAAM,SAAS,GAAG,MAAM,KAAK,MAAM,KAAK;AAExC,UAAQ,eAAe,KAAK,MAAM,IAAI,aAAa,MAAM,KAAK,CAAC;AAE/D,QAAM,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC;AAE7C,MAAI,MAAM,UAAU,eAAe;AACjC,YAAQ,IAAI,QAAQ,OAAO;AAAA,EAC7B,WAAW,MAAM,UAAU,WAAW;AACpC,YAAQ,KAAK,QAAQ,OAAO;AAAA,EAC9B,OAAO;AACL,YAAQ,MAAM,QAAQ,OAAO;AAAA,EAC/B;AAEA,UAAQ,SAAS;AACnB;AAGA,SAAS,cAAc,WAA2B;AAChD,QAAM,WAAW,UAAU,MAAM,IAAI,EAAE;AACvC,SAAO,SAAS,WAAW,IAAI,WAAW;AAC5C;;;ACrFO,IAAM,WAAW;AAMjB,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,oBAAoB;AAC1B,IAAM,2BAA2B;AACjC,IAAM,yBAAyB;AAC/B,IAAM,4BAA4B;AAGlC,IAAM,kBAAkB;AAExB,IAAM,uBAAuB;AAgCtB,SAAS,eACd,OACA,UAA4B,CAAC,GAClB;AACX,QAAM,WAA4B;AAAA,IAChC,UAAU,QAAQ,YAAY;AAAA,IAC9B,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,eAAe,QAAQ,iBAAiB;AAAA,IACxC,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,YAAY,IAAI;AAAA,OACb,QAAQ,cAAc,qBAAqB,IAAI,uBAAuB;AAAA,IACzE;AAAA,IACA,QAAQ,QAAQ,UAAU;AAAA,EAC5B;AAEA,MAAI;AACF,WAAO,iBAAiB,OAAO,UAAU,GAAG,KAAK,oBAAI,IAAI,CAAC;AAAA,EAC5D,SAAS,SAAS;AAEhB,WAAO,gBAAgB,gBAAgB,OAAO,CAAC;AAAA,EACjD;AACF;AAUO,SAAS,eACd,UACA,UAA4B,CAAC,GACjB;AACZ,QAAM,WAA4B;AAAA,IAChC,UAAU,QAAQ,YAAY;AAAA,IAC9B,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,eAAe,QAAQ,iBAAiB;AAAA,IACxC,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,YAAY,IAAI;AAAA,OACb,QAAQ,cAAc,qBAAqB,IAAI,uBAAuB;AAAA,IACzE;AAAA,IACA,QAAQ,QAAQ,UAAU;AAAA,EAC5B;AAEA,SAAO,uBAAuB,UAAU,UAAU,CAAC;AACrD;AAGA,SAAS,uBACP,UACA,SACA,YACY;AACZ,MAAI,EAAE,oBAAoB,QAAQ;AAChC,QAAI,cAAc,QAAQ,GAAG;AAE3B,YAAM,SAAS;AACf,YAAM,UAAU,OAAO,OAAO,SAAS,MAAM,WAAW,OAAO,SAAS,IAAI;AAC5E,YAAM,OAAO,OAAO,OAAO,MAAM,MAAM,WAAW,OAAO,MAAM,IAAI;AACnE,UAAI,YAAY,UAAa,SAAS,QAAW;AAC/C,eAAO;AAAA,UACL,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,UACrC,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,SAAS,cAAc,UAAU,QAAQ,eAAe,EAAE;AAAA,EACrE;AAEA,QAAM,aAAyB;AAAA,IAC7B,MAAM,SAAS;AAAA,IACf,SAAS,SAAS;AAAA,EACpB;AAEA,MAAI,SAAS,UAAU,QAAW;AAChC,eAAW,QAAQ,eAAe,SAAS,OAAO,QAAQ,eAAe;AAAA,EAC3E;AAEA,QAAM,QAAS,SAAiC;AAChD,MAAI,UAAU,QAAW;AACvB,QAAI,cAAc,iBAAiB;AACjC,iBAAW,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,EAAE;AAAA,IAC5D,WAAW,iBAAiB,OAAO;AACjC,iBAAW,QAAQ,uBAAuB,OAAO,SAAS,aAAa,CAAC;AAAA,IAC1E,OAAO;AACL,iBAAW,QAAQ,iBAAiB,OAAO,SAAS,GAAG,WAAW,oBAAI,IAAI,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,QAAM,aAAc,SAAkC;AACtD,MAAI,MAAM,QAAQ,UAAU,GAAG;AAC7B,eAAW,SAAS,WACjB,MAAM,GAAG,QAAQ,cAAc,EAC/B,IAAI,CAAC,WAAW,uBAAuB,QAAQ,SAAS,aAAa,CAAC,CAAC;AAAA,EAC5E;AAEA,SAAO;AACT;AAGA,SAAS,iBACP,OACA,SACA,OACA,MACA,WACW;AACX,MAAI,UAAU,KAAM,QAAO;AAE3B,QAAM,YAAY,OAAO;AAEzB,MAAI,cAAc,UAAU;AAC1B,WAAO,eAAe,OAAiB,QAAQ,eAAe;AAAA,EAChE;AAEA,MAAI,cAAc,UAAU;AAE1B,WAAO,OAAO,SAAS,KAAe,IAAK,QAAmB,OAAO,KAAK;AAAA,EAC5E;AAEA,MAAI,cAAc,UAAW,QAAO;AACpC,MAAI,cAAc,YAAa,QAAO;AACtC,MAAI,cAAc,SAAU,QAAO,GAAG,OAAO,KAAK,CAAC;AACnD,MAAI,cAAc,SAAU,QAAO,OAAO,KAAe;AAEzD,MAAI,cAAc,YAAY;AAC5B,UAAM,OAAQ,MAA4B;AAC1C,WAAO,cAAc,OAAO,OAAO,WAAW;AAAA,EAChD;AAEA,QAAM,cAAc;AAEpB,QAAM,eAAe,UAAU,IAAI,WAAW;AAC9C,MAAI,iBAAiB,QAAW;AAC9B,WAAO,oBAAe,YAAY;AAAA,EACpC;AAEA,MAAI,QAAQ,QAAQ,UAAU;AAC5B,WAAO,EAAE,cAAc,EAAE,MAAM,SAAS,OAAO,QAAQ,SAAS,EAAE;AAAA,EACpE;AAEA,QAAM,YAAY,mBAAmB,aAAa,SAAS,OAAO,MAAM,SAAS;AACjF,MAAI,cAAc,OAAW,QAAO;AAEpC,YAAU,IAAI,aAAa,IAAI;AAC/B,MAAI;AACF,QAAI,MAAM,QAAQ,WAAW,GAAG;AAC9B,aAAO,eAAe,aAAa,SAAS,OAAO,MAAM,SAAS;AAAA,IACpE;AACA,WAAO,gBAAgB,aAAa,SAAS,OAAO,MAAM,SAAS;AAAA,EACrE,UAAE;AAGA,cAAU,OAAO,WAAW;AAAA,EAC9B;AACF;AAMA,SAAS,mBACP,OACA,SACA,OACA,MACA,WACuB;AACvB,MAAI,iBAAiB,OAAO;AAC1B,WAAO,uBAAuB,OAAO,SAAS,CAAC;AAAA,EACjD;AAEA,MAAI,iBAAiB,MAAM;AACzB,UAAM,OAAO,MAAM,QAAQ;AAC3B,WAAO,OAAO,MAAM,IAAI,IAAI,mBAAmB,MAAM,YAAY;AAAA,EACnE;AAEA,MAAI,iBAAiB,OAAQ,QAAO,OAAO,KAAK;AAChD,MAAI,iBAAiB,IAAK,QAAO,MAAM;AAEvC,MAAI,iBAAiB,KAAK;AACxB,UAAM,UAAqC,CAAC;AAC5C,QAAI,QAAQ;AACZ,QAAI,UAAU;AACd,eAAW,CAAC,UAAU,UAAU,KAAK,OAAO;AAC1C,UAAI,SAAS,QAAQ,eAAe;AAClC,mBAAW;AACX;AAAA,MACF;AACA,YAAM,WAAW,cAAc,UAAU,QAAQ,eAAe;AAChE,cAAQ,QAAQ,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,GAAG,IAAI,IAAI,QAAQ;AAAA,QACnB;AAAA,MACF;AACA,eAAS;AAAA,IACX;AACA,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,GAAI,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,QAAQ,EAAE,IAAI,CAAC;AAAA,IACrE;AAAA,EACF;AAEA,MAAI,iBAAiB,KAAK;AACxB,UAAM,SAAsB,CAAC;AAC7B,QAAI,UAAU;AACd,eAAW,UAAU,OAAO;AAC1B,UAAI,OAAO,UAAU,QAAQ,gBAAgB;AAC3C,mBAAW;AACX;AAAA,MACF;AACA,aAAO;AAAA,QACL,iBAAiB,QAAQ,SAAS,QAAQ,GAAG,GAAG,IAAI,IAAI,OAAO,MAAM,KAAK,SAAS;AAAA,MACrF;AAAA,IACF;AACA,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,GAAI,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,QAAQ,EAAE,IAAI,CAAC;AAAA,IACpE;AAAA,EACF;AAEA,MAAI,iBAAiB,QAAS,QAAO;AACrC,MAAI,iBAAiB,QAAS,QAAO;AACrC,MAAI,iBAAiB,QAAS,QAAO;AAErC,MAAI,YAAY,OAAO,KAAK,KAAK,iBAAiB,aAAa;AAC7D,WAAO,eAAe,KAAK;AAAA,EAC7B;AAEA,QAAM,YAAY,WAAW,KAAK;AAClC,MAAI,cAAc,QAAW;AAC3B,WAAO,iBAAiB,WAAW,SAAS,OAAO,MAAM,SAAS;AAAA,EACpE;AAEA,SAAO;AACT;AAGA,SAAS,WAAW,OAAwB;AAC1C,MAAI;AACJ,MAAI;AACF,aAAU,MAA+B;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,WAAW,WAAY,QAAO;AAEzC,MAAI;AACF,WAAQ,OAAyB,KAAK,KAAK;AAAA,EAC7C,SAAS,SAAS;AAChB,WAAO,gBAAgB,gBAAgB,OAAO,CAAC;AAAA,EACjD;AACF;AAGA,SAAS,eAAe,OAAiD;AACvE,QAAM,WAAW,oBAAoB,KAAK,KAAK;AAC/C,QAAM,aAAa,MAAM;AAEzB,MAAI;AACJ,MAAI;AACF,UAAM,QACJ,iBAAiB,cACb,IAAI,WAAW,OAAO,GAAG,KAAK,IAAI,sBAAsB,UAAU,CAAC,IACnE,IAAI;AAAA,MACF,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,IAAI,sBAAsB,MAAM,UAAU;AAAA,IACjD;AACN,cAAU,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,GAAG;AAAA,EACjF,QAAQ;AACN,cAAU;AAAA,EACZ;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,aAAa,uBACb,EAAE,cAAc,EAAE,MAAM,SAAS,SAAS,aAAa,qBAAqB,EAAE,IAC9E,CAAC;AAAA,EACP;AACF;AAGA,SAAS,eACP,OACA,SACA,OACA,MACA,WACW;AACX,QAAM,OAAoB,CAAC;AAC3B,QAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,QAAQ,cAAc;AAE3D,WAAS,QAAQ,GAAG,QAAQ,OAAO,SAAS,GAAG;AAC7C,SAAK;AAAA,MACH,iBAAiB,MAAM,KAAK,GAAG,SAAS,QAAQ,GAAG,GAAG,IAAI,IAAI,KAAK,KAAK,SAAS;AAAA,IACnF;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,OAAO;AACxB,SAAK,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,SAAS,MAAM,SAAS,MAAM,EAAE,CAAC;AAAA,EAC9E;AAEA,SAAO;AACT;AAGA,SAAS,gBACP,OACA,SACA,OACA,MACA,WACW;AACX,QAAM,SAAoC,CAAC;AAE3C,QAAM,YAAY,oBAAoB,KAAK;AAC3C,MAAI,aAAa,cAAc,UAAU;AACvC,WAAO,OAAO,IAAI;AAAA,EACpB;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,OAAO,KAAK,KAAK;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,OAAO;AACX,MAAI,UAAU;AACd,aAAW,OAAO,MAAM;AACtB,QAAI,QAAQ,QAAQ,eAAe;AACjC,iBAAW;AACX;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,sBAAiB,MAAkC,GAAG;AAAA,IACxD,SAAS,SAAS;AAEhB,aAAO,GAAG,IAAI,gBAAgB,gBAAgB,OAAO,CAAC;AACtD,cAAQ;AACR;AAAA,IACF;AAGA,QAAI,kBAAkB,OAAW;AAEjC,WAAO,GAAG,IAAI;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,GAAG,IAAI,IAAI,GAAG;AAAA,MACd;AAAA,IACF;AACA,YAAQ;AAAA,EACV;AAIA,MAAI,SAAS;AACX,WAAO,YAAY,IAAI,EAAE,MAAM,cAAc,QAAQ;AAAA,EACvD;AAEA,SAAO;AACT;AAGA,SAAS,kBACP,KACA,OACA,SACA,OACA,MACA,WACW;AACX,MAAI,QAAQ,UAAU,QAAQ,WAAW,IAAI,wBAAwB,GAAG,CAAC,GAAG;AAC1E,WAAO;AAAA,EACT;AACA,SAAO,iBAAiB,OAAO,SAAS,OAAO,MAAM,SAAS;AAChE;AAGA,SAAS,wBAAwB,KAAqB;AACpD,SAAO,IAAI,QAAQ,iBAAiB,EAAE,EAAE,YAAY;AACtD;AAGA,SAAS,oBAAoB,OAAmC;AAC9D,MAAI;AACF,UAAM,YAAY,OAAO,eAAe,KAAK;AAC7C,QAAI,cAAc,KAAM,QAAO;AAC/B,UAAM,OAAO,UAAU,aAAa;AACpC,WAAO,OAAO,SAAS,YAAY,KAAK,SAAS,OAAO;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,cAAc,OAAyB;AAC9C,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAGA,SAAS,eAAe,OAAe,WAA2B;AAChE,MAAI,MAAM,UAAU,UAAW,QAAO;AACtC,SAAO,GAAG,MAAM,MAAM,GAAG,SAAS,CAAC,WAAM,MAAM,SAAS,SAAS;AACnE;AAGA,SAAS,cAAc,OAAgB,WAA2B;AAChE,MAAI;AACF,WAAO,eAAe,OAAO,KAAK,GAAG,SAAS;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,gBAAgB,SAA0B;AACjD,MAAI,mBAAmB,SAAS,QAAQ,QAAS,QAAO,QAAQ;AAChE,MAAI;AACF,WAAO,OAAO,OAAO;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC9eO,SAAS,eAAe,UAAiC,CAAC,GAAmB;AAClF,QAAM,SAAS,QAAQ,UAAU,oBAAoB;AACrD,QAAM,eAAe,oBAAoB,QAAQ,KAAK;AAEtD,SAAO;AAAA,IACL,MAAM,QAAQ,QAAQ;AAAA,IACtB,OAAO,CAAC,QAAQ,OAAO;AAAA,IACvB,SAAS,CAAC,aAAuB,WAAW,SAAS,OAAO,YAAY;AAAA,IACxE,MAAM,CAAC,aAAuB;AAC5B,aAAO,MAAM,GAAG,kBAAkB,QAAQ,CAAC;AAAA,CAAI;AAAA,IACjD;AAAA,EACF;AACF;AAMA,SAAS,kBAAkB,UAA4B;AACrD,MAAI;AACF,WAAO,KAAK,UAAU,QAAQ;AAAA,EAChC,QAAQ;AACN,QAAI;AACF,aAAO,KAAK,UAAU,eAAe,QAAQ,CAAC;AAAA,IAChD,QAAQ;AACN,aAAO,KAAK,UAAU;AAAA,QACpB,MAAM,SAAS;AAAA,QACf,OAAO,SAAS;AAAA,QAChB,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAGA,SAAS,sBAAkC;AACzC,QAAM,UAAU;AAIhB,QAAM,QAAQ,QAAQ,SAAS,QAAQ;AACvC,MAAI,OAAO,UAAU,YAAY;AAC/B,UAAM,SAAS,QAAQ,QAAS;AAChC,WAAO,EAAE,OAAO,CAAC,UAAkB,MAAM,KAAK,QAAQ,KAAK,EAAE;AAAA,EAC/D;AAGA,SAAO,EAAE,OAAO,CAAC,UAAkB,QAAQ,IAAI,MAAM,QAAQ,OAAO,EAAE,CAAC,EAAE;AAC3E;;;AC3DO,SAAS,YACd,OACA,UAAyB,CAAC,GACT;AACjB,QAAM;AAAA,IACJ,WAAW,KAAK,eAAe,EAAE,gBAAgB,EAAE;AAAA,IACnD,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,oBAAoB,IAAI,KAAK,eAAe,QAAQ;AAAA,IACxD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,gBAAgB,IAAI,KAAK,eAAe,QAAQ;AAAA,IACpD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AAGD,QAAM,eAAe,CAAC,GAAG,MAAM,KAAK,EAAE;AAAA,IACpC,CAAC,OAAO,UAAU,KAAK,MAAM,MAAM,SAAS,IAAI,KAAK,MAAM,MAAM,SAAS;AAAA,EAC5E;AAGA,QAAM,aAAa,MAAM,cAAc,sBAAsB,YAAY;AACzE,QAAM,WAAW,cAAc,OAAO,eAAe,UAAU,IAAI;AAEnE,QAAM,cAAc,aAAa,MAAM,GAAG,SAAS;AACnD,QAAM,cAA4B,YAAY,IAAI,CAAC,UAAU;AAAA,IAC3D,WAAW,KAAK;AAAA,IAChB,MAAM,cAAc,OAAO,IAAI,KAAK,KAAK,SAAS,CAAC;AAAA,IACnD,MAAM,KAAK;AAAA,IACX,MAAM,eAAe,MAAM,MAAM;AAAA,IACjC,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IACpC,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,IACvC,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IAC1C,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,EAC5C,EAAE;AAEF,QAAM,OAAoB;AAAA,IACxB,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb,MAAM,kBAAkB,OAAO,IAAI,KAAK,MAAM,SAAS,CAAC;AAAA,IACxD,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,IAC3C,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,OAAO;AAAA,IACP,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EAC9C;AAEA,QAAM,QAAQ,gBAAgB,OAAO,MAAM,aAAa,cAAc;AAAA,IACpE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,EAAE,MAAM,MAAM,KAAK,IAAI,GAAG,KAAK;AACxC;AAGA,SAAS,gBACP,OACA,MACA,aACA,cACA,SACU;AACV,QAAM,aAAa,cAAc,MAAM,KAAK;AAC5C,QAAM,QAAQ,CAAC,SACb,QAAQ,SAAS,GAAG,UAAU,GAAG,IAAI,GAAG,KAAK,KAAK,KAAK;AACzD,QAAM,cAAc,aAAa,MAAM,MAAM;AAE7C,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,MAAM,KAAK,EAAE;AAC9C,QAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,MAAM,KAAK,EAAE;AAC9C,QAAM,KAAK,GAAG,MAAM,MAAM,CAAC,KAAK,KAAK,IAAI,GAAG,QAAQ,WAAW,KAAK,QAAQ,QAAQ,MAAM,EAAE,EAAE;AAE9F,MAAI,aAAa;AACf,UAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,WAAW,EAAE;AAAA,EACjD;AAEA,MAAI,MAAM,OAAO;AACf,UAAM,YAAY,CAAC,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,EACrD,OAAO,OAAO,EACd,KAAK,IAAI;AACZ,QAAI,UAAW,OAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,SAAS,EAAE;AAAA,EAC7D;AAEA,MAAI,QAAQ,WAAW,WAAW,YAAY,QAAQ;AACpD,UAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG;AAC/B,eAAW,QAAQ,aAAa;AAC9B,YAAM,KAAK,KAAK,KAAK,IAAI,WAAM,KAAK,IAAI,EAAE;AAAA,IAC5C;AACA,QAAI,aAAa,SAAS,YAAY,QAAQ;AAC5C,YAAM,KAAK,aAAQ,aAAa,SAAS,YAAY,MAAM,QAAQ;AAAA,IACrE;AAAA,EACF;AAEA,MAAI,QAAQ,UAAU;AACpB,UAAM,KAAK,GAAG,MAAM,MAAM,CAAC,GAAG;AAC9B,UAAM,OAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AACzC,QAAI,QAAQ,QAAQ;AAClB,YAAM,UAAU,qBAAqB,MAAM;AAAA,QACzC,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,MACd,CAAC;AACD,YAAM,KAAK,GAAG,OAAO;AAAA,IACvB,OAAO;AACL,YAAM,KAAK,GAAG,KAAK,MAAM,IAAI,CAAC;AAAA,IAChC;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,sBAAsB,OAAwC;AACrE,MAAI,MAAM,UAAU,EAAG,QAAO;AAE9B,QAAM,YAAY,KAAK,MAAM,MAAM,CAAC,EAAG,SAAS;AAChD,QAAM,UAAU,KAAK,MAAM,MAAM,MAAM,SAAS,CAAC,EAAG,SAAS;AAE7D,SAAO,OAAO,SAAS,SAAS,KAAK,OAAO,SAAS,OAAO,IACxD,KAAK,IAAI,GAAG,UAAU,SAAS,IAC/B;AACN;AAGO,SAAS,eAAe,cAA8B;AAC3D,MAAI,eAAe,IAAM,QAAO,GAAG,YAAY;AAC/C,QAAM,UAAU,eAAe;AAC/B,MAAI,UAAU,GAAI,QAAO,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAC9C,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,QAAM,mBAAmB,KAAK,MAAM,UAAU,EAAE,EAC7C,SAAS,EACT,SAAS,GAAG,GAAG;AAClB,SAAO,GAAG,OAAO,IAAI,gBAAgB;AACvC;AAGO,IAAM,iBAAiB;AAG9B,SAAS,eACP,MACA,WACQ;AACR,MAAI,cAAc,OAAQ,QAAO,KAAK;AAEtC,QAAM,UAAoB,CAAC;AAC3B,QAAM,OAAO,KAAK;AAClB,QAAM,QAAQ,KAAK;AAEnB,MAAI,OAAO,SAAS,UAAU;AAC5B,YAAQ,KAAK,QAAQ,IAAI,EAAE;AAAA,EAC7B,WAAW,MAAM;AACf,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAI,SAAS,KAAM,SAAQ,KAAK,GAAG,GAAG,IAAI,OAAO,KAAK,CAAC,EAAE;AAAA,IAC3D;AAAA,EACF;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,YAAQ,KAAK,SAAS,KAAK,EAAE;AAAA,EAC/B,WAAW,OAAO;AAChB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,SAAS,KAAM,SAAQ,KAAK,GAAG,GAAG,IAAI,OAAO,KAAK,CAAC,EAAE;AAAA,IAC3D;AAAA,EACF;AACA,MAAI,KAAK,OAAO;AACd,UAAM,YAAY,CAAC,KAAK,MAAM,MAAM,KAAK,MAAM,OAAO,EACnD,OAAO,OAAO,EACd,KAAK,IAAI;AACZ,QAAI,UAAW,SAAQ,KAAK,SAAS,SAAS,EAAE;AAAA,EAClD;AAEA,SAAO,QAAQ,SACX,GAAG,KAAK,IAAI,KAAK,QAAQ,KAAK,GAAG,CAAC,MAClC,KAAK;AACX;;;ACgFO,IAAM,mBAAN,MAAuB;AAAA,EACpB,UAAU,oBAAI,IAA4B;AAAA;AAAA,EAGlD,IAAI,QAAwB;AAC1B,SAAK,QAAQ,IAAI,OAAO,MAAM,MAAM;AACpC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,MAAc;AACnB,SAAK,QAAQ,OAAO,IAAI;AACxB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS;AACP,WAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;AAAA,EAClC;AAAA;AAAA,EAGA,QAAQ,OAAiB;AACvB,WAAO,MAAM,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,OAAO,OAAO;AAAA,EACnE;AAAA;AAAA,EAGA,IAAI,MAAc;AAChB,WAAO,KAAK,QAAQ,IAAI,IAAI;AAAA,EAC9B;AAAA;AAAA,EAGA,QAAQ;AACN,WAAO,CAAC,GAAG,KAAK,QAAQ,KAAK,CAAC;AAAA,EAChC;AACF;AAcO,IAAM,cAAN,MAAM,aAAY;AAAA,EACP;AAAA,EAEC;AAAA,EACA;AAAA,EACT,QAAqB,CAAC;AAAA,EACtB;AAAA,EACS;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA,WAAW,oBAAI,IAAoB;AAAA;AAAA,EAE5C,mBAAmB;AAAA;AAAA,EAGnB,UAAU,cAAc;AAAA;AAAA,EAExB,eAAe;AAAA,EAEvB,YAAY,SAA8B;AACxC,UAAM,mBAAmB,gBAAgB,SAAS,MAAM;AACxD,QAAI,kBAAkB;AACpB,WAAK,SAAS;AAAA,IAChB;AAEA,QAAI,SAAS,kBAAkB,QAAW;AACxC,WAAK,gBAAgB,QAAQ;AAAA,IAC/B;AAEA,SAAK,YAAY,iBAAiB,SAAS,SAAS;AACpD,SAAK,eAAe,oBAAoB,SAAS,KAAK;AACtD,SAAK,kBAAkB,SAAS,mBAAmB;AACnD,SAAK,cAAc,SAAS,eAAe;AAE3C,QAAI,SAAS,UAAU;AAGrB,WAAK,WAAW,QAAQ;AAAA,IAC1B,OAAO;AACL,WAAK,WAAW,IAAI,iBAAiB;AAIrC,WAAK,SAAS;AAAA,QACZ,oBAAoB,SAAS,MAAM,MAAM,WACrC,eAAe,EAAE,OAAO,KAAK,aAAa,CAAC,IAC3C,gBAAgB;AAAA,MACtB;AAAA,IACF;AAEA,aAAS,WAAW,QAAQ,CAAC,aAAa,KAAK,SAAS,IAAI,QAAQ,CAAC;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QAAQ,WAA2B;AACjC,SAAK,YAAY,iBAAiB,SAAS;AAC3C,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,iBAAiB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BA,QAAQ,UAA0B,CAAC,GAAgB;AACjD,UAAM,eAAiC,EAAE,GAAG,KAAK,QAAQ,GAAG,QAAQ,OAAO;AAE3E,WAAO,IAAI,aAAY;AAAA,MACrB,UAAU,KAAK;AAAA;AAAA,MAEf,eAAe,KAAK;AAAA,MACpB,GAAI,OAAO,KAAK,YAAY,EAAE,SAAS,EAAE,QAAQ,aAAa,IAAI,CAAC;AAAA,MACnE,WAAW,QAAQ,aAAa,KAAK;AAAA,MACrC,OAAO,QAAQ,SAAS,KAAK;AAAA,MAC7B,iBAAiB,QAAQ,mBAAmB,KAAK;AAAA,MACjD,aAAa,QAAQ,eAAe,KAAK;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,OAAO,OAAgB,OAAiB,CAAC,GAAG;AAC1C,UAAM,YAAY,cAAc,KAAK;AACrC,UAAM,QAAQ,aAAa,KAAK,KAAK;AAErC,UAAM,OAAkB;AAAA,MACtB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,UAAU,KAAK;AAAA,MACf,MAAM,UAAU;AAAA,MAChB,GAAI,UAAU,gBAAgB,EAAE,MAAM,IAAI,CAAC;AAAA,MAC3C,GAAI,KAAK,QAAQ,SAAY,EAAE,KAAK,eAAe,KAAK,GAAG,EAAE,IAAI,CAAC;AAAA,MAClE,GAAI,KAAK,SAAS,SACd,EAAE,MAAM,eAAe,KAAK,IAAI,EAAE,IAClC,UAAU,SAAS,SACnB,EAAE,MAAM,UAAU,KAAK,IACvB,CAAC;AAAA,MACL,GAAI,KAAK,UAAU,SAAY,EAAE,OAAO,eAAe,KAAK,KAAK,EAAE,IAAI,CAAC;AAAA,MACxE,GAAI,KAAK,UAAU,SACf,EAAE,OAAO,eAAe,KAAK,KAAK,EAAE,IACpC,UAAU,UAAU,SACpB,EAAE,OAAO,UAAU,MAAM,IACzB,CAAC;AAAA,IACP;AAEA,SAAK,gBAAgB;AACrB,SAAK,MAAM,KAAK,IAAI;AAEpB,QAAI,KAAK,cAAc,UAAU,KAAK,MAAM;AAC1C,WAAK,SAAS,MAAM,OAAO,KAAK,EAAE;AAAA,IACpC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ;AACN,SAAK,QAAQ,CAAC;AACd,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAU,UAA0B,CAAC,GAAG;AACtC,UAAM;AAAA,MACJ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA,GAAG;AAAA,IACL,IAAI;AACJ,UAAM,QAAwB;AAAA,MAC5B,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC;AAAA,MACA;AAAA,MACA,SAAS,KAAK;AAAA,MACd,GAAI,KAAK,kBAAkB,SAAY,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;AAAA,MAChF,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,MAC7C,OAAO,CAAC,GAAG,KAAK,KAAK;AAAA,MACrB,GAAI,UAAU,SAAY,EAAE,OAAO,eAAe,KAAK,EAAE,IAAI,CAAC;AAAA,IAChE;AAEA,WAAO,YAAY,OAAO,aAAa;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,OAAO,OAAe,UAAyB,CAAC,GAAG;AACjD,WAAO,KAAK,eAAe,aAAa,QAAQ,KAAK,GAAG,OAAO,QAAQ,KAAK;AAAA,EAC9E;AAAA;AAAA,EAGA,KAAK,OAAe;AAClB,mBAAe,QAAQ,QAAQ;AAC/B,WAAO,KAAK,eAAe,eAAe,KAAK;AAAA,EACjD;AAAA;AAAA,EAGA,KAAK,OAAe;AAClB,mBAAe,QAAQ,kCAAkC;AACzD,WAAO,KAAK,eAAe,WAAW,KAAK;AAAA,EAC7C;AAAA;AAAA,EAGA,KAAK,OAAe,OAAiB;AACnC,mBAAe,QAAQ,yCAAyC;AAChE,WAAO,KAAK,eAAe,SAAS,OAAO,KAAK;AAAA,EAClD;AAAA;AAAA,EAGA,KAAK,OAAgB,OAAiB,CAAC,GAAG;AACxC,mBAAe,QAAQ,QAAQ;AAC/B,WAAO,KAAK,OAAO,OAAO,IAAI;AAAA,EAChC;AAAA;AAAA,EAGQ,SAAS,MAAiB,OAAmB,MAAiB;AACpE,UAAM,WAAyB;AAAA,MAC7B,GAAG;AAAA,MACH,MAAM;AAAA,MACN,SAAS,KAAK;AAAA,MACd,GAAI,KAAK,kBAAkB,SAAY,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;AAAA,MAChF,UAAU,KAAK,YAAY;AAAA,MAC3B;AAAA,MACA,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,IAC/C;AAEA,SAAK,KAAK,QAAQ,UAAU,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC;AAAA,EAClD;AAAA;AAAA,EAGQ,eAAe,OAAmB,OAAe,OAAiB;AACxE,UAAM,QAAQ,KAAK,WAAW,OAAO,OAAO,KAAK;AAEjD,QAAI,YAAY;AAChB,QAAI,mBAAmB;AAGvB,mBAAe,MAAM;AACnB,UAAI,aAAa,iBAAkB;AACnC,kBAAY;AACZ,WAAK,KAAK,QAAQ,KAAK;AAAA,IACzB,CAAC;AAED,WAAO;AAAA,MACL,IAAI,IAAI,UAAoB;AAC1B,2BAAmB;AACnB,YAAI,UAAW;AACf,oBAAY;AACZ,aAAK,KAAK,QAAQ,OAAO,EAAE,MAAM,MAAM,CAAC;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,WAAW,OAAmB,OAAe,OAA6B;AAChF,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,UAAU,KAAK;AAErB,UAAM,mBAAmB,KAAK;AAI9B,UAAM,cAAc,CAAC,GAAG,KAAK,KAAK,EAAE;AAAA,MAClC,CAAC,OAAO,UACN,KAAK,MAAM,MAAM,SAAS,IAAI,KAAK,MAAM,MAAM,SAAS,MACvD,MAAM,YAAY,MAAM,MAAM,YAAY;AAAA,IAC/C;AAEA,SAAK,QAAQ,CAAC;AACd,SAAK,cAAc;AAGnB,UAAM,aAAaA,uBAAsB,WAAW,EAAE;AAEtD,UAAM,QAAwB;AAAA,MAC5B,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,KAAK,kBAAkB,SAAY,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;AAAA,MAChF,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,MAC7C,OAAO;AAAA,MACP,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,MAC3C,GAAI,mBAAmB,EAAE,iBAAiB,IAAI,CAAC;AAAA,MAC/C,GAAI,UAAU,SAAY,EAAE,OAAO,eAAe,KAAK,EAAE,IAAI,CAAC;AAAA,IAChE;AAEA,UAAM,mBAAmB;AACzB,WAAO,eAAe,kBAAkB,QAAQ;AAAA,MAC9C,OAAO;AAAA,MACP,YAAY;AAAA,IACd,CAAC;AACD,WAAO,eAAe,kBAAkB,aAAa;AAAA,MACnD,OAAO,CAAC,YAA4B,YAAY,OAAO,OAAO;AAAA,MAC9D,YAAY;AAAA,IACd,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,gBAAgB;AACtB,SAAK,UAAU,cAAc;AAC7B,SAAK,eAAe;AACpB,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA,EAGA,MAAc,QAAQ,UAAoB,SAA+B;AAEvE,QAAI,CAAC,WAAW,SAAS,OAAO,KAAK,YAAY,EAAG;AAEpD,UAAM,UAAU,SAAS,MAAM,SAC3B,KAAK,SAAS,QAAQ,QAAQ,IAAI,IAClC,KAAK,SAAS,OAAO;AAEzB,UAAM,QAAQ;AAAA,MACZ,QACG,OAAO,CAAC,WAAW,UAAU,QAAQ,SAAS,IAAI,CAAC,EACnD,OAAO,CAAC,WAAW,KAAK,cAAc,QAAQ,QAAQ,CAAC,EACvD,IAAI,CAAC,WAAW,KAAK,WAAW,QAAQ,QAAQ,CAAC;AAAA,IACtD;AAAA,EACF;AAAA;AAAA,EAGQ,cAAc,QAAwB,UAA6B;AACzE,QAAI,CAAC,OAAO,QAAS,QAAO;AAE5B,QAAI;AACF,aAAO,OAAO,QAAQ,QAAQ;AAAA,IAChC,SAAS,OAAO;AACd,WAAK,oBAAoB,OAAO,QAAQ,QAAQ;AAChD,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,WAAW,QAAwB,UAAoB;AACnE,UAAM,UAAU,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK;AAClD,QAAI,WAAW,KAAK,aAAa;AAC/B,WAAK,oBAAoB;AACzB;AAAA,IACF;AAEA,SAAK,SAAS,IAAI,OAAO,MAAM,UAAU,CAAC;AAC1C,QAAI;AACF,YAAM,OAAO,KAAK,QAAQ;AAAA,IAC5B,SAAS,OAAO;AACd,WAAK,oBAAoB,OAAO,QAAQ,QAAQ;AAAA,IAClD,UAAE;AACA,YAAM,aAAa,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,KAAK;AAC1D,UAAI,YAAY,EAAG,MAAK,SAAS,IAAI,OAAO,MAAM,SAAS;AAAA,UACtD,MAAK,SAAS,OAAO,OAAO,IAAI;AAAA,IACvC;AAAA,EACF;AAAA;AAAA,EAGQ,oBAAoB,OAAgB,QAAwB,UAAoB;AACtF,QAAI;AACF,WAAK,gBAAgB,OAAO,QAAQ,QAAQ;AAAA,IAC9C,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAGA,IAAM,qBAAqB,oBAAI,IAAY;AAS3C,SAAS,eAAe,SAAiB,aAAqB;AAC5D,MAAI,mBAAmB,IAAI,OAAO,EAAG;AACrC,MAAI,qBAAqB,kCAAkC,MAAM,IAAK;AAEtE,qBAAmB,IAAI,OAAO;AAC9B,UAAQ;AAAA,IACN,gBAAgB,OAAO,0DAAqD,WAAW;AAAA,EACzF;AACF;AAGA,IAAM,wBAAwB;AAG9B,IAAM,6BAA6B;AAGnC,IAAM,4BAA4B,oBAAI,IAAoB;AAM1D,SAAS,6BACP,OACA,QACA,UACA;AACA,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,eAAe,0BAA0B,IAAI,OAAO,IAAI;AAC9D,MAAI,iBAAiB,UAAa,MAAM,eAAe,4BAA4B;AACjF;AAAA,EACF;AAEA,4BAA0B,IAAI,OAAO,MAAM,GAAG;AAC9C,QAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,UAAQ;AAAA,IACN,0BAA0B,OAAO,IAAI,sBAAsB,SAAS,IAAI,WAAM,MAAM;AAAA,EACtF;AACF;AAGA,SAAS,UAAU,QAAwB,MAA6B;AAEtE,QAAM,QAAQ,OAAO,SAAS,CAAC,OAAO;AACtC,SAAO,MAAM,SAAS,IAAI;AAC5B;AAGA,SAAS,iBAAiB,WAAuC;AAC/D,QAAM,QAAQ,aAAa,qBAAqB,uBAAuB;AACvE,MAAI,UAAU,UAAU,UAAU,OAAQ,QAAO;AACjD,SAAO;AACT;AAGA,SAAS,gBAAwB;AAC/B,MAAI;AACF,UAAM,gBAAgB,WAAW;AACjC,QAAI,OAAO,eAAe,eAAe,YAAY;AACnD,aAAO,cAAc,WAAW;AAAA,IAClC;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO,SAAS,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACpF;AAGA,SAAS,gBAAgB,QAAoD;AAC3E,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,aAA0B;AAAA,IAC9B,GAAI,OAAO,QAAQ,SAAY,EAAE,KAAK,eAAe,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,IACtE,GAAI,OAAO,SAAS,SAAY,EAAE,MAAM,eAAe,OAAO,IAAI,EAAE,IAAI,CAAC;AAAA,IACzE,GAAI,OAAO,UAAU,SAAY,EAAE,OAAO,eAAe,OAAO,KAAK,EAAE,IAAI,CAAC;AAAA,EAC9E;AAEA,SAAO,OAAO,KAAK,UAAU,EAAE,SAAS,aAAa;AACvD;AAOA,SAAS,cAAc,OAIrB;AACA,MAAI,OAAO,UAAU,SAAU,QAAO,EAAE,MAAM,MAAM;AAEpD,MAAI,iBAAiB,OAAO;AAC1B,UAAM,QAAQ,eAAe,KAAK;AAClC,UAAM,QAAQ,CAAC,MAAM,MAAM,MAAM,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AACnE,WAAO,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,EACzC;AAEA,MAAI,UAAU,KAAM,QAAO,EAAE,MAAM,OAAO;AAC1C,MAAI,UAAU,OAAW,QAAO,EAAE,MAAM,YAAY;AAEpD,QAAM,aAAa,eAAe,KAAK;AAIvC,MAAI,OAAO,eAAe,YAAY,eAAe,MAAM;AACzD,WAAO,EAAE,MAAM,OAAO,UAAU,GAAG,MAAM,WAAW;AAAA,EACtD;AAEA,MAAI,MAAM,QAAQ,UAAU,GAAG;AAC7B,WAAO,EAAE,MAAM,SAAS,WAAW,MAAM,KAAK,MAAM,WAAW;AAAA,EACjE;AAGA,aAAW,OAAO,CAAC,WAAW,SAAS,QAAQ,WAAW,OAAO,GAAG;AAClE,UAAM,YAAY,WAAW,GAAG;AAChC,QAAI,OAAO,cAAc,YAAY,UAAU,QAAQ;AACrD,aAAO,EAAE,MAAM,WAAW,MAAM,WAAW;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,WAAW,WAAW,OAAO;AACnC,SAAO;AAAA,IACL,MAAM,OAAO,aAAa,WAAW,WAAW;AAAA,IAChD,MAAM;AAAA,EACR;AACF;AAGA,SAASA,uBAAsB,OAAoB;AACjD,MAAI,MAAM,UAAU,GAAG;AACrB,WAAO;AAAA,MACL,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,YAAY,KAAK,MAAM,MAAM,CAAC,EAAG,SAAS;AAChD,QAAM,UAAU,KAAK,MAAM,MAAM,MAAM,SAAS,CAAC,EAAG,SAAS;AAE7D,SAAO;AAAA,IACL,YAAY,OAAO,SAAS,SAAS,KAAK,OAAO,SAAS,OAAO,IAC7D,KAAK,IAAI,GAAG,UAAU,SAAS,IAC/B;AAAA,EACN;AACF;;;ACx3BA,IAAI;AAqBG,SAAS,eACd,UAAoC,CAAC,GACxB;AACb,MAAI,CAAC,kBAAkB,QAAQ,OAAO;AACpC,qBAAiB,IAAI,YAAY;AAAA,MAC/B,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,MACjE,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IAC5E,CAAC;AACD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;ACbO,SAAS,WAAW,QAAqE;AAC9F,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,CAAC,OAAO;AAAA,IACf,SAAS,CAAC,aACR,SAAS,SAAS,YACjB,SAAS,UAAU,aAAa,SAAS,UAAU;AAAA,IACtD,MAAM,OAAO,aAAuB;AAClC,UAAI,SAAS,SAAS,QAAS;AAC/B,YAAM,OAAO,QAAQ;AAAA,IACvB;AAAA,EACF;AACF;;;ACrBO,SAAS,iBACd,SACA,UAA8B,CAAC,GACvB;AACR,QAAM;AAAA,IACJ,WAAW,KAAK,eAAe,EAAE,gBAAgB,EAAE;AAAA,IACnD,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,SAAS;AAAA,EACX,IAAI;AAEJ,MAAI,CAAC,QAAQ,QAAQ;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,CAAC,GAAG,OAAO,EAAE;AAAA,IAC1B,CAAC,QAAQ,WAAW,KAAK,MAAM,OAAO,SAAS,IAAI,KAAK,MAAM,OAAO,SAAS;AAAA,EAChF;AAEA,QAAM,gBAAgB,IAAI,KAAK,eAAe,QAAQ;AAAA,IACpD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,EACP,CAAC;AAED,QAAM,aAAa,OAAO,CAAC;AAC3B,QAAM,YAAY,OAAO,OAAO,SAAS,CAAC;AAC1C,MAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,uBAAuB,QAAQ,GAAG;AAC7C,QAAM;AAAA,IACJ,UAAU,cAAc,OAAO,IAAI,KAAK,WAAW,SAAS,CAAC,CAAC,WAAM,cAAc;AAAA,MAChF,IAAI,KAAK,UAAU,SAAS;AAAA,IAC9B,CAAC;AAAA,EACH;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,eAAe,oBAAI,IAA8B;AACvD,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,cAAc,OAAO,IAAI,KAAK,MAAM,SAAS,CAAC;AAC7D,UAAM,YAAY,aAAa,IAAI,MAAM,KAAK,CAAC;AAC/C,cAAU,KAAK,KAAK;AACpB,iBAAa,IAAI,QAAQ,SAAS;AAAA,EACpC;AAEA,aAAW,CAAC,KAAK,UAAU,KAAK,cAAc;AAC5C,UAAM,KAAK,GAAG;AAEd,eAAW,SAAS,YAAY;AAC9B,YAAM,SAAS,YAAY,OAAO;AAAA,QAChC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,cAAc,aAAa,MAAM,MAAM;AAC7C,YAAM,aAAa,cAAc,MAAM,KAAK;AAC5C,YAAM,QAAQ,CAAC,SACb,SAAS,GAAG,UAAU,GAAG,IAAI,GAAG,KAAK,KAAK,KAAK;AAEjD,YAAM,WAAW,KAAK,WAAW,KAAK,KAAK,QAAQ,MAAM;AACzD,YAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,MAAM,KAAK,EAAE;AAC9C,YAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,MAAM,KAAK,EAAE;AAC9C,YAAM,KAAK,GAAG,MAAM,MAAM,CAAC,KAAK,KAAK,IAAI,GAAG,QAAQ,EAAE;AAEtD,UAAI,aAAa;AACf,cAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,WAAW,EAAE;AAAA,MACjD;AAEA,UAAI,KAAK,OAAO;AACd,cAAM,YAAY;AAAA,UAChB,KAAK,MAAM;AAAA,UACX,KAAK,MAAM;AAAA,QACb,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AACZ,YAAI,UAAW,OAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,SAAS,EAAE;AAAA,MAC7D;AAEA,UAAI,WAAW,WAAW,KAAK,MAAM,QAAQ;AAC3C,cAAM,KAAK,KAAK,MAAM,OAAO,CAAC,GAAG;AAEjC,mBAAW,cAAc,KAAK,OAAO;AACnC,gBAAM,KAAK,OAAO,WAAW,IAAI,WAAM,WAAW,IAAI,EAAE;AAAA,QAC1D;AAEA,YAAI,MAAM,MAAM,SAAS,KAAK,MAAM,QAAQ;AAC1C,gBAAM;AAAA,YACJ,eAAU,MAAM,MAAM,SAAS,KAAK,MAAM,MAAM;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAEA,UAAI,UAAU;AACZ,cAAM,KAAK,GAAG,MAAM,MAAM,CAAC,GAAG;AAC9B,cAAM,OAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AACzC,YAAI,QAAQ;AACV,gBAAM,UAAU,qBAAqB,MAAM;AAAA,YACzC,MAAM,KAAK;AAAA,YACX,OAAO,KAAK;AAAA,YACZ,OAAO,KAAK;AAAA,UACd,CAAC;AACD,gBAAM,KAAK,GAAG,OAAO;AAAA,QACvB,OAAO;AACL,gBAAM,KAAK,GAAG,KAAK,MAAM,IAAI,CAAC;AAAA,QAChC;AAAA,MACF;AAEA,YAAM,KAAK,EAAE;AAAA,IACf;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI,EAAE,KAAK,IAAI;AACnC;","names":["calculateNoteDuration"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/utils.ts","../src/audiences/consoleAudience.ts","../src/redaction.ts","../src/normalize.ts","../src/audiences/ndjsonAudience.ts","../src/formatting.ts","../src/storyteller.ts","../src/useStoryteller.ts","../src/audiences/dbAudience.ts","../src/store/storyStore.ts","../src/store/memoryStore.ts","../src/store/storeAudience.ts","../src/store/stories.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 \"./store/index\";\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","/**\n * Redaction by value, not only by key.\n *\n * Key-name matching catches `password: \"hunter2\"`. It does not catch a Stripe\n * key inside an error message, a bearer token in a URL, or a private key\n * pasted into a note. Once stories are persisted and read back by agents, a\n * leaked secret is durable and retrievable, so the value itself has to be\n * recognised too.\n *\n * Defense in depth, not a guarantee: these are recognisable formats and\n * suspicious positions. A secret that looks like an ordinary word passes.\n * The balance leans toward keeping records readable — a viewer full of\n * `[redacted]` is no viewer — with a strict mode for the other trade.\n */\nimport type { JsonValue } from \"./normalize\";\n\n/** Marker written in place of a redacted value or span */\nexport const REDACTED = \"[redacted]\";\n\n/** How hard to look inside values. `off` leaves key matching only. */\nexport type RedactionStrictness = \"off\" | \"balanced\" | \"strict\";\n\n/**\n * Formats that are secrets by construction. Each pattern replaces only the\n * secret span, so the text around it — the sentence, the URL's host — stays.\n */\ntype Rule = { pattern: RegExp; replacement: string };\ntype RuleGroup = { anchor: RegExp; rules: Rule[] };\n\n/**\n * Grouped by what a string must contain for any rule in the group to match,\n * so one cheap anchor test per group decides whether its rules run. The\n * ordinary string — a sentence, a path, a UUID, a user agent — costs three\n * scans and no replacements.\n */\nconst RULE_GROUPS: RuleGroup[] = [\n {\n // Vendor key formats\n anchor: /sk[-_]|rk_|gh[opusr]_|github_pat_|glpat-|npm_|xox[abprs]-|AIza|SG\\.|AKIA|ASIA|eyJ|PRIVATE KEY/,\n rules: [\n // Stripe secret and restricted keys\n { pattern: /\\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,}\\b/g, replacement: REDACTED },\n // OpenAI, Anthropic and similar `sk-` keys\n { pattern: /\\bsk-(?:[A-Za-z0-9]+-)*[A-Za-z0-9_-]{16,}/g, replacement: REDACTED },\n // GitHub tokens, classic and fine-grained\n { pattern: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{16,}\\b/g, replacement: REDACTED },\n { pattern: /\\bgithub_pat_[A-Za-z0-9_]{20,}\\b/g, replacement: REDACTED },\n // GitLab, npm, Slack, Google, SendGrid, AWS access key ids\n { pattern: /\\bglpat-[A-Za-z0-9_-]{16,}\\b/g, replacement: REDACTED },\n { pattern: /\\bnpm_[A-Za-z0-9]{30,}\\b/g, replacement: REDACTED },\n { pattern: /\\bxox[abprs]-[A-Za-z0-9-]{10,}\\b/g, replacement: REDACTED },\n { pattern: /\\bAIza[0-9A-Za-z_-]{35}\\b/g, replacement: REDACTED },\n { pattern: /\\bSG\\.[A-Za-z0-9_-]{16,}\\.[A-Za-z0-9_-]{16,}\\b/g, replacement: REDACTED },\n { pattern: /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g, replacement: REDACTED },\n // JSON Web Tokens\n { pattern: /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\b/g, replacement: REDACTED },\n // PEM private keys, whole block\n { pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, replacement: REDACTED },\n ],\n },\n {\n // Authorization header values: keep the scheme, drop the credential\n anchor: /\\b(?:Bearer|Basic|Token|Digest) /,\n rules: [{ pattern: /\\b(Bearer|Basic|Token|Digest)\\s+[A-Za-z0-9._~+/=-]{12,}/g, replacement: `$1 ${REDACTED}` }],\n },\n {\n // URLs: a password in the userinfo, a secret in the query string\n anchor: /:\\/\\/|[?&]/,\n rules: [\n { pattern: /(:\\/\\/[^\\s/:@]+:)[^\\s@/]+(@)/g, replacement: `$1${REDACTED}$2` },\n {\n pattern: /([?&](?:token|access_token|refresh_token|id_token|api_key|apikey|api-key|key|secret|client_secret|password|passwd|pwd|signature|sig|auth|authorization)=)[^&\\s#]+/gi,\n replacement: `$1${REDACTED}`,\n },\n ],\n },\n];\n\n/**\n * Key names whose string values are secrets whenever they are long enough to\n * be one. Substring match on the reduced key, so `dbPassword`, `x-api-key`,\n * `STRIPE_SECRET_KEY` and `clientCredential` all count.\n */\nconst STRONG_KEY_HINT =\n /password|passwd|passphrase|secret|apikey|privatekey|credential|authorization|accesskey|sessionid|cookie|refreshtoken|accesstoken|idtoken|clientkey|signingkey|encryptionkey|masterkey|servicekey/;\n/** Weaker hints: only redact when the value also looks random */\nconst WEAK_KEY_HINT = /token|key|auth|bearer|cert|pwd/;\n\ntype KeyHint = \"strong\" | \"weak\" | \"none\";\n/** Property names repeat across every note of every story; classify each once */\nconst keyHints = new Map<string, KeyHint>();\nconst KEY_HINT_CACHE_LIMIT = 4096;\n\nfunction hintFor(key: string): KeyHint {\n const cached = keyHints.get(key);\n if (cached !== undefined) return cached;\n const reduced = normalizeKeyForMatching(key);\n const hint: KeyHint = STRONG_KEY_HINT.test(reduced) ? \"strong\" : WEAK_KEY_HINT.test(reduced) ? \"weak\" : \"none\";\n if (keyHints.size >= KEY_HINT_CACHE_LIMIT) keyHints.clear();\n keyHints.set(key, hint);\n return hint;\n}\n\nconst MIN_STRONG_LENGTH = 4;\nconst MIN_ENTROPY_LENGTH = 16;\nconst MIN_ENTROPY_BITS = 3;\nconst MIN_STRICT_LENGTH = 32;\n\n/** Reduce a property name to letters and digits so casing and separators do not matter */\nexport function normalizeKeyForMatching(key: string): string {\n return key.replace(/[^a-zA-Z0-9]/g, \"\").toLowerCase();\n}\n\n/** Shannon entropy in bits per character */\nfunction entropyOf(text: string): number {\n const counts = new Map<string, number>();\n for (const char of text) counts.set(char, (counts.get(char) ?? 0) + 1);\n let bits = 0;\n for (const count of counts.values()) {\n const p = count / text.length;\n bits -= p * Math.log2(p);\n }\n return bits;\n}\n\n/** A string that reads as random: long enough, no whitespace, mixed characters */\nfunction looksRandom(value: string, minimumLength: number): boolean {\n if (value.length < minimumLength || /\\s/.test(value)) return false;\n // Hex is a hash or an id far more often than a secret; digits alone are a number\n if (/^[0-9a-f-]+$/i.test(value) || /^[0-9.-]+$/.test(value)) return false;\n return entropyOf(value) >= MIN_ENTROPY_BITS;\n}\n\n/**\n * Does this property hold a secret, judging by its name and what the value\n * looks like? Only strings — a `tokenCount: 12` or `tokens: [\"a\", \"b\"]` is data.\n */\nexport function keyLooksSecret(key: string, value: unknown): boolean {\n if (typeof value !== \"string\") return false;\n const hint = hintFor(key);\n if (hint === \"strong\") return value.length >= MIN_STRONG_LENGTH;\n if (hint === \"weak\") return looksRandom(value, MIN_ENTROPY_LENGTH);\n return false;\n}\n\n/**\n * Replace every recognisable secret inside a string, keeping the rest.\n * In strict mode any long random-looking run goes too.\n */\n/** Any group's anchor, as one scan: the ordinary string pays for this test and nothing else */\nconst ANY_ANCHOR = new RegExp(RULE_GROUPS.map((group) => group.anchor.source).join(\"|\"));\n\nexport function redactString(value: string, strictness: RedactionStrictness = \"balanced\"): string {\n if (strictness === \"off\" || value.length < 8) return value;\n let result = value;\n if (!ANY_ANCHOR.test(value)) return strictness === \"strict\" ? redactRandomRuns(value) : value;\n for (const group of RULE_GROUPS) {\n if (!group.anchor.test(result)) continue;\n for (const { pattern, replacement } of group.rules) {\n pattern.lastIndex = 0;\n result = result.replace(pattern, replacement);\n }\n }\n return strictness === \"strict\" ? redactRandomRuns(result) : result;\n}\n\n/** Strict mode: any long run that reads as random goes, hashes and ids excepted */\nfunction redactRandomRuns(text: string): string {\n return text.replace(/(?<![A-Za-z0-9+/=_-])[A-Za-z0-9+/_-]{32,}={0,2}(?![A-Za-z0-9+/=_-])/g, (run) =>\n looksRandom(run, MIN_STRICT_LENGTH) && characterClasses(run) >= 3 ? REDACTED : run\n );\n}\n\nfunction characterClasses(text: string): number {\n return [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((test) => test.test(text)).length;\n}\n\nexport type RedactJsonOptions = {\n /** Property names redacted whatever their value; reduced like the keys they match */\n redactKeys?: Set<string>;\n strictness?: RedactionStrictness;\n};\n\n/**\n * Redact an already-normalized value: exact keys, secret-shaped keys, and\n * secret-shaped strings. This is the pass at the storage boundary, so a story\n * fed to a store by hand — or normalized with redaction off — is still covered\n * before it becomes durable. Returns a new value; the input is untouched.\n */\nexport function redactJson(value: JsonValue, options: RedactJsonOptions = {}): JsonValue {\n const strictness = options.strictness ?? \"balanced\";\n const keys = options.redactKeys;\n const walk = (node: JsonValue): JsonValue => {\n if (typeof node === \"string\") return redactString(node, strictness);\n if (Array.isArray(node)) return node.map(walk);\n if (node && typeof node === \"object\") {\n const out: { [key: string]: JsonValue } = {};\n for (const [key, child] of Object.entries(node)) {\n if (keys?.has(normalizeKeyForMatching(key)) || (strictness !== \"off\" && keyLooksSecret(key, child))) {\n out[key] = REDACTED;\n } else {\n out[key] = walk(child);\n }\n }\n return out;\n }\n return node;\n };\n return walk(value);\n}\n\nexport type RedactionFinding = {\n /** Where, as `$.a.b[0]` */\n path: string;\n /** Why it would be redacted */\n reason: \"key\" | \"key-hint\" | \"pattern\" | \"entropy\";\n /** The first characters, so a person can tell what it was without seeing it */\n preview: string;\n};\n\n/**\n * Report what redaction *would* do to a normalized value, changing nothing.\n * Run it over a corpus of real stories to see coverage rather than trust it —\n * and to find the false positives before they eat a record.\n */\nexport function auditRedaction(value: JsonValue, options: RedactJsonOptions = {}): RedactionFinding[] {\n const strictness = options.strictness ?? \"balanced\";\n const keys = options.redactKeys;\n const findings: RedactionFinding[] = [];\n const preview = (text: string) => (text.length <= 4 ? \"…\" : `${text.slice(0, 4)}…`);\n const walk = (node: JsonValue, path: string): void => {\n if (typeof node === \"string\") {\n if (node === REDACTED) return;\n const redacted = redactString(node, strictness);\n if (redacted !== node) {\n const strict = redactString(node, \"balanced\") === node;\n findings.push({ path, reason: strict ? \"entropy\" : \"pattern\", preview: preview(node) });\n }\n return;\n }\n if (Array.isArray(node)) {\n node.forEach((child, index) => walk(child, `${path}[${index}]`));\n return;\n }\n if (node && typeof node === \"object\") {\n for (const [key, child] of Object.entries(node)) {\n const childPath = `${path}.${key}`;\n if (child === REDACTED) continue;\n if (keys?.has(normalizeKeyForMatching(key))) {\n findings.push({ path: childPath, reason: \"key\", preview: typeof child === \"string\" ? preview(child) : \"…\" });\n } else if (strictness !== \"off\" && keyLooksSecret(key, child)) {\n findings.push({ path: childPath, reason: \"key-hint\", preview: preview(child as string) });\n } else {\n walk(child, childPath);\n }\n }\n }\n };\n walk(value, \"$\");\n return findings;\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 * How hard to look inside values for secrets: `balanced` (default) redacts\n * recognisable token formats and random-looking strings under secret-shaped\n * keys; `strict` also redacts any long random-looking run; `off` matches key\n * names only.\n */\n redactValues?: RedactionStrictness;\n};\n\nimport { REDACTED, keyLooksSecret, normalizeKeyForMatching, redactString, type RedactionStrictness } from \"./redaction\";\nexport { REDACTED } from \"./redaction\";\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 redactValues: RedactionStrictness;\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 redactValues: options.redactValues ?? \"balanced\",\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 redactValues: options.redactValues ?? \"balanced\",\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\" ? redactText(record[\"message\"], options) : 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: redactText(safeStringify(rawError, options.maxStringLength), options) };\n }\n\n const normalized: StoryError = {\n name: rawError.name,\n message: redactText(rawError.message, options),\n };\n\n if (rawError.stack !== undefined) {\n normalized.stack = truncateString(redactText(rawError.stack, options), 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(redactText(value as string, options), 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 if (options.redact && options.redactValues !== \"off\" && keyLooksSecret(key, value)) {\n return REDACTED;\n }\n return normalizeUnknown(value, options, depth, path, ancestors);\n}\n\n/** Replace recognisable secrets inside a string, when redaction is on */\nfunction redactText(value: string, options: ResolvedOptions): string {\n return options.redact ? redactString(value, options.redactValues) : value;\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","/**\n * The StoryStore contract: where stories go, and how they come back.\n *\n * A story is the unit of retrieval — self-contained, ordered, small enough to\n * fit in a context window. The contract is deliberately small: append, get by\n * id, query by structured criteria, walk chapters, and forget. Every adapter\n * (in memory, a file, SQLite, Postgres) answers the same questions, so the\n * questions — not each adapter's query language — are the contract.\n */\nimport type { StoryEvent, StoryEventBase, StoryLevel, StoryNote, StoryOrigin } from \"../storyteller\";\nimport type { JsonValue } from \"../normalize\";\nimport { DEFAULT_REDACT_KEYS } from \"../normalize\";\nimport { normalizeKeyForMatching, redactJson, type RedactionStrictness } from \"../redaction\";\nimport { meetsLevel } from \"../environment\";\n\n/** A story as kept: the record without the `summarize` method, with an id it can be fetched by */\nexport type StoredStory = StoryEventBase & { storyId: string };\n\n/**\n * Structured criteria. Every field narrows; an empty query matches everything.\n * Built by hand or by the query vocabulary; adapters receive this object,\n * never a string to parse.\n */\nexport type StoryQuery = {\n /** Stories whose timestamp is at or after this instant */\n since?: Date;\n /** Stories whose timestamp is before this instant */\n until?: Date;\n /** Exactly this level, or any of these */\n level?: StoryLevel | StoryLevel[];\n /** At least this level: Warning matches Warning and Error */\n minimumLevel?: StoryLevel;\n /** Case-insensitive match over the title, note text, scalar context and error messages */\n about?: string;\n /** Case-insensitive match over the origin's who / what / where */\n from?: string;\n /** Chapters of this story */\n parentStoryId?: string;\n /** Stories that took longer than this */\n slowerThanMs?: number;\n /** Stories that carry an error or closed at Error level */\n failed?: boolean;\n limit?: number;\n offset?: number;\n /** Newest first unless told otherwise */\n order?: \"newest\" | \"oldest\";\n};\n\nexport type StoryStore = {\n /** Keep a story. Replaces an earlier record with the same id. Never throws synchronously. */\n append(event: StoryEvent | StoredStory): Promise<void>;\n /** One story, complete, by id */\n get(storyId: string): Promise<StoredStory | undefined>;\n /** Stories matching the criteria, newest first by default */\n query(criteria?: StoryQuery): Promise<StoredStory[]>;\n /** The chapters of a story, in the order they were told */\n children(parentStoryId: string): Promise<StoredStory[]>;\n /** Forget stories that began before the boundary; returns how many were removed */\n prune(before: Date): Promise<number>;\n};\n\nexport type ToStoredStoryOptions = {\n /**\n * Redaction at the storage boundary. The normalizer already redacted at\n * capture; this pass covers a record fed to a store by hand, or one\n * normalized with redaction off, before it becomes durable. Default\n * `balanced`; `off` trusts the record as given.\n */\n redactValues?: RedactionStrictness;\n};\n\nconst DEFAULT_KEY_SET = new Set(DEFAULT_REDACT_KEYS.map(normalizeKeyForMatching));\n\n/**\n * The record as a store keeps it: a deep copy of the JSON-safe fields, so a\n * caller holding the event cannot change what was stored, and no method rides\n * along. A record without an id — one written before ids existed — is given\n * one. Secrets are redacted once more on the way in: persisted is the moment\n * a leaked value stops being a line that scrolled past.\n */\nexport function toStoredStory(event: StoryEvent | StoredStory, options: ToStoredStoryOptions = {}): StoredStory {\n const { timestamp, level, title, storyId, parentStoryId, origin, notes, durationMs, droppedEmissions, error } = event;\n const strictness = options.redactValues ?? \"balanced\";\n const scrub = <T>(value: T): T =>\n strictness === \"off\"\n ? clone(value)\n : (redactJson(clone(value) as unknown as JsonValue, { redactKeys: DEFAULT_KEY_SET, strictness }) as unknown as T);\n const record: StoredStory = {\n timestamp,\n level,\n title: scrub(title),\n storyId: storyId ?? generateStoryId(),\n notes: scrub(notes) as StoryNote[],\n };\n if (parentStoryId !== undefined) record.parentStoryId = parentStoryId;\n if (origin !== undefined) record.origin = scrub(origin);\n if (durationMs !== undefined) record.durationMs = durationMs;\n if (droppedEmissions !== undefined) record.droppedEmissions = droppedEmissions;\n if (error !== undefined) record.error = scrub(error);\n return record;\n}\n\n/** Records are JSON-safe by construction, so a JSON round trip is an exact deep copy */\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction generateStoryId(): string {\n const random = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto?.randomUUID;\n if (random) return random.call(globalThis.crypto);\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Everything a full-text search over a story should see: the title, each\n * note's text, scalar context values, and error messages. Adapters store this\n * as the canonical `search_text` column; the in-memory matcher searches it.\n */\nexport function storySearchText(story: StoredStory): string {\n const parts: string[] = [story.title];\n for (const note of story.notes) {\n parts.push(note.note);\n for (const value of [note.who, note.what, note.where]) {\n if (typeof value === \"string\") parts.push(value);\n }\n if (note.error?.message) parts.push(note.error.message);\n }\n if (story.error?.message) parts.push(story.error.message);\n return parts.join(\"\\n\");\n}\n\n/** The origin as three text columns: scalars as-is, objects as JSON */\nexport function flattenOrigin(origin?: StoryOrigin): { who?: string; what?: string; where?: string } {\n const flat: { who?: string; what?: string; where?: string } = {};\n if (!origin) return flat;\n for (const key of [\"who\", \"what\", \"where\"] as const) {\n const value = origin[key];\n if (value === undefined || value === null) continue;\n flat[key] = typeof value === \"object\" ? JSON.stringify(value) : String(value);\n }\n return flat;\n}\n\n/**\n * The canonical schema. An adapter that stores these columns can answer every\n * StoryQuery; `record` carries the whole story so `get` returns it unchanged.\n * Deliberately not a normalized notes table — nothing yet demands one.\n */\nexport type CanonicalRow = {\n story_id: string;\n parent_story_id: string | null;\n timestamp: string;\n level: StoryLevel;\n title: string;\n origin_who: string | null;\n origin_what: string | null;\n origin_where: string | null;\n duration_ms: number | null;\n error_message: string | null;\n /** The notes, as JSON */\n notes: string;\n /** Derived: what `about` searches */\n search_text: string;\n /** The whole story, as JSON */\n record: string;\n};\n\nexport function canonicalRow(event: StoryEvent | StoredStory): CanonicalRow {\n const story = toStoredStory(event);\n const origin = flattenOrigin(story.origin);\n return {\n story_id: story.storyId,\n parent_story_id: story.parentStoryId ?? null,\n timestamp: story.timestamp,\n level: story.level,\n title: story.title,\n origin_who: origin.who ?? null,\n origin_what: origin.what ?? null,\n origin_where: origin.where ?? null,\n duration_ms: story.durationMs ?? null,\n error_message: story.error?.message ?? null,\n notes: JSON.stringify(story.notes),\n search_text: storySearchText(story),\n record: JSON.stringify(story),\n };\n}\n\n/** Does one story satisfy the criteria? The reference matcher every adapter is measured against. */\nexport function matchesQuery(story: StoredStory, query: StoryQuery = {}): boolean {\n if (query.since && Date.parse(story.timestamp) < query.since.getTime()) return false;\n if (query.until && Date.parse(story.timestamp) >= query.until.getTime()) return false;\n if (query.level !== undefined) {\n const levels = Array.isArray(query.level) ? query.level : [query.level];\n if (!levels.includes(story.level)) return false;\n }\n if (query.minimumLevel && !meetsLevel(story.level, query.minimumLevel)) return false;\n if (query.parentStoryId !== undefined && story.parentStoryId !== query.parentStoryId) return false;\n if (query.slowerThanMs !== undefined && !((story.durationMs ?? -1) > query.slowerThanMs)) return false;\n if (query.failed !== undefined) {\n const failed = story.level === \"Error\" || story.error !== undefined;\n if (failed !== query.failed) return false;\n }\n if (query.about) {\n if (!storySearchText(story).toLowerCase().includes(query.about.toLowerCase())) return false;\n }\n if (query.from) {\n const origin = Object.values(flattenOrigin(story.origin)).join(\"\\n\").toLowerCase();\n if (!origin.includes(query.from.toLowerCase())) return false;\n }\n return true;\n}\n\n/** Filter, order and page a set of stories in memory — what a store without an index does */\nexport function applyQuery(stories: Iterable<StoredStory>, query: StoryQuery = {}): StoredStory[] {\n // Two stories in the same millisecond keep the order they were kept in,\n // so a page of results is stable across calls and across adapters\n const matched: { story: StoredStory; position: number }[] = [];\n let position = 0;\n for (const story of stories) {\n if (matchesQuery(story, query)) matched.push({ story, position });\n position++;\n }\n const direction = query.order === \"oldest\" ? 1 : -1;\n matched.sort((a, b) => {\n const byTime = Date.parse(a.story.timestamp) - Date.parse(b.story.timestamp);\n return (byTime !== 0 ? byTime : a.position - b.position) * direction;\n });\n const offset = Math.max(0, query.offset ?? 0);\n const end = query.limit !== undefined ? offset + Math.max(0, query.limit) : undefined;\n return matched.slice(offset, end).map((entry) => entry.story);\n}\n","import type { StoryEvent } from \"../storyteller\";\nimport type { StoredStory, StoryQuery, StoryStore } from \"./storyStore\";\nimport { applyQuery, toStoredStory } from \"./storyStore\";\n\nexport type MemoryStoreOptions = {\n /**\n * How many stories to keep before the oldest are forgotten. A process that\n * runs for a month must not grow without bound. Default 10 000.\n */\n capacity?: number;\n};\n\nexport type MemoryStore = StoryStore & {\n /** How many stories are kept right now */\n readonly size: number;\n /** Forget everything */\n clear(): void;\n};\n\n/**\n * The reference StoryStore: a Map, no dependencies, browser-safe.\n *\n * Every other adapter is measured against this one — same query, same\n * results. It is also the right store for tests, for a CLI run that reads its\n * own stories back before exiting, and for a browser session.\n */\nexport function memoryStore(options: MemoryStoreOptions = {}): MemoryStore {\n const capacity = Math.max(1, options.capacity ?? 10_000);\n const stories = new Map<string, StoredStory>();\n\n return {\n get size() {\n return stories.size;\n },\n clear() {\n stories.clear();\n },\n async append(event: StoryEvent | StoredStory) {\n const record = toStoredStory(event);\n // Re-insert so a replaced story counts as the newest for eviction\n stories.delete(record.storyId);\n stories.set(record.storyId, record);\n while (stories.size > capacity) {\n const oldest = stories.keys().next().value;\n if (oldest === undefined) break;\n stories.delete(oldest);\n }\n },\n async get(storyId: string) {\n return stories.get(storyId);\n },\n async query(criteria: StoryQuery = {}) {\n return applyQuery(stories.values(), criteria);\n },\n async children(parentStoryId: string) {\n return applyQuery(stories.values(), { parentStoryId, order: \"oldest\" });\n },\n async prune(before: Date) {\n const boundary = before.getTime();\n let removed = 0;\n for (const [id, story] of stories) {\n if (Date.parse(story.timestamp) < boundary) {\n stories.delete(id);\n removed++;\n }\n }\n return removed;\n },\n };\n}\n","import type { AudienceMember, StoryEvent, StoryLevel } from \"../storyteller\";\nimport { meetsLevel } from \"../environment\";\nimport type { StoryStore } from \"./storyStore\";\n\nexport type StoreAudienceOptions = {\n /** Register under a different name, e.g. to feed two stores */\n name?: string;\n /** Minimum level to keep. Default: everything. */\n level?: StoryLevel;\n /** A further filter, on top of the level */\n accepts?: (event: StoryEvent) => boolean;\n};\n\n/**\n * The bridge from delivery to persistence: every story the audience hears is\n * appended to the store, unchanged.\n *\n * Keeps everything by default. Storage that only keeps failures cannot answer\n * \"what happened\", only \"what broke\" — and a store has `prune()` for the rest.\n *\n * A store that rejects is reported through `onAudienceError`, like any other\n * audience failure; nothing propagates into the code that told the story.\n *\n * @example\n * ```ts\n * const stories = memoryStore();\n * story.audience.add(storeAudience(stories));\n * // later\n * await stories.query({ failed: true, since: new Date(Date.now() - 3_600_000) });\n * ```\n */\nexport function storeAudience(store: StoryStore, options: StoreAudienceOptions = {}): AudienceMember {\n return {\n name: options.name ?? \"store\",\n hears: [\"story\"],\n accepts: (event) =>\n (options.level === undefined || meetsLevel(event.level, options.level)) &&\n (options.accepts === undefined || options.accepts(event)),\n hear: async (event) => {\n await store.append(event);\n },\n };\n}\n","/**\n * The query vocabulary: ask a store a question in the library's own words.\n *\n * await stories(store).about(\"checkout\").from(\"payment-service\").failing().since(\"24h\");\n *\n * The same words have to read well aloud and be easy for a model to write.\n * Every clause returns a new builder; a builder compiles to a StoryQuery, so\n * an adapter receives structured criteria and never a string to parse.\n */\nimport type { LevelInput } from \"../environment\";\nimport { toStoryLevel } from \"../environment\";\nimport type { StoredStory, StoryQuery, StoryStore } from \"./storyStore\";\n\n/** `\"30s\"`, `\"5m\"`, `\"24h\"`, `\"7d\"`, `\"2w\"`, or milliseconds */\nexport type DurationInput = string | number;\n\nconst UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n};\n\n/** A duration as milliseconds. Throws on something that is not a duration, so a typo cannot become \"since forever\". */\nexport function parseDuration(input: DurationInput): number {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input) || input < 0) throw new RangeError(`Not a duration: ${input}`);\n return input;\n }\n const match = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)\\s*$/i.exec(input);\n if (!match) throw new RangeError(`Not a duration: \"${input}\" (expected e.g. \"30s\", \"5m\", \"24h\", \"7d\", \"2w\")`);\n return Number(match[1]) * UNITS[match[2]!.toLowerCase()]!;\n}\n\nexport type StoriesOptions = {\n /** The clock `since(\"24h\")` counts back from. Injectable for tests. */\n now?: () => Date;\n};\n\n/**\n * A question in progress. Immutable: every clause returns a new one.\n * Awaiting it runs `.all()`.\n *\n * Being thenable has one consequence: returning a builder from an `async`\n * function resolves it into its results. Return the store, or the\n * `toQuery()` object, when a caller should get the question rather than\n * the answer.\n */\nexport class StoryQueryBuilder implements PromiseLike<StoredStory[]> {\n constructor(\n private readonly store: StoryStore,\n private readonly query: StoryQuery,\n private readonly now: () => Date\n ) {}\n\n private with(patch: StoryQuery): StoryQueryBuilder {\n return new StoryQueryBuilder(this.store, { ...this.query, ...patch }, this.now);\n }\n\n private instant(input: DurationInput | Date): Date {\n return input instanceof Date ? input : new Date(this.now().getTime() - parseDuration(input));\n }\n\n /** Title, note text, scalar context or error message mentions this */\n about(text: string): StoryQueryBuilder {\n return this.with({ about: text });\n }\n\n /** The origin — who, what or where — mentions this */\n from(origin: string): StoryQueryBuilder {\n return this.with({ from: origin });\n }\n\n /** Exactly this level. Accepts the aliases: `\"info\"`, `\"warn\"`, `\"oops\"`. */\n level(level: LevelInput): StoryQueryBuilder {\n return this.with({ level: toStoryLevel(level) });\n }\n\n /** This level or worse */\n atLeast(level: LevelInput): StoryQueryBuilder {\n return this.with({ minimumLevel: toStoryLevel(level) });\n }\n\n /** Carried an error, or closed at Error level */\n failing(): StoryQueryBuilder {\n return this.with({ failed: true });\n }\n\n /** Neither an error nor closed at Error level */\n succeeding(): StoryQueryBuilder {\n return this.with({ failed: false });\n }\n\n /** Took longer than this: `\"5s\"`, `\"2m\"`, or milliseconds */\n slowerThan(duration: DurationInput): StoryQueryBuilder {\n return this.with({ slowerThanMs: parseDuration(duration) });\n }\n\n /** Began within this long ago (`\"24h\"`), or at or after this date */\n since(when: DurationInput | Date): StoryQueryBuilder {\n return this.with({ since: this.instant(when) });\n }\n\n /** Began before this long ago, or before this date */\n until(when: DurationInput | Date): StoryQueryBuilder {\n return this.with({ until: this.instant(when) });\n }\n\n /** The chapters of this story */\n under(parentStoryId: string): StoryQueryBuilder {\n return this.with({ parentStoryId });\n }\n\n /** Most recent first — the default */\n newest(): StoryQueryBuilder {\n return this.with({ order: \"newest\" });\n }\n\n /** Earliest first */\n oldest(): StoryQueryBuilder {\n return this.with({ order: \"oldest\" });\n }\n\n /** At most this many */\n limit(count: number): StoryQueryBuilder {\n return this.with({ limit: count });\n }\n\n /** Skip this many first */\n skip(count: number): StoryQueryBuilder {\n return this.with({ offset: count });\n }\n\n /** The structured criteria this question compiles to — what an adapter receives */\n toQuery(): StoryQuery {\n return { ...this.query };\n }\n\n /** Every matching story */\n all(): Promise<StoredStory[]> {\n return this.store.query(this.toQuery());\n }\n\n /** The first match, or nothing */\n async first(): Promise<StoredStory | undefined> {\n const [story] = await this.store.query({ ...this.query, limit: 1 });\n return story;\n }\n\n /** How many match, ignoring paging */\n async count(): Promise<number> {\n const { limit: _limit, offset: _offset, ...unpaged } = this.query;\n return (await this.store.query(unpaged)).length;\n }\n\n /** An awaited builder resolves to `.all()` */\n then<Result = StoredStory[], Failure = never>(\n onFulfilled?: ((stories: StoredStory[]) => Result | PromiseLike<Result>) | null,\n onRejected?: ((reason: unknown) => Failure | PromiseLike<Failure>) | null\n ): Promise<Result | Failure> {\n return this.all().then(onFulfilled, onRejected);\n }\n}\n\n/**\n * Start a question against a store.\n *\n * @example\n * ```ts\n * const recent = stories(store);\n * await recent.failing().since(\"1h\");\n * await recent.about(\"checkout\").from(\"payment-service\").level(\"oops\").since(\"24h\");\n * await recent.slowerThan(\"5s\").since(\"7d\").oldest().limit(10);\n * await recent.under(parentStoryId).count();\n * ```\n */\nexport function stories(store: StoryStore, options: StoriesOptions = {}): StoryQueryBuilder {\n return new StoryQueryBuilder(store, {}, options.now ?? (() => new Date()));\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","stories","stories","stories"]}
|