@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.cjs CHANGED
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  betterAudit: () => betterAudit,
26
26
  createAuditApi: () => createAuditApi,
27
27
  createAuditConsoleEndpoints: () => createAuditConsoleEndpoints,
28
+ createExportResponse: () => createExportResponse,
28
29
  fromBearerToken: () => fromBearerToken,
29
30
  fromCookie: () => fromCookie,
30
31
  fromHeader: () => fromHeader,
@@ -1159,6 +1160,65 @@ function createAuditConsoleEndpoints(api) {
1159
1160
  ];
1160
1161
  }
1161
1162
 
1163
+ // src/export-response.ts
1164
+ function contentTypeForFormat(format, jsonStyle) {
1165
+ if (format === "csv") {
1166
+ return "text/csv; charset=utf-8";
1167
+ }
1168
+ if (jsonStyle === "ndjson") {
1169
+ return "application/x-ndjson; charset=utf-8";
1170
+ }
1171
+ return "application/json; charset=utf-8";
1172
+ }
1173
+ function fileExtensionForFormat(format, jsonStyle) {
1174
+ if (format === "csv") {
1175
+ return ".csv";
1176
+ }
1177
+ if (jsonStyle === "ndjson") {
1178
+ return ".ndjson";
1179
+ }
1180
+ return ".json";
1181
+ }
1182
+ function formatDate(date) {
1183
+ const year = date.getFullYear();
1184
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1185
+ const day = String(date.getDate()).padStart(2, "0");
1186
+ return `${year}-${month}-${day}`;
1187
+ }
1188
+ function sanitiseFilename(name) {
1189
+ return name.replace(/["\\\r\n\x00-\x1f]/g, "");
1190
+ }
1191
+ function createExportResponse(executor, options = {}) {
1192
+ const format = options.format ?? "csv";
1193
+ const jsonStyle = options.jsonStyle ?? "ndjson";
1194
+ const stem = options.filename ?? `audit-export-${formatDate(/* @__PURE__ */ new Date())}`;
1195
+ const extension = fileExtensionForFormat(format, jsonStyle);
1196
+ const fullFilename = sanitiseFilename(`${stem}${extension}`);
1197
+ const contentType = contentTypeForFormat(format, jsonStyle);
1198
+ const transform = new TransformStream();
1199
+ const encoder = new TextEncoderStream();
1200
+ const readable = transform.readable.pipeThrough(encoder);
1201
+ runExport(executor, {
1202
+ format,
1203
+ jsonStyle,
1204
+ output: transform.writable,
1205
+ ...options.batchSize !== void 0 && { batchSize: options.batchSize },
1206
+ ...options.csvDelimiter !== void 0 && {
1207
+ csvDelimiter: options.csvDelimiter
1208
+ },
1209
+ ...options.query !== void 0 && { query: options.query }
1210
+ }).catch(() => {
1211
+ });
1212
+ return new Response(readable, {
1213
+ status: 200,
1214
+ headers: {
1215
+ "Content-Type": contentType,
1216
+ "Content-Disposition": `attachment; filename="${fullFilename}"`,
1217
+ "Cache-Control": "no-cache"
1218
+ }
1219
+ });
1220
+ }
1221
+
1162
1222
  // src/retention.ts
1163
1223
  function validateRetentionPolicy(policy) {
1164
1224
  if (!Number.isInteger(policy.days) || !Number.isFinite(policy.days) || policy.days <= 0) {
@@ -1183,6 +1243,11 @@ function betterAudit(config) {
1183
1243
  if (config.retention !== void 0) {
1184
1244
  validateRetentionPolicy(config.retention);
1185
1245
  }
1246
+ if (config.asyncWrite === true && config.onError === void 0) {
1247
+ console.warn(
1248
+ "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."
1249
+ );
1250
+ }
1186
1251
  const { database } = config;
1187
1252
  const auditTables = new Set(config.auditTables);
1188
1253
  if (config.retention?.tables !== void 0) {
@@ -1323,6 +1388,15 @@ function betterAudit(config) {
1323
1388
  const queryLogs = database.queryLogs;
1324
1389
  return runExport((spec) => queryLogs(spec), options);
1325
1390
  }
1391
+ function exportResponse(options) {
1392
+ if (database.queryLogs === void 0) {
1393
+ throw new Error(
1394
+ "audit.exportResponse() requires a database adapter that implements queryLogs(). Check that your ORM adapter supports querying."
1395
+ );
1396
+ }
1397
+ const queryLogs = database.queryLogs;
1398
+ return createExportResponse((spec) => queryLogs(spec), options);
1399
+ }
1326
1400
  if (config.console) {
1327
1401
  const api = createAuditApi(database, registry, config.maxQueryLimit);
1328
1402
  const endpoints = createAuditConsoleEndpoints(api);
@@ -1332,7 +1406,7 @@ function betterAudit(config) {
1332
1406
  endpoints
1333
1407
  });
1334
1408
  }
1335
- return { captureLog, query, export: exportLogs, withContext, enrich, onBeforeLog, onAfterLog, retentionPolicy };
1409
+ return { captureLog, query, export: exportLogs, exportResponse, withContext, enrich, onBeforeLog, onAfterLog, retentionPolicy };
1336
1410
  }
1337
1411
 
1338
1412
  // src/audit-log-schema.ts
@@ -1541,6 +1615,7 @@ async function handleMiddleware(extractor, request, next, options = {}) {
1541
1615
  betterAudit,
1542
1616
  createAuditApi,
1543
1617
  createAuditConsoleEndpoints,
1618
+ createExportResponse,
1544
1619
  fromBearerToken,
1545
1620
  fromCookie,
1546
1621
  fromHeader,