@replayio-app-building/netlify-recorder 0.19.0 → 0.20.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/README.md +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -464,13 +464,14 @@ Creates the `audit_log` table, its indexes, and a reusable PL/pgSQL trigger func
|
|
|
464
464
|
**Parameters:**
|
|
465
465
|
- `sql` — A Neon SQL tagged-template function
|
|
466
466
|
|
|
467
|
-
### `databaseAuditMonitorTable(sql, tableName): Promise<void>`
|
|
467
|
+
### `databaseAuditMonitorTable(sql, tableName, primaryKeyColumn?): Promise<void>`
|
|
468
468
|
|
|
469
469
|
Attaches a trigger to the specified table that logs INSERT, UPDATE, and DELETE operations to the `audit_log` table. Throws if `tableName` is `'audit_log'`.
|
|
470
470
|
|
|
471
471
|
**Parameters:**
|
|
472
472
|
- `sql` — A Neon SQL tagged-template function
|
|
473
473
|
- `tableName` — Name of the table to monitor (must match `^[a-zA-Z_][a-zA-Z0-9_]*$`)
|
|
474
|
+
- `primaryKeyColumn` — Name of the primary key column (default: `"id"`, must match `^[a-zA-Z_][a-zA-Z0-9_]*$`)
|
|
474
475
|
|
|
475
476
|
### `databaseAuditDumpLogTable(sql): Promise<Record<string, unknown>[]>`
|
|
476
477
|
|
package/dist/index.d.ts
CHANGED
|
@@ -332,7 +332,7 @@ declare function databaseAuditEnsureLogTable(sql: SqlFunction): Promise<void>;
|
|
|
332
332
|
*
|
|
333
333
|
* Throws if `tableName` is `'audit_log'` (cannot monitor itself).
|
|
334
334
|
*/
|
|
335
|
-
declare function databaseAuditMonitorTable(sql: SqlFunction, tableName: string): Promise<void>;
|
|
335
|
+
declare function databaseAuditMonitorTable(sql: SqlFunction, tableName: string, primaryKeyColumn?: string): Promise<void>;
|
|
336
336
|
/**
|
|
337
337
|
* Returns all rows from the `audit_log` table, ordered by `performed_at` DESC.
|
|
338
338
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1095,7 +1095,12 @@ async function databaseAuditEnsureLogTable(sql) {
|
|
|
1095
1095
|
changed_cols TEXT[];
|
|
1096
1096
|
req_id TEXT;
|
|
1097
1097
|
call_idx INTEGER;
|
|
1098
|
+
pk_col TEXT;
|
|
1099
|
+
pk_val TEXT;
|
|
1098
1100
|
BEGIN
|
|
1101
|
+
-- Primary key column name passed as trigger argument; default to 'id'
|
|
1102
|
+
pk_col := COALESCE(TG_ARGV[0], 'id');
|
|
1103
|
+
|
|
1099
1104
|
-- Read application context injected by the network interceptor
|
|
1100
1105
|
req_id := COALESCE(current_setting('app.replay_request_id', true), '');
|
|
1101
1106
|
IF req_id = '' THEN req_id := NULL; END IF;
|
|
@@ -1107,21 +1112,24 @@ async function databaseAuditEnsureLogTable(sql) {
|
|
|
1107
1112
|
END;
|
|
1108
1113
|
|
|
1109
1114
|
IF TG_OP = 'INSERT' THEN
|
|
1115
|
+
EXECUTE format('SELECT ($1).%I::TEXT', pk_col) USING NEW INTO pk_val;
|
|
1110
1116
|
INSERT INTO audit_log (table_name, record_id, action, new_data, replay_request_id, replay_request_call_index)
|
|
1111
|
-
VALUES (TG_TABLE_NAME,
|
|
1117
|
+
VALUES (TG_TABLE_NAME, pk_val, 'INSERT', to_jsonb(NEW), req_id, call_idx);
|
|
1112
1118
|
RETURN NEW;
|
|
1113
1119
|
ELSIF TG_OP = 'UPDATE' THEN
|
|
1120
|
+
EXECUTE format('SELECT ($1).%I::TEXT', pk_col) USING NEW INTO pk_val;
|
|
1114
1121
|
SELECT ARRAY_AGG(n.key) INTO changed_cols
|
|
1115
1122
|
FROM jsonb_each_text(to_jsonb(NEW)) n
|
|
1116
1123
|
LEFT JOIN jsonb_each_text(to_jsonb(OLD)) o ON n.key = o.key
|
|
1117
1124
|
WHERE o.value IS DISTINCT FROM n.value;
|
|
1118
1125
|
|
|
1119
1126
|
INSERT INTO audit_log (table_name, record_id, action, old_data, new_data, changed_fields, replay_request_id, replay_request_call_index)
|
|
1120
|
-
VALUES (TG_TABLE_NAME,
|
|
1127
|
+
VALUES (TG_TABLE_NAME, pk_val, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW), COALESCE(changed_cols, ARRAY[]::TEXT[]), req_id, call_idx);
|
|
1121
1128
|
RETURN NEW;
|
|
1122
1129
|
ELSIF TG_OP = 'DELETE' THEN
|
|
1130
|
+
EXECUTE format('SELECT ($1).%I::TEXT', pk_col) USING OLD INTO pk_val;
|
|
1123
1131
|
INSERT INTO audit_log (table_name, record_id, action, old_data, replay_request_id, replay_request_call_index)
|
|
1124
|
-
VALUES (TG_TABLE_NAME,
|
|
1132
|
+
VALUES (TG_TABLE_NAME, pk_val, 'DELETE', to_jsonb(OLD), req_id, call_idx);
|
|
1125
1133
|
RETURN OLD;
|
|
1126
1134
|
END IF;
|
|
1127
1135
|
RETURN NULL;
|
|
@@ -1129,19 +1137,22 @@ async function databaseAuditEnsureLogTable(sql) {
|
|
|
1129
1137
|
$$ LANGUAGE plpgsql
|
|
1130
1138
|
`;
|
|
1131
1139
|
}
|
|
1132
|
-
async function databaseAuditMonitorTable(sql, tableName) {
|
|
1140
|
+
async function databaseAuditMonitorTable(sql, tableName, primaryKeyColumn = "id") {
|
|
1133
1141
|
if (tableName === "audit_log") {
|
|
1134
1142
|
throw new Error("Cannot monitor the audit_log table itself");
|
|
1135
1143
|
}
|
|
1136
1144
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
|
|
1137
1145
|
throw new Error(`Invalid table name: ${tableName}`);
|
|
1138
1146
|
}
|
|
1147
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(primaryKeyColumn)) {
|
|
1148
|
+
throw new Error(`Invalid primary key column name: ${primaryKeyColumn}`);
|
|
1149
|
+
}
|
|
1139
1150
|
const triggerName = `audit_trigger_${tableName}`;
|
|
1140
1151
|
await sql(
|
|
1141
1152
|
`DROP TRIGGER IF EXISTS ${triggerName} ON "${tableName}"`
|
|
1142
1153
|
);
|
|
1143
1154
|
await sql(
|
|
1144
|
-
`CREATE TRIGGER ${triggerName} AFTER INSERT OR UPDATE OR DELETE ON "${tableName}" FOR EACH ROW EXECUTE FUNCTION audit_trigger_function()`
|
|
1155
|
+
`CREATE TRIGGER ${triggerName} AFTER INSERT OR UPDATE OR DELETE ON "${tableName}" FOR EACH ROW EXECUTE FUNCTION audit_trigger_function('${primaryKeyColumn}')`
|
|
1145
1156
|
);
|
|
1146
1157
|
}
|
|
1147
1158
|
async function databaseAuditDumpLogTable(sql) {
|