@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.cjs CHANGED
@@ -22,20 +22,33 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AUDIT_LOG_SCHEMA: () => AUDIT_LOG_SCHEMA,
24
24
  AuditQueryBuilder: () => AuditQueryBuilder,
25
+ VALID_OPERATIONS: () => VALID_OPERATIONS2,
26
+ VALID_SEVERITIES: () => VALID_SEVERITIES2,
27
+ assembleStats: () => assembleStats,
25
28
  betterAudit: () => betterAudit,
26
29
  createAuditApi: () => createAuditApi,
27
30
  createAuditConsoleEndpoints: () => createAuditConsoleEndpoints,
28
31
  createExportResponse: () => createExportResponse,
32
+ decodeCursor: () => decodeCursor,
33
+ defaultExtractor: () => defaultExtractor,
34
+ encodeCursor: () => encodeCursor,
35
+ escapeLikePattern: () => escapeLikePattern,
29
36
  fromBearerToken: () => fromBearerToken,
30
37
  fromCookie: () => fromCookie,
31
38
  fromHeader: () => fromHeader,
32
39
  getAuditContext: () => getAuditContext,
33
40
  handleMiddleware: () => handleMiddleware,
41
+ interpretFilters: () => interpretFilters,
42
+ isAuditOperation: () => isAuditOperation,
43
+ isAuditSeverity: () => isAuditSeverity,
34
44
  mergeAuditContext: () => mergeAuditContext,
35
45
  normalizeInput: () => normalizeInput,
36
46
  parseDuration: () => parseDuration,
47
+ resolveTimeFilter: () => resolveTimeFilter,
37
48
  runExport: () => runExport,
38
- runWithAuditContext: () => runWithAuditContext
49
+ runWithAuditContext: () => runWithAuditContext,
50
+ safeExtract: () => safeExtract,
51
+ toCount: () => toCount
39
52
  });
40
53
  module.exports = __toCommonJS(index_exports);
41
54
 
@@ -766,8 +779,41 @@ async function runExport(executor, options) {
766
779
  return { rowCount };
767
780
  }
768
781
 
782
+ // src/cursor.ts
783
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
784
+ function encodeCursor(timestamp, id) {
785
+ const payload = JSON.stringify({ t: timestamp.toISOString(), i: id });
786
+ return btoa(payload);
787
+ }
788
+ function decodeCursor(cursor) {
789
+ let parsed;
790
+ try {
791
+ parsed = JSON.parse(atob(cursor));
792
+ } catch {
793
+ throw new Error("Invalid cursor: failed to decode");
794
+ }
795
+ if (typeof parsed !== "object" || parsed === null || !("t" in parsed) || !("i" in parsed)) {
796
+ throw new Error("Invalid cursor: missing required fields");
797
+ }
798
+ const record = parsed;
799
+ const t = record["t"];
800
+ const i = record["i"];
801
+ if (typeof t !== "string" || typeof i !== "string") {
802
+ throw new Error("Invalid cursor: fields must be strings");
803
+ }
804
+ const timestamp = new Date(t);
805
+ if (isNaN(timestamp.getTime())) {
806
+ throw new Error("Invalid cursor: invalid timestamp");
807
+ }
808
+ if (!UUID_PATTERN.test(i)) {
809
+ throw new Error("Invalid cursor: id must be a valid UUID");
810
+ }
811
+ return { timestamp, id: i };
812
+ }
813
+
769
814
  // src/audit-api.ts
770
- var VALID_SEVERITIES = /* @__PURE__ */ new Set(["low", "medium", "high", "critical"]);
815
+ var SEVERITY_VALUES = ["low", "medium", "high", "critical"];
816
+ var VALID_SEVERITIES = new Set(SEVERITY_VALUES);
771
817
  var VALID_OPERATIONS = /* @__PURE__ */ new Set(["INSERT", "UPDATE", "DELETE"]);
772
818
  function toTimeFilter(date) {
773
819
  return { date };
@@ -779,18 +825,23 @@ function buildQuerySpec(filters, effectiveLimit) {
779
825
  limit
780
826
  };
781
827
  if (filters.tableName !== void 0) {
782
- spec.filters.resource = { tableName: filters.tableName };
828
+ spec.filters.resource = filters.recordId !== void 0 ? { tableName: filters.tableName, recordId: filters.recordId } : { tableName: filters.tableName };
783
829
  }
784
830
  if (filters.actorId !== void 0) {
785
- spec.filters.actorIds = [filters.actorId];
831
+ const raw = filters.actorId.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
832
+ spec.filters.actorIds = [...new Set(raw)];
786
833
  }
787
834
  if (filters.severity !== void 0) {
788
- if (!VALID_SEVERITIES.has(filters.severity)) {
789
- throw new Error(
790
- `Invalid severity "${filters.severity}". Must be one of: low, medium, high, critical`
791
- );
835
+ const raw = filters.severity.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
836
+ const unique = [...new Set(raw)];
837
+ for (const value of unique) {
838
+ if (!VALID_SEVERITIES.has(value)) {
839
+ throw new Error(
840
+ `Invalid severity "${value}". Must be one of: low, medium, high, critical`
841
+ );
842
+ }
792
843
  }
793
- spec.filters.severities = [filters.severity];
844
+ spec.filters.severities = unique.filter((v) => VALID_SEVERITIES.has(v));
794
845
  }
795
846
  if (filters.compliance !== void 0) {
796
847
  spec.filters.compliance = [filters.compliance];
@@ -816,6 +867,12 @@ function buildQuerySpec(filters, effectiveLimit) {
816
867
  if (filters.cursor !== void 0) {
817
868
  spec.cursor = filters.cursor;
818
869
  }
870
+ if (filters.direction === "before") {
871
+ spec.sortOrder = "asc";
872
+ }
873
+ if (filters.order !== void 0) {
874
+ spec.sortOrder = filters.order;
875
+ }
819
876
  return spec;
820
877
  }
821
878
  function createAuditApi(adapter, registry, maxQueryLimit) {
@@ -854,12 +911,66 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
854
911
  }
855
912
  async function queryLogs(filters) {
856
913
  const queryFn = requireQueryLogs();
857
- const spec = buildQuerySpec(filters ?? {}, effectiveLimit);
914
+ const resolved = filters ?? {};
915
+ const spec = buildQuerySpec(resolved, effectiveLimit);
858
916
  const result = await queryFn(spec);
917
+ if (resolved.direction === "before") {
918
+ const entries = [...result.entries].reverse();
919
+ const lastEntry = entries[entries.length - 1];
920
+ return {
921
+ entries,
922
+ // Adapter's "next" in ASC = newer entries = our prev
923
+ ...result.nextCursor !== void 0 && { prevCursor: result.nextCursor },
924
+ hasPrevPage: result.nextCursor !== void 0,
925
+ // Since we started from a cursor, there are older entries behind it
926
+ ...resolved.cursor !== void 0 && lastEntry !== void 0 && { nextCursor: encodeCursor(lastEntry.timestamp, lastEntry.id) },
927
+ hasNextPage: resolved.cursor !== void 0
928
+ };
929
+ }
930
+ const firstEntry = result.entries[0];
931
+ const prevCursor = resolved.cursor !== void 0 && firstEntry !== void 0 ? encodeCursor(firstEntry.timestamp, firstEntry.id) : void 0;
859
932
  return {
860
933
  entries: result.entries,
861
934
  ...result.nextCursor !== void 0 && { nextCursor: result.nextCursor },
862
- hasNextPage: result.nextCursor !== void 0
935
+ hasNextPage: result.nextCursor !== void 0,
936
+ hasPrevPage: resolved.cursor !== void 0,
937
+ ...prevCursor !== void 0 && { prevCursor }
938
+ };
939
+ }
940
+ async function queryLogsAround(anchorId, filters) {
941
+ const queryFn = requireQueryLogs();
942
+ const getLogFn = requireGetLogById();
943
+ const anchor = await getLogFn(anchorId);
944
+ if (anchor === null) {
945
+ return { entries: [], hasNextPage: false, hasPrevPage: false };
946
+ }
947
+ const resolved = filters ?? {};
948
+ const limit = Math.min(resolved.limit ?? effectiveLimit, effectiveLimit);
949
+ const remaining = Math.max(limit - 1, 0);
950
+ const olderCount = Math.ceil(remaining / 2);
951
+ const newerCount = Math.floor(remaining / 2);
952
+ const anchorCursor = encodeCursor(anchor.timestamp, anchor.id);
953
+ const { direction: _dir, ...baseFilters } = resolved;
954
+ const olderSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: olderCount }, effectiveLimit);
955
+ olderSpec.sortOrder = "desc";
956
+ const newerSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: newerCount }, effectiveLimit);
957
+ newerSpec.sortOrder = "asc";
958
+ const [olderResult, newerResult] = await Promise.all([
959
+ queryFn(olderSpec),
960
+ queryFn(newerSpec)
961
+ ]);
962
+ const newerEntries = [...newerResult.entries].reverse();
963
+ const entries = [...newerEntries, anchor, ...olderResult.entries];
964
+ const hasNextPage = olderResult.nextCursor !== void 0;
965
+ const hasPrevPage = newerResult.nextCursor !== void 0;
966
+ const oldest = entries[entries.length - 1];
967
+ const newest = entries[0];
968
+ return {
969
+ entries,
970
+ hasNextPage,
971
+ hasPrevPage,
972
+ ...hasNextPage && oldest !== void 0 && { nextCursor: encodeCursor(oldest.timestamp, oldest.id) },
973
+ ...hasPrevPage && newest !== void 0 && { prevCursor: encodeCursor(newest.timestamp, newest.id) }
863
974
  };
864
975
  }
865
976
  async function getLog(id) {
@@ -904,7 +1015,7 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
904
1015
  }
905
1016
  async function exportLogs(filters, format) {
906
1017
  const queryFn = requireQueryLogs();
907
- const exportFormat = format ?? "json";
1018
+ const exportFormat = format ?? "csv";
908
1019
  const spec = buildQuerySpec(filters ?? {}, effectiveLimit);
909
1020
  const queryBuilder = new AuditQueryBuilder(
910
1021
  (s) => queryFn(s),
@@ -930,7 +1041,7 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
930
1041
  const purgeFn = requirePurgeLogs();
931
1042
  return purgeFn(options);
932
1043
  }
933
- return { queryLogs, getLog, getStats, getEnrichments, exportLogs, purgeLogs };
1044
+ return { queryLogs, queryLogsAround, getLog, getStats, getEnrichments, exportLogs, purgeLogs };
934
1045
  }
935
1046
 
936
1047
  // ../../shared/console-utils/src/index.ts
@@ -964,7 +1075,7 @@ function exceedsMaxLength(value) {
964
1075
  return value !== void 0 && value.length > MAX_PARAM_LENGTH;
965
1076
  }
966
1077
  function hasLongQueryParam(query) {
967
- return exceedsMaxLength(query.tableName) || exceedsMaxLength(query.actorId) || exceedsMaxLength(query.cursor) || exceedsMaxLength(query.operation) || exceedsMaxLength(query.severity) || exceedsMaxLength(query.compliance) || exceedsMaxLength(query.search);
1078
+ return exceedsMaxLength(query.tableName) || exceedsMaxLength(query.recordId) || exceedsMaxLength(query.actorId) || exceedsMaxLength(query.cursor) || exceedsMaxLength(query.operation) || exceedsMaxLength(query.severity) || exceedsMaxLength(query.compliance) || exceedsMaxLength(query.search) || exceedsMaxLength(query.around) || exceedsMaxLength(query.direction) || exceedsMaxLength(query.order);
968
1079
  }
969
1080
  function parseConsoleQueryFilters(query) {
970
1081
  const filters = {};
@@ -978,6 +1089,12 @@ function parseConsoleQueryFilters(query) {
978
1089
  if (query.tableName !== void 0) {
979
1090
  filters.tableName = query.tableName;
980
1091
  }
1092
+ if (query.recordId !== void 0) {
1093
+ if (query.tableName === void 0) {
1094
+ return { error: "'recordId' requires 'tableName'" };
1095
+ }
1096
+ filters.recordId = query.recordId;
1097
+ }
981
1098
  if (query.operation !== void 0) {
982
1099
  filters.operation = query.operation;
983
1100
  }
@@ -996,6 +1113,18 @@ function parseConsoleQueryFilters(query) {
996
1113
  if (query.cursor !== void 0) {
997
1114
  filters.cursor = query.cursor;
998
1115
  }
1116
+ if (query.direction !== void 0) {
1117
+ if (query.direction !== "after" && query.direction !== "before") {
1118
+ return { error: "Invalid 'direction': must be 'after' or 'before'" };
1119
+ }
1120
+ filters.direction = query.direction;
1121
+ }
1122
+ if (query.order !== void 0) {
1123
+ if (query.order !== "asc" && query.order !== "desc") {
1124
+ return { error: "Invalid 'order': must be 'asc' or 'desc'" };
1125
+ }
1126
+ filters.order = query.order;
1127
+ }
999
1128
  if (query.since !== void 0) {
1000
1129
  const since = parseIsoDate(query.since);
1001
1130
  if (since === void 0) {
@@ -1010,6 +1139,9 @@ function parseConsoleQueryFilters(query) {
1010
1139
  }
1011
1140
  filters.until = until;
1012
1141
  }
1142
+ if (filters.since !== void 0 && filters.until !== void 0 && filters.since >= filters.until) {
1143
+ return { error: "'since' must be before 'until'" };
1144
+ }
1013
1145
  return { filters };
1014
1146
  }
1015
1147
  function serializeLog(log) {
@@ -1026,14 +1158,22 @@ function createAuditConsoleEndpoints(api) {
1026
1158
  requiredPermission: "read",
1027
1159
  async handler(request) {
1028
1160
  try {
1029
- if (hasLongQueryParam(request.query)) {
1161
+ const query = request.query;
1162
+ if (hasLongQueryParam(query)) {
1030
1163
  return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
1031
1164
  }
1032
- const parsed = parseConsoleQueryFilters(request.query);
1165
+ const parsed = parseConsoleQueryFilters(query);
1033
1166
  if ("error" in parsed) {
1034
1167
  return { status: 400, body: { error: parsed.error } };
1035
1168
  }
1036
- const result = await api.queryLogs(parsed.filters);
1169
+ if (parsed.filters.order !== void 0 && parsed.filters.direction !== void 0) {
1170
+ return { status: 400, body: { error: "'order' cannot be combined with 'direction'" } };
1171
+ }
1172
+ const around = query.around;
1173
+ if (around !== void 0 && (parsed.filters.cursor !== void 0 || parsed.filters.direction !== void 0 || parsed.filters.order !== void 0)) {
1174
+ return { status: 400, body: { error: "'around' cannot be combined with 'cursor', 'direction', or 'order'" } };
1175
+ }
1176
+ const result = around !== void 0 ? await api.queryLogsAround(around, parsed.filters) : await api.queryLogs(parsed.filters);
1037
1177
  const body = {
1038
1178
  entries: result.entries.map(serializeLog),
1039
1179
  hasNextPage: result.hasNextPage
@@ -1041,6 +1181,12 @@ function createAuditConsoleEndpoints(api) {
1041
1181
  if (result.nextCursor !== void 0) {
1042
1182
  body.nextCursor = result.nextCursor;
1043
1183
  }
1184
+ if (result.prevCursor !== void 0) {
1185
+ body.prevCursor = result.prevCursor;
1186
+ }
1187
+ if (result.hasPrevPage) {
1188
+ body.hasPrevPage = result.hasPrevPage;
1189
+ }
1044
1190
  return { status: 200, body };
1045
1191
  } catch {
1046
1192
  return { status: 500, body: { error: "Internal server error" } };
@@ -1073,14 +1219,28 @@ function createAuditConsoleEndpoints(api) {
1073
1219
  requiredPermission: "read",
1074
1220
  async handler(request) {
1075
1221
  try {
1222
+ const query = request.query;
1223
+ if (exceedsMaxLength(query.since) || exceedsMaxLength(query.until)) {
1224
+ return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
1225
+ }
1076
1226
  const options = {};
1077
- if (request.query.since !== void 0) {
1078
- const since = parseIsoDate(request.query.since);
1227
+ if (query.since !== void 0) {
1228
+ const since = parseIsoDate(query.since);
1079
1229
  if (since === void 0) {
1080
1230
  return { status: 400, body: { error: "Invalid 'since': must be an ISO-8601 date" } };
1081
1231
  }
1082
1232
  options.since = since;
1083
1233
  }
1234
+ if (query.until !== void 0) {
1235
+ const until = parseIsoDate(query.until);
1236
+ if (until === void 0) {
1237
+ return { status: 400, body: { error: "Invalid 'until': must be an ISO-8601 date" } };
1238
+ }
1239
+ options.until = until;
1240
+ }
1241
+ if (options.since !== void 0 && options.until !== void 0 && options.since >= options.until) {
1242
+ return { status: 400, body: { error: "'since' must be before 'until'" } };
1243
+ }
1084
1244
  const stats = await api.getStats(options);
1085
1245
  return { status: 200, body: stats };
1086
1246
  } catch {
@@ -1107,20 +1267,33 @@ function createAuditConsoleEndpoints(api) {
1107
1267
  requiredPermission: "read",
1108
1268
  async handler(request) {
1109
1269
  try {
1110
- const format = request.query.format;
1270
+ const query = request.query;
1271
+ const format = query.format;
1111
1272
  if (format !== void 0 && format !== "csv" && format !== "json") {
1112
1273
  return { status: 400, body: { error: "Invalid format. Must be 'csv' or 'json'" } };
1113
1274
  }
1114
- if (hasLongQueryParam(request.query)) {
1275
+ if (hasLongQueryParam(query)) {
1115
1276
  return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
1116
1277
  }
1117
- const parsed = parseConsoleQueryFilters(request.query);
1278
+ const parsed = parseConsoleQueryFilters(query);
1118
1279
  if ("error" in parsed) {
1119
1280
  return { status: 400, body: { error: parsed.error } };
1120
1281
  }
1121
- const exportFormat = format;
1122
- const data = await api.exportLogs(parsed.filters, exportFormat);
1123
- return { status: 200, body: data };
1282
+ const effectiveFormat = format ?? "csv";
1283
+ const data = await api.exportLogs(parsed.filters, effectiveFormat);
1284
+ const contentType = effectiveFormat === "csv" ? "text/csv; charset=utf-8" : "application/json; charset=utf-8";
1285
+ const ext = effectiveFormat === "csv" ? "csv" : "json";
1286
+ const dateStamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1287
+ const filename = `audit-export-${dateStamp}.${ext}`;
1288
+ return {
1289
+ status: 200,
1290
+ body: data,
1291
+ headers: {
1292
+ "content-type": contentType,
1293
+ "content-disposition": `attachment; filename="${filename}"`,
1294
+ "cache-control": "no-cache"
1295
+ }
1296
+ };
1124
1297
  } catch {
1125
1298
  return { status: 500, body: { error: "Internal server error" } };
1126
1299
  }
@@ -1509,6 +1682,139 @@ var AUDIT_LOG_SCHEMA = {
1509
1682
  }
1510
1683
  };
1511
1684
 
1685
+ // src/escape.ts
1686
+ function escapeLikePattern(input) {
1687
+ return input.replace(/[%_\\]/g, "\\$&");
1688
+ }
1689
+
1690
+ // src/time-filter.ts
1691
+ function resolveTimeFilter(filter) {
1692
+ if ("date" in filter && filter.date !== void 0) {
1693
+ return filter.date;
1694
+ }
1695
+ if ("duration" in filter && filter.duration !== void 0) {
1696
+ return parseDuration(filter.duration);
1697
+ }
1698
+ throw new Error("TimeFilter must have either date or duration");
1699
+ }
1700
+
1701
+ // src/filter-builder.ts
1702
+ function interpretFilters(filters, options) {
1703
+ const conditions = [];
1704
+ if (filters.resource !== void 0) {
1705
+ conditions.push({ kind: "eq", field: "tableName", value: filters.resource.tableName });
1706
+ if (filters.resource.recordId !== void 0) {
1707
+ conditions.push({ kind: "eq", field: "recordId", value: filters.resource.recordId });
1708
+ }
1709
+ }
1710
+ pushArrayFilter(conditions, "actorId", filters.actorIds);
1711
+ pushArrayFilter(conditions, "severity", filters.severities);
1712
+ pushArrayFilter(conditions, "operation", filters.operations);
1713
+ if (filters.since !== void 0) {
1714
+ conditions.push({ kind: "timestampGte", value: resolveTimeFilter(filters.since) });
1715
+ }
1716
+ if (filters.until !== void 0) {
1717
+ conditions.push({ kind: "timestampLte", value: resolveTimeFilter(filters.until) });
1718
+ }
1719
+ if (filters.searchText !== void 0 && filters.searchText.length > 0) {
1720
+ const escaped = escapeLikePattern(filters.searchText);
1721
+ conditions.push({ kind: "search", pattern: `%${escaped}%` });
1722
+ }
1723
+ if (filters.compliance !== void 0 && filters.compliance.length > 0) {
1724
+ conditions.push({ kind: "compliance", tags: [...filters.compliance] });
1725
+ }
1726
+ if (options?.cursor !== void 0) {
1727
+ conditions.push({
1728
+ kind: "cursor",
1729
+ timestamp: options.cursor.timestamp,
1730
+ id: options.cursor.id,
1731
+ sortOrder: options.sortOrder ?? "desc"
1732
+ });
1733
+ }
1734
+ return conditions;
1735
+ }
1736
+ function pushArrayFilter(conditions, field, values) {
1737
+ if (values === void 0 || values.length === 0) {
1738
+ return;
1739
+ }
1740
+ if (values.length === 1) {
1741
+ const first = values[0];
1742
+ if (first !== void 0) {
1743
+ conditions.push({ kind: "eq", field, value: first });
1744
+ }
1745
+ } else {
1746
+ conditions.push({ kind: "in", field, values: [...values] });
1747
+ }
1748
+ }
1749
+
1750
+ // src/stats.ts
1751
+ function toCount(value) {
1752
+ if (typeof value === "number") {
1753
+ return value;
1754
+ }
1755
+ if (typeof value === "string") {
1756
+ const n = Number(value);
1757
+ return Number.isNaN(n) ? 0 : n;
1758
+ }
1759
+ if (typeof value === "bigint") {
1760
+ return Number(value);
1761
+ }
1762
+ return 0;
1763
+ }
1764
+ function assembleStats(summaryRows, eventsPerDayRows, topActorsRows, topTablesRows, operationRows, severityRows) {
1765
+ const summary = summaryRows[0];
1766
+ const totalLogs = summary !== void 0 ? toCount(summary.totalLogs) : 0;
1767
+ const tablesAudited = summary !== void 0 ? toCount(summary.tablesAudited) : 0;
1768
+ const eventsPerDay = eventsPerDayRows.map((row) => ({
1769
+ date: row.date instanceof Date ? row.date.toISOString().split("T")[0] ?? "" : String(row.date),
1770
+ count: toCount(row.count)
1771
+ }));
1772
+ const topActors = topActorsRows.map((row) => ({
1773
+ actorId: String(row.actorId),
1774
+ count: toCount(row.count)
1775
+ }));
1776
+ const topTables = topTablesRows.map((row) => ({
1777
+ tableName: String(row.tableName),
1778
+ count: toCount(row.count)
1779
+ }));
1780
+ const operationBreakdown = {};
1781
+ for (const row of operationRows) {
1782
+ operationBreakdown[String(row.operation)] = toCount(row.count);
1783
+ }
1784
+ const severityBreakdown = {};
1785
+ for (const row of severityRows) {
1786
+ severityBreakdown[String(row.severity)] = toCount(row.count);
1787
+ }
1788
+ return {
1789
+ totalLogs,
1790
+ tablesAudited,
1791
+ eventsPerDay,
1792
+ topActors,
1793
+ topTables,
1794
+ operationBreakdown,
1795
+ severityBreakdown
1796
+ };
1797
+ }
1798
+
1799
+ // src/validation.ts
1800
+ var VALID_OPERATIONS2 = /* @__PURE__ */ new Set([
1801
+ "INSERT",
1802
+ "UPDATE",
1803
+ "DELETE"
1804
+ ]);
1805
+ var VALID_SEVERITIES2 = /* @__PURE__ */ new Set([
1806
+ "low",
1807
+ "medium",
1808
+ "high",
1809
+ "critical"
1810
+ ]);
1811
+ function isAuditOperation(value) {
1812
+ return VALID_OPERATIONS2.has(value);
1813
+ }
1814
+ function isAuditSeverity(value) {
1815
+ return VALID_SEVERITIES2.has(value);
1816
+ }
1817
+
1512
1818
  // src/context-extractor.ts
1513
1819
  function decodeJwtPayload(token) {
1514
1820
  try {
@@ -1586,6 +1892,9 @@ function fromHeader(headerName) {
1586
1892
  return value.trim();
1587
1893
  };
1588
1894
  }
1895
+ var defaultExtractor = {
1896
+ actor: fromBearerToken("sub")
1897
+ };
1589
1898
  async function safeExtract(extractor, request, onError) {
1590
1899
  if (!extractor) {
1591
1900
  return void 0;
@@ -1612,19 +1921,32 @@ async function handleMiddleware(extractor, request, next, options = {}) {
1612
1921
  0 && (module.exports = {
1613
1922
  AUDIT_LOG_SCHEMA,
1614
1923
  AuditQueryBuilder,
1924
+ VALID_OPERATIONS,
1925
+ VALID_SEVERITIES,
1926
+ assembleStats,
1615
1927
  betterAudit,
1616
1928
  createAuditApi,
1617
1929
  createAuditConsoleEndpoints,
1618
1930
  createExportResponse,
1931
+ decodeCursor,
1932
+ defaultExtractor,
1933
+ encodeCursor,
1934
+ escapeLikePattern,
1619
1935
  fromBearerToken,
1620
1936
  fromCookie,
1621
1937
  fromHeader,
1622
1938
  getAuditContext,
1623
1939
  handleMiddleware,
1940
+ interpretFilters,
1941
+ isAuditOperation,
1942
+ isAuditSeverity,
1624
1943
  mergeAuditContext,
1625
1944
  normalizeInput,
1626
1945
  parseDuration,
1946
+ resolveTimeFilter,
1627
1947
  runExport,
1628
- runWithAuditContext
1948
+ runWithAuditContext,
1949
+ safeExtract,
1950
+ toCount
1629
1951
  });
1630
1952
  //# sourceMappingURL=index.cjs.map