@replayio-app-building/netlify-recorder 0.12.0 → 0.13.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.d.ts +6 -8
- package/dist/index.js +24 -15
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -442,13 +442,11 @@ declare function databaseAuditMonitorTable(sql: SqlFunction, tableName: string):
|
|
|
442
442
|
*/
|
|
443
443
|
declare function databaseAuditDumpLogTable(sql: SqlFunction): Promise<Record<string, unknown>[]>;
|
|
444
444
|
/**
|
|
445
|
-
* Wraps a Neon SQL tagged-template function so that
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
* `createRecordingRequestHandler`. If no request is active, the
|
|
451
|
-
* wrapper passes calls through without tagging.
|
|
445
|
+
* Wraps a Neon SQL tagged-template function so that before each query,
|
|
446
|
+
* `SET LOCAL` is used to inject the current request ID and call index
|
|
447
|
+
* into the PostgreSQL session. The `audit_trigger_function` reads these
|
|
448
|
+
* via `current_setting()` and stamps audit rows atomically at INSERT
|
|
449
|
+
* time inside the trigger — no separate UPDATE required.
|
|
452
450
|
*
|
|
453
451
|
* @example
|
|
454
452
|
* ```ts
|
|
@@ -456,7 +454,7 @@ declare function databaseAuditDumpLogTable(sql: SqlFunction): Promise<Record<str
|
|
|
456
454
|
*
|
|
457
455
|
* const sql = createAuditedSql(getSql());
|
|
458
456
|
* await sql`INSERT INTO haikus (text) VALUES (${haiku})`;
|
|
459
|
-
* // audit_log
|
|
457
|
+
* // audit_log row created by the trigger already has the request ID
|
|
460
458
|
* ```
|
|
461
459
|
*/
|
|
462
460
|
declare function createAuditedSql(sql: SqlFunction): SqlFunction;
|
package/dist/index.js
CHANGED
|
@@ -1130,10 +1130,22 @@ async function databaseAuditEnsureLogTable(sql) {
|
|
|
1130
1130
|
RETURNS TRIGGER AS $$
|
|
1131
1131
|
DECLARE
|
|
1132
1132
|
changed_cols TEXT[];
|
|
1133
|
+
req_id TEXT;
|
|
1134
|
+
call_idx INTEGER;
|
|
1133
1135
|
BEGIN
|
|
1136
|
+
-- Read application context set via SET LOCAL by createAuditedSql
|
|
1137
|
+
req_id := COALESCE(current_setting('app.replay_request_id', true), '');
|
|
1138
|
+
IF req_id = '' THEN req_id := NULL; END IF;
|
|
1139
|
+
|
|
1140
|
+
BEGIN
|
|
1141
|
+
call_idx := current_setting('app.replay_call_index', true)::INTEGER;
|
|
1142
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1143
|
+
call_idx := NULL;
|
|
1144
|
+
END;
|
|
1145
|
+
|
|
1134
1146
|
IF TG_OP = 'INSERT' THEN
|
|
1135
|
-
INSERT INTO audit_log (table_name, record_id, action, new_data)
|
|
1136
|
-
VALUES (TG_TABLE_NAME, NEW.id::TEXT, 'INSERT', to_jsonb(NEW));
|
|
1147
|
+
INSERT INTO audit_log (table_name, record_id, action, new_data, replay_request_id, replay_request_call_index)
|
|
1148
|
+
VALUES (TG_TABLE_NAME, NEW.id::TEXT, 'INSERT', to_jsonb(NEW), req_id, call_idx);
|
|
1137
1149
|
RETURN NEW;
|
|
1138
1150
|
ELSIF TG_OP = 'UPDATE' THEN
|
|
1139
1151
|
SELECT ARRAY_AGG(n.key) INTO changed_cols
|
|
@@ -1141,12 +1153,12 @@ async function databaseAuditEnsureLogTable(sql) {
|
|
|
1141
1153
|
LEFT JOIN jsonb_each_text(to_jsonb(OLD)) o ON n.key = o.key
|
|
1142
1154
|
WHERE o.value IS DISTINCT FROM n.value;
|
|
1143
1155
|
|
|
1144
|
-
INSERT INTO audit_log (table_name, record_id, action, old_data, new_data, changed_fields)
|
|
1145
|
-
VALUES (TG_TABLE_NAME, NEW.id::TEXT, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW), COALESCE(changed_cols, ARRAY[]::TEXT[]));
|
|
1156
|
+
INSERT INTO audit_log (table_name, record_id, action, old_data, new_data, changed_fields, replay_request_id, replay_request_call_index)
|
|
1157
|
+
VALUES (TG_TABLE_NAME, NEW.id::TEXT, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW), COALESCE(changed_cols, ARRAY[]::TEXT[]), req_id, call_idx);
|
|
1146
1158
|
RETURN NEW;
|
|
1147
1159
|
ELSIF TG_OP = 'DELETE' THEN
|
|
1148
|
-
INSERT INTO audit_log (table_name, record_id, action, old_data)
|
|
1149
|
-
VALUES (TG_TABLE_NAME, OLD.id::TEXT, 'DELETE', to_jsonb(OLD));
|
|
1160
|
+
INSERT INTO audit_log (table_name, record_id, action, old_data, replay_request_id, replay_request_call_index)
|
|
1161
|
+
VALUES (TG_TABLE_NAME, OLD.id::TEXT, 'DELETE', to_jsonb(OLD), req_id, call_idx);
|
|
1150
1162
|
RETURN OLD;
|
|
1151
1163
|
END IF;
|
|
1152
1164
|
RETURN NULL;
|
|
@@ -1177,22 +1189,19 @@ async function databaseAuditDumpLogTable(sql) {
|
|
|
1177
1189
|
}
|
|
1178
1190
|
function createAuditedSql(sql) {
|
|
1179
1191
|
let callIndex = 0;
|
|
1180
|
-
const
|
|
1192
|
+
const rawSql = sql;
|
|
1181
1193
|
const auditedSql = async (strings, ...values) => {
|
|
1182
|
-
const
|
|
1194
|
+
const requestId = getCurrentRequestId();
|
|
1183
1195
|
callIndex++;
|
|
1184
1196
|
if (requestId) {
|
|
1185
1197
|
try {
|
|
1186
|
-
await
|
|
1187
|
-
|
|
1188
|
-
SET replay_request_id = ${requestId}, replay_request_call_index = ${callIndex}
|
|
1189
|
-
WHERE replay_request_id IS NULL
|
|
1190
|
-
`;
|
|
1198
|
+
await rawSql(`SET LOCAL app.replay_request_id = '${requestId.replace(/'/g, "''")}'`);
|
|
1199
|
+
await rawSql(`SET LOCAL app.replay_call_index = '${callIndex}'`);
|
|
1191
1200
|
} catch (err) {
|
|
1192
|
-
console.warn("netlify-recorder: failed to
|
|
1201
|
+
console.warn("netlify-recorder: failed to SET LOCAL audit context:", err);
|
|
1193
1202
|
}
|
|
1194
1203
|
}
|
|
1195
|
-
return
|
|
1204
|
+
return await sql(strings, ...values);
|
|
1196
1205
|
};
|
|
1197
1206
|
return auditedSql;
|
|
1198
1207
|
}
|