@forestadmin/agent 1.95.2 → 1.97.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.
Files changed (34) hide show
  1. package/dist/agent.d.ts +4 -0
  2. package/dist/agent.js +57 -6
  3. package/dist/audit-trail/action-capture.d.ts +17 -0
  4. package/dist/audit-trail/action-capture.js +97 -0
  5. package/dist/audit-trail/index.d.ts +8 -0
  6. package/dist/audit-trail/index.js +44 -0
  7. package/dist/audit-trail/instrument.d.ts +12 -0
  8. package/dist/audit-trail/instrument.js +393 -0
  9. package/dist/audit-trail/migrations.d.ts +34 -0
  10. package/dist/audit-trail/migrations.js +213 -0
  11. package/dist/audit-trail/revert.d.ts +8 -0
  12. package/dist/audit-trail/revert.js +81 -0
  13. package/dist/audit-trail/scope.d.ts +20 -0
  14. package/dist/audit-trail/scope.js +33 -0
  15. package/dist/audit-trail/sql-store.d.ts +30 -0
  16. package/dist/audit-trail/sql-store.js +316 -0
  17. package/dist/audit-trail/types.d.ts +129 -0
  18. package/dist/audit-trail/types.js +3 -0
  19. package/dist/routes/access/audit-trail-correlation.d.ts +18 -0
  20. package/dist/routes/access/audit-trail-correlation.js +84 -0
  21. package/dist/routes/access/audit-trail.d.ts +23 -0
  22. package/dist/routes/access/audit-trail.js +293 -0
  23. package/dist/routes/capabilities.js +5 -1
  24. package/dist/routes/index.js +14 -1
  25. package/dist/routes/modification/action/action.d.ts +3 -0
  26. package/dist/routes/modification/action/action.js +106 -2
  27. package/dist/types.d.ts +39 -1
  28. package/dist/types.js +1 -1
  29. package/dist/utils/correlation-id.d.ts +5 -0
  30. package/dist/utils/correlation-id.js +26 -0
  31. package/dist/utils/options-validator.js +19 -1
  32. package/dist/utils/query-string.d.ts +3 -1
  33. package/dist/utils/query-string.js +5 -5
  34. package/package.json +8 -7
@@ -0,0 +1,393 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REDACTED = exports.MAX_SNAPSHOT_RECORDS = void 0;
4
+ exports.buildRecorder = buildRecorder;
5
+ exports.default = installAuditTrailHooks;
6
+ const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
7
+ const stableKeyOrderReplacer = (key, value) => {
8
+ // JSON.stringify has no native BigInt support and throws on one, regardless of nesting depth or
9
+ // whether it's the root value; the replacer gets a chance to convert it first either way.
10
+ if (typeof value === 'bigint')
11
+ return value.toString();
12
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
13
+ return Object.keys(value)
14
+ .sort()
15
+ .reduce((sorted, name) => {
16
+ sorted[name] = value[name];
17
+ return sorted;
18
+ }, {});
19
+ }
20
+ return value;
21
+ };
22
+ // Date/BSON values round-trip through JSON.stringify via their toJSON; the replacer makes
23
+ // plain-object key order irrelevant.
24
+ const deepEquals = (a, b) => JSON.stringify(a, stableKeyOrderReplacer) === JSON.stringify(b, stableKeyOrderReplacer);
25
+ // A value with its own `toJSON` (Date, a BSON wrapper) has a canonical representation that
26
+ // `Object.entries` can't be trusted to reproduce: BSON's `toJSON` is typically on the prototype
27
+ // while the actual data lives in unrelated (and sometimes non-enumerable) own fields, so walking
28
+ // `Object.entries` would silently reconstruct the wrong — or an empty — object instead.
29
+ const hasCanonicalJson = (value) => typeof value.toJSON === 'function';
30
+ // A raw BigInt surviving into a stored `previousValues`/`newValues` would fail later — Sequelize
31
+ // serializing the JSON column, or Koa serializing an HTTP history response — the same way
32
+ // JSON.stringify does above. Recurses through arrays and plain objects (leaving anything with its
33
+ // own `toJSON` untouched, see above) since a BigInt can be nested inside a JSON column's value
34
+ // rather than being the column's value itself — `pickColumns` hands this a whole column value
35
+ // unprocessed by `diff`'s own recursion, and a primitive array is a `diff` leaf kept whole rather
36
+ // than walked.
37
+ const toJsonSafe = (value) => {
38
+ if (typeof value === 'bigint')
39
+ return value.toString();
40
+ if (Array.isArray(value))
41
+ return value.map(toJsonSafe);
42
+ if (value && typeof value === 'object' && !hasCanonicalJson(value)) {
43
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
44
+ key,
45
+ toJsonSafe(entry),
46
+ ]));
47
+ }
48
+ return value;
49
+ };
50
+ // Records touched by a bulk operation, bounded so one write can't hold an unbounded result set in
51
+ // memory (or fan out an unbounded number of pending-row inserts). Truncation is logged explicitly
52
+ // rather than silently auditing only part of the operation. Also the cap a smart action's audited
53
+ // selection is checked against (action.ts) — one constant, so the two can't drift apart the way this
54
+ // value and the Ruby agent's once did.
55
+ exports.MAX_SNAPSHOT_RECORDS = 1000;
56
+ // One caller-supplied `Filter` object can be reused across concurrent (or sequential, e.g. a
57
+ // continue-on-error bulk loop) operations sharing the same template, so each key holds a queue of
58
+ // snapshots rather than a single value — otherwise a second `Before` would overwrite the first
59
+ // snapshot and its `After` would silently emit nothing.
60
+ const snapshotsByFilter = new WeakMap();
61
+ function pushSnapshot(key, entry) {
62
+ const queue = snapshotsByFilter.get(key) ?? [];
63
+ queue.push(entry);
64
+ snapshotsByFilter.set(key, queue);
65
+ }
66
+ // Correlates by `patch` content rather than queue position: if a write throws, its `Before`
67
+ // snapshot is never consumed (there is no matching `After` call), so blindly taking the front of
68
+ // the queue would hand a later, unrelated operation someone else's stale snapshot instead of its
69
+ // own. Delete has no equivalent secondary key, so it falls back to FIFO order — a narrower version
70
+ // of the same risk that remains for deletes sharing one `Filter` reference.
71
+ function takeSnapshot(key, patch) {
72
+ const queue = snapshotsByFilter.get(key);
73
+ if (!queue || queue.length === 0)
74
+ return undefined;
75
+ const index = patch ? queue.findIndex(entry => deepEquals(entry.patch, patch)) : 0;
76
+ const [entry] = queue.splice(index === -1 ? 0 : index, 1);
77
+ if (queue.length === 0)
78
+ snapshotsByFilter.delete(key);
79
+ return entry;
80
+ }
81
+ // Create has no Filter to key by (there's nothing to select yet), but `context.data` is the exact
82
+ // same array reference in both the Before and After hook for one `.create()` call — verified via
83
+ // `HookBeforeCreateContext`/`HookAfterCreateContext`, which both wrap the literal `data` argument.
84
+ const pendingIdsByData = new WeakMap();
85
+ // Must match IdUtils.packId so recordIds align with the rest of Forest.
86
+ const toPackedRecordId = (record, primaryKeys) => {
87
+ if (primaryKeys.length === 0) {
88
+ throw new Error('Cannot audit a collection with no primary key');
89
+ }
90
+ return primaryKeys.map(pk => String(record[pk])).join('|');
91
+ };
92
+ const pickColumns = (record, columns) => Object.fromEntries(columns.map(column => [column, toJsonSafe(record[column]) ?? null]));
93
+ const isPlainObject = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date);
94
+ const isObjectArray = (value) => Array.isArray(value) && value.length > 0 && value.every(isPlainObject);
95
+ // Returns only the leaves that changed. Plain objects and object-arrays recurse; everything else
96
+ // (scalars, primitive arrays, Date, BSON) is compared and kept whole. A leaf that did not exist
97
+ // before/after (as opposed to one explicitly set to `null`) is marked `*Absent`, so the caller
98
+ // simply omits that key/index from that side rather than writing a placeholder value: the stored
99
+ // diff itself never carries a marker, it just doesn't have the key.
100
+ const diff = (before, after) => {
101
+ if (deepEquals(before, after))
102
+ return null;
103
+ if (isPlainObject(before) && isPlainObject(after)) {
104
+ const previous = {};
105
+ const next = {};
106
+ for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
107
+ const subDelta = diff(before[key], after[key]);
108
+ if (subDelta) {
109
+ if (!subDelta.previousAbsent)
110
+ previous[key] = subDelta.previous;
111
+ if (!subDelta.nextAbsent)
112
+ next[key] = subDelta.next;
113
+ }
114
+ }
115
+ return { previous, next, previousAbsent: false, nextAbsent: false };
116
+ }
117
+ if (isObjectArray(before) && isObjectArray(after)) {
118
+ const previous = {};
119
+ const next = {};
120
+ for (let index = 0; index < Math.max(before.length, after.length); index += 1) {
121
+ const subDelta = diff(before[index], after[index]);
122
+ if (subDelta) {
123
+ if (!subDelta.previousAbsent)
124
+ previous[index] = subDelta.previous;
125
+ if (!subDelta.nextAbsent)
126
+ next[index] = subDelta.next;
127
+ }
128
+ }
129
+ return { previous, next, previousAbsent: false, nextAbsent: false };
130
+ }
131
+ return {
132
+ previous: before === undefined ? undefined : toJsonSafe(before) ?? null,
133
+ next: after === undefined ? undefined : toJsonSafe(after) ?? null,
134
+ previousAbsent: before === undefined,
135
+ nextAbsent: after === undefined,
136
+ };
137
+ };
138
+ // Diffs against the record actually persisted by the write (`after`), not the requested patch, so
139
+ // the log reflects normalization/coercion/side effects applied by the datasource or a decorator.
140
+ const changedValues = (before, after, columns) => {
141
+ const previousValues = {};
142
+ const newValues = {};
143
+ for (const column of columns) {
144
+ const delta = diff(before[column], after[column]);
145
+ if (delta) {
146
+ if (!delta.previousAbsent)
147
+ previousValues[column] = delta.previous;
148
+ if (!delta.nextAbsent)
149
+ newValues[column] = delta.next;
150
+ }
151
+ }
152
+ return { previousValues, newValues };
153
+ };
154
+ exports.REDACTED = '[redacted]';
155
+ const redactValues = (values, redactedFields) => {
156
+ if (!redactedFields.length)
157
+ return values;
158
+ const result = { ...values };
159
+ for (const field of redactedFields)
160
+ if (field in result)
161
+ result[field] = exports.REDACTED;
162
+ return result;
163
+ };
164
+ const consoleSink = record => {
165
+ // eslint-disable-next-line no-console
166
+ console.info('[audit-trail]', JSON.stringify(record));
167
+ };
168
+ function buildRecorder(store, sink, critical, logger) {
169
+ if (store) {
170
+ return {
171
+ async insertPendingBatch(records) {
172
+ if (records.length === 0)
173
+ return [];
174
+ try {
175
+ return await store.insertPendingBatch(records);
176
+ }
177
+ catch (error) {
178
+ // Critical: no unaudited write — refuse the operation before it runs. Non-critical:
179
+ // logged and swallowed, exactly like today's single-shot failures; the write proceeds
180
+ // with nothing to confirm later.
181
+ if (critical)
182
+ throw error;
183
+ logger?.('Error', `[ForestAdmin] Audit trail: unable to record pending entries, continuing: ${error}`);
184
+ return records.map(() => null);
185
+ }
186
+ },
187
+ async confirm(id, patch) {
188
+ if (id === null)
189
+ return;
190
+ try {
191
+ await store.confirm(id, patch);
192
+ }
193
+ catch (error) {
194
+ // The write has already happened by now; there is nothing left to refuse.
195
+ logger?.('Error', `[ForestAdmin] Audit trail: unable to confirm entry, continuing: ${error}`);
196
+ }
197
+ },
198
+ };
199
+ }
200
+ const emit = sink ?? consoleSink;
201
+ const pendingById = new Map();
202
+ let nextId = 1;
203
+ return {
204
+ async insertPendingBatch(records) {
205
+ return records.map(record => {
206
+ const id = nextId;
207
+ nextId += 1;
208
+ pendingById.set(id, record);
209
+ return id;
210
+ });
211
+ },
212
+ async confirm(id, patch) {
213
+ if (id === null)
214
+ return;
215
+ const pending = pendingById.get(id);
216
+ pendingById.delete(id);
217
+ if (!pending)
218
+ return;
219
+ try {
220
+ await emit({ ...pending, ...patch });
221
+ }
222
+ catch (error) {
223
+ // The write has already happened by now; there is nothing left to refuse.
224
+ logger?.('Error', `[ForestAdmin] Audit trail: unable to confirm entry, continuing: ${error}`);
225
+ }
226
+ },
227
+ };
228
+ }
229
+ function instrumentCollection(collection, recorder, redactedFields, logger, critical) {
230
+ const { schema } = collection;
231
+ const writableColumns = Object.keys(schema.fields).filter(name => {
232
+ const field = schema.fields[name];
233
+ return field.type === 'Column' && !field.isReadOnly;
234
+ });
235
+ const primaryKeys = datasource_toolkit_1.SchemaUtils.getPrimaryKeys(schema);
236
+ // PKs may be read-only, but they are required to build the packed recordId; the diff itself
237
+ // stays restricted to writableColumns.
238
+ const readProjection = [...new Set([...primaryKeys, ...writableColumns])];
239
+ const { name } = collection;
240
+ const buildRecord = (caller, entry) => ({
241
+ timestamp: new Date().toISOString(),
242
+ collection: name,
243
+ userId: caller.id,
244
+ userFirstName: caller.firstName,
245
+ userLastName: caller.lastName,
246
+ userEmail: caller.email,
247
+ actionName: null,
248
+ correlationKey: caller.requestId,
249
+ ...entry,
250
+ previousValues: redactValues(entry.previousValues, redactedFields),
251
+ newValues: redactValues(entry.newValues, redactedFields),
252
+ });
253
+ // Requests one more than the cap: fetching exactly `MAX_SNAPSHOT_RECORDS` can never distinguish
254
+ // "matched exactly the cap" (nothing lost) from "matched more than it" (truncated) — the extra
255
+ // row is what makes that distinguishable, and is dropped again below before anything is audited.
256
+ // Constructs a real `PaginatedFilter` (via `.override`-equivalent spread of `context.filter`'s
257
+ // own fields, which is a plain `Filter` for Update/Delete) rather than merging `page` onto a bare
258
+ // object: the latter would depend on `context.collection.list` happening to reconstruct a proper
259
+ // instance from whatever shape it's handed, which isn't part of its contract.
260
+ const withLimit = (filter) => new datasource_toolkit_1.PaginatedFilter({ ...filter, page: new datasource_toolkit_1.Page(0, exports.MAX_SNAPSHOT_RECORDS + 1) });
261
+ // Hitting the cap means the operation may have matched more records than were actually audited.
262
+ // Under `critical: false`, silently dropping the excess would look identical to "everything got
263
+ // audited" — logged instead. Under `critical: true`, that gap is exactly what the option exists
264
+ // to refuse: no unaudited write, so the whole operation is refused rather than let through
265
+ // partially covered.
266
+ const enforceSnapshotCap = (operation, truncated) => {
267
+ if (!truncated)
268
+ return;
269
+ const message = `[ForestAdmin] Audit trail: "${name}" ${operation} matched more than ` +
270
+ `${exports.MAX_SNAPSHOT_RECORDS} records — only the first ${exports.MAX_SNAPSHOT_RECORDS} can be audited.`;
271
+ if (critical) {
272
+ throw new Error(`${message} Refusing the operation because "critical" is enabled.`);
273
+ }
274
+ logger?.('Warn', `${message} The rest were skipped.`);
275
+ };
276
+ collection.addInternalHook('Before', 'Create', async (context) => {
277
+ const pendingRecords = context.data.map(() => buildRecord(context.caller, {
278
+ operation: 'create',
279
+ recordId: null,
280
+ previousValues: {},
281
+ newValues: {},
282
+ }));
283
+ const pendingIds = await recorder.insertPendingBatch(pendingRecords);
284
+ pendingIdsByData.set(context.data, pendingIds);
285
+ });
286
+ collection.addInternalHook('After', 'Create', async (context) => {
287
+ const pendingIds = pendingIdsByData.get(context.data) ?? [];
288
+ pendingIdsByData.delete(context.data);
289
+ await Promise.all(context.records.map((record, index) => recorder.confirm(pendingIds[index] ?? null, {
290
+ operation: 'create',
291
+ recordId: toPackedRecordId(record, primaryKeys),
292
+ previousValues: {},
293
+ newValues: redactValues(pickColumns(record, writableColumns), redactedFields),
294
+ })));
295
+ });
296
+ collection.addInternalHook('Before', 'Update', async (context) => {
297
+ const fetched = (await context.collection.list(
298
+ // `context.filter` is typed as the plain-object `TFilter` shape for customer ergonomics, but
299
+ // is a real (frozen) `Filter` instance at runtime — the same cast the hook decorator itself
300
+ // performs to produce it.
301
+ withLimit(context.filter), readProjection));
302
+ const truncated = fetched.length > exports.MAX_SNAPSHOT_RECORDS;
303
+ enforceSnapshotCap('update', truncated);
304
+ const before = truncated ? fetched.slice(0, exports.MAX_SNAPSHOT_RECORDS) : fetched;
305
+ const pendingRecords = before.map(record => buildRecord(context.caller, {
306
+ operation: 'update',
307
+ recordId: toPackedRecordId(record, primaryKeys),
308
+ previousValues: pickColumns(record, writableColumns),
309
+ newValues: {},
310
+ }));
311
+ const pendingIds = await recorder.insertPendingBatch(pendingRecords);
312
+ pushSnapshot(context.filter, { before, patch: context.patch, pendingIds });
313
+ });
314
+ collection.addInternalHook('After', 'Update', async (context) => {
315
+ const snapshot = takeSnapshot(context.filter, context.patch);
316
+ if (!snapshot || snapshot.before.length === 0)
317
+ return;
318
+ const { before, pendingIds } = snapshot;
319
+ // Re-read each touched record by its own (possibly patch-changed) identity rather than via
320
+ // `context.filter`: if the update changed a field the filter matched on — including a
321
+ // writable primary key — re-querying with that same filter would find nothing, silently
322
+ // dropping the entry instead of filing it under the record's new id.
323
+ const expectedRecords = before.map(record => ({ ...record, ...context.patch }));
324
+ const expectedIds = expectedRecords.map(record => primaryKeys.map(pk => record[pk]));
325
+ const after = (await context.collection.list({ conditionTree: datasource_toolkit_1.ConditionTreeFactory.matchIds(schema, expectedIds) }, readProjection));
326
+ const afterById = new Map(after.map(record => [toPackedRecordId(record, primaryKeys), record]));
327
+ await Promise.all(before.map((record, index) => {
328
+ const updated = afterById.get(toPackedRecordId(expectedRecords[index], primaryKeys));
329
+ const pendingId = pendingIds[index] ?? null;
330
+ if (!updated)
331
+ return undefined;
332
+ const { previousValues, newValues } = changedValues(record, updated, writableColumns);
333
+ // Confirmed even when empty: the write still ran and resolved, so leaving the pending row
334
+ // unconfirmed would misrepresent a completed no-op write as one that never landed.
335
+ // Packed from the post-update values: if the update changed a writable primary key, the
336
+ // entry is filed under the record's new id, matching what later history lookups use.
337
+ return recorder.confirm(pendingId, {
338
+ operation: 'update',
339
+ recordId: toPackedRecordId(updated, primaryKeys),
340
+ previousValues: redactValues(previousValues, redactedFields),
341
+ newValues: redactValues(newValues, redactedFields),
342
+ });
343
+ }));
344
+ });
345
+ collection.addInternalHook('Before', 'Delete', async (context) => {
346
+ const fetched = (await context.collection.list(
347
+ // `context.filter` is typed as the plain-object `TFilter` shape for customer ergonomics, but
348
+ // is a real (frozen) `Filter` instance at runtime — the same cast the hook decorator itself
349
+ // performs to produce it.
350
+ withLimit(context.filter), readProjection));
351
+ const truncated = fetched.length > exports.MAX_SNAPSHOT_RECORDS;
352
+ enforceSnapshotCap('delete', truncated);
353
+ const before = truncated ? fetched.slice(0, exports.MAX_SNAPSHOT_RECORDS) : fetched;
354
+ const pendingRecords = before.map(record => buildRecord(context.caller, {
355
+ operation: 'delete',
356
+ recordId: toPackedRecordId(record, primaryKeys),
357
+ previousValues: pickColumns(record, writableColumns),
358
+ newValues: {},
359
+ }));
360
+ const pendingIds = await recorder.insertPendingBatch(pendingRecords);
361
+ pushSnapshot(context.filter, { before, pendingIds });
362
+ });
363
+ collection.addInternalHook('After', 'Delete', async (context) => {
364
+ const snapshot = takeSnapshot(context.filter);
365
+ if (!snapshot)
366
+ return;
367
+ const { before, pendingIds } = snapshot;
368
+ await Promise.all(before.map((record, index) => recorder.confirm(pendingIds[index] ?? null, {
369
+ operation: 'delete',
370
+ recordId: toPackedRecordId(record, primaryKeys),
371
+ previousValues: redactValues(pickColumns(record, writableColumns), redactedFields),
372
+ newValues: {},
373
+ })));
374
+ });
375
+ }
376
+ function installAuditTrailHooks(dataSourceCustomizer, _collectionCustomizer, options) {
377
+ const { sink, store, redact, critical = false, logger } = options ?? {};
378
+ const recorder = buildRecorder(store, sink, critical, logger);
379
+ for (const collection of dataSourceCustomizer.collections) {
380
+ // toPackedRecordId throws for a collection with no primary key — there is nothing to key an
381
+ // audit entry on, so skip instrumenting it entirely rather than installing hooks that would
382
+ // throw on the collection's first write.
383
+ if (datasource_toolkit_1.SchemaUtils.getPrimaryKeys(collection.schema).length === 0) {
384
+ logger?.('Warn', `[ForestAdmin] Audit trail: skipping "${collection.name}" — it has no primary key.`);
385
+ }
386
+ else {
387
+ instrumentCollection(collection, recorder, redact?.[collection.name] ?? [], logger, critical);
388
+ }
389
+ }
390
+ // Returned so a broken bootstrap fails agent.start() instead of the first write.
391
+ return store?.init?.();
392
+ }
393
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdHJ1bWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hdWRpdC10cmFpbC9pbnN0cnVtZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQW9QQSxzQ0EyRUM7QUFrTkQseUNBd0JDO0FBeGhCRCx3RUFLeUM7QUFFekMsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLEdBQVcsRUFBRSxLQUFjLEVBQVcsRUFBRTtJQUN0RSxnR0FBZ0c7SUFDaEcsMEZBQTBGO0lBQzFGLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUTtRQUFFLE9BQU8sS0FBSyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBRXZELElBQUksS0FBSyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNoRSxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBZ0MsQ0FBQzthQUNqRCxJQUFJLEVBQUU7YUFDTixNQUFNLENBQTBCLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxFQUFFO1lBQ2hELE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBSSxLQUFpQyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBRXhELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNYLENBQUM7SUFFRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMsQ0FBQztBQUVGLDBGQUEwRjtBQUMxRixxQ0FBcUM7QUFDckMsTUFBTSxVQUFVLEdBQUcsQ0FBQyxDQUFVLEVBQUUsQ0FBVSxFQUFXLEVBQUUsQ0FDckQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsc0JBQXNCLENBQUMsS0FBSyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxzQkFBc0IsQ0FBQyxDQUFDO0FBRTFGLDJGQUEyRjtBQUMzRixnR0FBZ0c7QUFDaEcsaUdBQWlHO0FBQ2pHLHdGQUF3RjtBQUN4RixNQUFN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
@@ -0,0 +1,34 @@
1
+ import type { QueryInterface, Sequelize, Transaction } from 'sequelize';
2
+ import { Umzug } from 'umzug';
3
+ export type MigrationContext = {
4
+ queryInterface: QueryInterface;
5
+ schema?: string;
6
+ tableName: string;
7
+ transaction?: Transaction;
8
+ };
9
+ export declare function buildUmzug(sequelize: Sequelize, options: {
10
+ schema?: string;
11
+ tableName: string;
12
+ transaction?: Transaction;
13
+ }): Umzug<MigrationContext>;
14
+ export declare function isSchemaAlreadyExistsError(error: unknown): boolean;
15
+ /**
16
+ * Idempotent CREATE SCHEMA. Must run (and commit) on its own before Umzug, because Umzug's storage
17
+ * opens its own connection to create the meta table and would not see a CREATE SCHEMA still
18
+ * pending in a transaction. Not covered by the advisory lock; the existence check + tolerated
19
+ * "already exists" error makes it safe under concurrent boots instead.
20
+ */
21
+ export declare function ensureSchema(sequelize: Sequelize, schema: string | undefined): Promise<void>;
22
+ /**
23
+ * Creates the schema then applies every pending audit-table migration. On Postgres the migrations
24
+ * run inside a transaction-scoped advisory lock, and every DDL statement runs on that same
25
+ * transaction, so concurrent agent boots serialize on the DDL and a failure partway through a
26
+ * migration rolls back cleanly instead of leaving partial DDL behind. Other dialects have no
27
+ * equivalent distributed lock; each migration instead checks what already exists before creating
28
+ * it, which closes most — but not all — of the race window on a truly simultaneous boot.
29
+ */
30
+ export declare function runAuditMigrations(sequelize: Sequelize, options: {
31
+ schema?: string;
32
+ tableName: string;
33
+ }): Promise<void>;
34
+ //# sourceMappingURL=migrations.d.ts.map