@usebetterdev/audit-core 0.5.0-beta.1 → 0.6.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.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { ConsoleRegistration, ConsoleProductEndpoint } from '@usebetterdev/console-contract';
2
- export { ConsoleRegistration } from '@usebetterdev/console-contract';
1
+ import { RetentionPolicy } from '@usebetterdev/contract/config-types';
2
+ export { RetentionPolicy } from '@usebetterdev/contract/config-types';
3
+ import { ConsoleRegistration, ConsoleProductEndpoint } from '@usebetterdev/contract';
4
+ export { ConsoleRegistration } from '@usebetterdev/contract';
3
5
 
4
6
  /** Identifies a specific resource (table + optional record). */
5
7
  interface ResourceFilter {
@@ -104,14 +106,33 @@ declare class AuditQueryBuilder {
104
106
  list(): Promise<AuditQueryResult>;
105
107
  }
106
108
 
107
- type AuditOperation = "INSERT" | "UPDATE" | "DELETE";
108
- /** Retention policy controlling automatic and CLI-driven purge of old audit logs. */
109
- interface RetentionPolicy {
110
- /** Purge entries older than this many days. Must be a positive integer. */
111
- days: number;
112
- /** When set, only purge logs for these specific tables. */
113
- tables?: string[];
109
+ /** Options for `createExportResponse()`. */
110
+ interface ExportResponseOptions {
111
+ /** Output format. Default: `"csv"`. */
112
+ format?: "csv" | "json";
113
+ /** JSON output style. Default: `"ndjson"`. */
114
+ jsonStyle?: "ndjson" | "array";
115
+ /** Rows per database round-trip. */
116
+ batchSize?: number;
117
+ /** CSV delimiter character. */
118
+ csvDelimiter?: string;
119
+ /** Optional query builder to filter exported entries. */
120
+ query?: AuditQueryBuilder;
121
+ /** Custom filename stem (without extension). Default: `"audit-export-YYYY-MM-DD"`. */
122
+ filename?: string;
114
123
  }
124
+ /**
125
+ * Create a streaming `Response` from the audit export engine.
126
+ *
127
+ * Uses `TransformStream` + `TextEncoderStream` to bridge the string-based
128
+ * `runExport()` output to a binary `ReadableStream` suitable for `new Response()`.
129
+ * The export runs asynchronously — the Response is returned immediately so the
130
+ * first byte can arrive before all rows are read.
131
+ */
132
+ declare function createExportResponse(executor: QueryExecutor, options?: ExportResponseOptions): Response;
133
+
134
+ type AuditOperation = "INSERT" | "UPDATE" | "DELETE";
135
+
115
136
  type AuditSeverity = "low" | "medium" | "high" | "critical";
116
137
  /** A single audit log entry as stored in the database. */
117
138
  interface AuditLog {
@@ -333,6 +354,14 @@ interface BetterAuditInstance {
333
354
  * regardless of export size. Requires `queryLogs` on the database adapter.
334
355
  */
335
356
  export(options: ExportOptions): Promise<ExportResult>;
357
+ /**
358
+ * Create a streaming HTTP `Response` for downloading audit logs.
359
+ *
360
+ * Returns a standard `Response` with correct Content-Type, Content-Disposition,
361
+ * and Cache-Control headers. The body is a streaming `ReadableStream` backed
362
+ * by the export engine. Requires `queryLogs` on the database adapter.
363
+ */
364
+ exportResponse(options?: ExportResponseOptions): Response;
336
365
  withContext<T>(context: AuditContext, fn: () => Promise<T>): Promise<T>;
337
366
  /**
338
367
  * Register an enrichment config for a table/operation pair.
@@ -568,4 +597,4 @@ declare function fromHeader(headerName: string): ValueExtractor;
568
597
  */
569
598
  declare function handleMiddleware(extractor: ContextExtractor, request: Request, next: () => Promise<void>, options?: MiddlewareHandlerOptions): Promise<void>;
570
599
 
571
- export { AUDIT_LOG_SCHEMA, type AfterLogHook, type AuditApi, type AuditContext, type AuditDatabaseAdapter, type AuditLog, type AuditLogColumnName, type AuditLogSchema, type AuditOperation, AuditQueryBuilder, type AuditQueryFilters, type AuditQueryResult, type AuditQuerySpec, type AuditSeverity, type AuditStats, type BeforeLogHook, type BetterAuditConfig, type BetterAuditInstance, type CaptureLogInput, type ColumnDefinition, type ColumnType, type ConsoleQueryFilters, type ConsoleQueryResult, type ContextExtractor, type EnrichmentConfig, type EnrichmentDescriptionContext, type EnrichmentSummary, type ExportOptions, type ExportResult, type MiddlewareHandlerOptions, type QueryExecutor, type ResourceFilter, type RetentionPolicy, type TimeFilter, type ValueExtractor, betterAudit, createAuditApi, createAuditConsoleEndpoints, fromBearerToken, fromCookie, fromHeader, getAuditContext, handleMiddleware, mergeAuditContext, normalizeInput, parseDuration, runExport, runWithAuditContext };
600
+ export { AUDIT_LOG_SCHEMA, type AfterLogHook, type AuditApi, type AuditContext, type AuditDatabaseAdapter, type AuditLog, type AuditLogColumnName, type AuditLogSchema, type AuditOperation, AuditQueryBuilder, type AuditQueryFilters, type AuditQueryResult, type AuditQuerySpec, type AuditSeverity, type AuditStats, type BeforeLogHook, type BetterAuditConfig, type BetterAuditInstance, type CaptureLogInput, type ColumnDefinition, type ColumnType, type ConsoleQueryFilters, type ConsoleQueryResult, type ContextExtractor, type EnrichmentConfig, type EnrichmentDescriptionContext, type EnrichmentSummary, type ExportOptions, type ExportResponseOptions, type ExportResult, type MiddlewareHandlerOptions, type QueryExecutor, type ResourceFilter, type TimeFilter, type ValueExtractor, betterAudit, createAuditApi, createAuditConsoleEndpoints, createExportResponse, fromBearerToken, fromCookie, fromHeader, getAuditContext, handleMiddleware, mergeAuditContext, normalizeInput, parseDuration, runExport, runWithAuditContext };
package/dist/index.js CHANGED
@@ -1119,6 +1119,65 @@ function createAuditConsoleEndpoints(api) {
1119
1119
  ];
1120
1120
  }
1121
1121
 
1122
+ // src/export-response.ts
1123
+ function contentTypeForFormat(format, jsonStyle) {
1124
+ if (format === "csv") {
1125
+ return "text/csv; charset=utf-8";
1126
+ }
1127
+ if (jsonStyle === "ndjson") {
1128
+ return "application/x-ndjson; charset=utf-8";
1129
+ }
1130
+ return "application/json; charset=utf-8";
1131
+ }
1132
+ function fileExtensionForFormat(format, jsonStyle) {
1133
+ if (format === "csv") {
1134
+ return ".csv";
1135
+ }
1136
+ if (jsonStyle === "ndjson") {
1137
+ return ".ndjson";
1138
+ }
1139
+ return ".json";
1140
+ }
1141
+ function formatDate(date) {
1142
+ const year = date.getFullYear();
1143
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1144
+ const day = String(date.getDate()).padStart(2, "0");
1145
+ return `${year}-${month}-${day}`;
1146
+ }
1147
+ function sanitiseFilename(name) {
1148
+ return name.replace(/["\\\r\n\x00-\x1f]/g, "");
1149
+ }
1150
+ function createExportResponse(executor, options = {}) {
1151
+ const format = options.format ?? "csv";
1152
+ const jsonStyle = options.jsonStyle ?? "ndjson";
1153
+ const stem = options.filename ?? `audit-export-${formatDate(/* @__PURE__ */ new Date())}`;
1154
+ const extension = fileExtensionForFormat(format, jsonStyle);
1155
+ const fullFilename = sanitiseFilename(`${stem}${extension}`);
1156
+ const contentType = contentTypeForFormat(format, jsonStyle);
1157
+ const transform = new TransformStream();
1158
+ const encoder = new TextEncoderStream();
1159
+ const readable = transform.readable.pipeThrough(encoder);
1160
+ runExport(executor, {
1161
+ format,
1162
+ jsonStyle,
1163
+ output: transform.writable,
1164
+ ...options.batchSize !== void 0 && { batchSize: options.batchSize },
1165
+ ...options.csvDelimiter !== void 0 && {
1166
+ csvDelimiter: options.csvDelimiter
1167
+ },
1168
+ ...options.query !== void 0 && { query: options.query }
1169
+ }).catch(() => {
1170
+ });
1171
+ return new Response(readable, {
1172
+ status: 200,
1173
+ headers: {
1174
+ "Content-Type": contentType,
1175
+ "Content-Disposition": `attachment; filename="${fullFilename}"`,
1176
+ "Cache-Control": "no-cache"
1177
+ }
1178
+ });
1179
+ }
1180
+
1122
1181
  // src/retention.ts
1123
1182
  function validateRetentionPolicy(policy) {
1124
1183
  if (!Number.isInteger(policy.days) || !Number.isFinite(policy.days) || policy.days <= 0) {
@@ -1143,6 +1202,11 @@ function betterAudit(config) {
1143
1202
  if (config.retention !== void 0) {
1144
1203
  validateRetentionPolicy(config.retention);
1145
1204
  }
1205
+ if (config.asyncWrite === true && config.onError === void 0) {
1206
+ console.warn(
1207
+ "audit: asyncWrite is enabled without onError \u2014 write failures will only log to console.error. Set onError to route failures to your error tracking system."
1208
+ );
1209
+ }
1146
1210
  const { database } = config;
1147
1211
  const auditTables = new Set(config.auditTables);
1148
1212
  if (config.retention?.tables !== void 0) {
@@ -1283,6 +1347,15 @@ function betterAudit(config) {
1283
1347
  const queryLogs = database.queryLogs;
1284
1348
  return runExport((spec) => queryLogs(spec), options);
1285
1349
  }
1350
+ function exportResponse(options) {
1351
+ if (database.queryLogs === void 0) {
1352
+ throw new Error(
1353
+ "audit.exportResponse() requires a database adapter that implements queryLogs(). Check that your ORM adapter supports querying."
1354
+ );
1355
+ }
1356
+ const queryLogs = database.queryLogs;
1357
+ return createExportResponse((spec) => queryLogs(spec), options);
1358
+ }
1286
1359
  if (config.console) {
1287
1360
  const api = createAuditApi(database, registry, config.maxQueryLimit);
1288
1361
  const endpoints = createAuditConsoleEndpoints(api);
@@ -1292,7 +1365,7 @@ function betterAudit(config) {
1292
1365
  endpoints
1293
1366
  });
1294
1367
  }
1295
- return { captureLog, query, export: exportLogs, withContext, enrich, onBeforeLog, onAfterLog, retentionPolicy };
1368
+ return { captureLog, query, export: exportLogs, exportResponse, withContext, enrich, onBeforeLog, onAfterLog, retentionPolicy };
1296
1369
  }
1297
1370
 
1298
1371
  // src/audit-log-schema.ts
@@ -1500,6 +1573,7 @@ export {
1500
1573
  betterAudit,
1501
1574
  createAuditApi,
1502
1575
  createAuditConsoleEndpoints,
1576
+ createExportResponse,
1503
1577
  fromBearerToken,
1504
1578
  fromCookie,
1505
1579
  fromHeader,