@richardmcquiston01/error-log-adapter 0.3.0 → 0.4.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,19 +22,23 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CompositeErrorAdapter: () => CompositeErrorAdapter,
24
24
  ConsoleErrorAdapter: () => ConsoleErrorAdapter,
25
+ FirestoreErrorAdapter: () => FirestoreErrorAdapter,
25
26
  FlatFileErrorAdapter: () => FlatFileErrorAdapter,
26
27
  IndexedDbErrorAdapter: () => IndexedDbErrorAdapter,
27
28
  JsonFileErrorAdapter: () => JsonFileErrorAdapter,
28
29
  MongoDbErrorAdapter: () => MongoDbErrorAdapter,
29
30
  MySqlErrorAdapter: () => MySqlErrorAdapter,
30
31
  PostgresErrorAdapter: () => PostgresErrorAdapter,
32
+ RealtimeDatabaseErrorAdapter: () => RealtimeDatabaseErrorAdapter,
31
33
  RedisErrorAdapter: () => RedisErrorAdapter,
32
34
  S3ErrorAdapter: () => S3ErrorAdapter,
33
35
  SqliteErrorAdapter: () => SqliteErrorAdapter,
36
+ SupabaseErrorAdapter: () => SupabaseErrorAdapter,
34
37
  WebStorageErrorAdapter: () => WebStorageErrorAdapter,
35
38
  WebhookErrorAdapter: () => WebhookErrorAdapter,
36
39
  buildCreateErrorLogTableSql: () => buildCreateErrorLogTableSql,
37
40
  buildErrorLogTableDdl: () => buildErrorLogTableDdl,
41
+ buildSupabaseSetupSql: () => buildSupabaseSetupSql,
38
42
  createErrorLogStore: () => createErrorLogStore,
39
43
  formatErrorLogLine: () => formatErrorLogLine,
40
44
  serializeCause: () => serializeCause,
@@ -1029,23 +1033,218 @@ var S3ErrorAdapter = class {
1029
1033
  };
1030
1034
  }
1031
1035
  };
1036
+
1037
+ // src/adapters/firebase-error-adapter.ts
1038
+ var DEFAULT_WRITE_TIMEOUT_MS = 1e4;
1039
+ function resolveTimeout(options, adapter) {
1040
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WRITE_TIMEOUT_MS;
1041
+ if (Number.isNaN(timeoutMs) || timeoutMs <= 0) {
1042
+ throw new RangeError(
1043
+ `${adapter}: timeoutMs must be a positive number or Infinity, received ${String(options.timeoutMs)}.`
1044
+ );
1045
+ }
1046
+ return timeoutMs;
1047
+ }
1048
+ function waitForWrite(write, timeoutMs) {
1049
+ if (timeoutMs === Infinity) {
1050
+ return Promise.resolve(write);
1051
+ }
1052
+ return new Promise(
1053
+ (resolve, reject) => {
1054
+ const timer = setTimeout(() => {
1055
+ reject(
1056
+ new Error(
1057
+ `the write was not saved within ${timeoutMs} ms (the connection may be down; the SDK keeps the write queued and may still save it later)`
1058
+ )
1059
+ );
1060
+ }, timeoutMs);
1061
+ write.then(
1062
+ (value) => {
1063
+ clearTimeout(timer);
1064
+ resolve(value);
1065
+ },
1066
+ (reason) => {
1067
+ clearTimeout(timer);
1068
+ reject(reason);
1069
+ }
1070
+ );
1071
+ }
1072
+ );
1073
+ }
1074
+ function buildRecord(entry) {
1075
+ const serialized = serializeEntry(entry);
1076
+ const record = {
1077
+ id: crypto.randomUUID(),
1078
+ code: serialized.code,
1079
+ message: serialized.message,
1080
+ timestamp: serialized.timestamp
1081
+ };
1082
+ if (serialized.source !== void 0) {
1083
+ record.source = serialized.source;
1084
+ }
1085
+ if (serialized.cause !== void 0) {
1086
+ record.cause = JSON.stringify(serialized.cause);
1087
+ }
1088
+ return record;
1089
+ }
1090
+ var FirestoreErrorAdapter = class {
1091
+ /** @throws {RangeError} if `options.timeoutMs` is not a positive number or `Infinity`. */
1092
+ constructor(collection, options = {}) {
1093
+ this.collection = collection;
1094
+ this.prefix = options.prefix ?? "[error-log-adapter]";
1095
+ this.timeoutMs = resolveTimeout(options, "FirestoreErrorAdapter");
1096
+ }
1097
+ async log(entry) {
1098
+ try {
1099
+ const record = buildRecord(entry);
1100
+ await waitForWrite(
1101
+ this.collection.doc(record.id).set(record),
1102
+ this.timeoutMs
1103
+ );
1104
+ return { ok: true };
1105
+ } catch (error) {
1106
+ console.error(`${this.prefix} FirestoreErrorAdapter:`, error);
1107
+ return {
1108
+ ok: false,
1109
+ error: `FirestoreErrorAdapter failed to write the document: ${describeError(error)}`
1110
+ };
1111
+ }
1112
+ }
1113
+ };
1114
+ var RealtimeDatabaseErrorAdapter = class {
1115
+ /** @throws {RangeError} if `options.timeoutMs` is not a positive number or `Infinity`. */
1116
+ constructor(reference, options = {}) {
1117
+ this.reference = reference;
1118
+ this.prefix = options.prefix ?? "[error-log-adapter]";
1119
+ this.timeoutMs = resolveTimeout(options, "RealtimeDatabaseErrorAdapter");
1120
+ }
1121
+ async log(entry) {
1122
+ try {
1123
+ await waitForWrite(
1124
+ this.reference.push(buildRecord(entry)),
1125
+ this.timeoutMs
1126
+ );
1127
+ return { ok: true };
1128
+ } catch (error) {
1129
+ console.error(`${this.prefix} RealtimeDatabaseErrorAdapter:`, error);
1130
+ return {
1131
+ ok: false,
1132
+ error: `RealtimeDatabaseErrorAdapter failed to write the entry: ${describeError(error)}`
1133
+ };
1134
+ }
1135
+ }
1136
+ };
1137
+
1138
+ // src/adapters/supabase-error-adapter.ts
1139
+ var IDENTIFIER_PATTERN3 = /^[A-Za-z_][A-Za-z0-9_]*$/;
1140
+ function buildSupabaseSetupSql(tableName = DEFAULT_TABLE_NAME2, options = {}) {
1141
+ const insertRoles = options.insertRoles ?? [];
1142
+ for (const role of insertRoles) {
1143
+ if (!IDENTIFIER_PATTERN3.test(role)) {
1144
+ throw new RangeError(
1145
+ `buildSupabaseSetupSql: invalid role name "${role}". Use only letters, digits and underscores, and do not start with a digit.`
1146
+ );
1147
+ }
1148
+ }
1149
+ const statements = [
1150
+ ...buildErrorLogTableDdl("postgres", tableName),
1151
+ `ALTER TABLE "${tableName}" ENABLE ROW LEVEL SECURITY`
1152
+ ];
1153
+ if (insertRoles.length > 0) {
1154
+ const policy = `${tableName}_insert_only`;
1155
+ statements.push(
1156
+ `DROP POLICY IF EXISTS "${policy}" ON "${tableName}"`,
1157
+ `CREATE POLICY "${policy}" ON "${tableName}" FOR INSERT TO ${insertRoles.map((role) => `"${role}"`).join(", ")} WITH CHECK (true)`
1158
+ );
1159
+ }
1160
+ return statements;
1161
+ }
1162
+ function describeSupabaseError(result) {
1163
+ const error = result.error ?? { message: "" };
1164
+ const parts = [];
1165
+ if (error.message) {
1166
+ parts.push(error.message);
1167
+ } else if (result.status) {
1168
+ parts.push(
1169
+ `HTTP ${result.status}${result.statusText ? ` ${result.statusText}` : ""} with no error message (a 404 usually means the table does not exist or is not exposed by the API)`
1170
+ );
1171
+ } else {
1172
+ parts.push("unknown error with no message");
1173
+ }
1174
+ if (error.code) {
1175
+ parts.push(`code ${error.code}`);
1176
+ }
1177
+ const detailsLine = (error.details ?? "").split("\n")[0].trim();
1178
+ if (detailsLine && detailsLine !== error.message) {
1179
+ parts.push(`details: ${detailsLine}`);
1180
+ }
1181
+ if (error.hint) {
1182
+ parts.push(`hint: ${error.hint}`);
1183
+ }
1184
+ return parts.join("; ");
1185
+ }
1186
+ var SupabaseErrorAdapter = class {
1187
+ /** @throws {RangeError} if `options.tableName` is empty. */
1188
+ constructor(client, options = {}) {
1189
+ this.client = client;
1190
+ this.tableName = options.tableName ?? DEFAULT_TABLE_NAME2;
1191
+ if (this.tableName.trim() === "") {
1192
+ throw new RangeError(
1193
+ "SupabaseErrorAdapter: tableName must not be empty."
1194
+ );
1195
+ }
1196
+ this.prefix = options.prefix ?? "[error-log-adapter]";
1197
+ }
1198
+ async log(entry) {
1199
+ try {
1200
+ const serialized = serializeEntry(entry);
1201
+ const row = {
1202
+ id: crypto.randomUUID(),
1203
+ code: serialized.code,
1204
+ message: serialized.message,
1205
+ cause: serialized.cause === void 0 ? null : serialized.cause,
1206
+ source: serialized.source ?? null,
1207
+ occurred_at: serialized.timestamp
1208
+ };
1209
+ const result = await this.client.from(this.tableName).insert(row);
1210
+ if (result.error) {
1211
+ console.error(`${this.prefix} SupabaseErrorAdapter:`, result);
1212
+ return this.fail(describeSupabaseError(result));
1213
+ }
1214
+ return { ok: true };
1215
+ } catch (error) {
1216
+ console.error(`${this.prefix} SupabaseErrorAdapter:`, error);
1217
+ return this.fail(describeError(error));
1218
+ }
1219
+ }
1220
+ fail(reason) {
1221
+ return {
1222
+ ok: false,
1223
+ error: `SupabaseErrorAdapter failed to insert into table "${this.tableName}": ${reason}`
1224
+ };
1225
+ }
1226
+ };
1032
1227
  // Annotate the CommonJS export names for ESM import in node:
1033
1228
  0 && (module.exports = {
1034
1229
  CompositeErrorAdapter,
1035
1230
  ConsoleErrorAdapter,
1231
+ FirestoreErrorAdapter,
1036
1232
  FlatFileErrorAdapter,
1037
1233
  IndexedDbErrorAdapter,
1038
1234
  JsonFileErrorAdapter,
1039
1235
  MongoDbErrorAdapter,
1040
1236
  MySqlErrorAdapter,
1041
1237
  PostgresErrorAdapter,
1238
+ RealtimeDatabaseErrorAdapter,
1042
1239
  RedisErrorAdapter,
1043
1240
  S3ErrorAdapter,
1044
1241
  SqliteErrorAdapter,
1242
+ SupabaseErrorAdapter,
1045
1243
  WebStorageErrorAdapter,
1046
1244
  WebhookErrorAdapter,
1047
1245
  buildCreateErrorLogTableSql,
1048
1246
  buildErrorLogTableDdl,
1247
+ buildSupabaseSetupSql,
1049
1248
  createErrorLogStore,
1050
1249
  formatErrorLogLine,
1051
1250
  serializeCause,