UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

375 lines 12.2 kB
import { type ProviderInstrumentationTarget } from "../providers/instrumentation.js"; /** * The normalized category of actor that caused application activity. * * Actors describe who or what performed work for request context, * authorization, audit logs, and diagnostics. They do not authenticate the * request by themselves. */ export type ActivityActorType = "anonymous" | "service" | "system" | "user"; /** * Whether an audited activity completed successfully or intentionally records * a failed attempt. */ export type AuditOutcome = "success" | "failure"; /** * JSON-like metadata values accepted by activity and audit descriptors. * * Metadata should stay intentionally small. Prefer stable IDs and short labels * over full request bodies, secrets, PHI, or PII. */ export type ActivityMetadataValue = ActivityMetadataValue[] | boolean | null | number | string | { [key: string]: ActivityMetadataValue | undefined; }; /** * Additional structured metadata attached to actors, tenants, resources, or * audit entries. */ export type ActivityMetadata = Record<string, ActivityMetadataValue | undefined>; /** * A normalized descriptor for the person, service, or system process that * caused application activity. * * Store this on application context as `ctx.actor` so routes, use cases, jobs, * policies, audit logs, and devtools share one identity shape. */ export interface ActivityActor { /** * The actor category. */ type: ActivityActorType; /** * Stable application ID for this actor, when known. */ id?: string; /** * Human-readable label for diagnostics and audit views. */ displayName?: string; /** * Small, redaction-safe metadata about the actor. */ metadata?: ActivityMetadata; } /** * A normalized tenant/account/workspace scope for activity. * * This is a context value used by audit logs, authorization, and diagnostics. * It does not create, load, or persist a tenant record. */ export interface ActivityTenant { /** * Stable tenant/account/workspace ID. */ id: string; /** * Optional human-readable tenant slug. */ slug?: string; /** * Small, redaction-safe metadata about the tenant. */ metadata?: ActivityMetadata; } /** * A normalized descriptor for the business object affected by an audit entry. */ export interface ActivityResource { /** * Resource type, usually a singular domain noun such as "post", "invoice", * or "appointment". */ type: string; /** * Stable resource ID, when known. */ id?: string; /** * Human-readable resource label for audit views. */ name?: string; /** * Small, redaction-safe metadata about the resource. */ metadata?: ActivityMetadata; } /** * A normalized audit/activity log entry. * * Application code usually records entries through an audit port wrapped with * `createAmbientAuditLog(...)` from `@beignet/core/server`, which fills * missing actor, tenant, request ID, and trace ID fields from the ambient * request context at record time. Durability depends on the `AuditLogPort` * implementation. */ export interface AuditLogEntry { /** * Stable action name, usually namespaced by feature and workflow. * * @example "posts.publish" */ action: string; /** * Actor that caused the activity. */ actor: ActivityActor; /** * Timestamp assigned when the activity occurred. */ occurredAt: Date; /** * Whether the activity succeeded or records a failed attempt. */ outcome: AuditOutcome; /** * Small, redaction-safe metadata about the activity. */ metadata?: ActivityMetadata; /** * Optional human-readable audit message. */ message?: string; /** * Request correlation ID, when the activity originated from a request or * background context. */ requestId?: string; /** * Business resource affected by the activity. */ resource?: ActivityResource; /** * Tenant/account/workspace scope for the activity. */ tenant?: ActivityTenant; /** * Trace correlation ID, when tracing is enabled. */ traceId?: string; } /** * Input accepted by `AuditLogPort.record(...)`. * * `actor`, `occurredAt`, and `outcome` are optional at call sites. Wrappers * such as `createAmbientAuditLog(...)` fill a missing actor from the ambient * request context. Adapters that store audit entries should call * `normalizeAuditLogEntry(...)` before persistence or otherwise apply * equivalent defaults; entries without an actor normalize to an anonymous * actor. */ export type AuditLogEntryInput = Omit<AuditLogEntry, "actor" | "occurredAt" | "outcome"> & { actor?: ActivityActor; occurredAt?: Date; outcome?: AuditOutcome; }; /** * App-facing port for audit/activity logging. * * Production implementations should usually write to a durable database table, * append-only log, or external audit service. Tests can use an in-memory * adapter. Application code should depend on this interface, not on a concrete * audit provider. */ export interface AuditLogPort { /** * Persist or capture an audit entry. */ record(entry: AuditLogEntryInput): Promise<void> | void; } /** * In-memory audit log port used by tests and local examples. */ export interface MemoryAuditLogPort extends AuditLogPort { /** * Captured, normalized, redacted audit entries. */ entries: AuditLogEntry[]; } /** * Options shared by audit log wrappers and in-memory audit adapters. */ export interface AuditLogOptions { /** * Optional final redaction/customization step applied after Beignet's default * metadata redaction. */ redact?: (entry: AuditLogEntry) => AuditLogEntry; } /** * Create an anonymous actor descriptor for unauthenticated activity. * * This helper only creates a normalized context value. It does not perform * authentication. * * @example * ```ts * const actor = createAnonymousActor(); * ``` * * @param options - Optional display name or metadata to include. * @returns An activity actor with `type: "anonymous"`. */ export declare function createAnonymousActor(options?: Omit<ActivityActor, "type">): ActivityActor; /** * Create a service actor descriptor for work initiated by another service or * integration. * * This is useful for webhooks, internal service calls, or integration-driven * background jobs. * * @example * ```ts * const actor = createServiceActor("stripe-webhook"); * ``` * * @param id - Stable service or integration ID. * @param options - Optional display name or metadata to include. * @returns An activity actor with `type: "service"`. */ export declare function createServiceActor(id: string, options?: Omit<ActivityActor, "type" | "id">): ActivityActor; /** * Create a system actor descriptor for framework or app-owned background work. * * Use this for schedules, scripts, maintenance jobs, and other work that * is not directly caused by a user or external service. * * @example * ```ts * const actor = createSystemActor("nightly-maintenance"); * ``` * * @param id - Stable system actor ID. Defaults to `"system"`. * @param options - Optional display name or metadata to include. * @returns An activity actor with `type: "system"`. */ export declare function createSystemActor(id?: string, options?: Omit<ActivityActor, "type" | "id">): ActivityActor; /** * Create a user actor descriptor for authenticated user activity. * * This helper only normalizes a known user ID for context, authorization, * audit, and diagnostics. It does not verify a session or load a user record. * Resolve authentication first, then call this helper with the authenticated * user ID. * * @example * ```ts * const actor = createUserActor(session.user.id, { * displayName: session.user.name, * }); * ``` * * @param id - Stable application user ID. * @param options - Optional display name or metadata to include. * @returns An activity actor with `type: "user"`. */ export declare function createUserActor(id: string, options?: Omit<ActivityActor, "type" | "id">): ActivityActor; /** * Create a tenant/account/workspace descriptor for request or background * context. * * This helper only creates a normalized context value used by audit, * authorization, logs, and diagnostics. It does not create, load, or persist a * tenant record. * * @example * ```ts * const tenant = createTenant(session.organizationId, { * slug: session.organizationSlug, * }); * ``` * * @param id - Stable tenant/account/workspace ID. * @param options - Optional slug or metadata to include. * @returns A normalized activity tenant descriptor. */ export declare function createTenant(id: string, options?: Omit<ActivityTenant, "id">): ActivityTenant; /** * Fill default audit fields for an input entry. * * @param entry - Partial audit entry accepted by `AuditLogPort.record(...)`. * @returns A complete audit entry with `actor`, `occurredAt`, and `outcome` * populated. Entries without an actor default to an anonymous actor. */ export declare function normalizeAuditLogEntry(entry: AuditLogEntryInput): AuditLogEntry; /** * Redact metadata on an already-normalized audit entry. * * This redacts metadata values on the entry, actor, tenant, and resource using * the default redaction rules from `redactValue(...)`. * * @param entry - Audit entry to redact. * @returns A shallow copy with redacted metadata fields. */ export declare function redactAuditLogEntry(entry: AuditLogEntry): AuditLogEntry; /** * Wrap an audit log port with default audit metadata redaction. * * Use this around durable adapters so application code can record entries * without each call site remembering to redact metadata. * * @param audit - Underlying audit log port to write to after redaction. * @param options - Optional final redaction/customization hook. * @returns An audit log port that normalizes and redacts before writing. */ export declare function createRedactedAuditLog(audit: AuditLogPort, options?: AuditLogOptions): AuditLogPort; /** * Options for wrapping an audit log with instrumentation emission. */ export interface InstrumentedAuditLogOptions { /** * Durable audit log to write first. */ audit: AuditLogPort; /** * Instrumentation sink, port, or ports object. Pass the app ports object so * the sink (`ports.instrumentation`, then `ports.devtools`) is resolved * lazily on each write and observes provider startup order. */ instrumentation?: ProviderInstrumentationTarget; /** * Whether to emit instrumentation events. Defaults to true. */ emit?: boolean; /** * Optional app-owned redactor applied after Beignet's audit redaction. */ redact?: (entry: AuditLogEntry) => AuditLogEntry; } /** * Wrap an audit log so durable audit writes also appear in instrumentation * sinks such as devtools. * * Instrumentation failures are ignored so audit persistence remains the * source of truth. * * @example * ```ts * const audit = createInstrumentedAuditLog({ * audit: createDrizzleSqliteAuditLogPort(db), * instrumentation: ports, * }); * ``` */ export declare function createInstrumentedAuditLog(options: InstrumentedAuditLogOptions): AuditLogPort; /** * Create an in-memory audit log for tests and local examples. * * Entries are normalized and redacted before being pushed into the shared * `entries` array. * * @example * ```ts * const audit = createMemoryAuditLog(); * await audit.record({ * action: "posts.publish", * actor: createUserActor("user_1"), * }); * expect(audit.entries).toHaveLength(1); * ``` * * @param entries - Optional backing array, useful when tests need shared state. * @param options - Optional final redaction/customization hook. * @returns An in-memory audit log port with captured `entries`. */ export declare function createMemoryAuditLog(entries?: AuditLogEntry[], options?: AuditLogOptions): MemoryAuditLogPort; //# sourceMappingURL=audit.d.ts.map