@usebetterdev/audit-core 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/diff.ts","../src/enrichment-registry.ts","../src/normalize.ts","../src/duration.ts","../src/query-builder.ts","../src/export.ts","../src/audit-api.ts","../../../shared/console-utils/src/index.ts","../src/console-endpoints.ts","../src/export-response.ts","../src/retention.ts","../src/better-audit.ts","../src/audit-log-schema.ts","../src/context-extractor.ts"],"sourcesContent":["import type { AuditContext } from \"./types.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nconst storage = new AsyncLocalStorage<AuditContext>();\n\n/**\n * Returns the current audit context, or undefined when not inside a request\n * scope (middleware or withContext).\n */\nexport function getAuditContext(): AuditContext | undefined {\n return storage.getStore();\n}\n\n/**\n * Run fn inside a scope where getAuditContext() returns the given context.\n */\nexport function runWithAuditContext<T>(\n context: AuditContext,\n fn: () => T | Promise<T>,\n): Promise<T> {\n return Promise.resolve(storage.run(context, () => fn()));\n}\n\n/**\n * Merge additional context into the current scope and run fn.\n * If no scope exists, a new one is created from the partial context.\n * Properties in override take precedence over the existing context.\n */\nexport function mergeAuditContext<T>(\n override: Partial<AuditContext>,\n fn: () => T | Promise<T>,\n): Promise<T> {\n const current = storage.getStore() ?? {};\n const merged: AuditContext = { ...current, ...override };\n return Promise.resolve(storage.run(merged, () => fn()));\n}\n","/**\n * Compute which fields changed between two row snapshots.\n * Uses JSON.stringify for deep equality — order-sensitive for objects/arrays.\n */\nexport function computeDiff(\n before: Record<string, unknown> | undefined,\n after: Record<string, unknown> | undefined,\n): { changedFields: string[] } {\n const b = before ?? {};\n const a = after ?? {};\n const allKeys = new Set([...Object.keys(b), ...Object.keys(a)]);\n const changedFields: string[] = [];\n\n for (const key of allKeys) {\n const inBefore = Object.prototype.hasOwnProperty.call(b, key);\n const inAfter = Object.prototype.hasOwnProperty.call(a, key);\n if (!inBefore || !inAfter) {\n changedFields.push(key);\n continue;\n }\n try {\n if (JSON.stringify(b[key]) !== JSON.stringify(a[key])) {\n changedFields.push(key);\n }\n } catch {\n // Non-serializable value (e.g. circular reference) — treat as changed\n changedFields.push(key);\n }\n }\n\n return { changedFields };\n}\n","import type {\n AuditLog,\n AuditOperation,\n AuditSeverity,\n EnrichmentConfig,\n EnrichmentDescriptionContext,\n} from \"./types.js\";\n\n/**\n * Specificity tiers for enrichment resolution (least → most specific):\n *\n * 1. \"*:*\" — global catch-all\n * 2. \"*:OP\" — any table, specific operation\n * 3. \"table:*\" — specific table, any operation\n * 4. \"table:OP\" — exact match\n *\n * A table-scoped rule is more specific than an operation-scoped rule because\n * tables are the primary organizational unit for audit policies. A rule like\n * \"users:*\" (all ops on users) should override \"*:DELETE\" (all deletes) for\n * scalar fields, since the policy author has explicitly targeted that table.\n */\n\nfunction makeKey(table: string, operation: string): string {\n return `${table}:${operation.toUpperCase()}`;\n}\n\nfunction validateRedactInclude(config: EnrichmentConfig, key: string): void {\n if (\n config.redact !== undefined &&\n config.redact.length > 0 &&\n config.include !== undefined &&\n config.include.length > 0\n ) {\n throw new Error(\n `Enrichment for \"${key}\" cannot specify both \"redact\" and \"include\". ` +\n \"Use one or the other.\",\n );\n }\n}\n\n/** Resolved enrichment config after merging all matching tiers. */\nexport interface ResolvedEnrichment {\n label?: string;\n description?: (context: EnrichmentDescriptionContext) => string;\n severity?: AuditSeverity;\n compliance?: string[];\n notify?: boolean;\n redact?: string[];\n include?: string[];\n}\n\nexport class EnrichmentRegistry {\n private readonly entries = new Map<string, EnrichmentConfig[]>();\n\n register(table: string, operation: AuditOperation | \"*\", config: EnrichmentConfig): void {\n const key = makeKey(table, operation);\n validateRedactInclude(config, key);\n\n const existing = this.entries.get(key);\n if (existing !== undefined) {\n existing.push(config);\n } else {\n this.entries.set(key, [config]);\n }\n }\n\n getEntries(): Array<{ table: string; operation: string; configs: EnrichmentConfig[] }> {\n const result: Array<{ table: string; operation: string; configs: EnrichmentConfig[] }> = [];\n for (const [key, configs] of this.entries) {\n const separatorIndex = key.indexOf(\":\");\n const table = key.slice(0, separatorIndex);\n const operation = key.slice(separatorIndex + 1);\n result.push({ table, operation, configs });\n }\n return result;\n }\n\n resolve(\n table: string,\n operation: string,\n ): ResolvedEnrichment | undefined {\n const normalizedOp = operation.toUpperCase();\n\n // Collect configs from all matching tiers in specificity order\n const keysToCheck = [\n makeKey(\"*\", \"*\"),\n makeKey(\"*\", normalizedOp),\n makeKey(table, \"*\"),\n makeKey(table, normalizedOp),\n ];\n\n const allConfigs: EnrichmentConfig[] = [];\n for (const key of keysToCheck) {\n const configs = this.entries.get(key);\n if (configs !== undefined) {\n for (const config of configs) {\n allConfigs.push(config);\n }\n }\n }\n\n if (allConfigs.length === 0) {\n return undefined;\n }\n\n return mergeEnrichmentConfigs(allConfigs, `${table}:${normalizedOp}`);\n }\n}\n\n/**\n * Merge multiple enrichment configs in order (earlier = less specific).\n * - Scalars: last-write-wins (more specific overrides)\n * - Arrays: concatenate & deduplicate\n * - `description` function: last-write-wins\n *\n * Throws if the merged result contains both `redact` and `include`.\n */\nexport function mergeEnrichmentConfigs(\n configs: readonly EnrichmentConfig[],\n contextKey: string,\n): ResolvedEnrichment {\n const result: ResolvedEnrichment = {};\n\n for (const config of configs) {\n if (config.label !== undefined) {\n result.label = config.label;\n }\n if (config.description !== undefined) {\n result.description = config.description;\n }\n if (config.severity !== undefined) {\n result.severity = config.severity;\n }\n if (config.notify !== undefined) {\n result.notify = config.notify;\n }\n\n if (config.compliance !== undefined) {\n if (result.compliance !== undefined) {\n const merged = [...result.compliance, ...config.compliance];\n result.compliance = [...new Set(merged)];\n } else {\n result.compliance = [...config.compliance];\n }\n }\n\n if (config.redact !== undefined) {\n if (result.redact !== undefined) {\n const merged = [...result.redact, ...config.redact];\n result.redact = [...new Set(merged)];\n } else {\n result.redact = [...config.redact];\n }\n }\n\n if (config.include !== undefined) {\n if (result.include !== undefined) {\n const merged = [...result.include, ...config.include];\n result.include = [...new Set(merged)];\n } else {\n result.include = [...config.include];\n }\n }\n }\n\n // Post-merge conflict check: redact + include from different registrations\n if (\n result.redact !== undefined &&\n result.redact.length > 0 &&\n result.include !== undefined &&\n result.include.length > 0\n ) {\n throw new Error(\n `Enrichment merge for \"${contextKey}\" produced both \"redact\" and \"include\". ` +\n \"These are mutually exclusive — fix the conflicting registrations.\",\n );\n }\n\n return result;\n}\n\n/**\n * Remove or filter fields from beforeData, afterData, and diff.changedFields.\n * Operates on the log in place.\n */\nexport function applyFieldRedaction(\n log: AuditLog,\n resolved: ResolvedEnrichment,\n): void {\n const { redact, include } = resolved;\n const removedFields = new Set<string>();\n\n if (redact !== undefined && redact.length > 0) {\n const redactSet = new Set(redact);\n\n if (log.beforeData !== undefined) {\n for (const key of Object.keys(log.beforeData)) {\n if (redactSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.beforeData = filterOutKeys(log.beforeData, redactSet);\n }\n if (log.afterData !== undefined) {\n for (const key of Object.keys(log.afterData)) {\n if (redactSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.afterData = filterOutKeys(log.afterData, redactSet);\n }\n if (log.diff !== undefined) {\n log.diff = {\n changedFields: log.diff.changedFields.filter(\n (field) => !redactSet.has(field),\n ),\n };\n }\n } else if (include !== undefined && include.length > 0) {\n const includeSet = new Set(include);\n\n if (log.beforeData !== undefined) {\n for (const key of Object.keys(log.beforeData)) {\n if (!includeSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.beforeData = keepOnlyKeys(log.beforeData, includeSet);\n }\n if (log.afterData !== undefined) {\n for (const key of Object.keys(log.afterData)) {\n if (!includeSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.afterData = keepOnlyKeys(log.afterData, includeSet);\n }\n if (log.diff !== undefined) {\n log.diff = {\n changedFields: log.diff.changedFields.filter((field) =>\n includeSet.has(field),\n ),\n };\n }\n }\n\n if (removedFields.size > 0) {\n log.redactedFields = [...removedFields].sort();\n }\n}\n\n/**\n * Apply resolved enrichment to an AuditLog.\n * Enrichment values only fill gaps — explicit per-call and context values take precedence.\n *\n * Flow:\n * 1. Redact fields (before description sees data)\n * 2. Call description function with post-redaction context\n * 3. Apply scalar/array enrichment fields\n */\nexport function applyEnrichment(\n log: AuditLog,\n resolved: ResolvedEnrichment,\n): void {\n // Step 1: Redact fields first\n applyFieldRedaction(log, resolved);\n\n // Step 2: Call description with post-redaction data\n if (resolved.description !== undefined && log.description === undefined) {\n try {\n const descriptionContext: EnrichmentDescriptionContext = {\n before: log.beforeData !== undefined ? structuredClone(log.beforeData) : undefined,\n after: log.afterData !== undefined ? structuredClone(log.afterData) : undefined,\n diff: log.diff !== undefined ? structuredClone(log.diff) : undefined,\n actorId: log.actorId,\n metadata: log.metadata !== undefined ? structuredClone(log.metadata) : undefined,\n };\n\n log.description = resolved.description(descriptionContext);\n } catch {\n // Description function or data cloning threw — leave description unset, log still gets written\n }\n }\n\n // Step 3: Apply scalar/array enrichment (only if not already set)\n if (resolved.label !== undefined && log.label === undefined) {\n log.label = resolved.label;\n }\n if (resolved.severity !== undefined && log.severity === undefined) {\n log.severity = resolved.severity;\n }\n if (resolved.notify !== undefined && log.notify === undefined) {\n log.notify = resolved.notify;\n }\n if (resolved.compliance !== undefined) {\n if (log.compliance !== undefined) {\n const merged = [...log.compliance, ...resolved.compliance];\n log.compliance = [...new Set(merged)];\n } else {\n log.compliance = [...resolved.compliance];\n }\n }\n}\n\nfunction filterOutKeys(\n data: Record<string, unknown>,\n keysToRemove: Set<string>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (!keysToRemove.has(key)) {\n result[key] = value;\n }\n }\n return result;\n}\n\nfunction keepOnlyKeys(\n data: Record<string, unknown>,\n keysToKeep: Set<string>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (keysToKeep.has(key)) {\n result[key] = value;\n }\n }\n return result;\n}\n","import type { AuditOperation } from \"./types.js\";\n\n/**\n * Normalize before/after data based on the operation type.\n *\n * - **INSERT** → `before` is dropped (only `after` is meaningful)\n * - **DELETE** → `after` is dropped (only `before` is meaningful)\n * - **UPDATE** → both are kept as-is\n */\nexport function normalizeInput(\n operation: AuditOperation,\n before: Record<string, unknown> | undefined,\n after: Record<string, unknown> | undefined,\n): {\n before: Record<string, unknown> | undefined;\n after: Record<string, unknown> | undefined;\n} {\n switch (operation) {\n case \"INSERT\": {\n return { before: undefined, after };\n }\n case \"DELETE\": {\n return { before, after: undefined };\n }\n case \"UPDATE\": {\n return { before, after };\n }\n }\n}\n","const DURATION_PATTERN = /^(\\d+)([hdwmy])$/;\n\ntype DurationUnit = \"h\" | \"d\" | \"w\" | \"m\" | \"y\";\n\nfunction isValidUnit(raw: string): raw is DurationUnit {\n return raw === \"h\" || raw === \"d\" || raw === \"w\" || raw === \"m\" || raw === \"y\";\n}\n\n/**\n * Parses a duration string (e.g. \"30d\", \"2w\", \"3m\", \"1y\") and returns\n * a Date that is `duration` before `referenceDate`.\n *\n * Supported units: h (hours), d (days), w (weeks), m (months), y (years).\n * Zero values are rejected.\n */\nexport function parseDuration(input: string, referenceDate?: Date): Date {\n const match = DURATION_PATTERN.exec(input);\n if (match === null) {\n throw new Error(\n `Invalid duration \"${input}\". Expected format: <number><unit> where unit is h, d, w, m, or y (e.g. \"4h\", \"30d\", \"2w\", \"3m\", \"1y\").`,\n );\n }\n\n const value = Number(match[1]);\n const rawUnit = match[2] as string;\n\n if (value === 0) {\n throw new Error(\n `Invalid duration \"${input}\". Value must be greater than zero.`,\n );\n }\n\n if (!isValidUnit(rawUnit)) {\n throw new Error(\n `Invalid duration unit \"${rawUnit}\". Expected h, d, w, m, or y.`,\n );\n }\n\n const result = referenceDate !== undefined ? new Date(referenceDate) : new Date();\n\n if (rawUnit === \"h\") {\n result.setHours(result.getHours() - value);\n } else if (rawUnit === \"d\") {\n result.setDate(result.getDate() - value);\n } else if (rawUnit === \"w\") {\n result.setDate(result.getDate() - value * 7);\n } else if (rawUnit === \"m\") {\n result.setMonth(result.getMonth() - value);\n } else {\n result.setFullYear(result.getFullYear() - value);\n }\n\n return result;\n}\n","import type { AuditOperation, AuditSeverity } from \"./types.js\";\nimport type {\n AuditQueryFilters,\n AuditQueryResult,\n AuditQuerySpec,\n TimeFilter,\n} from \"./query-types.js\";\nimport { parseDuration } from \"./duration.js\";\n\n/** Callback that executes a query spec against the adapter. */\nexport type QueryExecutor = (spec: AuditQuerySpec) => Promise<AuditQueryResult>;\n\nconst DEFAULT_MAX_QUERY_LIMIT = 1000;\nconst MAX_SEARCH_TEXT_LENGTH = 500;\n\n/**\n * Fluent, immutable query builder for audit logs.\n *\n * Each method returns a **new** instance — safe to fork and share.\n * Call `.list()` to execute, or `.toSpec()` to inspect without executing.\n */\nexport class AuditQueryBuilder {\n readonly #executor: QueryExecutor;\n readonly #filters: AuditQueryFilters;\n readonly #limit: number | undefined;\n readonly #cursor: string | undefined;\n readonly #maxLimit: number;\n readonly #sortOrder: \"asc\" | \"desc\" | undefined;\n\n constructor(\n executor: QueryExecutor,\n filters?: AuditQueryFilters,\n limit?: number,\n cursor?: string,\n maxLimit?: number,\n sortOrder?: \"asc\" | \"desc\",\n ) {\n this.#executor = executor;\n this.#filters = filters ?? {};\n this.#limit = limit;\n this.#cursor = cursor;\n this.#maxLimit = maxLimit ?? DEFAULT_MAX_QUERY_LIMIT;\n this.#sortOrder = sortOrder;\n }\n\n /** Filter by table name and optional record ID. Last-write-wins. */\n resource(tableName: string, recordId?: string): AuditQueryBuilder {\n return new AuditQueryBuilder(\n this.#executor,\n {\n ...this.#filters,\n resource: recordId !== undefined\n ? { tableName, recordId }\n : { tableName },\n },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Filter by actor IDs (OR semantics, deduplicates). */\n actor(...ids: string[]): AuditQueryBuilder {\n const existing = this.#filters.actorIds ?? [];\n const merged = [...new Set([...existing, ...ids])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, actorIds: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Add severity levels to the filter (OR semantics, deduplicates). */\n severity(...levels: AuditSeverity[]): AuditQueryBuilder {\n const existing = this.#filters.severities ?? [];\n const merged = [...new Set([...existing, ...levels])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, severities: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Add compliance tags to the filter (AND semantics, deduplicates). */\n compliance(...tags: string[]): AuditQueryBuilder {\n const existing = this.#filters.compliance ?? [];\n const merged = [...new Set([...existing, ...tags])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, compliance: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /**\n * Filter entries created after a point in time.\n *\n * Accepts a `Date` or a duration string (e.g. \"4h\", \"30d\", \"2w\", \"3m\", \"1y\").\n * Duration strings are eagerly validated but resolved at query time for a fresh \"now\".\n * Last-write-wins.\n */\n since(value: Date | string): AuditQueryBuilder {\n const filter = this.#parseTimeFilter(value);\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, since: filter },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /**\n * Filter entries created before a point in time.\n *\n * Accepts a `Date` or a duration string (e.g. \"4h\", \"30d\", \"2w\", \"3m\", \"1y\").\n * Duration strings are eagerly validated but resolved at query time for a fresh \"now\".\n * Last-write-wins.\n */\n until(value: Date | string): AuditQueryBuilder {\n const filter = this.#parseTimeFilter(value);\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, until: filter },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Full-text search filter. Last-write-wins. Max 500 characters. */\n search(text: string): AuditQueryBuilder {\n if (text.length > MAX_SEARCH_TEXT_LENGTH) {\n throw new Error(\n `searchText must be at most ${MAX_SEARCH_TEXT_LENGTH} characters, got ${text.length}`,\n );\n }\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, searchText: text },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Add operation types to the filter (OR semantics, deduplicates). */\n operation(...ops: AuditOperation[]): AuditQueryBuilder {\n const existing = this.#filters.operations ?? [];\n const merged = [...new Set([...existing, ...ops])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, operations: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Set maximum number of entries to return. Must be > 0 and <= maxLimit. */\n limit(n: number): AuditQueryBuilder {\n if (n <= 0) {\n throw new Error(`limit must be greater than 0, got ${n}`);\n }\n if (n > this.#maxLimit) {\n throw new Error(\n `limit ${n} exceeds maximum allowed limit of ${this.#maxLimit}`,\n );\n }\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters },\n n,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Set the pagination cursor for fetching the next page. */\n after(cursor: string): AuditQueryBuilder {\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters },\n this.#limit,\n cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Set the sort direction for results. */\n order(direction: \"asc\" | \"desc\"): AuditQueryBuilder {\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n direction,\n );\n }\n\n /** Returns the query specification without executing. Useful for tests and debugging. */\n toSpec(): AuditQuerySpec {\n const filters: AuditQueryFilters = { ...this.#filters };\n // Deep-clone arrays so the returned spec is fully detached from builder internals\n if (filters.severities !== undefined) {\n filters.severities = [...filters.severities];\n }\n if (filters.operations !== undefined) {\n filters.operations = [...filters.operations];\n }\n if (filters.compliance !== undefined) {\n filters.compliance = [...filters.compliance];\n }\n if (filters.actorIds !== undefined) {\n filters.actorIds = [...filters.actorIds];\n }\n const spec: AuditQuerySpec = { filters };\n const effectiveLimit = this.#limit ?? this.#maxLimit;\n spec.limit = effectiveLimit;\n if (this.#cursor !== undefined) {\n spec.cursor = this.#cursor;\n }\n if (this.#sortOrder !== undefined) {\n spec.sortOrder = this.#sortOrder;\n }\n return spec;\n }\n\n /** Execute the query against the adapter. */\n list(): Promise<AuditQueryResult> {\n return this.#executor(this.toSpec());\n }\n\n #parseTimeFilter(value: Date | string): TimeFilter {\n if (value instanceof Date) {\n return { date: value };\n }\n // Eagerly validate — throws if format is invalid\n parseDuration(value);\n return { duration: value };\n }\n}\n","import type { AuditLog, ExportOptions, ExportResult } from \"./types.js\";\nimport type { AuditQuerySpec } from \"./query-types.js\";\nimport type { QueryExecutor } from \"./query-builder.js\";\n\nconst DEFAULT_BATCH_SIZE = 500;\nconst DEFAULT_CSV_DELIMITER = \",\";\n\n/**\n * Ordered list of AuditLog fields for CSV columns.\n * Matches the full schema — JSONB columns are JSON.stringify'd.\n */\nconst CSV_COLUMNS: ReadonlyArray<keyof AuditLog> = [\n \"id\",\n \"timestamp\",\n \"tableName\",\n \"operation\",\n \"recordId\",\n \"actorId\",\n \"beforeData\",\n \"afterData\",\n \"diff\",\n \"label\",\n \"description\",\n \"severity\",\n \"compliance\",\n \"notify\",\n \"reason\",\n \"metadata\",\n \"redactedFields\",\n];\n\n/** Fields whose values are objects/arrays and need JSON.stringify in CSV. */\nconst JSON_FIELDS = new Set<keyof AuditLog>([\n \"beforeData\",\n \"afterData\",\n \"diff\",\n \"compliance\",\n \"metadata\",\n \"redactedFields\",\n]);\n\n/**\n * Escape a CSV field per RFC 4180.\n * Fields containing the delimiter, double quotes, or line breaks are quoted.\n * Embedded double quotes are doubled.\n */\nfunction escapeCsvField(value: string, delimiter: string): string {\n if (\n value.includes('\"') ||\n value.includes(delimiter) ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\")\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nfunction formatCsvValue(\n log: AuditLog,\n field: keyof AuditLog,\n delimiter: string,\n): string {\n const value = log[field];\n if (value === undefined || value === null) {\n return \"\";\n }\n if (JSON_FIELDS.has(field)) {\n return escapeCsvField(JSON.stringify(value), delimiter);\n }\n if (value instanceof Date) {\n return escapeCsvField(value.toISOString(), delimiter);\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\";\n }\n return escapeCsvField(String(value), delimiter);\n}\n\n/** Abstraction over WritableStream<string> and in-memory string buffer. */\ninterface ExportSink {\n write(chunk: string): Promise<void>;\n finish(): Promise<string | undefined>;\n abort(error: unknown): Promise<void>;\n}\n\nfunction createStringSink(): ExportSink {\n const chunks: string[] = [];\n return {\n async write(chunk: string): Promise<void> {\n chunks.push(chunk);\n },\n async finish(): Promise<string> {\n return chunks.join(\"\");\n },\n async abort(): Promise<void> {\n // Nothing to clean up for in-memory buffer\n },\n };\n}\n\nfunction createStreamSink(stream: WritableStream<string>): ExportSink {\n const writer = stream.getWriter();\n return {\n async write(chunk: string): Promise<void> {\n await writer.write(chunk);\n },\n async finish(): Promise<undefined> {\n await writer.close();\n return undefined;\n },\n async abort(error: unknown): Promise<void> {\n await writer.abort(error);\n },\n };\n}\n\n/**\n * Core export engine. Fetches rows in cursor-paginated batches and writes\n * them to the sink as CSV or JSON. Memory stays flat regardless of total rows.\n */\nexport async function runExport(\n executor: QueryExecutor,\n options: ExportOptions,\n): Promise<ExportResult> {\n const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n if (batchSize <= 0) {\n throw new Error(`batchSize must be greater than 0, got ${batchSize}`);\n }\n\n const delimiter = options.csvDelimiter ?? DEFAULT_CSV_DELIMITER;\n if (delimiter.length !== 1) {\n throw new Error(\"csvDelimiter must be exactly one character\");\n }\n\n const jsonStyle = options.jsonStyle ?? \"ndjson\";\n\n const sink =\n options.output === \"string\"\n ? createStringSink()\n : createStreamSink(options.output);\n const isStringSink = options.output === \"string\";\n\n // Build initial query spec from the optional query builder\n const baseSpec: AuditQuerySpec = options.query !== undefined\n ? options.query.toSpec()\n : { filters: {} };\n\n // Respect the user's query limit as a total cap; use batchSize for per-page fetching\n const totalLimit = baseSpec.limit;\n const spec: AuditQuerySpec = { ...baseSpec, limit: batchSize };\n\n let rowCount = 0;\n\n try {\n if (options.format === \"csv\") {\n // Write header row\n const header = CSV_COLUMNS.map((col) => escapeCsvField(col, delimiter)).join(delimiter);\n await sink.write(header + \"\\n\");\n\n // Paginate and write rows\n let cursor: string | undefined;\n for (;;) {\n const currentSpec: AuditQuerySpec = cursor !== undefined\n ? { ...spec, cursor }\n : spec;\n const result = await executor(currentSpec);\n\n if (isStringSink) {\n // Batch writes for string sink — one write() per page\n const lines: string[] = [];\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n const row = CSV_COLUMNS.map((col) => formatCsvValue(entry, col, delimiter)).join(delimiter);\n lines.push(row + \"\\n\");\n rowCount++;\n }\n if (lines.length > 0) {\n await sink.write(lines.join(\"\"));\n }\n } else {\n // Stream sink — per-row writes for backpressure\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n const row = CSV_COLUMNS.map((col) => formatCsvValue(entry, col, delimiter)).join(delimiter);\n await sink.write(row + \"\\n\");\n rowCount++;\n }\n }\n\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n if (result.nextCursor === undefined) {\n break;\n }\n cursor = result.nextCursor;\n }\n } else {\n // JSON format\n if (jsonStyle === \"array\") {\n let cursor: string | undefined;\n const entries: AuditLog[] = [];\n\n for (;;) {\n const currentSpec: AuditQuerySpec = cursor !== undefined\n ? { ...spec, cursor }\n : spec;\n const result = await executor(currentSpec);\n\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n entries.push(entry);\n rowCount++;\n }\n\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n if (result.nextCursor === undefined) {\n break;\n }\n cursor = result.nextCursor;\n }\n\n await sink.write(JSON.stringify(entries, null, 2) + \"\\n\");\n } else {\n // ndjson\n let cursor: string | undefined;\n for (;;) {\n const currentSpec: AuditQuerySpec = cursor !== undefined\n ? { ...spec, cursor }\n : spec;\n const result = await executor(currentSpec);\n\n if (isStringSink) {\n // Batch writes for string sink — one write() per page\n const lines: string[] = [];\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n lines.push(JSON.stringify(entry) + \"\\n\");\n rowCount++;\n }\n if (lines.length > 0) {\n await sink.write(lines.join(\"\"));\n }\n } else {\n // Stream sink — per-row writes for backpressure\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n await sink.write(JSON.stringify(entry) + \"\\n\");\n rowCount++;\n }\n }\n\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n if (result.nextCursor === undefined) {\n break;\n }\n cursor = result.nextCursor;\n }\n }\n }\n } catch (error) {\n await sink.abort(error);\n throw error;\n }\n\n const data = await sink.finish();\n\n if (data !== undefined) {\n return { rowCount, data };\n }\n return { rowCount };\n}\n","import type { AuditDatabaseAdapter, AuditLog, AuditStats, AuditSeverity, AuditOperation } from \"./types.js\";\nimport type { AuditQueryResult, AuditQuerySpec, TimeFilter } from \"./query-types.js\";\nimport type { EnrichmentRegistry } from \"./enrichment-registry.js\";\nimport { AuditQueryBuilder } from \"./query-builder.js\";\nimport { runExport } from \"./export.js\";\n\nconst VALID_SEVERITIES = new Set<string>([\"low\", \"medium\", \"high\", \"critical\"]);\nconst VALID_OPERATIONS = new Set<string>([\"INSERT\", \"UPDATE\", \"DELETE\"]);\n\n/** Flat console-friendly query filters. Single-value fields translated to multi-value internal filters. */\nexport interface ConsoleQueryFilters {\n tableName?: string;\n operation?: string;\n actorId?: string;\n severity?: string;\n compliance?: string;\n since?: Date;\n until?: Date;\n search?: string;\n limit?: number;\n cursor?: string;\n}\n\n/** Serializable summary of an enrichment config (function fields stripped). */\nexport interface EnrichmentSummary {\n table: string;\n operation: string;\n label?: string;\n severity?: AuditSeverity;\n compliance?: string[];\n notify?: boolean;\n redact?: string[];\n include?: string[];\n}\n\n/** Query result extended with a convenience `hasNextPage` flag. */\nexport interface ConsoleQueryResult extends AuditQueryResult {\n hasNextPage: boolean;\n}\n\n/**\n * High-level API consumed by console endpoints.\n *\n * Wraps the low-level `AuditDatabaseAdapter` methods with sensible defaults\n * (e.g. clamping `limit` to the configured maximum).\n */\nexport interface AuditApi {\n /** Query audit log entries with optional flat filters and cursor-based pagination. */\n queryLogs(filters?: ConsoleQueryFilters): Promise<ConsoleQueryResult>;\n /** Retrieve a single audit log entry by its ID. Returns `null` when not found. */\n getLog(id: string): Promise<AuditLog | null>;\n /** Get aggregated audit statistics. Requires adapter.getStats. */\n getStats(options?: { since?: Date }): Promise<AuditStats>;\n /** Get serializable summaries of all registered enrichments. */\n getEnrichments(): EnrichmentSummary[];\n /** Export logs as CSV or JSON string. */\n exportLogs(filters?: ConsoleQueryFilters, format?: \"csv\" | \"json\"): Promise<string>;\n /** Purge audit logs before a given date. Requires adapter.purgeLogs. */\n purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }>;\n}\n\nfunction toTimeFilter(date: Date): TimeFilter {\n return { date };\n}\n\nfunction buildQuerySpec(filters: ConsoleQueryFilters, effectiveLimit: number): AuditQuerySpec {\n const limit = Math.min(filters.limit ?? effectiveLimit, effectiveLimit);\n\n const spec: AuditQuerySpec = {\n filters: {},\n limit,\n };\n\n if (filters.tableName !== undefined) {\n spec.filters.resource = { tableName: filters.tableName };\n }\n if (filters.actorId !== undefined) {\n spec.filters.actorIds = [filters.actorId];\n }\n if (filters.severity !== undefined) {\n if (!VALID_SEVERITIES.has(filters.severity)) {\n throw new Error(\n `Invalid severity \"${filters.severity}\". Must be one of: low, medium, high, critical`,\n );\n }\n spec.filters.severities = [filters.severity as AuditSeverity];\n }\n if (filters.compliance !== undefined) {\n spec.filters.compliance = [filters.compliance];\n }\n if (filters.operation !== undefined) {\n const normalized = filters.operation.toUpperCase();\n if (!VALID_OPERATIONS.has(normalized)) {\n throw new Error(\n `Invalid operation \"${filters.operation}\". Must be one of: INSERT, UPDATE, DELETE`,\n );\n }\n spec.filters.operations = [normalized as AuditOperation];\n }\n if (filters.since !== undefined) {\n spec.filters.since = toTimeFilter(filters.since);\n }\n if (filters.until !== undefined) {\n spec.filters.until = toTimeFilter(filters.until);\n }\n if (filters.search !== undefined) {\n spec.filters.searchText = filters.search;\n }\n if (filters.cursor !== undefined) {\n spec.cursor = filters.cursor;\n }\n\n return spec;\n}\n\nexport function createAuditApi(\n adapter: AuditDatabaseAdapter,\n registry: EnrichmentRegistry,\n maxQueryLimit?: number,\n): AuditApi {\n const effectiveLimit = maxQueryLimit ?? 1000;\n\n function requireQueryLogs(): NonNullable<AuditDatabaseAdapter[\"queryLogs\"]> {\n if (adapter.queryLogs === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n return adapter.queryLogs;\n }\n\n function requireGetLogById(): NonNullable<AuditDatabaseAdapter[\"getLogById\"]> {\n if (adapter.getLogById === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements getLogById(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n return adapter.getLogById;\n }\n\n function requireGetStats(): NonNullable<AuditDatabaseAdapter[\"getStats\"]> {\n if (adapter.getStats === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements getStats(). \" +\n \"Check that your ORM adapter supports statistics.\",\n );\n }\n return adapter.getStats;\n }\n\n function requirePurgeLogs(): NonNullable<AuditDatabaseAdapter[\"purgeLogs\"]> {\n if (adapter.purgeLogs === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements purgeLogs(). \" +\n \"Check that your ORM adapter supports log purging.\",\n );\n }\n return adapter.purgeLogs;\n }\n\n async function queryLogs(filters?: ConsoleQueryFilters): Promise<ConsoleQueryResult> {\n const queryFn = requireQueryLogs();\n const spec = buildQuerySpec(filters ?? {}, effectiveLimit);\n const result = await queryFn(spec);\n return {\n entries: result.entries,\n ...(result.nextCursor !== undefined && { nextCursor: result.nextCursor }),\n hasNextPage: result.nextCursor !== undefined,\n };\n }\n\n async function getLog(id: string): Promise<AuditLog | null> {\n const getLogFn = requireGetLogById();\n return getLogFn(id);\n }\n\n async function getStats(options?: { since?: Date }): Promise<AuditStats> {\n const getStatsFn = requireGetStats();\n return getStatsFn(options);\n }\n\n function getEnrichments(): EnrichmentSummary[] {\n const entries = registry.getEntries();\n const summaries: EnrichmentSummary[] = [];\n\n for (const entry of entries) {\n for (const config of entry.configs) {\n const summary: EnrichmentSummary = {\n table: entry.table,\n operation: entry.operation,\n };\n if (config.label !== undefined) {\n summary.label = config.label;\n }\n if (config.severity !== undefined) {\n summary.severity = config.severity;\n }\n if (config.compliance !== undefined) {\n summary.compliance = config.compliance;\n }\n if (config.notify !== undefined) {\n summary.notify = config.notify;\n }\n if (config.redact !== undefined) {\n summary.redact = config.redact;\n }\n if (config.include !== undefined) {\n summary.include = config.include;\n }\n summaries.push(summary);\n }\n }\n\n return summaries;\n }\n\n async function exportLogs(filters?: ConsoleQueryFilters, format?: \"csv\" | \"json\"): Promise<string> {\n const queryFn = requireQueryLogs();\n const exportFormat = format ?? \"json\";\n\n // Build a query spec from the console filters (without cursor — export fetches all pages)\n const spec = buildQuerySpec(filters ?? {}, effectiveLimit);\n\n // Construct a query builder pre-loaded with the console filters\n const queryBuilder = new AuditQueryBuilder(\n (s) => queryFn(s),\n spec.filters,\n spec.limit,\n undefined,\n effectiveLimit,\n spec.sortOrder,\n );\n\n const exportOptions = {\n format: exportFormat,\n query: queryBuilder,\n output: \"string\" as const,\n ...(exportFormat === \"json\" && { jsonStyle: \"array\" as const }),\n };\n\n const result = await runExport(\n (s) => queryFn(s),\n exportOptions,\n );\n return result.data ?? \"\";\n }\n\n async function purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n const purgeFn = requirePurgeLogs();\n return purgeFn(options);\n }\n\n return { queryLogs, getLog, getStats, getEnrichments, exportLogs, purgeLogs };\n}\n","/**\n * Shared parse and validation utilities for console endpoint handlers.\n *\n * These are small, pure, zero-dep helpers used by product endpoint handlers\n * (audit, tenant) to validate and parse incoming request data.\n */\n\n/** Type guard that narrows `unknown` to a plain object (not array, not null). */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Parse a string to an integer within [min, max].\n * Returns `undefined` when `value` is `undefined` (param absent),\n * not a finite number, or below `min`.\n */\nexport function parseBoundedInt(\n value: string | undefined,\n min: number,\n max: number,\n): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < min) {\n return undefined;\n }\n return Math.min(Math.floor(parsed), max);\n}\n\n/**\n * Parse an ISO-8601 date string.\n * Returns `undefined` when `value` is `undefined` or not a valid date.\n */\nexport function parseIsoDate(value: string | undefined): Date | undefined {\n if (value === undefined) {\n return undefined;\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return undefined;\n }\n return date;\n}\n","import type {\n ConsoleProductEndpoint,\n ConsoleProductRequest,\n ConsoleErrorResponse,\n AuditLogWire,\n AuditLogsResponse,\n AuditStatsResponse,\n AuditEnrichmentsResponse,\n AuditPurgeResponse,\n} from \"@usebetterdev/contract\";\nimport {\n isPlainObject,\n parseBoundedInt,\n parseIsoDate,\n} from \"@usebetterdev/console-utils\";\nimport type { AuditApi, ConsoleQueryFilters } from \"./audit-api.js\";\nimport type { AuditLog } from \"./types.js\";\n\nconst MAX_PARAM_LENGTH = 1000;\n\nfunction exceedsMaxLength(value: string | undefined): boolean {\n return value !== undefined && value.length > MAX_PARAM_LENGTH;\n}\n\nfunction hasLongQueryParam(query: Record<string, string>): boolean {\n return (\n exceedsMaxLength(query.tableName) ||\n exceedsMaxLength(query.actorId) ||\n exceedsMaxLength(query.cursor) ||\n exceedsMaxLength(query.operation) ||\n exceedsMaxLength(query.severity) ||\n exceedsMaxLength(query.compliance) ||\n exceedsMaxLength(query.search)\n );\n}\n\nfunction parseConsoleQueryFilters(\n query: Record<string, string>,\n): { filters: ConsoleQueryFilters } | { error: string } {\n const filters: ConsoleQueryFilters = {};\n\n if (query.limit !== undefined) {\n const limit = parseBoundedInt(query.limit, 1, 1000);\n if (limit === undefined) {\n return { error: \"Invalid 'limit': must be a positive integer (max 1000)\" };\n }\n filters.limit = limit;\n }\n if (query.tableName !== undefined) {\n filters.tableName = query.tableName;\n }\n if (query.operation !== undefined) {\n filters.operation = query.operation;\n }\n if (query.actorId !== undefined) {\n filters.actorId = query.actorId;\n }\n if (query.severity !== undefined) {\n filters.severity = query.severity;\n }\n if (query.compliance !== undefined) {\n filters.compliance = query.compliance;\n }\n if (query.search !== undefined) {\n filters.search = query.search;\n }\n if (query.cursor !== undefined) {\n filters.cursor = query.cursor;\n }\n\n if (query.since !== undefined) {\n const since = parseIsoDate(query.since);\n if (since === undefined) {\n return { error: \"Invalid 'since': must be an ISO-8601 date\" };\n }\n filters.since = since;\n }\n if (query.until !== undefined) {\n const until = parseIsoDate(query.until);\n if (until === undefined) {\n return { error: \"Invalid 'until': must be an ISO-8601 date\" };\n }\n filters.until = until;\n }\n\n return { filters };\n}\n\nfunction serializeLog(log: AuditLog): AuditLogWire {\n return {\n ...log,\n timestamp: log.timestamp.toISOString(),\n };\n}\n\nexport function createAuditConsoleEndpoints(\n api: AuditApi,\n): ConsoleProductEndpoint[] {\n return [\n {\n method: \"GET\",\n path: \"/logs\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n if (hasLongQueryParam(request.query)) {\n return { status: 400, body: { error: \"Query parameter exceeds maximum length\" } satisfies ConsoleErrorResponse };\n }\n const parsed = parseConsoleQueryFilters(request.query);\n if (\"error\" in parsed) {\n return { status: 400, body: { error: parsed.error } satisfies ConsoleErrorResponse };\n }\n const result = await api.queryLogs(parsed.filters);\n const body: AuditLogsResponse = {\n entries: result.entries.map(serializeLog),\n hasNextPage: result.hasNextPage,\n };\n if (result.nextCursor !== undefined) {\n body.nextCursor = result.nextCursor;\n }\n return { status: 200, body };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/logs/:id\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n const id = request.params.id?.trim();\n if (!id) {\n return { status: 400, body: { error: \"Missing log id\" } satisfies ConsoleErrorResponse };\n }\n const log = await api.getLog(id);\n if (!log) {\n return { status: 404, body: { error: \"Audit log not found\" } satisfies ConsoleErrorResponse };\n }\n return { status: 200, body: serializeLog(log) satisfies AuditLogWire };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/stats\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n const options: { since?: Date } = {};\n if (request.query.since !== undefined) {\n const since = parseIsoDate(request.query.since);\n if (since === undefined) {\n return { status: 400, body: { error: \"Invalid 'since': must be an ISO-8601 date\" } satisfies ConsoleErrorResponse };\n }\n options.since = since;\n }\n const stats = await api.getStats(options);\n return { status: 200, body: stats satisfies AuditStatsResponse };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/enrichments\",\n requiredPermission: \"read\",\n async handler() {\n try {\n const enrichments = api.getEnrichments();\n return { status: 200, body: enrichments satisfies AuditEnrichmentsResponse };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/export\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n const format = request.query.format;\n if (format !== undefined && format !== \"csv\" && format !== \"json\") {\n return { status: 400, body: { error: \"Invalid format. Must be 'csv' or 'json'\" } satisfies ConsoleErrorResponse };\n }\n if (hasLongQueryParam(request.query)) {\n return { status: 400, body: { error: \"Query parameter exceeds maximum length\" } satisfies ConsoleErrorResponse };\n }\n const parsed = parseConsoleQueryFilters(request.query);\n if (\"error\" in parsed) {\n return { status: 400, body: { error: parsed.error } satisfies ConsoleErrorResponse };\n }\n const exportFormat: \"csv\" | \"json\" | undefined = format;\n const data = await api.exportLogs(parsed.filters, exportFormat);\n return { status: 200, body: data };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"DELETE\",\n path: \"/logs\",\n requiredPermission: \"admin\",\n async handler(request: ConsoleProductRequest) {\n try {\n if (!isPlainObject(request.body)) {\n return { status: 400, body: { error: \"Request body is required\" } satisfies ConsoleErrorResponse };\n }\n const beforeValue = request.body.before;\n if (typeof beforeValue !== \"string\") {\n return { status: 400, body: { error: \"Missing required field: before\" } satisfies ConsoleErrorResponse };\n }\n const before = new Date(beforeValue);\n if (Number.isNaN(before.getTime())) {\n return { status: 400, body: { error: \"Invalid date for 'before' field\" } satisfies ConsoleErrorResponse };\n }\n const options: { before: Date; tableName?: string } = { before };\n if (typeof request.body.tableName === \"string\" && request.body.tableName.length > 0) {\n if (exceedsMaxLength(request.body.tableName)) {\n return { status: 400, body: { error: \"tableName exceeds maximum length\" } satisfies ConsoleErrorResponse };\n }\n options.tableName = request.body.tableName;\n }\n const result = await api.purgeLogs(options);\n return { status: 200, body: result satisfies AuditPurgeResponse };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n ];\n}\n","import type { AuditQueryBuilder, QueryExecutor } from \"./query-builder.js\";\nimport { runExport } from \"./export.js\";\n\n/** Options for `createExportResponse()`. */\nexport interface ExportResponseOptions {\n /** Output format. Default: `\"csv\"`. */\n format?: \"csv\" | \"json\";\n /** JSON output style. Default: `\"ndjson\"`. */\n jsonStyle?: \"ndjson\" | \"array\";\n /** Rows per database round-trip. */\n batchSize?: number;\n /** CSV delimiter character. */\n csvDelimiter?: string;\n /** Optional query builder to filter exported entries. */\n query?: AuditQueryBuilder;\n /** Custom filename stem (without extension). Default: `\"audit-export-YYYY-MM-DD\"`. */\n filename?: string;\n}\n\nfunction contentTypeForFormat(\n format: \"csv\" | \"json\",\n jsonStyle: \"ndjson\" | \"array\",\n): string {\n if (format === \"csv\") {\n return \"text/csv; charset=utf-8\";\n }\n if (jsonStyle === \"ndjson\") {\n return \"application/x-ndjson; charset=utf-8\";\n }\n return \"application/json; charset=utf-8\";\n}\n\nfunction fileExtensionForFormat(\n format: \"csv\" | \"json\",\n jsonStyle: \"ndjson\" | \"array\",\n): string {\n if (format === \"csv\") {\n return \".csv\";\n }\n if (jsonStyle === \"ndjson\") {\n return \".ndjson\";\n }\n return \".json\";\n}\n\nfunction formatDate(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Sanitise a filename for use in Content-Disposition headers.\n * Strips characters that could break the header or enable response splitting.\n */\nfunction sanitiseFilename(name: string): string {\n return name.replace(/[\"\\\\\\r\\n\\x00-\\x1f]/g, \"\");\n}\n\n/**\n * Create a streaming `Response` from the audit export engine.\n *\n * Uses `TransformStream` + `TextEncoderStream` to bridge the string-based\n * `runExport()` output to a binary `ReadableStream` suitable for `new Response()`.\n * The export runs asynchronously — the Response is returned immediately so the\n * first byte can arrive before all rows are read.\n */\nexport function createExportResponse(\n executor: QueryExecutor,\n options: ExportResponseOptions = {},\n): Response {\n const format = options.format ?? \"csv\";\n const jsonStyle = options.jsonStyle ?? \"ndjson\";\n\n const stem =\n options.filename ?? `audit-export-${formatDate(new Date())}`;\n const extension = fileExtensionForFormat(format, jsonStyle);\n const fullFilename = sanitiseFilename(`${stem}${extension}`);\n\n const contentType = contentTypeForFormat(format, jsonStyle);\n\n // Bridge: runExport writes strings into the writable side of the transform.\n // The readable side feeds into TextEncoderStream, producing Uint8Array for the Response.\n const transform = new TransformStream<string, string>();\n const encoder = new TextEncoderStream();\n const readable = transform.readable.pipeThrough(encoder);\n\n // Start the export asynchronously — do NOT await.\n // runExport closes/aborts the writable side on completion/error.\n // The .catch() prevents unhandled rejection — the error is already\n // propagated to the reader via sink.abort() inside runExport.\n runExport(executor, {\n format,\n jsonStyle,\n output: transform.writable,\n ...(options.batchSize !== undefined && { batchSize: options.batchSize }),\n ...(options.csvDelimiter !== undefined && {\n csvDelimiter: options.csvDelimiter,\n }),\n ...(options.query !== undefined && { query: options.query }),\n }).catch(() => {\n // Error already propagated through the transform stream\n });\n\n return new Response(readable, {\n status: 200,\n headers: {\n \"Content-Type\": contentType,\n \"Content-Disposition\": `attachment; filename=\"${fullFilename}\"`,\n \"Cache-Control\": \"no-cache\",\n },\n });\n}\n","import type { RetentionPolicy } from \"./types.js\";\n\nexport function validateRetentionPolicy(policy: RetentionPolicy): void {\n if (\n !Number.isInteger(policy.days) ||\n !Number.isFinite(policy.days) ||\n policy.days <= 0\n ) {\n throw new Error(\n `retention: 'days' must be a positive integer, got ${String(policy.days)}`,\n );\n }\n\n if (policy.tables !== undefined) {\n if (\n !Array.isArray(policy.tables) ||\n policy.tables.length === 0 ||\n policy.tables.some((t) => typeof t !== \"string\" || t === \"\")\n ) {\n throw new Error(\n \"retention: 'tables' must be a non-empty array of non-empty strings\",\n );\n }\n }\n}\n","import type {\n AfterLogHook,\n AuditContext,\n AuditLog,\n AuditOperation,\n BeforeLogHook,\n BetterAuditConfig,\n BetterAuditInstance,\n CaptureLogInput,\n EnrichmentConfig,\n ExportOptions,\n ExportResult,\n ExportResponseOptions,\n RetentionPolicy,\n} from \"./types.js\";\nimport { getAuditContext, runWithAuditContext } from \"./context.js\";\nimport { computeDiff } from \"./diff.js\";\nimport { EnrichmentRegistry, applyEnrichment } from \"./enrichment-registry.js\";\nimport { normalizeInput } from \"./normalize.js\";\nimport { AuditQueryBuilder } from \"./query-builder.js\";\nimport { createAuditApi } from \"./audit-api.js\";\nimport { createAuditConsoleEndpoints } from \"./console-endpoints.js\";\nimport { runExport } from \"./export.js\";\nimport { createExportResponse } from \"./export-response.js\";\nimport { validateRetentionPolicy } from \"./retention.js\";\n\nfunction withContext<T>(\n context: AuditContext,\n fn: () => Promise<T>,\n): Promise<T> {\n return runWithAuditContext(context, fn);\n}\n\nexport function betterAudit(config: BetterAuditConfig): BetterAuditInstance {\n if (config.retention !== undefined) {\n validateRetentionPolicy(config.retention);\n }\n\n if (config.asyncWrite === true && config.onError === undefined) {\n console.warn(\n \"audit: asyncWrite is enabled without onError — write failures will only log to console.error. \" +\n \"Set onError to route failures to your error tracking system.\",\n );\n }\n\n const { database } = config;\n const auditTables = new Set(config.auditTables);\n\n if (config.retention?.tables !== undefined) {\n const unknown = config.retention.tables.filter((t) => !auditTables.has(t));\n if (unknown.length > 0) {\n throw new Error(\n `retention: 'tables' contains table(s) not in auditTables: ${unknown.join(\", \")}. ` +\n `Registered tables: ${[...auditTables].join(\", \")}`,\n );\n }\n }\n const registry = new EnrichmentRegistry();\n\n const resolvedRetention: RetentionPolicy | undefined =\n config.retention !== undefined\n ? {\n ...config.retention,\n ...(config.retention.tables !== undefined && { tables: [...config.retention.tables] }),\n }\n : undefined;\n\n function retentionPolicy(): RetentionPolicy | undefined {\n if (resolvedRetention === undefined) {\n return undefined;\n }\n return {\n ...resolvedRetention,\n ...(resolvedRetention.tables !== undefined && { tables: [...resolvedRetention.tables] }),\n };\n }\n const beforeLogHooks: BeforeLogHook[] = config.beforeLog !== undefined ? [...config.beforeLog] : [];\n const afterLogHooks: AfterLogHook[] = config.afterLog !== undefined ? [...config.afterLog] : [];\n\n function enrich(\n table: string,\n operation: AuditOperation | \"*\",\n enrichmentConfig: EnrichmentConfig,\n ): void {\n if (table !== \"*\" && !auditTables.has(table)) {\n throw new Error(\n `Cannot register enrichment for table \"${table}\": it is not in auditTables. ` +\n `Registered tables: ${[...auditTables].join(\", \")}`,\n );\n }\n\n registry.register(table, operation, enrichmentConfig);\n }\n\n function onBeforeLog(hook: BeforeLogHook): () => void {\n beforeLogHooks.push(hook);\n return () => {\n const index = beforeLogHooks.indexOf(hook);\n if (index !== -1) {\n beforeLogHooks.splice(index, 1);\n }\n };\n }\n\n function onAfterLog(hook: AfterLogHook): () => void {\n afterLogHooks.push(hook);\n return () => {\n const index = afterLogHooks.indexOf(hook);\n if (index !== -1) {\n afterLogHooks.splice(index, 1);\n }\n };\n }\n\n async function writeAndRunAfterHooks(log: AuditLog): Promise<void> {\n await database.writeLog(log);\n for (const hook of afterLogHooks) {\n await hook(log);\n }\n }\n\n async function captureLog(input: CaptureLogInput): Promise<void> {\n // 1. Early return if table not in auditTables\n if (!auditTables.has(input.tableName)) {\n return;\n }\n\n if (input.recordId === \"\") {\n throw new Error(\"captureLog requires a non-empty recordId\");\n }\n\n // 2. Normalize input based on operation type\n const normalized = normalizeInput(input.operation, input.before, input.after);\n\n const context = getAuditContext();\n\n // 3. Assemble log (merge input + AuditContext)\n // Conditional spreads satisfy exactOptionalPropertyTypes — properties\n // are either absent or carry a narrowed non-undefined value.\n const actorId = input.actorId ?? context?.actorId;\n const label = input.label ?? context?.label;\n const reason = input.reason ?? context?.reason;\n const compliance = input.compliance ?? context?.compliance;\n const metadata = input.metadata ?? context?.metadata;\n\n const log: AuditLog = {\n id: crypto.randomUUID(),\n timestamp: new Date(),\n tableName: input.tableName,\n operation: input.operation,\n recordId: input.recordId,\n ...(actorId !== undefined && { actorId }),\n ...(label !== undefined && { label }),\n ...(reason !== undefined && { reason }),\n ...(compliance !== undefined && { compliance }),\n ...(metadata !== undefined && { metadata }),\n ...(input.description !== undefined && { description: input.description }),\n ...(input.severity !== undefined && { severity: input.severity }),\n ...(input.notify !== undefined && { notify: input.notify }),\n ...(normalized.before !== undefined && { beforeData: { ...normalized.before } }),\n ...(normalized.after !== undefined && { afterData: { ...normalized.after } }),\n };\n\n // 4. Compute diff only for UPDATE operations\n if (input.operation === \"UPDATE\") {\n const diff = computeDiff(normalized.before, normalized.after);\n if (diff.changedFields.length > 0) {\n log.diff = diff;\n }\n }\n\n // 5. Resolve enrichment → Redact → Describe → Apply scalars\n const resolved = registry.resolve(input.tableName, input.operation);\n if (resolved !== undefined) {\n applyEnrichment(log, resolved);\n }\n\n // 6. Run beforeLog hooks (sequential, may mutate log, errors abort)\n for (const hook of beforeLogHooks) {\n await hook(log);\n }\n\n // 7. Write log (sync or async) + afterLog hooks\n const isAsync = input.asyncWrite ?? config.asyncWrite ?? false;\n if (isAsync) {\n void writeAndRunAfterHooks(log).catch((error: unknown) => {\n if (config.onError !== undefined) {\n config.onError(error);\n } else {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`audit: async write failed for ${log.tableName}/${log.id} — ${message}`);\n }\n });\n } else {\n await writeAndRunAfterHooks(log);\n }\n }\n\n function query(): AuditQueryBuilder {\n if (database.queryLogs === undefined) {\n throw new Error(\n \"audit.query() requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n const queryLogs = database.queryLogs;\n return new AuditQueryBuilder(\n (spec) => queryLogs(spec),\n undefined,\n undefined,\n undefined,\n config.maxQueryLimit,\n );\n }\n\n async function exportLogs(options: ExportOptions): Promise<ExportResult> {\n if (database.queryLogs === undefined) {\n throw new Error(\n \"audit.export() requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n const queryLogs = database.queryLogs;\n return runExport((spec) => queryLogs(spec), options);\n }\n\n function exportResponse(options?: ExportResponseOptions): Response {\n if (database.queryLogs === undefined) {\n throw new Error(\n \"audit.exportResponse() requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n const queryLogs = database.queryLogs;\n return createExportResponse((spec) => queryLogs(spec), options);\n }\n\n if (config.console) {\n const api = createAuditApi(database, registry, config.maxQueryLimit);\n const endpoints = createAuditConsoleEndpoints(api);\n config.console.registerProduct({\n id: \"audit\",\n name: \"Better Audit\",\n endpoints,\n });\n }\n\n return { captureLog, query, export: exportLogs, exportResponse, withContext, enrich, onBeforeLog, onAfterLog, retentionPolicy };\n}\n","/**\n * Logical schema for the `audit_logs` table.\n *\n * ORM adapters translate this into their own migration format.\n * Core never runs SQL — this is a declarative data structure only.\n */\n\nexport type ColumnType =\n | \"uuid\"\n | \"timestamptz\"\n | \"text\"\n | \"jsonb\"\n | \"boolean\";\n\nexport interface ColumnDefinition {\n type: ColumnType;\n nullable: boolean;\n primaryKey?: boolean;\n defaultExpression?: string;\n indexed?: boolean;\n description: string;\n}\n\n/**\n * Column names in the `audit_logs` table use snake_case.\n * These map to camelCase properties in the `AuditLog` TypeScript type:\n *\n * | Column (snake_case) | AuditLog property (camelCase) |\n * |---------------------|-------------------------------|\n * | table_name | tableName |\n * | record_id | recordId |\n * | actor_id | actorId |\n * | before_data | beforeData |\n * | after_data | afterData |\n * | redacted_fields | redactedFields |\n */\nexport type AuditLogColumnName =\n | \"id\"\n | \"timestamp\"\n | \"table_name\"\n | \"operation\"\n | \"record_id\"\n | \"actor_id\"\n | \"before_data\"\n | \"after_data\"\n | \"diff\"\n | \"label\"\n | \"description\"\n | \"severity\"\n | \"compliance\"\n | \"notify\"\n | \"reason\"\n | \"metadata\"\n | \"redacted_fields\";\n\nexport interface AuditLogSchema {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n}\n\nexport const AUDIT_LOG_SCHEMA: AuditLogSchema = {\n tableName: \"audit_logs\",\n columns: {\n id: {\n type: \"uuid\",\n nullable: false,\n primaryKey: true,\n defaultExpression: \"gen_random_uuid()\",\n description: \"Unique identifier for the audit log entry\",\n },\n timestamp: {\n type: \"timestamptz\",\n nullable: false,\n defaultExpression: \"now()\",\n indexed: true,\n description: \"When the audited event occurred\",\n },\n table_name: {\n type: \"text\",\n nullable: false,\n indexed: true,\n description: \"Name of the table that was modified\",\n },\n operation: {\n type: \"text\",\n nullable: false,\n indexed: true,\n description: \"Type of operation: INSERT, UPDATE, or DELETE\",\n },\n record_id: {\n type: \"text\",\n nullable: false,\n indexed: true,\n description: \"Primary key of the affected record\",\n },\n actor_id: {\n type: \"text\",\n nullable: true,\n indexed: true,\n description: \"Identifier of the user or system that performed the action\",\n },\n before_data: {\n type: \"jsonb\",\n nullable: true,\n description: \"Row snapshot before the mutation (DELETE and UPDATE only)\",\n },\n after_data: {\n type: \"jsonb\",\n nullable: true,\n description: \"Row snapshot after the mutation (INSERT and UPDATE only)\",\n },\n diff: {\n type: \"jsonb\",\n nullable: true,\n description: \"Changed field names for UPDATE operations\",\n },\n label: {\n type: \"text\",\n nullable: true,\n description: \"Human-readable label for the event\",\n },\n description: {\n type: \"text\",\n nullable: true,\n description: \"Detailed description of what happened\",\n },\n severity: {\n type: \"text\",\n nullable: true,\n description: \"Severity level: low, medium, high, or critical\",\n },\n compliance: {\n type: \"jsonb\",\n nullable: true,\n description: \"Compliance framework tags (e.g. soc2, gdpr, hipaa)\",\n },\n notify: {\n type: \"boolean\",\n nullable: true,\n description: \"Whether this event should trigger notifications\",\n },\n reason: {\n type: \"text\",\n nullable: true,\n description: \"Justification or reason for the action\",\n },\n metadata: {\n type: \"jsonb\",\n nullable: true,\n description: \"Arbitrary key-value metadata attached to the event\",\n },\n redacted_fields: {\n type: \"jsonb\",\n nullable: true,\n description: \"Field names that were removed by redaction rules\",\n },\n },\n};\n","import type { AuditContext } from \"./types.js\";\nimport { runWithAuditContext } from \"./context.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Function that extracts a string value from a Web Request.\n * Return undefined if the value cannot be extracted.\n * May be sync or async.\n */\nexport type ValueExtractor = (\n request: Request,\n) => string | undefined | Promise<string | undefined>;\n\n/**\n * Describes how to extract audit identity from an incoming HTTP request.\n * All fields are optional — if omitted, the corresponding context field\n * will be undefined.\n */\nexport interface ContextExtractor {\n /** Extracts the actor identifier (user / service account). */\n actor?: ValueExtractor;\n}\n\n/**\n * Options for `handleMiddleware`.\n */\nexport interface MiddlewareHandlerOptions {\n /** Called when an extractor throws. Defaults to silent fail-open. */\n onError?: (error: unknown) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Built-in extractors\n// ---------------------------------------------------------------------------\n\n/**\n * Decode a JWT payload without verification.\n * Uses only Web-standard APIs (atob) — safe on edge runtimes.\n */\nfunction decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split(\".\");\n if (parts.length < 2) {\n return null;\n }\n const payload = parts[1];\n if (!payload) {\n return null;\n }\n let base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n const decoded = atob(base64);\n const parsed: unknown = JSON.parse(decoded);\n if (typeof parsed !== \"object\" || parsed === null) {\n return null;\n }\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Extracts a JWT claim from the `Authorization: Bearer <token>` header.\n *\n * Decodes the token **without** signature verification — signing is the\n * auth layer's responsibility. This is intentional: the audit layer only\n * needs the identity, not proof of authenticity.\n *\n * @param claim - JWT claim name to extract (e.g. `\"sub\"`, `\"tenant_id\"`)\n */\nexport function fromBearerToken(claim: string): ValueExtractor {\n return (request: Request) => {\n const authorization = request.headers.get(\"authorization\");\n if (!authorization) {\n return undefined;\n }\n const parts = authorization.split(\" \");\n if (parts.length !== 2 || parts[0]?.toLowerCase() !== \"bearer\") {\n return undefined;\n }\n const token = parts[1];\n if (!token) {\n return undefined;\n }\n const payload = decodeJwtPayload(token);\n if (!payload) {\n return undefined;\n }\n const value = payload[claim];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n };\n}\n\n/**\n * Extracts a value from a named cookie in the `Cookie` header.\n *\n * The raw cookie value is returned as-is. To resolve it into a user identity,\n * compose with a resolver function (e.g. look up a session in a database).\n *\n * @param cookieName - Name of the cookie to read\n */\nexport function fromCookie(cookieName: string): ValueExtractor {\n return (request: Request) => {\n const cookieHeader = request.headers.get(\"cookie\");\n if (!cookieHeader) {\n return undefined;\n }\n const cookies = cookieHeader.split(\";\");\n for (const cookie of cookies) {\n const separatorIndex = cookie.indexOf(\"=\");\n if (separatorIndex === -1) {\n continue;\n }\n const name = cookie.slice(0, separatorIndex).trim();\n if (name === cookieName) {\n const value = cookie.slice(separatorIndex + 1).trim();\n return value.length > 0 ? value : undefined;\n }\n }\n return undefined;\n };\n}\n\n/**\n * Extracts a value from a custom request header.\n *\n * @param headerName - Header name (case-insensitive per the Web API)\n */\nexport function fromHeader(headerName: string): ValueExtractor {\n return (request: Request) => {\n const value = request.headers.get(headerName);\n if (!value || value.trim().length === 0) {\n return undefined;\n }\n return value.trim();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared middleware handler\n// ---------------------------------------------------------------------------\n\n/**\n * Runs an extractor safely, catching errors and returning undefined on failure.\n */\nasync function safeExtract(\n extractor: ValueExtractor | undefined,\n request: Request,\n onError: ((error: unknown) => void) | undefined,\n): Promise<string | undefined> {\n if (!extractor) {\n return undefined;\n }\n try {\n return await extractor(request);\n } catch (error: unknown) {\n if (onError) {\n onError(error);\n }\n return undefined;\n }\n}\n\n/**\n * Shared middleware handler used by all framework adapters.\n *\n * 1. Extracts actor from the request using the provided extractor\n * 2. Builds an `AuditContext` (undefined if nothing was extracted)\n * 3. Wraps `next()` inside `runWithAuditContext()` if context is available\n * 4. Calls `next()` without context if extraction yields nothing\n *\n * Extraction failures never break the request (fail open).\n */\nexport async function handleMiddleware(\n extractor: ContextExtractor,\n request: Request,\n next: () => Promise<void>,\n options: MiddlewareHandlerOptions = {},\n): Promise<void> {\n const actorId = await safeExtract(extractor.actor, request, options.onError);\n\n if (actorId === undefined) {\n await next();\n return;\n }\n\n const auditContext: AuditContext = { actorId };\n\n await runWithAuditContext(auditContext, () => next());\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/context.ts","../src/diff.ts","../src/enrichment-registry.ts","../src/normalize.ts","../src/duration.ts","../src/query-builder.ts","../src/export.ts","../src/cursor.ts","../src/audit-api.ts","../../../shared/console-utils/src/index.ts","../src/console-endpoints.ts","../src/export-response.ts","../src/retention.ts","../src/better-audit.ts","../src/audit-log-schema.ts","../src/escape.ts","../src/time-filter.ts","../src/filter-builder.ts","../src/stats.ts","../src/validation.ts","../src/context-extractor.ts"],"sourcesContent":["import type { AuditContext } from \"./types.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nconst storage = new AsyncLocalStorage<AuditContext>();\n\n/**\n * Returns the current audit context, or undefined when not inside a request\n * scope (middleware or withContext).\n */\nexport function getAuditContext(): AuditContext | undefined {\n return storage.getStore();\n}\n\n/**\n * Run fn inside a scope where getAuditContext() returns the given context.\n */\nexport function runWithAuditContext<T>(\n context: AuditContext,\n fn: () => T | Promise<T>,\n): Promise<T> {\n return Promise.resolve(storage.run(context, () => fn()));\n}\n\n/**\n * Merge additional context into the current scope and run fn.\n * If no scope exists, a new one is created from the partial context.\n * Properties in override take precedence over the existing context.\n */\nexport function mergeAuditContext<T>(\n override: Partial<AuditContext>,\n fn: () => T | Promise<T>,\n): Promise<T> {\n const current = storage.getStore() ?? {};\n const merged: AuditContext = { ...current, ...override };\n return Promise.resolve(storage.run(merged, () => fn()));\n}\n","/**\n * Compute which fields changed between two row snapshots.\n * Uses JSON.stringify for deep equality — order-sensitive for objects/arrays.\n */\nexport function computeDiff(\n before: Record<string, unknown> | undefined,\n after: Record<string, unknown> | undefined,\n): { changedFields: string[] } {\n const b = before ?? {};\n const a = after ?? {};\n const allKeys = new Set([...Object.keys(b), ...Object.keys(a)]);\n const changedFields: string[] = [];\n\n for (const key of allKeys) {\n const inBefore = Object.prototype.hasOwnProperty.call(b, key);\n const inAfter = Object.prototype.hasOwnProperty.call(a, key);\n if (!inBefore || !inAfter) {\n changedFields.push(key);\n continue;\n }\n try {\n if (JSON.stringify(b[key]) !== JSON.stringify(a[key])) {\n changedFields.push(key);\n }\n } catch {\n // Non-serializable value (e.g. circular reference) — treat as changed\n changedFields.push(key);\n }\n }\n\n return { changedFields };\n}\n","import type {\n AuditLog,\n AuditOperation,\n AuditSeverity,\n EnrichmentConfig,\n EnrichmentDescriptionContext,\n} from \"./types.js\";\n\n/**\n * Specificity tiers for enrichment resolution (least → most specific):\n *\n * 1. \"*:*\" — global catch-all\n * 2. \"*:OP\" — any table, specific operation\n * 3. \"table:*\" — specific table, any operation\n * 4. \"table:OP\" — exact match\n *\n * A table-scoped rule is more specific than an operation-scoped rule because\n * tables are the primary organizational unit for audit policies. A rule like\n * \"users:*\" (all ops on users) should override \"*:DELETE\" (all deletes) for\n * scalar fields, since the policy author has explicitly targeted that table.\n */\n\nfunction makeKey(table: string, operation: string): string {\n return `${table}:${operation.toUpperCase()}`;\n}\n\nfunction validateRedactInclude(config: EnrichmentConfig, key: string): void {\n if (\n config.redact !== undefined &&\n config.redact.length > 0 &&\n config.include !== undefined &&\n config.include.length > 0\n ) {\n throw new Error(\n `Enrichment for \"${key}\" cannot specify both \"redact\" and \"include\". ` +\n \"Use one or the other.\",\n );\n }\n}\n\n/** Resolved enrichment config after merging all matching tiers. */\nexport interface ResolvedEnrichment {\n label?: string;\n description?: (context: EnrichmentDescriptionContext) => string;\n severity?: AuditSeverity;\n compliance?: string[];\n notify?: boolean;\n redact?: string[];\n include?: string[];\n}\n\nexport class EnrichmentRegistry {\n private readonly entries = new Map<string, EnrichmentConfig[]>();\n\n register(table: string, operation: AuditOperation | \"*\", config: EnrichmentConfig): void {\n const key = makeKey(table, operation);\n validateRedactInclude(config, key);\n\n const existing = this.entries.get(key);\n if (existing !== undefined) {\n existing.push(config);\n } else {\n this.entries.set(key, [config]);\n }\n }\n\n getEntries(): Array<{ table: string; operation: string; configs: EnrichmentConfig[] }> {\n const result: Array<{ table: string; operation: string; configs: EnrichmentConfig[] }> = [];\n for (const [key, configs] of this.entries) {\n const separatorIndex = key.indexOf(\":\");\n const table = key.slice(0, separatorIndex);\n const operation = key.slice(separatorIndex + 1);\n result.push({ table, operation, configs });\n }\n return result;\n }\n\n resolve(\n table: string,\n operation: string,\n ): ResolvedEnrichment | undefined {\n const normalizedOp = operation.toUpperCase();\n\n // Collect configs from all matching tiers in specificity order\n const keysToCheck = [\n makeKey(\"*\", \"*\"),\n makeKey(\"*\", normalizedOp),\n makeKey(table, \"*\"),\n makeKey(table, normalizedOp),\n ];\n\n const allConfigs: EnrichmentConfig[] = [];\n for (const key of keysToCheck) {\n const configs = this.entries.get(key);\n if (configs !== undefined) {\n for (const config of configs) {\n allConfigs.push(config);\n }\n }\n }\n\n if (allConfigs.length === 0) {\n return undefined;\n }\n\n return mergeEnrichmentConfigs(allConfigs, `${table}:${normalizedOp}`);\n }\n}\n\n/**\n * Merge multiple enrichment configs in order (earlier = less specific).\n * - Scalars: last-write-wins (more specific overrides)\n * - Arrays: concatenate & deduplicate\n * - `description` function: last-write-wins\n *\n * Throws if the merged result contains both `redact` and `include`.\n */\nexport function mergeEnrichmentConfigs(\n configs: readonly EnrichmentConfig[],\n contextKey: string,\n): ResolvedEnrichment {\n const result: ResolvedEnrichment = {};\n\n for (const config of configs) {\n if (config.label !== undefined) {\n result.label = config.label;\n }\n if (config.description !== undefined) {\n result.description = config.description;\n }\n if (config.severity !== undefined) {\n result.severity = config.severity;\n }\n if (config.notify !== undefined) {\n result.notify = config.notify;\n }\n\n if (config.compliance !== undefined) {\n if (result.compliance !== undefined) {\n const merged = [...result.compliance, ...config.compliance];\n result.compliance = [...new Set(merged)];\n } else {\n result.compliance = [...config.compliance];\n }\n }\n\n if (config.redact !== undefined) {\n if (result.redact !== undefined) {\n const merged = [...result.redact, ...config.redact];\n result.redact = [...new Set(merged)];\n } else {\n result.redact = [...config.redact];\n }\n }\n\n if (config.include !== undefined) {\n if (result.include !== undefined) {\n const merged = [...result.include, ...config.include];\n result.include = [...new Set(merged)];\n } else {\n result.include = [...config.include];\n }\n }\n }\n\n // Post-merge conflict check: redact + include from different registrations\n if (\n result.redact !== undefined &&\n result.redact.length > 0 &&\n result.include !== undefined &&\n result.include.length > 0\n ) {\n throw new Error(\n `Enrichment merge for \"${contextKey}\" produced both \"redact\" and \"include\". ` +\n \"These are mutually exclusive — fix the conflicting registrations.\",\n );\n }\n\n return result;\n}\n\n/**\n * Remove or filter fields from beforeData, afterData, and diff.changedFields.\n * Operates on the log in place.\n */\nexport function applyFieldRedaction(\n log: AuditLog,\n resolved: ResolvedEnrichment,\n): void {\n const { redact, include } = resolved;\n const removedFields = new Set<string>();\n\n if (redact !== undefined && redact.length > 0) {\n const redactSet = new Set(redact);\n\n if (log.beforeData !== undefined) {\n for (const key of Object.keys(log.beforeData)) {\n if (redactSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.beforeData = filterOutKeys(log.beforeData, redactSet);\n }\n if (log.afterData !== undefined) {\n for (const key of Object.keys(log.afterData)) {\n if (redactSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.afterData = filterOutKeys(log.afterData, redactSet);\n }\n if (log.diff !== undefined) {\n log.diff = {\n changedFields: log.diff.changedFields.filter(\n (field) => !redactSet.has(field),\n ),\n };\n }\n } else if (include !== undefined && include.length > 0) {\n const includeSet = new Set(include);\n\n if (log.beforeData !== undefined) {\n for (const key of Object.keys(log.beforeData)) {\n if (!includeSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.beforeData = keepOnlyKeys(log.beforeData, includeSet);\n }\n if (log.afterData !== undefined) {\n for (const key of Object.keys(log.afterData)) {\n if (!includeSet.has(key)) {\n removedFields.add(key);\n }\n }\n log.afterData = keepOnlyKeys(log.afterData, includeSet);\n }\n if (log.diff !== undefined) {\n log.diff = {\n changedFields: log.diff.changedFields.filter((field) =>\n includeSet.has(field),\n ),\n };\n }\n }\n\n if (removedFields.size > 0) {\n log.redactedFields = [...removedFields].sort();\n }\n}\n\n/**\n * Apply resolved enrichment to an AuditLog.\n * Enrichment values only fill gaps — explicit per-call and context values take precedence.\n *\n * Flow:\n * 1. Redact fields (before description sees data)\n * 2. Call description function with post-redaction context\n * 3. Apply scalar/array enrichment fields\n */\nexport function applyEnrichment(\n log: AuditLog,\n resolved: ResolvedEnrichment,\n): void {\n // Step 1: Redact fields first\n applyFieldRedaction(log, resolved);\n\n // Step 2: Call description with post-redaction data\n if (resolved.description !== undefined && log.description === undefined) {\n try {\n const descriptionContext: EnrichmentDescriptionContext = {\n before: log.beforeData !== undefined ? structuredClone(log.beforeData) : undefined,\n after: log.afterData !== undefined ? structuredClone(log.afterData) : undefined,\n diff: log.diff !== undefined ? structuredClone(log.diff) : undefined,\n actorId: log.actorId,\n metadata: log.metadata !== undefined ? structuredClone(log.metadata) : undefined,\n };\n\n log.description = resolved.description(descriptionContext);\n } catch {\n // Description function or data cloning threw — leave description unset, log still gets written\n }\n }\n\n // Step 3: Apply scalar/array enrichment (only if not already set)\n if (resolved.label !== undefined && log.label === undefined) {\n log.label = resolved.label;\n }\n if (resolved.severity !== undefined && log.severity === undefined) {\n log.severity = resolved.severity;\n }\n if (resolved.notify !== undefined && log.notify === undefined) {\n log.notify = resolved.notify;\n }\n if (resolved.compliance !== undefined) {\n if (log.compliance !== undefined) {\n const merged = [...log.compliance, ...resolved.compliance];\n log.compliance = [...new Set(merged)];\n } else {\n log.compliance = [...resolved.compliance];\n }\n }\n}\n\nfunction filterOutKeys(\n data: Record<string, unknown>,\n keysToRemove: Set<string>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (!keysToRemove.has(key)) {\n result[key] = value;\n }\n }\n return result;\n}\n\nfunction keepOnlyKeys(\n data: Record<string, unknown>,\n keysToKeep: Set<string>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (keysToKeep.has(key)) {\n result[key] = value;\n }\n }\n return result;\n}\n","import type { AuditOperation } from \"./types.js\";\n\n/**\n * Normalize before/after data based on the operation type.\n *\n * - **INSERT** → `before` is dropped (only `after` is meaningful)\n * - **DELETE** → `after` is dropped (only `before` is meaningful)\n * - **UPDATE** → both are kept as-is\n */\nexport function normalizeInput(\n operation: AuditOperation,\n before: Record<string, unknown> | undefined,\n after: Record<string, unknown> | undefined,\n): {\n before: Record<string, unknown> | undefined;\n after: Record<string, unknown> | undefined;\n} {\n switch (operation) {\n case \"INSERT\": {\n return { before: undefined, after };\n }\n case \"DELETE\": {\n return { before, after: undefined };\n }\n case \"UPDATE\": {\n return { before, after };\n }\n }\n}\n","const DURATION_PATTERN = /^(\\d+)([hdwmy])$/;\n\ntype DurationUnit = \"h\" | \"d\" | \"w\" | \"m\" | \"y\";\n\nfunction isValidUnit(raw: string): raw is DurationUnit {\n return raw === \"h\" || raw === \"d\" || raw === \"w\" || raw === \"m\" || raw === \"y\";\n}\n\n/**\n * Parses a duration string (e.g. \"30d\", \"2w\", \"3m\", \"1y\") and returns\n * a Date that is `duration` before `referenceDate`.\n *\n * Supported units: h (hours), d (days), w (weeks), m (months), y (years).\n * Zero values are rejected.\n */\nexport function parseDuration(input: string, referenceDate?: Date): Date {\n const match = DURATION_PATTERN.exec(input);\n if (match === null) {\n throw new Error(\n `Invalid duration \"${input}\". Expected format: <number><unit> where unit is h, d, w, m, or y (e.g. \"4h\", \"30d\", \"2w\", \"3m\", \"1y\").`,\n );\n }\n\n const value = Number(match[1]);\n const rawUnit = match[2] as string;\n\n if (value === 0) {\n throw new Error(\n `Invalid duration \"${input}\". Value must be greater than zero.`,\n );\n }\n\n if (!isValidUnit(rawUnit)) {\n throw new Error(\n `Invalid duration unit \"${rawUnit}\". Expected h, d, w, m, or y.`,\n );\n }\n\n const result = referenceDate !== undefined ? new Date(referenceDate) : new Date();\n\n if (rawUnit === \"h\") {\n result.setHours(result.getHours() - value);\n } else if (rawUnit === \"d\") {\n result.setDate(result.getDate() - value);\n } else if (rawUnit === \"w\") {\n result.setDate(result.getDate() - value * 7);\n } else if (rawUnit === \"m\") {\n result.setMonth(result.getMonth() - value);\n } else {\n result.setFullYear(result.getFullYear() - value);\n }\n\n return result;\n}\n","import type { AuditOperation, AuditSeverity } from \"./types.js\";\nimport type {\n AuditQueryFilters,\n AuditQueryResult,\n AuditQuerySpec,\n TimeFilter,\n} from \"./query-types.js\";\nimport { parseDuration } from \"./duration.js\";\n\n/** Callback that executes a query spec against the adapter. */\nexport type QueryExecutor = (spec: AuditQuerySpec) => Promise<AuditQueryResult>;\n\nconst DEFAULT_MAX_QUERY_LIMIT = 1000;\nconst MAX_SEARCH_TEXT_LENGTH = 500;\n\n/**\n * Fluent, immutable query builder for audit logs.\n *\n * Each method returns a **new** instance — safe to fork and share.\n * Call `.list()` to execute, or `.toSpec()` to inspect without executing.\n */\nexport class AuditQueryBuilder {\n readonly #executor: QueryExecutor;\n readonly #filters: AuditQueryFilters;\n readonly #limit: number | undefined;\n readonly #cursor: string | undefined;\n readonly #maxLimit: number;\n readonly #sortOrder: \"asc\" | \"desc\" | undefined;\n\n constructor(\n executor: QueryExecutor,\n filters?: AuditQueryFilters,\n limit?: number,\n cursor?: string,\n maxLimit?: number,\n sortOrder?: \"asc\" | \"desc\",\n ) {\n this.#executor = executor;\n this.#filters = filters ?? {};\n this.#limit = limit;\n this.#cursor = cursor;\n this.#maxLimit = maxLimit ?? DEFAULT_MAX_QUERY_LIMIT;\n this.#sortOrder = sortOrder;\n }\n\n /** Filter by table name and optional record ID. Last-write-wins. */\n resource(tableName: string, recordId?: string): AuditQueryBuilder {\n return new AuditQueryBuilder(\n this.#executor,\n {\n ...this.#filters,\n resource: recordId !== undefined\n ? { tableName, recordId }\n : { tableName },\n },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Filter by actor IDs (OR semantics, deduplicates). */\n actor(...ids: string[]): AuditQueryBuilder {\n const existing = this.#filters.actorIds ?? [];\n const merged = [...new Set([...existing, ...ids])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, actorIds: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Add severity levels to the filter (OR semantics, deduplicates). */\n severity(...levels: AuditSeverity[]): AuditQueryBuilder {\n const existing = this.#filters.severities ?? [];\n const merged = [...new Set([...existing, ...levels])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, severities: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Add compliance tags to the filter (AND semantics, deduplicates). */\n compliance(...tags: string[]): AuditQueryBuilder {\n const existing = this.#filters.compliance ?? [];\n const merged = [...new Set([...existing, ...tags])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, compliance: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /**\n * Filter entries created after a point in time.\n *\n * Accepts a `Date` or a duration string (e.g. \"4h\", \"30d\", \"2w\", \"3m\", \"1y\").\n * Duration strings are eagerly validated but resolved at query time for a fresh \"now\".\n * Last-write-wins.\n */\n since(value: Date | string): AuditQueryBuilder {\n const filter = this.#parseTimeFilter(value);\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, since: filter },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /**\n * Filter entries created before a point in time.\n *\n * Accepts a `Date` or a duration string (e.g. \"4h\", \"30d\", \"2w\", \"3m\", \"1y\").\n * Duration strings are eagerly validated but resolved at query time for a fresh \"now\".\n * Last-write-wins.\n */\n until(value: Date | string): AuditQueryBuilder {\n const filter = this.#parseTimeFilter(value);\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, until: filter },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Full-text search filter. Last-write-wins. Max 500 characters. */\n search(text: string): AuditQueryBuilder {\n if (text.length > MAX_SEARCH_TEXT_LENGTH) {\n throw new Error(\n `searchText must be at most ${MAX_SEARCH_TEXT_LENGTH} characters, got ${text.length}`,\n );\n }\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, searchText: text },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Add operation types to the filter (OR semantics, deduplicates). */\n operation(...ops: AuditOperation[]): AuditQueryBuilder {\n const existing = this.#filters.operations ?? [];\n const merged = [...new Set([...existing, ...ops])];\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters, operations: merged },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Set maximum number of entries to return. Must be > 0 and <= maxLimit. */\n limit(n: number): AuditQueryBuilder {\n if (n <= 0) {\n throw new Error(`limit must be greater than 0, got ${n}`);\n }\n if (n > this.#maxLimit) {\n throw new Error(\n `limit ${n} exceeds maximum allowed limit of ${this.#maxLimit}`,\n );\n }\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters },\n n,\n this.#cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Set the pagination cursor for fetching the next page. */\n after(cursor: string): AuditQueryBuilder {\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters },\n this.#limit,\n cursor,\n this.#maxLimit,\n this.#sortOrder,\n );\n }\n\n /** Set the sort direction for results. */\n order(direction: \"asc\" | \"desc\"): AuditQueryBuilder {\n return new AuditQueryBuilder(\n this.#executor,\n { ...this.#filters },\n this.#limit,\n this.#cursor,\n this.#maxLimit,\n direction,\n );\n }\n\n /** Returns the query specification without executing. Useful for tests and debugging. */\n toSpec(): AuditQuerySpec {\n const filters: AuditQueryFilters = { ...this.#filters };\n // Deep-clone arrays so the returned spec is fully detached from builder internals\n if (filters.severities !== undefined) {\n filters.severities = [...filters.severities];\n }\n if (filters.operations !== undefined) {\n filters.operations = [...filters.operations];\n }\n if (filters.compliance !== undefined) {\n filters.compliance = [...filters.compliance];\n }\n if (filters.actorIds !== undefined) {\n filters.actorIds = [...filters.actorIds];\n }\n const spec: AuditQuerySpec = { filters };\n const effectiveLimit = this.#limit ?? this.#maxLimit;\n spec.limit = effectiveLimit;\n if (this.#cursor !== undefined) {\n spec.cursor = this.#cursor;\n }\n if (this.#sortOrder !== undefined) {\n spec.sortOrder = this.#sortOrder;\n }\n return spec;\n }\n\n /** Execute the query against the adapter. */\n list(): Promise<AuditQueryResult> {\n return this.#executor(this.toSpec());\n }\n\n #parseTimeFilter(value: Date | string): TimeFilter {\n if (value instanceof Date) {\n return { date: value };\n }\n // Eagerly validate — throws if format is invalid\n parseDuration(value);\n return { duration: value };\n }\n}\n","import type { AuditLog, ExportOptions, ExportResult } from \"./types.js\";\nimport type { AuditQuerySpec } from \"./query-types.js\";\nimport type { QueryExecutor } from \"./query-builder.js\";\n\nconst DEFAULT_BATCH_SIZE = 500;\nconst DEFAULT_CSV_DELIMITER = \",\";\n\n/**\n * Ordered list of AuditLog fields for CSV columns.\n * Matches the full schema — JSONB columns are JSON.stringify'd.\n */\nconst CSV_COLUMNS: ReadonlyArray<keyof AuditLog> = [\n \"id\",\n \"timestamp\",\n \"tableName\",\n \"operation\",\n \"recordId\",\n \"actorId\",\n \"beforeData\",\n \"afterData\",\n \"diff\",\n \"label\",\n \"description\",\n \"severity\",\n \"compliance\",\n \"notify\",\n \"reason\",\n \"metadata\",\n \"redactedFields\",\n];\n\n/** Fields whose values are objects/arrays and need JSON.stringify in CSV. */\nconst JSON_FIELDS = new Set<keyof AuditLog>([\n \"beforeData\",\n \"afterData\",\n \"diff\",\n \"compliance\",\n \"metadata\",\n \"redactedFields\",\n]);\n\n/**\n * Escape a CSV field per RFC 4180.\n * Fields containing the delimiter, double quotes, or line breaks are quoted.\n * Embedded double quotes are doubled.\n */\nfunction escapeCsvField(value: string, delimiter: string): string {\n if (\n value.includes('\"') ||\n value.includes(delimiter) ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\")\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nfunction formatCsvValue(\n log: AuditLog,\n field: keyof AuditLog,\n delimiter: string,\n): string {\n const value = log[field];\n if (value === undefined || value === null) {\n return \"\";\n }\n if (JSON_FIELDS.has(field)) {\n return escapeCsvField(JSON.stringify(value), delimiter);\n }\n if (value instanceof Date) {\n return escapeCsvField(value.toISOString(), delimiter);\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\";\n }\n return escapeCsvField(String(value), delimiter);\n}\n\n/** Abstraction over WritableStream<string> and in-memory string buffer. */\ninterface ExportSink {\n write(chunk: string): Promise<void>;\n finish(): Promise<string | undefined>;\n abort(error: unknown): Promise<void>;\n}\n\nfunction createStringSink(): ExportSink {\n const chunks: string[] = [];\n return {\n async write(chunk: string): Promise<void> {\n chunks.push(chunk);\n },\n async finish(): Promise<string> {\n return chunks.join(\"\");\n },\n async abort(): Promise<void> {\n // Nothing to clean up for in-memory buffer\n },\n };\n}\n\nfunction createStreamSink(stream: WritableStream<string>): ExportSink {\n const writer = stream.getWriter();\n return {\n async write(chunk: string): Promise<void> {\n await writer.write(chunk);\n },\n async finish(): Promise<undefined> {\n await writer.close();\n return undefined;\n },\n async abort(error: unknown): Promise<void> {\n await writer.abort(error);\n },\n };\n}\n\n/**\n * Core export engine. Fetches rows in cursor-paginated batches and writes\n * them to the sink as CSV or JSON. Memory stays flat regardless of total rows.\n */\nexport async function runExport(\n executor: QueryExecutor,\n options: ExportOptions,\n): Promise<ExportResult> {\n const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n if (batchSize <= 0) {\n throw new Error(`batchSize must be greater than 0, got ${batchSize}`);\n }\n\n const delimiter = options.csvDelimiter ?? DEFAULT_CSV_DELIMITER;\n if (delimiter.length !== 1) {\n throw new Error(\"csvDelimiter must be exactly one character\");\n }\n\n const jsonStyle = options.jsonStyle ?? \"ndjson\";\n\n const sink =\n options.output === \"string\"\n ? createStringSink()\n : createStreamSink(options.output);\n const isStringSink = options.output === \"string\";\n\n // Build initial query spec from the optional query builder\n const baseSpec: AuditQuerySpec = options.query !== undefined\n ? options.query.toSpec()\n : { filters: {} };\n\n // Respect the user's query limit as a total cap; use batchSize for per-page fetching\n const totalLimit = baseSpec.limit;\n const spec: AuditQuerySpec = { ...baseSpec, limit: batchSize };\n\n let rowCount = 0;\n\n try {\n if (options.format === \"csv\") {\n // Write header row\n const header = CSV_COLUMNS.map((col) => escapeCsvField(col, delimiter)).join(delimiter);\n await sink.write(header + \"\\n\");\n\n // Paginate and write rows\n let cursor: string | undefined;\n for (;;) {\n const currentSpec: AuditQuerySpec = cursor !== undefined\n ? { ...spec, cursor }\n : spec;\n const result = await executor(currentSpec);\n\n if (isStringSink) {\n // Batch writes for string sink — one write() per page\n const lines: string[] = [];\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n const row = CSV_COLUMNS.map((col) => formatCsvValue(entry, col, delimiter)).join(delimiter);\n lines.push(row + \"\\n\");\n rowCount++;\n }\n if (lines.length > 0) {\n await sink.write(lines.join(\"\"));\n }\n } else {\n // Stream sink — per-row writes for backpressure\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n const row = CSV_COLUMNS.map((col) => formatCsvValue(entry, col, delimiter)).join(delimiter);\n await sink.write(row + \"\\n\");\n rowCount++;\n }\n }\n\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n if (result.nextCursor === undefined) {\n break;\n }\n cursor = result.nextCursor;\n }\n } else {\n // JSON format\n if (jsonStyle === \"array\") {\n let cursor: string | undefined;\n const entries: AuditLog[] = [];\n\n for (;;) {\n const currentSpec: AuditQuerySpec = cursor !== undefined\n ? { ...spec, cursor }\n : spec;\n const result = await executor(currentSpec);\n\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n entries.push(entry);\n rowCount++;\n }\n\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n if (result.nextCursor === undefined) {\n break;\n }\n cursor = result.nextCursor;\n }\n\n await sink.write(JSON.stringify(entries, null, 2) + \"\\n\");\n } else {\n // ndjson\n let cursor: string | undefined;\n for (;;) {\n const currentSpec: AuditQuerySpec = cursor !== undefined\n ? { ...spec, cursor }\n : spec;\n const result = await executor(currentSpec);\n\n if (isStringSink) {\n // Batch writes for string sink — one write() per page\n const lines: string[] = [];\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n lines.push(JSON.stringify(entry) + \"\\n\");\n rowCount++;\n }\n if (lines.length > 0) {\n await sink.write(lines.join(\"\"));\n }\n } else {\n // Stream sink — per-row writes for backpressure\n for (const entry of result.entries) {\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n await sink.write(JSON.stringify(entry) + \"\\n\");\n rowCount++;\n }\n }\n\n if (totalLimit !== undefined && rowCount >= totalLimit) {\n break;\n }\n if (result.nextCursor === undefined) {\n break;\n }\n cursor = result.nextCursor;\n }\n }\n }\n } catch (error) {\n await sink.abort(error);\n throw error;\n }\n\n const data = await sink.finish();\n\n if (data !== undefined) {\n return { rowCount, data };\n }\n return { rowCount };\n}\n","const UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Encodes a cursor from a timestamp and id.\n * Format: base64(JSON.stringify({ t: iso_string, i: id }))\n */\nexport function encodeCursor(timestamp: Date, id: string): string {\n const payload = JSON.stringify({ t: timestamp.toISOString(), i: id });\n return btoa(payload);\n}\n\n/**\n * Decodes an opaque cursor string back to timestamp + id.\n * Throws on invalid input.\n */\nexport function decodeCursor(cursor: string): { timestamp: Date; id: string } {\n let parsed: unknown;\n try {\n parsed = JSON.parse(atob(cursor));\n } catch {\n throw new Error(\"Invalid cursor: failed to decode\");\n }\n\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n !(\"t\" in parsed) ||\n !(\"i\" in parsed)\n ) {\n throw new Error(\"Invalid cursor: missing required fields\");\n }\n\n const record = parsed as Record<string, unknown>;\n const t = record[\"t\"];\n const i = record[\"i\"];\n\n if (typeof t !== \"string\" || typeof i !== \"string\") {\n throw new Error(\"Invalid cursor: fields must be strings\");\n }\n\n const timestamp = new Date(t);\n if (isNaN(timestamp.getTime())) {\n throw new Error(\"Invalid cursor: invalid timestamp\");\n }\n\n if (!UUID_PATTERN.test(i)) {\n throw new Error(\"Invalid cursor: id must be a valid UUID\");\n }\n\n return { timestamp, id: i };\n}\n\n","import type { AuditDatabaseAdapter, AuditLog, AuditStats, AuditSeverity, AuditOperation } from \"./types.js\";\nimport type { AuditQueryResult, AuditQuerySpec, TimeFilter } from \"./query-types.js\";\nimport type { EnrichmentRegistry } from \"./enrichment-registry.js\";\nimport { AuditQueryBuilder } from \"./query-builder.js\";\nimport { runExport } from \"./export.js\";\nimport { encodeCursor } from \"./cursor.js\";\n\nconst SEVERITY_VALUES: readonly AuditSeverity[] = [\"low\", \"medium\", \"high\", \"critical\"];\nconst VALID_SEVERITIES: ReadonlySet<string> = new Set<string>(SEVERITY_VALUES);\nconst VALID_OPERATIONS = new Set<string>([\"INSERT\", \"UPDATE\", \"DELETE\"]);\n\n/** Flat console-friendly query filters. Single-value fields translated to multi-value internal filters. */\nexport interface ConsoleQueryFilters {\n tableName?: string;\n recordId?: string;\n operation?: string;\n actorId?: string;\n severity?: string;\n compliance?: string;\n since?: Date;\n until?: Date;\n search?: string;\n limit?: number;\n cursor?: string;\n /** Pagination direction: \"after\" fetches older entries (default), \"before\" fetches newer entries. */\n direction?: \"after\" | \"before\";\n /** Sort order: \"asc\" (oldest first) or \"desc\" (newest first). Overrides direction-based sort when both present. */\n order?: \"asc\" | \"desc\";\n}\n\n/** Serializable summary of an enrichment config (function fields stripped). */\nexport interface EnrichmentSummary {\n table: string;\n operation: string;\n label?: string;\n severity?: AuditSeverity;\n compliance?: string[];\n notify?: boolean;\n redact?: string[];\n include?: string[];\n}\n\n/** Query result extended with convenience pagination flags. */\nexport interface ConsoleQueryResult extends AuditQueryResult {\n hasNextPage: boolean;\n hasPrevPage: boolean;\n}\n\n/**\n * High-level API consumed by console endpoints.\n *\n * Wraps the low-level `AuditDatabaseAdapter` methods with sensible defaults\n * (e.g. clamping `limit` to the configured maximum).\n */\nexport interface AuditApi {\n /** Query audit log entries with optional flat filters and cursor-based pagination. */\n queryLogs(filters?: ConsoleQueryFilters): Promise<ConsoleQueryResult>;\n /** Query audit log entries centered around a specific log ID. */\n queryLogsAround(anchorId: string, filters?: ConsoleQueryFilters): Promise<ConsoleQueryResult>;\n /** Retrieve a single audit log entry by its ID. Returns `null` when not found. */\n getLog(id: string): Promise<AuditLog | null>;\n /** Get aggregated audit statistics. Requires adapter.getStats. */\n getStats(options?: { since?: Date; until?: Date }): Promise<AuditStats>;\n /** Get serializable summaries of all registered enrichments. */\n getEnrichments(): EnrichmentSummary[];\n /** Export logs as CSV or JSON string. */\n exportLogs(filters?: ConsoleQueryFilters, format?: \"csv\" | \"json\"): Promise<string>;\n /** Purge audit logs before a given date. Requires adapter.purgeLogs. */\n purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }>;\n}\n\nfunction toTimeFilter(date: Date): TimeFilter {\n return { date };\n}\n\nfunction buildQuerySpec(filters: ConsoleQueryFilters, effectiveLimit: number): AuditQuerySpec {\n const limit = Math.min(filters.limit ?? effectiveLimit, effectiveLimit);\n\n const spec: AuditQuerySpec = {\n filters: {},\n limit,\n };\n\n if (filters.tableName !== undefined) {\n spec.filters.resource = filters.recordId !== undefined\n ? { tableName: filters.tableName, recordId: filters.recordId }\n : { tableName: filters.tableName };\n }\n if (filters.actorId !== undefined) {\n const raw = filters.actorId.split(\",\").map((v) => v.trim()).filter((v) => v.length > 0);\n spec.filters.actorIds = [...new Set(raw)];\n }\n if (filters.severity !== undefined) {\n const raw = filters.severity.split(\",\").map((v) => v.trim()).filter((v) => v.length > 0);\n const unique = [...new Set(raw)];\n for (const value of unique) {\n if (!VALID_SEVERITIES.has(value)) {\n throw new Error(\n `Invalid severity \"${value}\". Must be one of: low, medium, high, critical`,\n );\n }\n }\n spec.filters.severities = unique.filter((v): v is AuditSeverity => VALID_SEVERITIES.has(v));\n }\n if (filters.compliance !== undefined) {\n spec.filters.compliance = [filters.compliance];\n }\n if (filters.operation !== undefined) {\n const normalized = filters.operation.toUpperCase();\n if (!VALID_OPERATIONS.has(normalized)) {\n throw new Error(\n `Invalid operation \"${filters.operation}\". Must be one of: INSERT, UPDATE, DELETE`,\n );\n }\n spec.filters.operations = [normalized as AuditOperation];\n }\n if (filters.since !== undefined) {\n spec.filters.since = toTimeFilter(filters.since);\n }\n if (filters.until !== undefined) {\n spec.filters.until = toTimeFilter(filters.until);\n }\n if (filters.search !== undefined) {\n spec.filters.searchText = filters.search;\n }\n if (filters.cursor !== undefined) {\n spec.cursor = filters.cursor;\n }\n if (filters.direction === \"before\") {\n spec.sortOrder = \"asc\";\n }\n if (filters.order !== undefined) {\n spec.sortOrder = filters.order;\n }\n\n return spec;\n}\n\nexport function createAuditApi(\n adapter: AuditDatabaseAdapter,\n registry: EnrichmentRegistry,\n maxQueryLimit?: number,\n): AuditApi {\n const effectiveLimit = maxQueryLimit ?? 1000;\n\n function requireQueryLogs(): NonNullable<AuditDatabaseAdapter[\"queryLogs\"]> {\n if (adapter.queryLogs === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n return adapter.queryLogs;\n }\n\n function requireGetLogById(): NonNullable<AuditDatabaseAdapter[\"getLogById\"]> {\n if (adapter.getLogById === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements getLogById(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n return adapter.getLogById;\n }\n\n function requireGetStats(): NonNullable<AuditDatabaseAdapter[\"getStats\"]> {\n if (adapter.getStats === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements getStats(). \" +\n \"Check that your ORM adapter supports statistics.\",\n );\n }\n return adapter.getStats;\n }\n\n function requirePurgeLogs(): NonNullable<AuditDatabaseAdapter[\"purgeLogs\"]> {\n if (adapter.purgeLogs === undefined) {\n throw new Error(\n \"Console API requires a database adapter that implements purgeLogs(). \" +\n \"Check that your ORM adapter supports log purging.\",\n );\n }\n return adapter.purgeLogs;\n }\n\n async function queryLogs(filters?: ConsoleQueryFilters): Promise<ConsoleQueryResult> {\n const queryFn = requireQueryLogs();\n const resolved = filters ?? {};\n const spec = buildQuerySpec(resolved, effectiveLimit);\n const result = await queryFn(spec);\n\n if (resolved.direction === \"before\") {\n // Adapter returned ASC results (newer entries from cursor going up).\n // Reverse to maintain DESC output order.\n const entries = [...result.entries].reverse();\n const lastEntry = entries[entries.length - 1];\n return {\n entries,\n // Adapter's \"next\" in ASC = newer entries = our prev\n ...(result.nextCursor !== undefined && { prevCursor: result.nextCursor }),\n hasPrevPage: result.nextCursor !== undefined,\n // Since we started from a cursor, there are older entries behind it\n ...(resolved.cursor !== undefined && lastEntry !== undefined && { nextCursor: encodeCursor(lastEntry.timestamp, lastEntry.id) }),\n hasNextPage: resolved.cursor !== undefined,\n };\n }\n\n const firstEntry = result.entries[0];\n const prevCursor = resolved.cursor !== undefined && firstEntry !== undefined\n ? encodeCursor(firstEntry.timestamp, firstEntry.id)\n : undefined;\n\n return {\n entries: result.entries,\n ...(result.nextCursor !== undefined && { nextCursor: result.nextCursor }),\n hasNextPage: result.nextCursor !== undefined,\n hasPrevPage: resolved.cursor !== undefined,\n ...(prevCursor !== undefined && { prevCursor }),\n };\n }\n\n async function queryLogsAround(anchorId: string, filters?: ConsoleQueryFilters): Promise<ConsoleQueryResult> {\n const queryFn = requireQueryLogs();\n const getLogFn = requireGetLogById();\n\n const anchor = await getLogFn(anchorId);\n if (anchor === null) {\n return { entries: [], hasNextPage: false, hasPrevPage: false };\n }\n\n const resolved = filters ?? {};\n const limit = Math.min(resolved.limit ?? effectiveLimit, effectiveLimit);\n // Subtract 1 for the anchor so total entries = limit exactly\n const remaining = Math.max(limit - 1, 0);\n const olderCount = Math.ceil(remaining / 2);\n const newerCount = Math.floor(remaining / 2);\n\n const anchorCursor = encodeCursor(anchor.timestamp, anchor.id);\n\n // Strip direction from filters to avoid sortOrder override in buildQuerySpec\n const { direction: _dir, ...baseFilters } = resolved;\n\n // Fetch older entries (DESC, exclusive of anchor)\n const olderSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: olderCount }, effectiveLimit);\n olderSpec.sortOrder = \"desc\";\n\n // Fetch newer entries (ASC, exclusive of anchor)\n const newerSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: newerCount }, effectiveLimit);\n newerSpec.sortOrder = \"asc\";\n\n const [olderResult, newerResult] = await Promise.all([\n queryFn(olderSpec),\n queryFn(newerSpec),\n ]);\n\n // Combine: newer (reversed to DESC) + anchor + older (already DESC)\n const newerEntries = [...newerResult.entries].reverse();\n const entries = [...newerEntries, anchor, ...olderResult.entries];\n\n const hasNextPage = olderResult.nextCursor !== undefined;\n const hasPrevPage = newerResult.nextCursor !== undefined;\n\n const oldest = entries[entries.length - 1];\n const newest = entries[0];\n\n return {\n entries,\n hasNextPage,\n hasPrevPage,\n ...(hasNextPage && oldest !== undefined && { nextCursor: encodeCursor(oldest.timestamp, oldest.id) }),\n ...(hasPrevPage && newest !== undefined && { prevCursor: encodeCursor(newest.timestamp, newest.id) }),\n };\n }\n\n async function getLog(id: string): Promise<AuditLog | null> {\n const getLogFn = requireGetLogById();\n return getLogFn(id);\n }\n\n async function getStats(options?: { since?: Date; until?: Date }): Promise<AuditStats> {\n const getStatsFn = requireGetStats();\n return getStatsFn(options);\n }\n\n function getEnrichments(): EnrichmentSummary[] {\n const entries = registry.getEntries();\n const summaries: EnrichmentSummary[] = [];\n\n for (const entry of entries) {\n for (const config of entry.configs) {\n const summary: EnrichmentSummary = {\n table: entry.table,\n operation: entry.operation,\n };\n if (config.label !== undefined) {\n summary.label = config.label;\n }\n if (config.severity !== undefined) {\n summary.severity = config.severity;\n }\n if (config.compliance !== undefined) {\n summary.compliance = config.compliance;\n }\n if (config.notify !== undefined) {\n summary.notify = config.notify;\n }\n if (config.redact !== undefined) {\n summary.redact = config.redact;\n }\n if (config.include !== undefined) {\n summary.include = config.include;\n }\n summaries.push(summary);\n }\n }\n\n return summaries;\n }\n\n async function exportLogs(filters?: ConsoleQueryFilters, format?: \"csv\" | \"json\"): Promise<string> {\n const queryFn = requireQueryLogs();\n const exportFormat = format ?? \"csv\";\n\n // Build a query spec from the console filters (without cursor — export fetches all pages)\n const spec = buildQuerySpec(filters ?? {}, effectiveLimit);\n\n // Construct a query builder pre-loaded with the console filters\n const queryBuilder = new AuditQueryBuilder(\n (s) => queryFn(s),\n spec.filters,\n spec.limit,\n undefined,\n effectiveLimit,\n spec.sortOrder,\n );\n\n const exportOptions = {\n format: exportFormat,\n query: queryBuilder,\n output: \"string\" as const,\n ...(exportFormat === \"json\" && { jsonStyle: \"array\" as const }),\n };\n\n const result = await runExport(\n (s) => queryFn(s),\n exportOptions,\n );\n return result.data ?? \"\";\n }\n\n async function purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n const purgeFn = requirePurgeLogs();\n return purgeFn(options);\n }\n\n return { queryLogs, queryLogsAround, getLog, getStats, getEnrichments, exportLogs, purgeLogs };\n}\n","/**\n * Shared parse and validation utilities for console endpoint handlers.\n *\n * These are small, pure, zero-dep helpers used by product endpoint handlers\n * (audit, tenant) to validate and parse incoming request data.\n */\n\n/** Type guard that narrows `unknown` to a plain object (not array, not null). */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Parse a string to an integer within [min, max].\n * Returns `undefined` when `value` is `undefined` (param absent),\n * not a finite number, or below `min`.\n */\nexport function parseBoundedInt(\n value: string | undefined,\n min: number,\n max: number,\n): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < min) {\n return undefined;\n }\n return Math.min(Math.floor(parsed), max);\n}\n\n/**\n * Parse an ISO-8601 date string.\n * Returns `undefined` when `value` is `undefined` or not a valid date.\n */\nexport function parseIsoDate(value: string | undefined): Date | undefined {\n if (value === undefined) {\n return undefined;\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return undefined;\n }\n return date;\n}\n","import type {\n ConsoleProductEndpoint,\n ConsoleProductRequest,\n ConsoleErrorResponse,\n AuditLogWire,\n AuditLogsResponse,\n AuditStatsResponse,\n AuditEnrichmentsResponse,\n AuditPurgeResponse,\n AuditLogsQuery,\n AuditStatsQuery,\n AuditExportQuery,\n} from \"@usebetterdev/contract\";\nimport {\n isPlainObject,\n parseBoundedInt,\n parseIsoDate,\n} from \"@usebetterdev/console-utils\";\nimport type { AuditApi, ConsoleQueryFilters } from \"./audit-api.js\";\nimport type { AuditLog } from \"./types.js\";\n\nconst MAX_PARAM_LENGTH = 1000;\n\nfunction exceedsMaxLength(value: string | undefined): boolean {\n return value !== undefined && value.length > MAX_PARAM_LENGTH;\n}\n\nfunction hasLongQueryParam(query: AuditLogsQuery): boolean {\n return (\n exceedsMaxLength(query.tableName) ||\n exceedsMaxLength(query.recordId) ||\n exceedsMaxLength(query.actorId) ||\n exceedsMaxLength(query.cursor) ||\n exceedsMaxLength(query.operation) ||\n exceedsMaxLength(query.severity) ||\n exceedsMaxLength(query.compliance) ||\n exceedsMaxLength(query.search) ||\n exceedsMaxLength(query.around) ||\n exceedsMaxLength(query.direction) ||\n exceedsMaxLength(query.order)\n );\n}\n\nfunction parseConsoleQueryFilters(\n query: AuditLogsQuery,\n): { filters: ConsoleQueryFilters } | { error: string } {\n const filters: ConsoleQueryFilters = {};\n\n if (query.limit !== undefined) {\n const limit = parseBoundedInt(query.limit, 1, 1000);\n if (limit === undefined) {\n return { error: \"Invalid 'limit': must be a positive integer (max 1000)\" };\n }\n filters.limit = limit;\n }\n if (query.tableName !== undefined) {\n filters.tableName = query.tableName;\n }\n if (query.recordId !== undefined) {\n if (query.tableName === undefined) {\n return { error: \"'recordId' requires 'tableName'\" };\n }\n filters.recordId = query.recordId;\n }\n if (query.operation !== undefined) {\n filters.operation = query.operation;\n }\n if (query.actorId !== undefined) {\n filters.actorId = query.actorId;\n }\n if (query.severity !== undefined) {\n filters.severity = query.severity;\n }\n if (query.compliance !== undefined) {\n filters.compliance = query.compliance;\n }\n if (query.search !== undefined) {\n filters.search = query.search;\n }\n if (query.cursor !== undefined) {\n filters.cursor = query.cursor;\n }\n if (query.direction !== undefined) {\n if (query.direction !== \"after\" && query.direction !== \"before\") {\n return { error: \"Invalid 'direction': must be 'after' or 'before'\" };\n }\n filters.direction = query.direction;\n }\n if (query.order !== undefined) {\n if (query.order !== \"asc\" && query.order !== \"desc\") {\n return { error: \"Invalid 'order': must be 'asc' or 'desc'\" };\n }\n filters.order = query.order;\n }\n\n if (query.since !== undefined) {\n const since = parseIsoDate(query.since);\n if (since === undefined) {\n return { error: \"Invalid 'since': must be an ISO-8601 date\" };\n }\n filters.since = since;\n }\n if (query.until !== undefined) {\n const until = parseIsoDate(query.until);\n if (until === undefined) {\n return { error: \"Invalid 'until': must be an ISO-8601 date\" };\n }\n filters.until = until;\n }\n\n if (filters.since !== undefined && filters.until !== undefined && filters.since >= filters.until) {\n return { error: \"'since' must be before 'until'\" };\n }\n\n return { filters };\n}\n\n// Compile-time: every AuditLogsQuery key must be handled by parseConsoleQueryFilters or the /logs handler.\n// Adding a field to AuditLogsQuery without listing it here causes a build error.\ntype _HandledLogsKeys = keyof ConsoleQueryFilters | \"around\";\ntype _UnhandledLogsKeys = Exclude<keyof AuditLogsQuery, _HandledLogsKeys>;\ntype _AssertAllLogsKeysHandled = [_UnhandledLogsKeys] extends [never]\n ? true\n : { error: \"AuditLogsQuery has unhandled keys\"; keys: _UnhandledLogsKeys };\nconst _logsExhaustive: _AssertAllLogsKeysHandled = true;\n\n// Compile-time: AuditExportQuery filter keys (minus format) must exist in AuditLogsQuery.\ntype _ExportFilterKeys = Exclude<keyof AuditExportQuery, \"format\">;\ntype _MissingInLogs = Exclude<_ExportFilterKeys, keyof AuditLogsQuery>;\ntype _AssertExportSubset = [_MissingInLogs] extends [never]\n ? true\n : { error: \"AuditExportQuery has filter keys missing from AuditLogsQuery\"; keys: _MissingInLogs };\nconst _exportSubset: _AssertExportSubset = true;\n\nfunction serializeLog(log: AuditLog): AuditLogWire {\n return {\n ...log,\n timestamp: log.timestamp.toISOString(),\n };\n}\n\nexport function createAuditConsoleEndpoints(\n api: AuditApi,\n): ConsoleProductEndpoint[] {\n return [\n {\n method: \"GET\",\n path: \"/logs\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n const query: AuditLogsQuery = request.query;\n if (hasLongQueryParam(query)) {\n return { status: 400, body: { error: \"Query parameter exceeds maximum length\" } satisfies ConsoleErrorResponse };\n }\n const parsed = parseConsoleQueryFilters(query);\n if (\"error\" in parsed) {\n return { status: 400, body: { error: parsed.error } satisfies ConsoleErrorResponse };\n }\n if (parsed.filters.order !== undefined && parsed.filters.direction !== undefined) {\n return { status: 400, body: { error: \"'order' cannot be combined with 'direction'\" } satisfies ConsoleErrorResponse };\n }\n const around = query.around;\n if (around !== undefined && (parsed.filters.cursor !== undefined || parsed.filters.direction !== undefined || parsed.filters.order !== undefined)) {\n return { status: 400, body: { error: \"'around' cannot be combined with 'cursor', 'direction', or 'order'\" } satisfies ConsoleErrorResponse };\n }\n const result = around !== undefined\n ? await api.queryLogsAround(around, parsed.filters)\n : await api.queryLogs(parsed.filters);\n const body: AuditLogsResponse = {\n entries: result.entries.map(serializeLog),\n hasNextPage: result.hasNextPage,\n };\n if (result.nextCursor !== undefined) {\n body.nextCursor = result.nextCursor;\n }\n if (result.prevCursor !== undefined) {\n body.prevCursor = result.prevCursor;\n }\n if (result.hasPrevPage) {\n body.hasPrevPage = result.hasPrevPage;\n }\n return { status: 200, body };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/logs/:id\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n const id = request.params.id?.trim();\n if (!id) {\n return { status: 400, body: { error: \"Missing log id\" } satisfies ConsoleErrorResponse };\n }\n const log = await api.getLog(id);\n if (!log) {\n return { status: 404, body: { error: \"Audit log not found\" } satisfies ConsoleErrorResponse };\n }\n return { status: 200, body: serializeLog(log) satisfies AuditLogWire };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/stats\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n const query: AuditStatsQuery = request.query;\n if (exceedsMaxLength(query.since) || exceedsMaxLength(query.until)) {\n return { status: 400, body: { error: \"Query parameter exceeds maximum length\" } satisfies ConsoleErrorResponse };\n }\n const options: { since?: Date; until?: Date } = {};\n if (query.since !== undefined) {\n const since = parseIsoDate(query.since);\n if (since === undefined) {\n return { status: 400, body: { error: \"Invalid 'since': must be an ISO-8601 date\" } satisfies ConsoleErrorResponse };\n }\n options.since = since;\n }\n if (query.until !== undefined) {\n const until = parseIsoDate(query.until);\n if (until === undefined) {\n return { status: 400, body: { error: \"Invalid 'until': must be an ISO-8601 date\" } satisfies ConsoleErrorResponse };\n }\n options.until = until;\n }\n if (options.since !== undefined && options.until !== undefined && options.since >= options.until) {\n return { status: 400, body: { error: \"'since' must be before 'until'\" } satisfies ConsoleErrorResponse };\n }\n const stats = await api.getStats(options);\n return { status: 200, body: stats satisfies AuditStatsResponse };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/enrichments\",\n requiredPermission: \"read\",\n async handler() {\n try {\n const enrichments = api.getEnrichments();\n return { status: 200, body: enrichments satisfies AuditEnrichmentsResponse };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"GET\",\n path: \"/export\",\n requiredPermission: \"read\",\n async handler(request: ConsoleProductRequest) {\n try {\n const query: AuditExportQuery = request.query;\n const format = query.format;\n if (format !== undefined && format !== \"csv\" && format !== \"json\") {\n return { status: 400, body: { error: \"Invalid format. Must be 'csv' or 'json'\" } satisfies ConsoleErrorResponse };\n }\n if (hasLongQueryParam(query)) {\n return { status: 400, body: { error: \"Query parameter exceeds maximum length\" } satisfies ConsoleErrorResponse };\n }\n const parsed = parseConsoleQueryFilters(query);\n if (\"error\" in parsed) {\n return { status: 400, body: { error: parsed.error } satisfies ConsoleErrorResponse };\n }\n const effectiveFormat: \"csv\" | \"json\" = format ?? \"csv\";\n const data = await api.exportLogs(parsed.filters, effectiveFormat);\n\n const contentType = effectiveFormat === \"csv\"\n ? \"text/csv; charset=utf-8\"\n : \"application/json; charset=utf-8\";\n const ext = effectiveFormat === \"csv\" ? \"csv\" : \"json\";\n const dateStamp = new Date().toISOString().slice(0, 10);\n const filename = `audit-export-${dateStamp}.${ext}`;\n\n return {\n status: 200,\n body: data,\n headers: {\n \"content-type\": contentType,\n \"content-disposition\": `attachment; filename=\"${filename}\"`,\n \"cache-control\": \"no-cache\",\n },\n };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n {\n method: \"DELETE\",\n path: \"/logs\",\n requiredPermission: \"admin\",\n async handler(request: ConsoleProductRequest) {\n try {\n if (!isPlainObject(request.body)) {\n return { status: 400, body: { error: \"Request body is required\" } satisfies ConsoleErrorResponse };\n }\n const beforeValue = request.body.before;\n if (typeof beforeValue !== \"string\") {\n return { status: 400, body: { error: \"Missing required field: before\" } satisfies ConsoleErrorResponse };\n }\n const before = new Date(beforeValue);\n if (Number.isNaN(before.getTime())) {\n return { status: 400, body: { error: \"Invalid date for 'before' field\" } satisfies ConsoleErrorResponse };\n }\n const options: { before: Date; tableName?: string } = { before };\n if (typeof request.body.tableName === \"string\" && request.body.tableName.length > 0) {\n if (exceedsMaxLength(request.body.tableName)) {\n return { status: 400, body: { error: \"tableName exceeds maximum length\" } satisfies ConsoleErrorResponse };\n }\n options.tableName = request.body.tableName;\n }\n const result = await api.purgeLogs(options);\n return { status: 200, body: result satisfies AuditPurgeResponse };\n } catch {\n return { status: 500, body: { error: \"Internal server error\" } satisfies ConsoleErrorResponse };\n }\n },\n },\n ];\n}\n","import type { AuditQueryBuilder, QueryExecutor } from \"./query-builder.js\";\nimport { runExport } from \"./export.js\";\n\n/** Options for `createExportResponse()`. */\nexport interface ExportResponseOptions {\n /** Output format. Default: `\"csv\"`. */\n format?: \"csv\" | \"json\";\n /** JSON output style. Default: `\"ndjson\"`. */\n jsonStyle?: \"ndjson\" | \"array\";\n /** Rows per database round-trip. */\n batchSize?: number;\n /** CSV delimiter character. */\n csvDelimiter?: string;\n /** Optional query builder to filter exported entries. */\n query?: AuditQueryBuilder;\n /** Custom filename stem (without extension). Default: `\"audit-export-YYYY-MM-DD\"`. */\n filename?: string;\n}\n\nfunction contentTypeForFormat(\n format: \"csv\" | \"json\",\n jsonStyle: \"ndjson\" | \"array\",\n): string {\n if (format === \"csv\") {\n return \"text/csv; charset=utf-8\";\n }\n if (jsonStyle === \"ndjson\") {\n return \"application/x-ndjson; charset=utf-8\";\n }\n return \"application/json; charset=utf-8\";\n}\n\nfunction fileExtensionForFormat(\n format: \"csv\" | \"json\",\n jsonStyle: \"ndjson\" | \"array\",\n): string {\n if (format === \"csv\") {\n return \".csv\";\n }\n if (jsonStyle === \"ndjson\") {\n return \".ndjson\";\n }\n return \".json\";\n}\n\nfunction formatDate(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Sanitise a filename for use in Content-Disposition headers.\n * Strips characters that could break the header or enable response splitting.\n */\nfunction sanitiseFilename(name: string): string {\n return name.replace(/[\"\\\\\\r\\n\\x00-\\x1f]/g, \"\");\n}\n\n/**\n * Create a streaming `Response` from the audit export engine.\n *\n * Uses `TransformStream` + `TextEncoderStream` to bridge the string-based\n * `runExport()` output to a binary `ReadableStream` suitable for `new Response()`.\n * The export runs asynchronously — the Response is returned immediately so the\n * first byte can arrive before all rows are read.\n */\nexport function createExportResponse(\n executor: QueryExecutor,\n options: ExportResponseOptions = {},\n): Response {\n const format = options.format ?? \"csv\";\n const jsonStyle = options.jsonStyle ?? \"ndjson\";\n\n const stem =\n options.filename ?? `audit-export-${formatDate(new Date())}`;\n const extension = fileExtensionForFormat(format, jsonStyle);\n const fullFilename = sanitiseFilename(`${stem}${extension}`);\n\n const contentType = contentTypeForFormat(format, jsonStyle);\n\n // Bridge: runExport writes strings into the writable side of the transform.\n // The readable side feeds into TextEncoderStream, producing Uint8Array for the Response.\n const transform = new TransformStream<string, string>();\n const encoder = new TextEncoderStream();\n const readable = transform.readable.pipeThrough(encoder);\n\n // Start the export asynchronously — do NOT await.\n // runExport closes/aborts the writable side on completion/error.\n // The .catch() prevents unhandled rejection — the error is already\n // propagated to the reader via sink.abort() inside runExport.\n runExport(executor, {\n format,\n jsonStyle,\n output: transform.writable,\n ...(options.batchSize !== undefined && { batchSize: options.batchSize }),\n ...(options.csvDelimiter !== undefined && {\n csvDelimiter: options.csvDelimiter,\n }),\n ...(options.query !== undefined && { query: options.query }),\n }).catch(() => {\n // Error already propagated through the transform stream\n });\n\n return new Response(readable, {\n status: 200,\n headers: {\n \"Content-Type\": contentType,\n \"Content-Disposition\": `attachment; filename=\"${fullFilename}\"`,\n \"Cache-Control\": \"no-cache\",\n },\n });\n}\n","import type { RetentionPolicy } from \"./types.js\";\n\nexport function validateRetentionPolicy(policy: RetentionPolicy): void {\n if (\n !Number.isInteger(policy.days) ||\n !Number.isFinite(policy.days) ||\n policy.days <= 0\n ) {\n throw new Error(\n `retention: 'days' must be a positive integer, got ${String(policy.days)}`,\n );\n }\n\n if (policy.tables !== undefined) {\n if (\n !Array.isArray(policy.tables) ||\n policy.tables.length === 0 ||\n policy.tables.some((t) => typeof t !== \"string\" || t === \"\")\n ) {\n throw new Error(\n \"retention: 'tables' must be a non-empty array of non-empty strings\",\n );\n }\n }\n}\n","import type {\n AfterLogHook,\n AuditContext,\n AuditLog,\n AuditOperation,\n BeforeLogHook,\n BetterAuditConfig,\n BetterAuditInstance,\n CaptureLogInput,\n EnrichmentConfig,\n ExportOptions,\n ExportResult,\n ExportResponseOptions,\n RetentionPolicy,\n} from \"./types.js\";\nimport { getAuditContext, runWithAuditContext } from \"./context.js\";\nimport { computeDiff } from \"./diff.js\";\nimport { EnrichmentRegistry, applyEnrichment } from \"./enrichment-registry.js\";\nimport { normalizeInput } from \"./normalize.js\";\nimport { AuditQueryBuilder } from \"./query-builder.js\";\nimport { createAuditApi } from \"./audit-api.js\";\nimport { createAuditConsoleEndpoints } from \"./console-endpoints.js\";\nimport { runExport } from \"./export.js\";\nimport { createExportResponse } from \"./export-response.js\";\nimport { validateRetentionPolicy } from \"./retention.js\";\n\nfunction withContext<T>(\n context: AuditContext,\n fn: () => Promise<T>,\n): Promise<T> {\n return runWithAuditContext(context, fn);\n}\n\nexport function betterAudit(config: BetterAuditConfig): BetterAuditInstance {\n if (config.retention !== undefined) {\n validateRetentionPolicy(config.retention);\n }\n\n if (config.asyncWrite === true && config.onError === undefined) {\n console.warn(\n \"audit: asyncWrite is enabled without onError — write failures will only log to console.error. \" +\n \"Set onError to route failures to your error tracking system.\",\n );\n }\n\n const { database } = config;\n const auditTables = new Set(config.auditTables);\n\n if (config.retention?.tables !== undefined) {\n const unknown = config.retention.tables.filter((t) => !auditTables.has(t));\n if (unknown.length > 0) {\n throw new Error(\n `retention: 'tables' contains table(s) not in auditTables: ${unknown.join(\", \")}. ` +\n `Registered tables: ${[...auditTables].join(\", \")}`,\n );\n }\n }\n const registry = new EnrichmentRegistry();\n\n const resolvedRetention: RetentionPolicy | undefined =\n config.retention !== undefined\n ? {\n ...config.retention,\n ...(config.retention.tables !== undefined && { tables: [...config.retention.tables] }),\n }\n : undefined;\n\n function retentionPolicy(): RetentionPolicy | undefined {\n if (resolvedRetention === undefined) {\n return undefined;\n }\n return {\n ...resolvedRetention,\n ...(resolvedRetention.tables !== undefined && { tables: [...resolvedRetention.tables] }),\n };\n }\n const beforeLogHooks: BeforeLogHook[] = config.beforeLog !== undefined ? [...config.beforeLog] : [];\n const afterLogHooks: AfterLogHook[] = config.afterLog !== undefined ? [...config.afterLog] : [];\n\n function enrich(\n table: string,\n operation: AuditOperation | \"*\",\n enrichmentConfig: EnrichmentConfig,\n ): void {\n if (table !== \"*\" && !auditTables.has(table)) {\n throw new Error(\n `Cannot register enrichment for table \"${table}\": it is not in auditTables. ` +\n `Registered tables: ${[...auditTables].join(\", \")}`,\n );\n }\n\n registry.register(table, operation, enrichmentConfig);\n }\n\n function onBeforeLog(hook: BeforeLogHook): () => void {\n beforeLogHooks.push(hook);\n return () => {\n const index = beforeLogHooks.indexOf(hook);\n if (index !== -1) {\n beforeLogHooks.splice(index, 1);\n }\n };\n }\n\n function onAfterLog(hook: AfterLogHook): () => void {\n afterLogHooks.push(hook);\n return () => {\n const index = afterLogHooks.indexOf(hook);\n if (index !== -1) {\n afterLogHooks.splice(index, 1);\n }\n };\n }\n\n async function writeAndRunAfterHooks(log: AuditLog): Promise<void> {\n await database.writeLog(log);\n for (const hook of afterLogHooks) {\n await hook(log);\n }\n }\n\n async function captureLog(input: CaptureLogInput): Promise<void> {\n // 1. Early return if table not in auditTables\n if (!auditTables.has(input.tableName)) {\n return;\n }\n\n if (input.recordId === \"\") {\n throw new Error(\"captureLog requires a non-empty recordId\");\n }\n\n // 2. Normalize input based on operation type\n const normalized = normalizeInput(input.operation, input.before, input.after);\n\n const context = getAuditContext();\n\n // 3. Assemble log (merge input + AuditContext)\n // Conditional spreads satisfy exactOptionalPropertyTypes — properties\n // are either absent or carry a narrowed non-undefined value.\n const actorId = input.actorId ?? context?.actorId;\n const label = input.label ?? context?.label;\n const reason = input.reason ?? context?.reason;\n const compliance = input.compliance ?? context?.compliance;\n const metadata = input.metadata ?? context?.metadata;\n\n const log: AuditLog = {\n id: crypto.randomUUID(),\n timestamp: new Date(),\n tableName: input.tableName,\n operation: input.operation,\n recordId: input.recordId,\n ...(actorId !== undefined && { actorId }),\n ...(label !== undefined && { label }),\n ...(reason !== undefined && { reason }),\n ...(compliance !== undefined && { compliance }),\n ...(metadata !== undefined && { metadata }),\n ...(input.description !== undefined && { description: input.description }),\n ...(input.severity !== undefined && { severity: input.severity }),\n ...(input.notify !== undefined && { notify: input.notify }),\n ...(normalized.before !== undefined && { beforeData: { ...normalized.before } }),\n ...(normalized.after !== undefined && { afterData: { ...normalized.after } }),\n };\n\n // 4. Compute diff only for UPDATE operations\n if (input.operation === \"UPDATE\") {\n const diff = computeDiff(normalized.before, normalized.after);\n if (diff.changedFields.length > 0) {\n log.diff = diff;\n }\n }\n\n // 5. Resolve enrichment → Redact → Describe → Apply scalars\n const resolved = registry.resolve(input.tableName, input.operation);\n if (resolved !== undefined) {\n applyEnrichment(log, resolved);\n }\n\n // 6. Run beforeLog hooks (sequential, may mutate log, errors abort)\n for (const hook of beforeLogHooks) {\n await hook(log);\n }\n\n // 7. Write log (sync or async) + afterLog hooks\n const isAsync = input.asyncWrite ?? config.asyncWrite ?? false;\n if (isAsync) {\n void writeAndRunAfterHooks(log).catch((error: unknown) => {\n if (config.onError !== undefined) {\n config.onError(error);\n } else {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`audit: async write failed for ${log.tableName}/${log.id} — ${message}`);\n }\n });\n } else {\n await writeAndRunAfterHooks(log);\n }\n }\n\n function query(): AuditQueryBuilder {\n if (database.queryLogs === undefined) {\n throw new Error(\n \"audit.query() requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n const queryLogs = database.queryLogs;\n return new AuditQueryBuilder(\n (spec) => queryLogs(spec),\n undefined,\n undefined,\n undefined,\n config.maxQueryLimit,\n );\n }\n\n async function exportLogs(options: ExportOptions): Promise<ExportResult> {\n if (database.queryLogs === undefined) {\n throw new Error(\n \"audit.export() requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n const queryLogs = database.queryLogs;\n return runExport((spec) => queryLogs(spec), options);\n }\n\n function exportResponse(options?: ExportResponseOptions): Response {\n if (database.queryLogs === undefined) {\n throw new Error(\n \"audit.exportResponse() requires a database adapter that implements queryLogs(). \" +\n \"Check that your ORM adapter supports querying.\",\n );\n }\n const queryLogs = database.queryLogs;\n return createExportResponse((spec) => queryLogs(spec), options);\n }\n\n if (config.console) {\n const api = createAuditApi(database, registry, config.maxQueryLimit);\n const endpoints = createAuditConsoleEndpoints(api);\n config.console.registerProduct({\n id: \"audit\",\n name: \"Better Audit\",\n endpoints,\n });\n }\n\n return { captureLog, query, export: exportLogs, exportResponse, withContext, enrich, onBeforeLog, onAfterLog, retentionPolicy };\n}\n","/**\n * Logical schema for the `audit_logs` table.\n *\n * ORM adapters translate this into their own migration format.\n * Core never runs SQL — this is a declarative data structure only.\n */\n\nexport type ColumnType =\n | \"uuid\"\n | \"timestamptz\"\n | \"text\"\n | \"jsonb\"\n | \"boolean\"\n | \"integer\";\n\nexport interface ColumnDefinition {\n type: ColumnType;\n nullable: boolean;\n primaryKey?: boolean;\n defaultExpression?: string;\n indexed?: boolean;\n description: string;\n}\n\n/**\n * Column names in the `audit_logs` table use snake_case.\n * These map to camelCase properties in the `AuditLog` TypeScript type:\n *\n * | Column (snake_case) | AuditLog property (camelCase) |\n * |---------------------|-------------------------------|\n * | table_name | tableName |\n * | record_id | recordId |\n * | actor_id | actorId |\n * | before_data | beforeData |\n * | after_data | afterData |\n * | redacted_fields | redactedFields |\n */\nexport type AuditLogColumnName =\n | \"id\"\n | \"timestamp\"\n | \"table_name\"\n | \"operation\"\n | \"record_id\"\n | \"actor_id\"\n | \"before_data\"\n | \"after_data\"\n | \"diff\"\n | \"label\"\n | \"description\"\n | \"severity\"\n | \"compliance\"\n | \"notify\"\n | \"reason\"\n | \"metadata\"\n | \"redacted_fields\";\n\nexport interface AuditLogSchema {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n}\n\nexport const AUDIT_LOG_SCHEMA: AuditLogSchema = {\n tableName: \"audit_logs\",\n columns: {\n id: {\n type: \"uuid\",\n nullable: false,\n primaryKey: true,\n defaultExpression: \"gen_random_uuid()\",\n description: \"Unique identifier for the audit log entry\",\n },\n timestamp: {\n type: \"timestamptz\",\n nullable: false,\n defaultExpression: \"now()\",\n indexed: true,\n description: \"When the audited event occurred\",\n },\n table_name: {\n type: \"text\",\n nullable: false,\n indexed: true,\n description: \"Name of the table that was modified\",\n },\n operation: {\n type: \"text\",\n nullable: false,\n indexed: true,\n description: \"Type of operation: INSERT, UPDATE, or DELETE\",\n },\n record_id: {\n type: \"text\",\n nullable: false,\n indexed: true,\n description: \"Primary key of the affected record\",\n },\n actor_id: {\n type: \"text\",\n nullable: true,\n indexed: true,\n description: \"Identifier of the user or system that performed the action\",\n },\n before_data: {\n type: \"jsonb\",\n nullable: true,\n description: \"Row snapshot before the mutation (DELETE and UPDATE only)\",\n },\n after_data: {\n type: \"jsonb\",\n nullable: true,\n description: \"Row snapshot after the mutation (INSERT and UPDATE only)\",\n },\n diff: {\n type: \"jsonb\",\n nullable: true,\n description: \"Changed field names for UPDATE operations\",\n },\n label: {\n type: \"text\",\n nullable: true,\n description: \"Human-readable label for the event\",\n },\n description: {\n type: \"text\",\n nullable: true,\n description: \"Detailed description of what happened\",\n },\n severity: {\n type: \"text\",\n nullable: true,\n description: \"Severity level: low, medium, high, or critical\",\n },\n compliance: {\n type: \"jsonb\",\n nullable: true,\n description: \"Compliance framework tags (e.g. soc2, gdpr, hipaa)\",\n },\n notify: {\n type: \"boolean\",\n nullable: true,\n description: \"Whether this event should trigger notifications\",\n },\n reason: {\n type: \"text\",\n nullable: true,\n description: \"Justification or reason for the action\",\n },\n metadata: {\n type: \"jsonb\",\n nullable: true,\n description: \"Arbitrary key-value metadata attached to the event\",\n },\n redacted_fields: {\n type: \"jsonb\",\n nullable: true,\n description: \"Field names that were removed by redaction rules\",\n },\n },\n};\n","/**\n * Escapes LIKE/ILIKE wildcard characters so they match literally.\n * Backslash is the default escape character in PostgreSQL and SQLite LIKE patterns.\n */\nexport function escapeLikePattern(input: string): string {\n return input.replace(/[%_\\\\]/g, \"\\\\$&\");\n}\n","import type { TimeFilter } from \"./query-types.js\";\nimport { parseDuration } from \"./duration.js\";\n\n/**\n * Resolves a `TimeFilter` to an absolute `Date`.\n * Absolute dates are returned as-is; duration strings are parsed relative to now.\n */\nexport function resolveTimeFilter(filter: TimeFilter): Date {\n if (\"date\" in filter && filter.date !== undefined) {\n return filter.date;\n }\n if (\"duration\" in filter && filter.duration !== undefined) {\n return parseDuration(filter.duration);\n }\n throw new Error(\"TimeFilter must have either date or duration\");\n}\n","import type { AuditQueryFilters } from \"./query-types.js\";\nimport { resolveTimeFilter } from \"./time-filter.js\";\nimport { escapeLikePattern } from \"./escape.js\";\n\n/** Column names that support simple equality / IN filtering. */\nexport type AuditFilterField =\n | \"tableName\"\n | \"recordId\"\n | \"actorId\"\n | \"severity\"\n | \"operation\";\n\n/**\n * Intermediate representation for a single filter condition.\n *\n * Adapters map each variant to their native expression type\n * (Drizzle `SQL`, Prisma `QueryState` fragments, etc.).\n */\nexport type FilterCondition =\n | { readonly kind: \"eq\"; readonly field: AuditFilterField; readonly value: string }\n | { readonly kind: \"in\"; readonly field: AuditFilterField; readonly values: string[] }\n | { readonly kind: \"timestampGte\"; readonly value: Date }\n | { readonly kind: \"timestampLte\"; readonly value: Date }\n | { readonly kind: \"search\"; readonly pattern: string }\n | { readonly kind: \"compliance\"; readonly tags: string[] }\n | {\n readonly kind: \"cursor\";\n readonly timestamp: Date;\n readonly id: string;\n readonly sortOrder: \"asc\" | \"desc\";\n };\n\nexport interface InterpretFiltersOptions {\n cursor?: { timestamp: Date; id: string } | undefined;\n sortOrder?: \"asc\" | \"desc\" | undefined;\n}\n\n/**\n * Converts `AuditQueryFilters` + optional cursor into a flat array of\n * `FilterCondition` values. Pure, zero-dep, independently testable.\n *\n * Decision logic (shared across all adapters):\n * - Array filters with 1 element → `eq`, >1 → `in`, 0/undefined → skip\n * - Time filters → resolved to absolute `Date` via `resolveTimeFilter()`\n * - Search text → escaped + wrapped with `%` for LIKE/ILIKE\n * - Compliance tags → passed through (adapter handles JSONB vs json_each)\n * - Cursor → emitted with sort order for adapter-specific comparison\n */\nexport function interpretFilters(\n filters: AuditQueryFilters,\n options?: InterpretFiltersOptions,\n): readonly FilterCondition[] {\n const conditions: FilterCondition[] = [];\n\n // resource filter → tableName eq + optional recordId eq\n if (filters.resource !== undefined) {\n conditions.push({ kind: \"eq\", field: \"tableName\", value: filters.resource.tableName });\n if (filters.resource.recordId !== undefined) {\n conditions.push({ kind: \"eq\", field: \"recordId\", value: filters.resource.recordId });\n }\n }\n\n // actorIds\n pushArrayFilter(conditions, \"actorId\", filters.actorIds);\n\n // severities\n pushArrayFilter(conditions, \"severity\", filters.severities);\n\n // operations\n pushArrayFilter(conditions, \"operation\", filters.operations);\n\n // since → timestampGte\n if (filters.since !== undefined) {\n conditions.push({ kind: \"timestampGte\", value: resolveTimeFilter(filters.since) });\n }\n\n // until → timestampLte\n if (filters.until !== undefined) {\n conditions.push({ kind: \"timestampLte\", value: resolveTimeFilter(filters.until) });\n }\n\n // searchText → escaped + wrapped with %\n if (filters.searchText !== undefined && filters.searchText.length > 0) {\n const escaped = escapeLikePattern(filters.searchText);\n conditions.push({ kind: \"search\", pattern: `%${escaped}%` });\n }\n\n // compliance tags (AND semantics — adapter decides expression)\n if (filters.compliance !== undefined && filters.compliance.length > 0) {\n conditions.push({ kind: \"compliance\", tags: [...filters.compliance] });\n }\n\n // cursor pagination\n if (options?.cursor !== undefined) {\n conditions.push({\n kind: \"cursor\",\n timestamp: options.cursor.timestamp,\n id: options.cursor.id,\n sortOrder: options.sortOrder ?? \"desc\",\n });\n }\n\n return conditions;\n}\n\n/** Pushes an `eq` or `in` condition for an array filter field. */\nfunction pushArrayFilter(\n conditions: FilterCondition[],\n field: AuditFilterField,\n values: readonly string[] | undefined,\n): void {\n if (values === undefined || values.length === 0) {\n return;\n }\n if (values.length === 1) {\n const first = values[0];\n if (first !== undefined) {\n conditions.push({ kind: \"eq\", field, value: first });\n }\n } else {\n conditions.push({ kind: \"in\", field, values: [...values] });\n }\n}\n","import type { AuditStats } from \"./types.js\";\n\n/**\n * Safely converts a count value (number, string, or bigint) to a JavaScript number.\n * Handles PostgreSQL bigint-as-string, Prisma BigInt, and SQLite numeric returns.\n */\nexport function toCount(value: unknown): number {\n if (typeof value === \"number\") {\n return value;\n }\n if (typeof value === \"string\") {\n const n = Number(value);\n return Number.isNaN(n) ? 0 : n;\n }\n if (typeof value === \"bigint\") {\n return Number(value);\n }\n return 0;\n}\n\n/**\n * Assembles raw query result arrays into an `AuditStats` object.\n * Works for both Date objects (PostgreSQL) and strings (SQLite, Prisma raw).\n */\nexport function assembleStats(\n summaryRows: Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n eventsPerDayRows: Array<{ date: unknown; count: unknown }>,\n topActorsRows: Array<{ actorId: unknown; count: unknown }>,\n topTablesRows: Array<{ tableName: unknown; count: unknown }>,\n operationRows: Array<{ operation: unknown; count: unknown }>,\n severityRows: Array<{ severity: unknown; count: unknown }>,\n): AuditStats {\n const summary = summaryRows[0];\n const totalLogs = summary !== undefined ? toCount(summary.totalLogs) : 0;\n const tablesAudited = summary !== undefined ? toCount(summary.tablesAudited) : 0;\n\n const eventsPerDay = eventsPerDayRows.map((row) => ({\n date:\n row.date instanceof Date\n ? row.date.toISOString().split(\"T\")[0] ?? \"\"\n : String(row.date),\n count: toCount(row.count),\n }));\n\n const topActors = topActorsRows.map((row) => ({\n actorId: String(row.actorId),\n count: toCount(row.count),\n }));\n\n const topTables = topTablesRows.map((row) => ({\n tableName: String(row.tableName),\n count: toCount(row.count),\n }));\n\n const operationBreakdown: Record<string, number> = {};\n for (const row of operationRows) {\n operationBreakdown[String(row.operation)] = toCount(row.count);\n }\n\n const severityBreakdown: Record<string, number> = {};\n for (const row of severityRows) {\n severityBreakdown[String(row.severity)] = toCount(row.count);\n }\n\n return {\n totalLogs,\n tablesAudited,\n eventsPerDay,\n topActors,\n topTables,\n operationBreakdown,\n severityBreakdown,\n };\n}\n","import type { AuditOperation, AuditSeverity } from \"./types.js\";\n\nexport const VALID_OPERATIONS: ReadonlySet<string> = new Set([\n \"INSERT\",\n \"UPDATE\",\n \"DELETE\",\n]);\n\nexport const VALID_SEVERITIES: ReadonlySet<string> = new Set([\n \"low\",\n \"medium\",\n \"high\",\n \"critical\",\n]);\n\nexport function isAuditOperation(value: string): value is AuditOperation {\n return VALID_OPERATIONS.has(value);\n}\n\nexport function isAuditSeverity(value: string): value is AuditSeverity {\n return VALID_SEVERITIES.has(value);\n}\n","import type { AuditContext } from \"./types.js\";\nimport { runWithAuditContext } from \"./context.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Function that extracts a string value from a Web Request.\n * Return undefined if the value cannot be extracted.\n * May be sync or async.\n */\nexport type ValueExtractor = (\n request: Request,\n) => string | undefined | Promise<string | undefined>;\n\n/**\n * Describes how to extract audit identity from an incoming HTTP request.\n * All fields are optional — if omitted, the corresponding context field\n * will be undefined.\n */\nexport interface ContextExtractor {\n /** Extracts the actor identifier (user / service account). */\n actor?: ValueExtractor;\n}\n\n/**\n * Options for `handleMiddleware`.\n */\nexport interface MiddlewareHandlerOptions {\n /** Called when an extractor throws. Defaults to silent fail-open. */\n onError?: (error: unknown) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Built-in extractors\n// ---------------------------------------------------------------------------\n\n/**\n * Decode a JWT payload without verification.\n * Uses only Web-standard APIs (atob) — safe on edge runtimes.\n */\nfunction decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split(\".\");\n if (parts.length < 2) {\n return null;\n }\n const payload = parts[1];\n if (!payload) {\n return null;\n }\n let base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n const decoded = atob(base64);\n const parsed: unknown = JSON.parse(decoded);\n if (typeof parsed !== \"object\" || parsed === null) {\n return null;\n }\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Extracts a JWT claim from the `Authorization: Bearer <token>` header.\n *\n * Decodes the token **without** signature verification — signing is the\n * auth layer's responsibility. This is intentional: the audit layer only\n * needs the identity, not proof of authenticity.\n *\n * @param claim - JWT claim name to extract (e.g. `\"sub\"`, `\"tenant_id\"`)\n */\nexport function fromBearerToken(claim: string): ValueExtractor {\n return (request: Request) => {\n const authorization = request.headers.get(\"authorization\");\n if (!authorization) {\n return undefined;\n }\n const parts = authorization.split(\" \");\n if (parts.length !== 2 || parts[0]?.toLowerCase() !== \"bearer\") {\n return undefined;\n }\n const token = parts[1];\n if (!token) {\n return undefined;\n }\n const payload = decodeJwtPayload(token);\n if (!payload) {\n return undefined;\n }\n const value = payload[claim];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n };\n}\n\n/**\n * Extracts a value from a named cookie in the `Cookie` header.\n *\n * The raw cookie value is returned as-is. To resolve it into a user identity,\n * compose with a resolver function (e.g. look up a session in a database).\n *\n * @param cookieName - Name of the cookie to read\n */\nexport function fromCookie(cookieName: string): ValueExtractor {\n return (request: Request) => {\n const cookieHeader = request.headers.get(\"cookie\");\n if (!cookieHeader) {\n return undefined;\n }\n const cookies = cookieHeader.split(\";\");\n for (const cookie of cookies) {\n const separatorIndex = cookie.indexOf(\"=\");\n if (separatorIndex === -1) {\n continue;\n }\n const name = cookie.slice(0, separatorIndex).trim();\n if (name === cookieName) {\n const value = cookie.slice(separatorIndex + 1).trim();\n return value.length > 0 ? value : undefined;\n }\n }\n return undefined;\n };\n}\n\n/**\n * Extracts a value from a custom request header.\n *\n * @param headerName - Header name (case-insensitive per the Web API)\n */\nexport function fromHeader(headerName: string): ValueExtractor {\n return (request: Request) => {\n const value = request.headers.get(headerName);\n if (!value || value.trim().length === 0) {\n return undefined;\n }\n return value.trim();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Default extractor\n// ---------------------------------------------------------------------------\n\n/** Default extractor: reads `sub` from `Authorization: Bearer <jwt>` as actor. */\nexport const defaultExtractor: ContextExtractor = {\n actor: fromBearerToken(\"sub\"),\n};\n\n// ---------------------------------------------------------------------------\n// Shared middleware handler\n// ---------------------------------------------------------------------------\n\n/**\n * Runs an extractor safely, catching errors and returning undefined on failure.\n */\nexport async function safeExtract(\n extractor: ValueExtractor | undefined,\n request: Request,\n onError: ((error: unknown) => void) | undefined,\n): Promise<string | undefined> {\n if (!extractor) {\n return undefined;\n }\n try {\n return await extractor(request);\n } catch (error: unknown) {\n if (onError) {\n onError(error);\n }\n return undefined;\n }\n}\n\n/**\n * Shared middleware handler used by all framework adapters.\n *\n * 1. Extracts actor from the request using the provided extractor\n * 2. Builds an `AuditContext` (undefined if nothing was extracted)\n * 3. Wraps `next()` inside `runWithAuditContext()` if context is available\n * 4. Calls `next()` without context if extraction yields nothing\n *\n * Extraction failures never break the request (fail open).\n */\nexport async function handleMiddleware(\n extractor: ContextExtractor,\n request: Request,\n next: () => Promise<void>,\n options: MiddlewareHandlerOptions = {},\n): Promise<void> {\n const actorId = await safeExtract(extractor.actor, request, options.onError);\n\n if (actorId === undefined) {\n await next();\n return;\n }\n\n const auditContext: AuditContext = { actorId };\n\n await runWithAuditContext(auditContext, () => next());\n}\n"],"mappings":"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","names":["VALID_OPERATIONS","VALID_SEVERITIES"]}