UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

49 lines (46 loc) 1.81 kB
import type { AuditLogPort } from "../ports/index.js"; import { getActiveRequestContext } from "./request-context.js"; /** * Wrap an audit log port so missing `actor`, `tenant`, `requestId`, and * `traceId` fields are filled from the ambient request context at record time. * * The server keeps the ambient context current for requests (including * identity elevated by hooks) and for service contexts created for jobs, * listeners, schedules, and tasks. Because enrichment happens when * `record(...)` runs, the wrapper also works for ports rebuilt per * transaction inside a unit of work — wrap both the top-level audit port and * the per-transaction rebuild. * * Fields provided on the entry always win; only missing fields are filled. * When no ambient context is active (for example on runtimes without * `AsyncLocalStorage` propagation), entries pass through unchanged. * * @example * ```ts * const audit = createAmbientAuditLog( * createInstrumentedAuditLog({ * audit: createDrizzleSqliteAuditLogPort(db), * instrumentation: ports, * }), * ); * await audit.record({ action: "posts.publish", resource: { type: "post", id } }); * ``` * * @param audit - Underlying audit log port to write enriched entries to. * @returns An audit log port that fills missing context fields before writing. */ export function createAmbientAuditLog(audit: AuditLogPort): AuditLogPort { return { record(entry) { const context = getActiveRequestContext(); if (!context) return audit.record(entry); return audit.record({ ...entry, actor: entry.actor ?? context.actor, tenant: entry.tenant ?? context.tenant, requestId: entry.requestId ?? context.requestId, traceId: entry.traceId ?? context.traceId, }); }, }; }