@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.js CHANGED
@@ -725,8 +725,41 @@ async function runExport(executor, options) {
725
725
  return { rowCount };
726
726
  }
727
727
 
728
+ // src/cursor.ts
729
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
730
+ function encodeCursor(timestamp, id) {
731
+ const payload = JSON.stringify({ t: timestamp.toISOString(), i: id });
732
+ return btoa(payload);
733
+ }
734
+ function decodeCursor(cursor) {
735
+ let parsed;
736
+ try {
737
+ parsed = JSON.parse(atob(cursor));
738
+ } catch {
739
+ throw new Error("Invalid cursor: failed to decode");
740
+ }
741
+ if (typeof parsed !== "object" || parsed === null || !("t" in parsed) || !("i" in parsed)) {
742
+ throw new Error("Invalid cursor: missing required fields");
743
+ }
744
+ const record = parsed;
745
+ const t = record["t"];
746
+ const i = record["i"];
747
+ if (typeof t !== "string" || typeof i !== "string") {
748
+ throw new Error("Invalid cursor: fields must be strings");
749
+ }
750
+ const timestamp = new Date(t);
751
+ if (isNaN(timestamp.getTime())) {
752
+ throw new Error("Invalid cursor: invalid timestamp");
753
+ }
754
+ if (!UUID_PATTERN.test(i)) {
755
+ throw new Error("Invalid cursor: id must be a valid UUID");
756
+ }
757
+ return { timestamp, id: i };
758
+ }
759
+
728
760
  // src/audit-api.ts
729
- var VALID_SEVERITIES = /* @__PURE__ */ new Set(["low", "medium", "high", "critical"]);
761
+ var SEVERITY_VALUES = ["low", "medium", "high", "critical"];
762
+ var VALID_SEVERITIES = new Set(SEVERITY_VALUES);
730
763
  var VALID_OPERATIONS = /* @__PURE__ */ new Set(["INSERT", "UPDATE", "DELETE"]);
731
764
  function toTimeFilter(date) {
732
765
  return { date };
@@ -738,18 +771,23 @@ function buildQuerySpec(filters, effectiveLimit) {
738
771
  limit
739
772
  };
740
773
  if (filters.tableName !== void 0) {
741
- spec.filters.resource = { tableName: filters.tableName };
774
+ spec.filters.resource = filters.recordId !== void 0 ? { tableName: filters.tableName, recordId: filters.recordId } : { tableName: filters.tableName };
742
775
  }
743
776
  if (filters.actorId !== void 0) {
744
- spec.filters.actorIds = [filters.actorId];
777
+ const raw = filters.actorId.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
778
+ spec.filters.actorIds = [...new Set(raw)];
745
779
  }
746
780
  if (filters.severity !== void 0) {
747
- if (!VALID_SEVERITIES.has(filters.severity)) {
748
- throw new Error(
749
- `Invalid severity "${filters.severity}". Must be one of: low, medium, high, critical`
750
- );
781
+ const raw = filters.severity.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
782
+ const unique = [...new Set(raw)];
783
+ for (const value of unique) {
784
+ if (!VALID_SEVERITIES.has(value)) {
785
+ throw new Error(
786
+ `Invalid severity "${value}". Must be one of: low, medium, high, critical`
787
+ );
788
+ }
751
789
  }
752
- spec.filters.severities = [filters.severity];
790
+ spec.filters.severities = unique.filter((v) => VALID_SEVERITIES.has(v));
753
791
  }
754
792
  if (filters.compliance !== void 0) {
755
793
  spec.filters.compliance = [filters.compliance];
@@ -775,6 +813,12 @@ function buildQuerySpec(filters, effectiveLimit) {
775
813
  if (filters.cursor !== void 0) {
776
814
  spec.cursor = filters.cursor;
777
815
  }
816
+ if (filters.direction === "before") {
817
+ spec.sortOrder = "asc";
818
+ }
819
+ if (filters.order !== void 0) {
820
+ spec.sortOrder = filters.order;
821
+ }
778
822
  return spec;
779
823
  }
780
824
  function createAuditApi(adapter, registry, maxQueryLimit) {
@@ -813,12 +857,66 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
813
857
  }
814
858
  async function queryLogs(filters) {
815
859
  const queryFn = requireQueryLogs();
816
- const spec = buildQuerySpec(filters ?? {}, effectiveLimit);
860
+ const resolved = filters ?? {};
861
+ const spec = buildQuerySpec(resolved, effectiveLimit);
817
862
  const result = await queryFn(spec);
863
+ if (resolved.direction === "before") {
864
+ const entries = [...result.entries].reverse();
865
+ const lastEntry = entries[entries.length - 1];
866
+ return {
867
+ entries,
868
+ // Adapter's "next" in ASC = newer entries = our prev
869
+ ...result.nextCursor !== void 0 && { prevCursor: result.nextCursor },
870
+ hasPrevPage: result.nextCursor !== void 0,
871
+ // Since we started from a cursor, there are older entries behind it
872
+ ...resolved.cursor !== void 0 && lastEntry !== void 0 && { nextCursor: encodeCursor(lastEntry.timestamp, lastEntry.id) },
873
+ hasNextPage: resolved.cursor !== void 0
874
+ };
875
+ }
876
+ const firstEntry = result.entries[0];
877
+ const prevCursor = resolved.cursor !== void 0 && firstEntry !== void 0 ? encodeCursor(firstEntry.timestamp, firstEntry.id) : void 0;
818
878
  return {
819
879
  entries: result.entries,
820
880
  ...result.nextCursor !== void 0 && { nextCursor: result.nextCursor },
821
- hasNextPage: result.nextCursor !== void 0
881
+ hasNextPage: result.nextCursor !== void 0,
882
+ hasPrevPage: resolved.cursor !== void 0,
883
+ ...prevCursor !== void 0 && { prevCursor }
884
+ };
885
+ }
886
+ async function queryLogsAround(anchorId, filters) {
887
+ const queryFn = requireQueryLogs();
888
+ const getLogFn = requireGetLogById();
889
+ const anchor = await getLogFn(anchorId);
890
+ if (anchor === null) {
891
+ return { entries: [], hasNextPage: false, hasPrevPage: false };
892
+ }
893
+ const resolved = filters ?? {};
894
+ const limit = Math.min(resolved.limit ?? effectiveLimit, effectiveLimit);
895
+ const remaining = Math.max(limit - 1, 0);
896
+ const olderCount = Math.ceil(remaining / 2);
897
+ const newerCount = Math.floor(remaining / 2);
898
+ const anchorCursor = encodeCursor(anchor.timestamp, anchor.id);
899
+ const { direction: _dir, ...baseFilters } = resolved;
900
+ const olderSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: olderCount }, effectiveLimit);
901
+ olderSpec.sortOrder = "desc";
902
+ const newerSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: newerCount }, effectiveLimit);
903
+ newerSpec.sortOrder = "asc";
904
+ const [olderResult, newerResult] = await Promise.all([
905
+ queryFn(olderSpec),
906
+ queryFn(newerSpec)
907
+ ]);
908
+ const newerEntries = [...newerResult.entries].reverse();
909
+ const entries = [...newerEntries, anchor, ...olderResult.entries];
910
+ const hasNextPage = olderResult.nextCursor !== void 0;
911
+ const hasPrevPage = newerResult.nextCursor !== void 0;
912
+ const oldest = entries[entries.length - 1];
913
+ const newest = entries[0];
914
+ return {
915
+ entries,
916
+ hasNextPage,
917
+ hasPrevPage,
918
+ ...hasNextPage && oldest !== void 0 && { nextCursor: encodeCursor(oldest.timestamp, oldest.id) },
919
+ ...hasPrevPage && newest !== void 0 && { prevCursor: encodeCursor(newest.timestamp, newest.id) }
822
920
  };
823
921
  }
824
922
  async function getLog(id) {
@@ -863,7 +961,7 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
863
961
  }
864
962
  async function exportLogs(filters, format) {
865
963
  const queryFn = requireQueryLogs();
866
- const exportFormat = format ?? "json";
964
+ const exportFormat = format ?? "csv";
867
965
  const spec = buildQuerySpec(filters ?? {}, effectiveLimit);
868
966
  const queryBuilder = new AuditQueryBuilder(
869
967
  (s) => queryFn(s),
@@ -889,7 +987,7 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
889
987
  const purgeFn = requirePurgeLogs();
890
988
  return purgeFn(options);
891
989
  }
892
- return { queryLogs, getLog, getStats, getEnrichments, exportLogs, purgeLogs };
990
+ return { queryLogs, queryLogsAround, getLog, getStats, getEnrichments, exportLogs, purgeLogs };
893
991
  }
894
992
 
895
993
  // ../../shared/console-utils/src/index.ts
@@ -923,7 +1021,7 @@ function exceedsMaxLength(value) {
923
1021
  return value !== void 0 && value.length > MAX_PARAM_LENGTH;
924
1022
  }
925
1023
  function hasLongQueryParam(query) {
926
- return exceedsMaxLength(query.tableName) || exceedsMaxLength(query.actorId) || exceedsMaxLength(query.cursor) || exceedsMaxLength(query.operation) || exceedsMaxLength(query.severity) || exceedsMaxLength(query.compliance) || exceedsMaxLength(query.search);
1024
+ 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);
927
1025
  }
928
1026
  function parseConsoleQueryFilters(query) {
929
1027
  const filters = {};
@@ -937,6 +1035,12 @@ function parseConsoleQueryFilters(query) {
937
1035
  if (query.tableName !== void 0) {
938
1036
  filters.tableName = query.tableName;
939
1037
  }
1038
+ if (query.recordId !== void 0) {
1039
+ if (query.tableName === void 0) {
1040
+ return { error: "'recordId' requires 'tableName'" };
1041
+ }
1042
+ filters.recordId = query.recordId;
1043
+ }
940
1044
  if (query.operation !== void 0) {
941
1045
  filters.operation = query.operation;
942
1046
  }
@@ -955,6 +1059,18 @@ function parseConsoleQueryFilters(query) {
955
1059
  if (query.cursor !== void 0) {
956
1060
  filters.cursor = query.cursor;
957
1061
  }
1062
+ if (query.direction !== void 0) {
1063
+ if (query.direction !== "after" && query.direction !== "before") {
1064
+ return { error: "Invalid 'direction': must be 'after' or 'before'" };
1065
+ }
1066
+ filters.direction = query.direction;
1067
+ }
1068
+ if (query.order !== void 0) {
1069
+ if (query.order !== "asc" && query.order !== "desc") {
1070
+ return { error: "Invalid 'order': must be 'asc' or 'desc'" };
1071
+ }
1072
+ filters.order = query.order;
1073
+ }
958
1074
  if (query.since !== void 0) {
959
1075
  const since = parseIsoDate(query.since);
960
1076
  if (since === void 0) {
@@ -969,6 +1085,9 @@ function parseConsoleQueryFilters(query) {
969
1085
  }
970
1086
  filters.until = until;
971
1087
  }
1088
+ if (filters.since !== void 0 && filters.until !== void 0 && filters.since >= filters.until) {
1089
+ return { error: "'since' must be before 'until'" };
1090
+ }
972
1091
  return { filters };
973
1092
  }
974
1093
  function serializeLog(log) {
@@ -985,14 +1104,22 @@ function createAuditConsoleEndpoints(api) {
985
1104
  requiredPermission: "read",
986
1105
  async handler(request) {
987
1106
  try {
988
- if (hasLongQueryParam(request.query)) {
1107
+ const query = request.query;
1108
+ if (hasLongQueryParam(query)) {
989
1109
  return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
990
1110
  }
991
- const parsed = parseConsoleQueryFilters(request.query);
1111
+ const parsed = parseConsoleQueryFilters(query);
992
1112
  if ("error" in parsed) {
993
1113
  return { status: 400, body: { error: parsed.error } };
994
1114
  }
995
- const result = await api.queryLogs(parsed.filters);
1115
+ if (parsed.filters.order !== void 0 && parsed.filters.direction !== void 0) {
1116
+ return { status: 400, body: { error: "'order' cannot be combined with 'direction'" } };
1117
+ }
1118
+ const around = query.around;
1119
+ if (around !== void 0 && (parsed.filters.cursor !== void 0 || parsed.filters.direction !== void 0 || parsed.filters.order !== void 0)) {
1120
+ return { status: 400, body: { error: "'around' cannot be combined with 'cursor', 'direction', or 'order'" } };
1121
+ }
1122
+ const result = around !== void 0 ? await api.queryLogsAround(around, parsed.filters) : await api.queryLogs(parsed.filters);
996
1123
  const body = {
997
1124
  entries: result.entries.map(serializeLog),
998
1125
  hasNextPage: result.hasNextPage
@@ -1000,6 +1127,12 @@ function createAuditConsoleEndpoints(api) {
1000
1127
  if (result.nextCursor !== void 0) {
1001
1128
  body.nextCursor = result.nextCursor;
1002
1129
  }
1130
+ if (result.prevCursor !== void 0) {
1131
+ body.prevCursor = result.prevCursor;
1132
+ }
1133
+ if (result.hasPrevPage) {
1134
+ body.hasPrevPage = result.hasPrevPage;
1135
+ }
1003
1136
  return { status: 200, body };
1004
1137
  } catch {
1005
1138
  return { status: 500, body: { error: "Internal server error" } };
@@ -1032,14 +1165,28 @@ function createAuditConsoleEndpoints(api) {
1032
1165
  requiredPermission: "read",
1033
1166
  async handler(request) {
1034
1167
  try {
1168
+ const query = request.query;
1169
+ if (exceedsMaxLength(query.since) || exceedsMaxLength(query.until)) {
1170
+ return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
1171
+ }
1035
1172
  const options = {};
1036
- if (request.query.since !== void 0) {
1037
- const since = parseIsoDate(request.query.since);
1173
+ if (query.since !== void 0) {
1174
+ const since = parseIsoDate(query.since);
1038
1175
  if (since === void 0) {
1039
1176
  return { status: 400, body: { error: "Invalid 'since': must be an ISO-8601 date" } };
1040
1177
  }
1041
1178
  options.since = since;
1042
1179
  }
1180
+ if (query.until !== void 0) {
1181
+ const until = parseIsoDate(query.until);
1182
+ if (until === void 0) {
1183
+ return { status: 400, body: { error: "Invalid 'until': must be an ISO-8601 date" } };
1184
+ }
1185
+ options.until = until;
1186
+ }
1187
+ if (options.since !== void 0 && options.until !== void 0 && options.since >= options.until) {
1188
+ return { status: 400, body: { error: "'since' must be before 'until'" } };
1189
+ }
1043
1190
  const stats = await api.getStats(options);
1044
1191
  return { status: 200, body: stats };
1045
1192
  } catch {
@@ -1066,20 +1213,33 @@ function createAuditConsoleEndpoints(api) {
1066
1213
  requiredPermission: "read",
1067
1214
  async handler(request) {
1068
1215
  try {
1069
- const format = request.query.format;
1216
+ const query = request.query;
1217
+ const format = query.format;
1070
1218
  if (format !== void 0 && format !== "csv" && format !== "json") {
1071
1219
  return { status: 400, body: { error: "Invalid format. Must be 'csv' or 'json'" } };
1072
1220
  }
1073
- if (hasLongQueryParam(request.query)) {
1221
+ if (hasLongQueryParam(query)) {
1074
1222
  return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
1075
1223
  }
1076
- const parsed = parseConsoleQueryFilters(request.query);
1224
+ const parsed = parseConsoleQueryFilters(query);
1077
1225
  if ("error" in parsed) {
1078
1226
  return { status: 400, body: { error: parsed.error } };
1079
1227
  }
1080
- const exportFormat = format;
1081
- const data = await api.exportLogs(parsed.filters, exportFormat);
1082
- return { status: 200, body: data };
1228
+ const effectiveFormat = format ?? "csv";
1229
+ const data = await api.exportLogs(parsed.filters, effectiveFormat);
1230
+ const contentType = effectiveFormat === "csv" ? "text/csv; charset=utf-8" : "application/json; charset=utf-8";
1231
+ const ext = effectiveFormat === "csv" ? "csv" : "json";
1232
+ const dateStamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1233
+ const filename = `audit-export-${dateStamp}.${ext}`;
1234
+ return {
1235
+ status: 200,
1236
+ body: data,
1237
+ headers: {
1238
+ "content-type": contentType,
1239
+ "content-disposition": `attachment; filename="${filename}"`,
1240
+ "cache-control": "no-cache"
1241
+ }
1242
+ };
1083
1243
  } catch {
1084
1244
  return { status: 500, body: { error: "Internal server error" } };
1085
1245
  }
@@ -1468,6 +1628,139 @@ var AUDIT_LOG_SCHEMA = {
1468
1628
  }
1469
1629
  };
1470
1630
 
1631
+ // src/escape.ts
1632
+ function escapeLikePattern(input) {
1633
+ return input.replace(/[%_\\]/g, "\\$&");
1634
+ }
1635
+
1636
+ // src/time-filter.ts
1637
+ function resolveTimeFilter(filter) {
1638
+ if ("date" in filter && filter.date !== void 0) {
1639
+ return filter.date;
1640
+ }
1641
+ if ("duration" in filter && filter.duration !== void 0) {
1642
+ return parseDuration(filter.duration);
1643
+ }
1644
+ throw new Error("TimeFilter must have either date or duration");
1645
+ }
1646
+
1647
+ // src/filter-builder.ts
1648
+ function interpretFilters(filters, options) {
1649
+ const conditions = [];
1650
+ if (filters.resource !== void 0) {
1651
+ conditions.push({ kind: "eq", field: "tableName", value: filters.resource.tableName });
1652
+ if (filters.resource.recordId !== void 0) {
1653
+ conditions.push({ kind: "eq", field: "recordId", value: filters.resource.recordId });
1654
+ }
1655
+ }
1656
+ pushArrayFilter(conditions, "actorId", filters.actorIds);
1657
+ pushArrayFilter(conditions, "severity", filters.severities);
1658
+ pushArrayFilter(conditions, "operation", filters.operations);
1659
+ if (filters.since !== void 0) {
1660
+ conditions.push({ kind: "timestampGte", value: resolveTimeFilter(filters.since) });
1661
+ }
1662
+ if (filters.until !== void 0) {
1663
+ conditions.push({ kind: "timestampLte", value: resolveTimeFilter(filters.until) });
1664
+ }
1665
+ if (filters.searchText !== void 0 && filters.searchText.length > 0) {
1666
+ const escaped = escapeLikePattern(filters.searchText);
1667
+ conditions.push({ kind: "search", pattern: `%${escaped}%` });
1668
+ }
1669
+ if (filters.compliance !== void 0 && filters.compliance.length > 0) {
1670
+ conditions.push({ kind: "compliance", tags: [...filters.compliance] });
1671
+ }
1672
+ if (options?.cursor !== void 0) {
1673
+ conditions.push({
1674
+ kind: "cursor",
1675
+ timestamp: options.cursor.timestamp,
1676
+ id: options.cursor.id,
1677
+ sortOrder: options.sortOrder ?? "desc"
1678
+ });
1679
+ }
1680
+ return conditions;
1681
+ }
1682
+ function pushArrayFilter(conditions, field, values) {
1683
+ if (values === void 0 || values.length === 0) {
1684
+ return;
1685
+ }
1686
+ if (values.length === 1) {
1687
+ const first = values[0];
1688
+ if (first !== void 0) {
1689
+ conditions.push({ kind: "eq", field, value: first });
1690
+ }
1691
+ } else {
1692
+ conditions.push({ kind: "in", field, values: [...values] });
1693
+ }
1694
+ }
1695
+
1696
+ // src/stats.ts
1697
+ function toCount(value) {
1698
+ if (typeof value === "number") {
1699
+ return value;
1700
+ }
1701
+ if (typeof value === "string") {
1702
+ const n = Number(value);
1703
+ return Number.isNaN(n) ? 0 : n;
1704
+ }
1705
+ if (typeof value === "bigint") {
1706
+ return Number(value);
1707
+ }
1708
+ return 0;
1709
+ }
1710
+ function assembleStats(summaryRows, eventsPerDayRows, topActorsRows, topTablesRows, operationRows, severityRows) {
1711
+ const summary = summaryRows[0];
1712
+ const totalLogs = summary !== void 0 ? toCount(summary.totalLogs) : 0;
1713
+ const tablesAudited = summary !== void 0 ? toCount(summary.tablesAudited) : 0;
1714
+ const eventsPerDay = eventsPerDayRows.map((row) => ({
1715
+ date: row.date instanceof Date ? row.date.toISOString().split("T")[0] ?? "" : String(row.date),
1716
+ count: toCount(row.count)
1717
+ }));
1718
+ const topActors = topActorsRows.map((row) => ({
1719
+ actorId: String(row.actorId),
1720
+ count: toCount(row.count)
1721
+ }));
1722
+ const topTables = topTablesRows.map((row) => ({
1723
+ tableName: String(row.tableName),
1724
+ count: toCount(row.count)
1725
+ }));
1726
+ const operationBreakdown = {};
1727
+ for (const row of operationRows) {
1728
+ operationBreakdown[String(row.operation)] = toCount(row.count);
1729
+ }
1730
+ const severityBreakdown = {};
1731
+ for (const row of severityRows) {
1732
+ severityBreakdown[String(row.severity)] = toCount(row.count);
1733
+ }
1734
+ return {
1735
+ totalLogs,
1736
+ tablesAudited,
1737
+ eventsPerDay,
1738
+ topActors,
1739
+ topTables,
1740
+ operationBreakdown,
1741
+ severityBreakdown
1742
+ };
1743
+ }
1744
+
1745
+ // src/validation.ts
1746
+ var VALID_OPERATIONS2 = /* @__PURE__ */ new Set([
1747
+ "INSERT",
1748
+ "UPDATE",
1749
+ "DELETE"
1750
+ ]);
1751
+ var VALID_SEVERITIES2 = /* @__PURE__ */ new Set([
1752
+ "low",
1753
+ "medium",
1754
+ "high",
1755
+ "critical"
1756
+ ]);
1757
+ function isAuditOperation(value) {
1758
+ return VALID_OPERATIONS2.has(value);
1759
+ }
1760
+ function isAuditSeverity(value) {
1761
+ return VALID_SEVERITIES2.has(value);
1762
+ }
1763
+
1471
1764
  // src/context-extractor.ts
1472
1765
  function decodeJwtPayload(token) {
1473
1766
  try {
@@ -1545,6 +1838,9 @@ function fromHeader(headerName) {
1545
1838
  return value.trim();
1546
1839
  };
1547
1840
  }
1841
+ var defaultExtractor = {
1842
+ actor: fromBearerToken("sub")
1843
+ };
1548
1844
  async function safeExtract(extractor, request, onError) {
1549
1845
  if (!extractor) {
1550
1846
  return void 0;
@@ -1570,19 +1866,32 @@ async function handleMiddleware(extractor, request, next, options = {}) {
1570
1866
  export {
1571
1867
  AUDIT_LOG_SCHEMA,
1572
1868
  AuditQueryBuilder,
1869
+ VALID_OPERATIONS2 as VALID_OPERATIONS,
1870
+ VALID_SEVERITIES2 as VALID_SEVERITIES,
1871
+ assembleStats,
1573
1872
  betterAudit,
1574
1873
  createAuditApi,
1575
1874
  createAuditConsoleEndpoints,
1576
1875
  createExportResponse,
1876
+ decodeCursor,
1877
+ defaultExtractor,
1878
+ encodeCursor,
1879
+ escapeLikePattern,
1577
1880
  fromBearerToken,
1578
1881
  fromCookie,
1579
1882
  fromHeader,
1580
1883
  getAuditContext,
1581
1884
  handleMiddleware,
1885
+ interpretFilters,
1886
+ isAuditOperation,
1887
+ isAuditSeverity,
1582
1888
  mergeAuditContext,
1583
1889
  normalizeInput,
1584
1890
  parseDuration,
1891
+ resolveTimeFilter,
1585
1892
  runExport,
1586
- runWithAuditContext
1893
+ runWithAuditContext,
1894
+ safeExtract,
1895
+ toCount
1587
1896
  };
1588
1897
  //# sourceMappingURL=index.js.map