@tiangong-lca/cli 0.0.21 → 0.0.23
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 +61 -4
- package/dist/src/cli.js +334 -22
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-alias-rewrite.js +541 -0
- package/dist/src/lib/dataset-maintenance-alias-rewrite.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-apply.js +1540 -0
- package/dist/src/lib/dataset-maintenance-apply.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-clear-account.js +121 -58
- package/dist/src/lib/dataset-maintenance-clear-account.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-contract.js +844 -0
- package/dist/src/lib/dataset-maintenance-contract.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-pagination.js +270 -0
- package/dist/src/lib/dataset-maintenance-pagination.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-plan.js +488 -0
- package/dist/src/lib/dataset-maintenance-plan.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-remote.js +295 -0
- package/dist/src/lib/dataset-maintenance-remote.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-support-validation.js +18 -0
- package/dist/src/lib/dataset-maintenance-support-validation.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-verify.js +693 -0
- package/dist/src/lib/dataset-maintenance-verify.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { collectRemoteReferences } from './dataset-remote-verify.js';
|
|
3
|
+
import { buildAliasRewritePlan, } from './dataset-maintenance-alias-rewrite.js';
|
|
4
|
+
import { CliError } from './errors.js';
|
|
5
|
+
import { computePlanSha256, isJsonObject, maintenanceRowKey, parseMaintenanceScope, readJsonFile, safeActionFileName, sha256Json, snapshotRemoteRow, writeImmutableJson, writeImmutableJsonLines, } from './dataset-maintenance-contract.js';
|
|
6
|
+
import { inspectMaintenanceSupportPayload, maintenancePayloadIdentity, } from './dataset-maintenance-support-validation.js';
|
|
7
|
+
import { fetchMaintenanceAccountRows, fetchMaintenanceExactRows, normalizeMaintenancePageSize, resolveMaintenanceRemoteContext, } from './dataset-maintenance-remote.js';
|
|
8
|
+
function normalizeEmail(value) {
|
|
9
|
+
return value.trim().toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
function blocker(action, code, message, details) {
|
|
12
|
+
return {
|
|
13
|
+
code,
|
|
14
|
+
message,
|
|
15
|
+
action_id: action.action_id,
|
|
16
|
+
table: action.table,
|
|
17
|
+
id: action.id,
|
|
18
|
+
version: action.version,
|
|
19
|
+
...(details === undefined ? {} : { details }),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function referenceImpacts(options) {
|
|
23
|
+
const payloadRows = options.rows
|
|
24
|
+
.filter((row) => row.json_ordered !== null)
|
|
25
|
+
.map((row) => ({
|
|
26
|
+
table: row.table,
|
|
27
|
+
id: row.id,
|
|
28
|
+
version: row.version,
|
|
29
|
+
json_ordered: row.json_ordered,
|
|
30
|
+
}));
|
|
31
|
+
const references = collectRemoteReferences(payloadRows).filter((reference) => reference.role === 'reference');
|
|
32
|
+
const impacts = [];
|
|
33
|
+
for (const reference of references) {
|
|
34
|
+
const source = payloadRows[reference.row_index];
|
|
35
|
+
if (!reference.table || !reference.id) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
for (const target of options.deletes) {
|
|
39
|
+
const sameTarget = reference.table === target.table &&
|
|
40
|
+
reference.id === target.id &&
|
|
41
|
+
(!reference.version || reference.version === target.version);
|
|
42
|
+
if (sameTarget) {
|
|
43
|
+
impacts.push({
|
|
44
|
+
target_action_id: target.action_id,
|
|
45
|
+
target_table: target.table,
|
|
46
|
+
target_id: target.id,
|
|
47
|
+
target_version: target.version,
|
|
48
|
+
phase: options.phase,
|
|
49
|
+
source_table: source.table,
|
|
50
|
+
source_id: source.id,
|
|
51
|
+
source_version: source.version,
|
|
52
|
+
reference_path: reference.path,
|
|
53
|
+
reference_version: reference.version,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return impacts.sort((left, right) => [
|
|
59
|
+
left.target_action_id,
|
|
60
|
+
left.source_table,
|
|
61
|
+
left.source_id,
|
|
62
|
+
left.source_version,
|
|
63
|
+
left.reference_path,
|
|
64
|
+
]
|
|
65
|
+
.join('\u0000')
|
|
66
|
+
.localeCompare([
|
|
67
|
+
right.target_action_id,
|
|
68
|
+
right.source_table,
|
|
69
|
+
right.source_id,
|
|
70
|
+
right.source_version,
|
|
71
|
+
right.reference_path,
|
|
72
|
+
].join('\u0000')));
|
|
73
|
+
}
|
|
74
|
+
function projectedRows(options) {
|
|
75
|
+
const projected = new Map(options.current.map((row) => [maintenanceRowKey(row), { ...row }]));
|
|
76
|
+
for (const action of options.actions.filter((entry) => ['save_draft', 'update_json_ordered'].includes(entry.action))) {
|
|
77
|
+
const key = maintenanceRowKey(action);
|
|
78
|
+
const row = projected.get(key);
|
|
79
|
+
const payload = options.desiredPayloads.get(action.action_id);
|
|
80
|
+
if (row && payload) {
|
|
81
|
+
projected.set(key, { ...row, json_ordered: payload });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const action of options.actions.filter((entry) => entry.action === 'delete')) {
|
|
85
|
+
projected.delete(maintenanceRowKey(action));
|
|
86
|
+
}
|
|
87
|
+
return [...projected.values()].sort((left, right) => maintenanceRowKey(left).localeCompare(maintenanceRowKey(right)));
|
|
88
|
+
}
|
|
89
|
+
export function maintenanceProjectedReferenceFingerprint(rows) {
|
|
90
|
+
const payloadRows = rows
|
|
91
|
+
.filter((row) => row.json_ordered !== null)
|
|
92
|
+
.map((row) => ({
|
|
93
|
+
table: row.table,
|
|
94
|
+
id: row.id,
|
|
95
|
+
version: row.version,
|
|
96
|
+
json_ordered: row.json_ordered,
|
|
97
|
+
}));
|
|
98
|
+
return collectRemoteReferences(payloadRows)
|
|
99
|
+
.filter((reference) => reference.role === 'reference')
|
|
100
|
+
.map((reference) => ({
|
|
101
|
+
source: maintenanceRowKey(payloadRows[reference.row_index]),
|
|
102
|
+
table: reference.table,
|
|
103
|
+
id: reference.id,
|
|
104
|
+
version: reference.version,
|
|
105
|
+
path: reference.path,
|
|
106
|
+
}))
|
|
107
|
+
.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
108
|
+
}
|
|
109
|
+
function protectedRows(options) {
|
|
110
|
+
const readyActionKeys = new Set(options.actions
|
|
111
|
+
.filter((action) => action.before && action.status === 'ready')
|
|
112
|
+
.map(maintenanceRowKey));
|
|
113
|
+
const blockedActionKeys = new Set(options.actions
|
|
114
|
+
.filter((action) => action.before && action.status === 'blocked')
|
|
115
|
+
.map(maintenanceRowKey));
|
|
116
|
+
return options.snapshot
|
|
117
|
+
.filter((row) => !readyActionKeys.has(maintenanceRowKey(row)))
|
|
118
|
+
.map((row) => ({
|
|
119
|
+
table: row.table,
|
|
120
|
+
id: row.id,
|
|
121
|
+
version: row.version,
|
|
122
|
+
modified_at: row.modified_at,
|
|
123
|
+
row_sha256: row.row_sha256,
|
|
124
|
+
payload_sha256: row.payload_sha256,
|
|
125
|
+
reason: blockedActionKeys.has(maintenanceRowKey(row))
|
|
126
|
+
? 'blocked_action_row'
|
|
127
|
+
: 'non_action_visible_row',
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
export async function runDatasetMaintenancePlan(options) {
|
|
131
|
+
const scopePath = path.resolve(options.scopePath);
|
|
132
|
+
const outDir = path.resolve(options.outDir);
|
|
133
|
+
const pageSize = normalizeMaintenancePageSize(options.pageSize);
|
|
134
|
+
const generatedAtUtc = (options.now ?? new Date()).toISOString();
|
|
135
|
+
const scope = parseMaintenanceScope(readJsonFile(scopePath, 'Maintenance scope'), options.operation);
|
|
136
|
+
const context = await resolveMaintenanceRemoteContext({
|
|
137
|
+
env: options.env,
|
|
138
|
+
fetchImpl: options.fetchImpl,
|
|
139
|
+
timeoutMs: options.timeoutMs,
|
|
140
|
+
now: options.now,
|
|
141
|
+
});
|
|
142
|
+
if (context.account.user_id !== scope.account.user_id) {
|
|
143
|
+
throw new CliError('Current authenticated user does not match maintenance scope account.', {
|
|
144
|
+
code: 'DATASET_MAINTENANCE_ACCOUNT_MISMATCH',
|
|
145
|
+
exitCode: 1,
|
|
146
|
+
details: {
|
|
147
|
+
expected_user_id: scope.account.user_id,
|
|
148
|
+
current_user_id: context.account.user_id,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (scope.account.email &&
|
|
153
|
+
normalizeEmail(scope.account.email) !== normalizeEmail(context.account.email)) {
|
|
154
|
+
throw new CliError('Current authenticated email does not match maintenance scope account.', {
|
|
155
|
+
code: 'DATASET_MAINTENANCE_ACCOUNT_EMAIL_MISMATCH',
|
|
156
|
+
exitCode: 1,
|
|
157
|
+
details: {
|
|
158
|
+
expected_email: scope.account.email,
|
|
159
|
+
current_email: context.account.email,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const accountSnapshot = await fetchMaintenanceAccountRows({
|
|
164
|
+
context,
|
|
165
|
+
userId: scope.account.user_id,
|
|
166
|
+
pageSize,
|
|
167
|
+
});
|
|
168
|
+
const snapshotRows = accountSnapshot.rows
|
|
169
|
+
.map(snapshotRemoteRow)
|
|
170
|
+
.sort((left, right) => maintenanceRowKey(left).localeCompare(maintenanceRowKey(right)));
|
|
171
|
+
const snapshotByKey = new Map(snapshotRows.map((row) => [maintenanceRowKey(row), row]));
|
|
172
|
+
const desiredPayloads = new Map();
|
|
173
|
+
const actionPlans = [];
|
|
174
|
+
const aliasTargetSnapshots = new Map();
|
|
175
|
+
for (const batch of scope.alias_batches ?? []) {
|
|
176
|
+
const targetRows = await Promise.all(['unitgroup', 'flowproperty'].map(async (kind) => {
|
|
177
|
+
const target = batch.target[kind];
|
|
178
|
+
const table = kind === 'unitgroup' ? 'unitgroups' : 'flowproperties';
|
|
179
|
+
const exact = await fetchMaintenanceExactRows({
|
|
180
|
+
context,
|
|
181
|
+
table,
|
|
182
|
+
id: target.id,
|
|
183
|
+
version: target.version,
|
|
184
|
+
});
|
|
185
|
+
const row = exact.rows.length === 1 ? exact.rows[0] : null;
|
|
186
|
+
if (!row?.json_ordered)
|
|
187
|
+
return null;
|
|
188
|
+
const inspection = inspectMaintenanceSupportPayload({
|
|
189
|
+
table,
|
|
190
|
+
payload: row.json_ordered,
|
|
191
|
+
schemas: options.supportSchemas,
|
|
192
|
+
});
|
|
193
|
+
return inspection.identity.id === target.id &&
|
|
194
|
+
inspection.identity.version === target.version &&
|
|
195
|
+
inspection.schemaResult.success
|
|
196
|
+
? snapshotRemoteRow(row)
|
|
197
|
+
: null;
|
|
198
|
+
}));
|
|
199
|
+
const sourceUnitGroupSnapshot = snapshotByKey.get(maintenanceRowKey({
|
|
200
|
+
table: 'unitgroups',
|
|
201
|
+
id: batch.source.unitgroup.id,
|
|
202
|
+
version: batch.source.unitgroup.version,
|
|
203
|
+
}));
|
|
204
|
+
const sourceUnitGroupInspection = sourceUnitGroupSnapshot?.json_ordered
|
|
205
|
+
? inspectMaintenanceSupportPayload({
|
|
206
|
+
table: 'unitgroups',
|
|
207
|
+
payload: sourceUnitGroupSnapshot.json_ordered,
|
|
208
|
+
schemas: options.supportSchemas,
|
|
209
|
+
})
|
|
210
|
+
: null;
|
|
211
|
+
aliasTargetSnapshots.set(batch.batch_id, {
|
|
212
|
+
unitgroup: targetRows[0],
|
|
213
|
+
flowproperty: targetRows[1],
|
|
214
|
+
source_unitgroup: sourceUnitGroupSnapshot &&
|
|
215
|
+
sourceUnitGroupInspection?.identity.id === batch.source.unitgroup.id &&
|
|
216
|
+
sourceUnitGroupInspection.identity.version === batch.source.unitgroup.version &&
|
|
217
|
+
sourceUnitGroupInspection.schemaResult.success
|
|
218
|
+
? sourceUnitGroupSnapshot
|
|
219
|
+
: null,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
for (const [ordinal, action] of scope.actions.entries()) {
|
|
223
|
+
const actionBlockers = [];
|
|
224
|
+
const exact = await fetchMaintenanceExactRows({
|
|
225
|
+
context,
|
|
226
|
+
table: action.table,
|
|
227
|
+
id: action.id,
|
|
228
|
+
version: action.version,
|
|
229
|
+
});
|
|
230
|
+
const remote = exact.rows[0] ?? null;
|
|
231
|
+
if (exact.rows.length === 0) {
|
|
232
|
+
actionBlockers.push(blocker(action, 'TARGET_NOT_VISIBLE', 'Exact target row is not visible under the current authenticated RLS session.'));
|
|
233
|
+
}
|
|
234
|
+
if (exact.rows.length > 1) {
|
|
235
|
+
actionBlockers.push(blocker(action, 'TARGET_NOT_UNIQUE', 'Exact target lookup returned multiple rows.'));
|
|
236
|
+
}
|
|
237
|
+
const before = remote ? snapshotRemoteRow(remote) : null;
|
|
238
|
+
if (before && before.user_id !== action.expected_user_id) {
|
|
239
|
+
actionBlockers.push(blocker(action, 'TARGET_OWNER_MISMATCH', 'Target row is not owned by the expected user.', {
|
|
240
|
+
visible_user_id: before.user_id,
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
if (before && before.state_code !== 0) {
|
|
244
|
+
actionBlockers.push(blocker(action, 'TARGET_NOT_DRAFT', 'Target row is not a draft with state_code=0.', {
|
|
245
|
+
visible_state_code: before.state_code,
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
if (before && !before.json_ordered) {
|
|
249
|
+
actionBlockers.push(blocker(action, 'TARGET_PAYLOAD_MISSING', 'Target row has no object json_ordered payload.'));
|
|
250
|
+
}
|
|
251
|
+
if (action.action === 'update_json_ordered' && before && !before.modified_at) {
|
|
252
|
+
actionBlockers.push(blocker(action, 'ALIAS_EXPECTED_MODIFIED_AT_MISSING', `${action.action} target requires a non-null modified_at optimistic-lock value.`));
|
|
253
|
+
}
|
|
254
|
+
if (before &&
|
|
255
|
+
action.expected_before_sha256 &&
|
|
256
|
+
action.expected_before_sha256 !== before.row_sha256) {
|
|
257
|
+
actionBlockers.push(blocker(action, 'EXPECTED_BEFORE_HASH_MISMATCH', 'Target row hash differs from scope.', {
|
|
258
|
+
expected: action.expected_before_sha256,
|
|
259
|
+
actual: before.row_sha256,
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
const snapshotRow = snapshotByKey.get(maintenanceRowKey(action));
|
|
263
|
+
if (before && (!snapshotRow || snapshotRow.row_sha256 !== before.row_sha256)) {
|
|
264
|
+
actionBlockers.push(blocker(action, 'SNAPSHOT_DRIFT', 'Exact target row differs from the same-account visible snapshot.'));
|
|
265
|
+
}
|
|
266
|
+
let desiredPayload = null;
|
|
267
|
+
if (action.action === 'save_draft' && action.desired_payload_path) {
|
|
268
|
+
const sourcePayloadPath = path.resolve(path.dirname(scopePath), action.desired_payload_path);
|
|
269
|
+
const rawPayload = readJsonFile(sourcePayloadPath, 'Maintenance desired payload');
|
|
270
|
+
if (!isJsonObject(rawPayload)) {
|
|
271
|
+
throw new CliError(`Desired payload must be a JSON object: ${sourcePayloadPath}`, {
|
|
272
|
+
code: 'DATASET_MAINTENANCE_DESIRED_PAYLOAD_INVALID',
|
|
273
|
+
exitCode: 2,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
const payloadPath = path.join(outDir, 'payloads', `${safeActionFileName(action.action_id)}.json`);
|
|
277
|
+
writeImmutableJson(payloadPath, rawPayload);
|
|
278
|
+
desiredPayloads.set(action.action_id, rawPayload);
|
|
279
|
+
desiredPayload = {
|
|
280
|
+
path: path.relative(outDir, payloadPath),
|
|
281
|
+
sha256: sha256Json(rawPayload),
|
|
282
|
+
};
|
|
283
|
+
const identity = maintenancePayloadIdentity(rawPayload);
|
|
284
|
+
if (identity.id !== action.id || identity.version !== action.version) {
|
|
285
|
+
actionBlockers.push(blocker(action, 'DESIRED_PAYLOAD_IDENTITY_MISMATCH', 'Desired payload root id/version does not match the target row.', identity));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
actionPlans.push({
|
|
289
|
+
...action,
|
|
290
|
+
ordinal,
|
|
291
|
+
status: actionBlockers.length ? 'blocked' : 'ready',
|
|
292
|
+
before,
|
|
293
|
+
desired_payload: desiredPayload,
|
|
294
|
+
blockers: actionBlockers,
|
|
295
|
+
rollback: {
|
|
296
|
+
strategy: action.action === 'save_draft'
|
|
297
|
+
? 'save_before_snapshot'
|
|
298
|
+
: action.action === 'delete'
|
|
299
|
+
? 'restore_deleted_before_snapshot'
|
|
300
|
+
: 'restore_atomic_alias_before_snapshot',
|
|
301
|
+
before_payload_sha256: before?.payload_sha256 ?? null,
|
|
302
|
+
before_payload: before?.json_ordered ?? null,
|
|
303
|
+
model_id: before?.model_id ?? null,
|
|
304
|
+
rule_verification: before?.rule_verification ?? null,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
let aliasBatches;
|
|
309
|
+
if (scope.operation === 'merge-support-aliases') {
|
|
310
|
+
const aliasPlan = buildAliasRewritePlan({
|
|
311
|
+
scope,
|
|
312
|
+
actions: actionPlans,
|
|
313
|
+
accountRows: accountSnapshot.rows,
|
|
314
|
+
targetSnapshots: aliasTargetSnapshots,
|
|
315
|
+
schemas: options.aliasSchemas,
|
|
316
|
+
});
|
|
317
|
+
aliasBatches = aliasPlan.batches;
|
|
318
|
+
for (const action of actionPlans) {
|
|
319
|
+
const rawPayload = aliasPlan.desired_payloads.get(action.action_id);
|
|
320
|
+
if (!rawPayload)
|
|
321
|
+
continue;
|
|
322
|
+
const payloadPath = path.join(outDir, 'payloads', `${safeActionFileName(action.action_id)}.json`);
|
|
323
|
+
writeImmutableJson(payloadPath, rawPayload);
|
|
324
|
+
desiredPayloads.set(action.action_id, rawPayload);
|
|
325
|
+
action.desired_payload = {
|
|
326
|
+
path: path.relative(outDir, payloadPath),
|
|
327
|
+
sha256: sha256Json(rawPayload),
|
|
328
|
+
};
|
|
329
|
+
const identity = maintenancePayloadIdentity(rawPayload);
|
|
330
|
+
if (identity.id !== action.id || identity.version !== action.version) {
|
|
331
|
+
action.blockers.push(blocker(action, 'DESIRED_PAYLOAD_IDENTITY_MISMATCH', 'Generated alias payload root id/version does not match the target row.', identity));
|
|
332
|
+
action.status = 'blocked';
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const intendedRows = projectedRows({
|
|
337
|
+
current: accountSnapshot.rows,
|
|
338
|
+
actions: actionPlans,
|
|
339
|
+
desiredPayloads,
|
|
340
|
+
});
|
|
341
|
+
const deleteActions = scope.actions.filter((action) => action.action === 'delete');
|
|
342
|
+
const currentImpacts = referenceImpacts({
|
|
343
|
+
rows: accountSnapshot.rows,
|
|
344
|
+
deletes: deleteActions,
|
|
345
|
+
phase: 'current',
|
|
346
|
+
});
|
|
347
|
+
const projectedImpacts = referenceImpacts({
|
|
348
|
+
rows: intendedRows,
|
|
349
|
+
deletes: deleteActions,
|
|
350
|
+
phase: 'projected',
|
|
351
|
+
});
|
|
352
|
+
for (const action of actionPlans.filter((entry) => entry.action === 'delete')) {
|
|
353
|
+
const impacts = projectedImpacts.filter((impact) => impact.target_action_id === action.action_id);
|
|
354
|
+
if (impacts.length) {
|
|
355
|
+
action.blockers.push(blocker(action, 'PROJECTED_INBOUND_REFERENCES', `Projected state still contains ${impacts.length} inbound reference(s) to this delete target.`, impacts));
|
|
356
|
+
action.status = 'blocked';
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const allBlockers = actionPlans.flatMap((action) => action.blockers);
|
|
360
|
+
const protectedRowList = protectedRows({ snapshot: snapshotRows, actions: actionPlans });
|
|
361
|
+
const scopeSha256 = sha256Json(scope);
|
|
362
|
+
const plan = {
|
|
363
|
+
schema_version: 1,
|
|
364
|
+
generated_at_utc: generatedAtUtc,
|
|
365
|
+
task_id: scope.task_id,
|
|
366
|
+
operation: scope.operation,
|
|
367
|
+
operation_id: `maintenance-${scopeSha256.slice(0, 20)}`,
|
|
368
|
+
account: {
|
|
369
|
+
user_id: scope.account.user_id,
|
|
370
|
+
email: context.account.email,
|
|
371
|
+
},
|
|
372
|
+
source_import_run_id: scope.source_import_run_id ?? null,
|
|
373
|
+
source_lineage: scope.source_lineage ?? null,
|
|
374
|
+
target_mode: scope.target_mode ?? null,
|
|
375
|
+
status: allBlockers.length ? 'blocked' : 'ready',
|
|
376
|
+
scope_sha256: scopeSha256,
|
|
377
|
+
visible_snapshot_sha256: sha256Json(snapshotRows),
|
|
378
|
+
snapshot_completeness: accountSnapshot.completeness,
|
|
379
|
+
projected_reference_sha256: sha256Json(maintenanceProjectedReferenceFingerprint(intendedRows)),
|
|
380
|
+
plan_sha256: '',
|
|
381
|
+
summary: {
|
|
382
|
+
actions: actionPlans.length,
|
|
383
|
+
save_draft: actionPlans.filter((action) => action.action === 'save_draft').length,
|
|
384
|
+
delete: actionPlans.filter((action) => action.action === 'delete').length,
|
|
385
|
+
update_json_ordered: actionPlans.filter((action) => action.action === 'update_json_ordered')
|
|
386
|
+
.length,
|
|
387
|
+
atomic_batches: aliasBatches?.length ?? 0,
|
|
388
|
+
scaled_exchanges: aliasBatches?.reduce((sum, batch) => sum + batch.summary.exchanges, 0) ?? 0,
|
|
389
|
+
scaled_amount_fields: aliasBatches?.reduce((sum, batch) => sum + batch.summary.amount_fields, 0) ?? 0,
|
|
390
|
+
unrelated_exchanges_preserved: aliasBatches?.reduce((sum, batch) => sum + batch.summary.unrelated_exchanges, 0) ?? 0,
|
|
391
|
+
protected_rows: protectedRowList.length,
|
|
392
|
+
blockers: allBlockers.length,
|
|
393
|
+
current_reference_impacts: currentImpacts.length,
|
|
394
|
+
projected_reference_impacts: projectedImpacts.length,
|
|
395
|
+
},
|
|
396
|
+
artifacts: {
|
|
397
|
+
maintenance_scope: 'maintenance-scope.json',
|
|
398
|
+
rls_visible_snapshot: 'rls-visible-snapshot.json',
|
|
399
|
+
protected_rows: 'protected-rows.jsonl',
|
|
400
|
+
reference_impact_report: 'reference-impact-report.json',
|
|
401
|
+
maintenance_plan: 'maintenance-plan.json',
|
|
402
|
+
dry_run_report: 'dry-run-report.json',
|
|
403
|
+
payload_dir: 'payloads',
|
|
404
|
+
...(aliasBatches ? { exchange_rewrite_plan: 'exchange-rewrite-plan.jsonl' } : {}),
|
|
405
|
+
},
|
|
406
|
+
actions: actionPlans,
|
|
407
|
+
...(aliasBatches ? { alias_batches: aliasBatches } : {}),
|
|
408
|
+
protected_rows: protectedRowList,
|
|
409
|
+
blockers: allBlockers,
|
|
410
|
+
};
|
|
411
|
+
plan.plan_sha256 = computePlanSha256(plan);
|
|
412
|
+
writeImmutableJson(path.join(outDir, plan.artifacts.maintenance_scope), scope);
|
|
413
|
+
writeImmutableJson(path.join(outDir, plan.artifacts.rls_visible_snapshot), {
|
|
414
|
+
schema_version: 1,
|
|
415
|
+
generated_at_utc: generatedAtUtc,
|
|
416
|
+
account: {
|
|
417
|
+
user_id: scope.account.user_id,
|
|
418
|
+
email: context.account.email,
|
|
419
|
+
session_source: context.account.session_source,
|
|
420
|
+
},
|
|
421
|
+
page_size: pageSize,
|
|
422
|
+
completeness: accountSnapshot.completeness,
|
|
423
|
+
source_urls: accountSnapshot.source_urls,
|
|
424
|
+
row_count: snapshotRows.length,
|
|
425
|
+
snapshot_sha256: plan.visible_snapshot_sha256,
|
|
426
|
+
rows: snapshotRows,
|
|
427
|
+
});
|
|
428
|
+
writeImmutableJsonLines(path.join(outDir, plan.artifacts.protected_rows), protectedRowList);
|
|
429
|
+
if (plan.artifacts.exchange_rewrite_plan) {
|
|
430
|
+
writeImmutableJsonLines(path.join(outDir, plan.artifacts.exchange_rewrite_plan), plan.alias_batches.flatMap((batch) => batch.exchange_rewrites.map((rewrite) => ({
|
|
431
|
+
schema_version: 1,
|
|
432
|
+
plan_sha256: plan.plan_sha256,
|
|
433
|
+
operation_id: plan.operation_id,
|
|
434
|
+
batch_id: batch.batch_id,
|
|
435
|
+
factor: batch.factor,
|
|
436
|
+
...rewrite,
|
|
437
|
+
}))));
|
|
438
|
+
}
|
|
439
|
+
writeImmutableJson(path.join(outDir, plan.artifacts.reference_impact_report), {
|
|
440
|
+
schema_version: 1,
|
|
441
|
+
generated_at_utc: generatedAtUtc,
|
|
442
|
+
plan_sha256: plan.plan_sha256,
|
|
443
|
+
status: plan.status,
|
|
444
|
+
current: currentImpacts,
|
|
445
|
+
projected: projectedImpacts,
|
|
446
|
+
projected_reference_sha256: plan.projected_reference_sha256,
|
|
447
|
+
});
|
|
448
|
+
writeImmutableJson(path.join(outDir, plan.artifacts.dry_run_report), {
|
|
449
|
+
schema_version: 1,
|
|
450
|
+
generated_at_utc: generatedAtUtc,
|
|
451
|
+
status: plan.status,
|
|
452
|
+
operation: plan.operation,
|
|
453
|
+
target_mode: plan.target_mode,
|
|
454
|
+
task_id: plan.task_id,
|
|
455
|
+
plan_sha256: plan.plan_sha256,
|
|
456
|
+
account: plan.account,
|
|
457
|
+
snapshot_completeness: accountSnapshot.completeness,
|
|
458
|
+
summary: plan.summary,
|
|
459
|
+
actions: plan.actions.map((action) => ({
|
|
460
|
+
action_id: action.action_id,
|
|
461
|
+
action: action.action,
|
|
462
|
+
table: action.table,
|
|
463
|
+
id: action.id,
|
|
464
|
+
version: action.version,
|
|
465
|
+
status: action.status,
|
|
466
|
+
blockers: action.blockers,
|
|
467
|
+
})),
|
|
468
|
+
alias_batches: plan.alias_batches?.map((batch) => ({
|
|
469
|
+
batch_id: batch.batch_id,
|
|
470
|
+
dimension: batch.dimension,
|
|
471
|
+
factor: batch.factor,
|
|
472
|
+
summary: batch.summary,
|
|
473
|
+
postconditions: batch.postconditions,
|
|
474
|
+
target_snapshots: batch.target_snapshots,
|
|
475
|
+
})),
|
|
476
|
+
blockers: plan.blockers,
|
|
477
|
+
});
|
|
478
|
+
writeImmutableJson(path.join(outDir, plan.artifacts.maintenance_plan), plan);
|
|
479
|
+
return plan;
|
|
480
|
+
}
|
|
481
|
+
export const __testInternals = {
|
|
482
|
+
desiredPayloadIdentity: maintenancePayloadIdentity,
|
|
483
|
+
maintenanceProjectedReferenceFingerprint,
|
|
484
|
+
projectedRows,
|
|
485
|
+
protectedRows,
|
|
486
|
+
referenceImpacts,
|
|
487
|
+
};
|
|
488
|
+
//# sourceMappingURL=dataset-maintenance-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-maintenance-plan.js","sourceRoot":"","sources":["../../../src/lib/dataset-maintenance-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { collectRemoteReferences } from './dataset-remote-verify.js';\nimport {\n buildAliasRewritePlan,\n type DatasetMaintenanceAliasSchemas,\n} from './dataset-maintenance-alias-rewrite.js';\nimport { CliError } from './errors.js';\nimport type { FetchLike } from './http.js';\nimport {\n computePlanSha256,\n isJsonObject,\n maintenanceRowKey,\n parseMaintenanceScope,\n readJsonFile,\n safeActionFileName,\n sha256Json,\n snapshotRemoteRow,\n writeImmutableJson,\n writeImmutableJsonLines,\n type DatasetMaintenanceBlocker,\n type DatasetMaintenanceAliasBatchPlan,\n type DatasetMaintenanceOperation,\n type DatasetMaintenancePlan,\n type DatasetMaintenancePlanAction,\n type DatasetMaintenanceProtectedRow,\n type DatasetMaintenanceReferenceImpact,\n type DatasetMaintenanceRemoteRow,\n type DatasetMaintenanceRowSnapshot,\n type DatasetMaintenanceScopeAction,\n type JsonObject,\n} from './dataset-maintenance-contract.js';\nimport {\n inspectMaintenanceSupportPayload,\n maintenancePayloadIdentity,\n type DatasetMaintenanceSupportSchemas,\n} from './dataset-maintenance-support-validation.js';\nimport {\n fetchMaintenanceAccountRows,\n fetchMaintenanceExactRows,\n normalizeMaintenancePageSize,\n resolveMaintenanceRemoteContext,\n} from './dataset-maintenance-remote.js';\n\nexport type RunDatasetMaintenancePlanOptions = {\n scopePath: string;\n operation: DatasetMaintenanceOperation;\n outDir: string;\n pageSize?: number;\n timeoutMs?: number;\n env: NodeJS.ProcessEnv;\n fetchImpl: FetchLike;\n now?: Date;\n supportSchemas?: DatasetMaintenanceSupportSchemas;\n aliasSchemas?: DatasetMaintenanceAliasSchemas;\n};\n\nfunction normalizeEmail(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction blocker(\n action: DatasetMaintenanceScopeAction,\n code: string,\n message: string,\n details?: unknown,\n): DatasetMaintenanceBlocker {\n return {\n code,\n message,\n action_id: action.action_id,\n table: action.table,\n id: action.id,\n version: action.version,\n ...(details === undefined ? {} : { details }),\n };\n}\n\nfunction referenceImpacts(options: {\n rows: DatasetMaintenanceRemoteRow[];\n deletes: DatasetMaintenanceScopeAction[];\n phase: DatasetMaintenanceReferenceImpact['phase'];\n}): DatasetMaintenanceReferenceImpact[] {\n const payloadRows = options.rows\n .filter((row) => row.json_ordered !== null)\n .map((row) => ({\n table: row.table,\n id: row.id,\n version: row.version,\n json_ordered: row.json_ordered as JsonObject,\n }));\n const references = collectRemoteReferences(payloadRows).filter(\n (reference) => reference.role === 'reference',\n );\n const impacts: DatasetMaintenanceReferenceImpact[] = [];\n for (const reference of references) {\n const source = payloadRows[reference.row_index];\n if (!reference.table || !reference.id) {\n continue;\n }\n for (const target of options.deletes) {\n const sameTarget =\n reference.table === target.table &&\n reference.id === target.id &&\n (!reference.version || reference.version === target.version);\n if (sameTarget) {\n impacts.push({\n target_action_id: target.action_id,\n target_table: target.table,\n target_id: target.id,\n target_version: target.version,\n phase: options.phase,\n source_table: source!.table,\n source_id: source!.id,\n source_version: source!.version,\n reference_path: reference.path,\n reference_version: reference.version,\n });\n }\n }\n }\n return impacts.sort((left, right) =>\n [\n left.target_action_id,\n left.source_table,\n left.source_id,\n left.source_version,\n left.reference_path,\n ]\n .join('\\u0000')\n .localeCompare(\n [\n right.target_action_id,\n right.source_table,\n right.source_id,\n right.source_version,\n right.reference_path,\n ].join('\\u0000'),\n ),\n );\n}\n\nfunction projectedRows(options: {\n current: DatasetMaintenanceRemoteRow[];\n actions: DatasetMaintenancePlanAction[];\n desiredPayloads: Map<string, JsonObject>;\n}): DatasetMaintenanceRemoteRow[] {\n const projected = new Map(options.current.map((row) => [maintenanceRowKey(row), { ...row }]));\n for (const action of options.actions.filter((entry) =>\n ['save_draft', 'update_json_ordered'].includes(entry.action),\n )) {\n const key = maintenanceRowKey(action);\n const row = projected.get(key);\n const payload = options.desiredPayloads.get(action.action_id);\n if (row && payload) {\n projected.set(key, { ...row, json_ordered: payload });\n }\n }\n for (const action of options.actions.filter((entry) => entry.action === 'delete')) {\n projected.delete(maintenanceRowKey(action));\n }\n return [...projected.values()].sort((left, right) =>\n maintenanceRowKey(left).localeCompare(maintenanceRowKey(right)),\n );\n}\n\nexport function maintenanceProjectedReferenceFingerprint(\n rows: DatasetMaintenanceRemoteRow[],\n): unknown[] {\n const payloadRows = rows\n .filter((row) => row.json_ordered !== null)\n .map((row) => ({\n table: row.table,\n id: row.id,\n version: row.version,\n json_ordered: row.json_ordered as JsonObject,\n }));\n return collectRemoteReferences(payloadRows)\n .filter((reference) => reference.role === 'reference')\n .map((reference) => ({\n source: maintenanceRowKey(payloadRows[reference.row_index]!),\n table: reference.table,\n id: reference.id,\n version: reference.version,\n path: reference.path,\n }))\n .sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));\n}\n\nfunction protectedRows(options: {\n snapshot: DatasetMaintenanceRowSnapshot[];\n actions: DatasetMaintenancePlanAction[];\n}): DatasetMaintenanceProtectedRow[] {\n const readyActionKeys = new Set(\n options.actions\n .filter((action) => action.before && action.status === 'ready')\n .map(maintenanceRowKey),\n );\n const blockedActionKeys = new Set(\n options.actions\n .filter((action) => action.before && action.status === 'blocked')\n .map(maintenanceRowKey),\n );\n return options.snapshot\n .filter((row) => !readyActionKeys.has(maintenanceRowKey(row)))\n .map((row) => ({\n table: row.table,\n id: row.id,\n version: row.version,\n modified_at: row.modified_at,\n row_sha256: row.row_sha256,\n payload_sha256: row.payload_sha256,\n reason: blockedActionKeys.has(maintenanceRowKey(row))\n ? ('blocked_action_row' as const)\n : ('non_action_visible_row' as const),\n }));\n}\n\nexport async function runDatasetMaintenancePlan(\n options: RunDatasetMaintenancePlanOptions,\n): Promise<DatasetMaintenancePlan> {\n const scopePath = path.resolve(options.scopePath);\n const outDir = path.resolve(options.outDir);\n const pageSize = normalizeMaintenancePageSize(options.pageSize);\n const generatedAtUtc = (options.now ?? new Date()).toISOString();\n const scope = parseMaintenanceScope(\n readJsonFile(scopePath, 'Maintenance scope'),\n options.operation,\n );\n const context = await resolveMaintenanceRemoteContext({\n env: options.env,\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n now: options.now,\n });\n if (context.account.user_id !== scope.account.user_id) {\n throw new CliError('Current authenticated user does not match maintenance scope account.', {\n code: 'DATASET_MAINTENANCE_ACCOUNT_MISMATCH',\n exitCode: 1,\n details: {\n expected_user_id: scope.account.user_id,\n current_user_id: context.account.user_id,\n },\n });\n }\n if (\n scope.account.email &&\n normalizeEmail(scope.account.email) !== normalizeEmail(context.account.email)\n ) {\n throw new CliError('Current authenticated email does not match maintenance scope account.', {\n code: 'DATASET_MAINTENANCE_ACCOUNT_EMAIL_MISMATCH',\n exitCode: 1,\n details: {\n expected_email: scope.account.email,\n current_email: context.account.email,\n },\n });\n }\n\n const accountSnapshot = await fetchMaintenanceAccountRows({\n context,\n userId: scope.account.user_id,\n pageSize,\n });\n const snapshotRows = accountSnapshot.rows\n .map(snapshotRemoteRow)\n .sort((left, right) => maintenanceRowKey(left).localeCompare(maintenanceRowKey(right)));\n const snapshotByKey = new Map(snapshotRows.map((row) => [maintenanceRowKey(row), row]));\n const desiredPayloads = new Map<string, JsonObject>();\n const actionPlans: DatasetMaintenancePlanAction[] = [];\n const aliasTargetSnapshots = new Map<\n string,\n DatasetMaintenanceAliasBatchPlan['target_snapshots']\n >();\n for (const batch of scope.alias_batches ?? []) {\n const targetRows = await Promise.all(\n (['unitgroup', 'flowproperty'] as const).map(async (kind) => {\n const target = batch.target[kind];\n const table = kind === 'unitgroup' ? 'unitgroups' : 'flowproperties';\n const exact = await fetchMaintenanceExactRows({\n context,\n table,\n id: target.id,\n version: target.version,\n });\n const row = exact.rows.length === 1 ? exact.rows[0] : null;\n if (!row?.json_ordered) return null;\n const inspection = inspectMaintenanceSupportPayload({\n table,\n payload: row.json_ordered,\n schemas: options.supportSchemas,\n });\n return inspection.identity.id === target.id &&\n inspection.identity.version === target.version &&\n inspection.schemaResult.success\n ? snapshotRemoteRow(row)\n : null;\n }),\n );\n const sourceUnitGroupSnapshot = snapshotByKey.get(\n maintenanceRowKey({\n table: 'unitgroups',\n id: batch.source.unitgroup.id,\n version: batch.source.unitgroup.version,\n }),\n );\n const sourceUnitGroupInspection = sourceUnitGroupSnapshot?.json_ordered\n ? inspectMaintenanceSupportPayload({\n table: 'unitgroups',\n payload: sourceUnitGroupSnapshot.json_ordered,\n schemas: options.supportSchemas,\n })\n : null;\n aliasTargetSnapshots.set(batch.batch_id, {\n unitgroup: targetRows[0],\n flowproperty: targetRows[1],\n source_unitgroup:\n sourceUnitGroupSnapshot &&\n sourceUnitGroupInspection?.identity.id === batch.source.unitgroup.id &&\n sourceUnitGroupInspection.identity.version === batch.source.unitgroup.version &&\n sourceUnitGroupInspection.schemaResult.success\n ? sourceUnitGroupSnapshot\n : null,\n });\n }\n\n for (const [ordinal, action] of scope.actions.entries()) {\n const actionBlockers: DatasetMaintenanceBlocker[] = [];\n const exact = await fetchMaintenanceExactRows({\n context,\n table: action.table,\n id: action.id,\n version: action.version,\n });\n const remote = exact.rows[0] ?? null;\n if (exact.rows.length === 0) {\n actionBlockers.push(\n blocker(\n action,\n 'TARGET_NOT_VISIBLE',\n 'Exact target row is not visible under the current authenticated RLS session.',\n ),\n );\n }\n if (exact.rows.length > 1) {\n actionBlockers.push(\n blocker(action, 'TARGET_NOT_UNIQUE', 'Exact target lookup returned multiple rows.'),\n );\n }\n const before = remote ? snapshotRemoteRow(remote) : null;\n if (before && before.user_id !== action.expected_user_id) {\n actionBlockers.push(\n blocker(action, 'TARGET_OWNER_MISMATCH', 'Target row is not owned by the expected user.', {\n visible_user_id: before.user_id,\n }),\n );\n }\n if (before && before.state_code !== 0) {\n actionBlockers.push(\n blocker(action, 'TARGET_NOT_DRAFT', 'Target row is not a draft with state_code=0.', {\n visible_state_code: before.state_code,\n }),\n );\n }\n if (before && !before.json_ordered) {\n actionBlockers.push(\n blocker(action, 'TARGET_PAYLOAD_MISSING', 'Target row has no object json_ordered payload.'),\n );\n }\n if (action.action === 'update_json_ordered' && before && !before.modified_at) {\n actionBlockers.push(\n blocker(\n action,\n 'ALIAS_EXPECTED_MODIFIED_AT_MISSING',\n `${action.action} target requires a non-null modified_at optimistic-lock value.`,\n ),\n );\n }\n if (\n before &&\n action.expected_before_sha256 &&\n action.expected_before_sha256 !== before.row_sha256\n ) {\n actionBlockers.push(\n blocker(action, 'EXPECTED_BEFORE_HASH_MISMATCH', 'Target row hash differs from scope.', {\n expected: action.expected_before_sha256,\n actual: before.row_sha256,\n }),\n );\n }\n const snapshotRow = snapshotByKey.get(maintenanceRowKey(action));\n if (before && (!snapshotRow || snapshotRow.row_sha256 !== before.row_sha256)) {\n actionBlockers.push(\n blocker(\n action,\n 'SNAPSHOT_DRIFT',\n 'Exact target row differs from the same-account visible snapshot.',\n ),\n );\n }\n\n let desiredPayload: DatasetMaintenancePlanAction['desired_payload'] = null;\n if (action.action === 'save_draft' && action.desired_payload_path) {\n const sourcePayloadPath = path.resolve(path.dirname(scopePath), action.desired_payload_path);\n const rawPayload = readJsonFile(sourcePayloadPath, 'Maintenance desired payload');\n if (!isJsonObject(rawPayload)) {\n throw new CliError(`Desired payload must be a JSON object: ${sourcePayloadPath}`, {\n code: 'DATASET_MAINTENANCE_DESIRED_PAYLOAD_INVALID',\n exitCode: 2,\n });\n }\n const payloadPath = path.join(\n outDir,\n 'payloads',\n `${safeActionFileName(action.action_id)}.json`,\n );\n writeImmutableJson(payloadPath, rawPayload);\n desiredPayloads.set(action.action_id, rawPayload);\n desiredPayload = {\n path: path.relative(outDir, payloadPath),\n sha256: sha256Json(rawPayload),\n };\n const identity = maintenancePayloadIdentity(rawPayload);\n if (identity.id !== action.id || identity.version !== action.version) {\n actionBlockers.push(\n blocker(\n action,\n 'DESIRED_PAYLOAD_IDENTITY_MISMATCH',\n 'Desired payload root id/version does not match the target row.',\n identity,\n ),\n );\n }\n }\n actionPlans.push({\n ...action,\n ordinal,\n status: actionBlockers.length ? 'blocked' : 'ready',\n before,\n desired_payload: desiredPayload,\n blockers: actionBlockers,\n rollback: {\n strategy:\n action.action === 'save_draft'\n ? 'save_before_snapshot'\n : action.action === 'delete'\n ? 'restore_deleted_before_snapshot'\n : 'restore_atomic_alias_before_snapshot',\n before_payload_sha256: before?.payload_sha256 ?? null,\n before_payload: before?.json_ordered ?? null,\n model_id: before?.model_id ?? null,\n rule_verification: before?.rule_verification ?? null,\n },\n });\n }\n\n let aliasBatches: DatasetMaintenanceAliasBatchPlan[] | undefined;\n if (scope.operation === 'merge-support-aliases') {\n const aliasPlan = buildAliasRewritePlan({\n scope,\n actions: actionPlans,\n accountRows: accountSnapshot.rows,\n targetSnapshots: aliasTargetSnapshots,\n schemas: options.aliasSchemas,\n });\n aliasBatches = aliasPlan.batches;\n for (const action of actionPlans) {\n const rawPayload = aliasPlan.desired_payloads.get(action.action_id);\n if (!rawPayload) continue;\n const payloadPath = path.join(\n outDir,\n 'payloads',\n `${safeActionFileName(action.action_id)}.json`,\n );\n writeImmutableJson(payloadPath, rawPayload);\n desiredPayloads.set(action.action_id, rawPayload);\n action.desired_payload = {\n path: path.relative(outDir, payloadPath),\n sha256: sha256Json(rawPayload),\n };\n const identity = maintenancePayloadIdentity(rawPayload);\n if (identity.id !== action.id || identity.version !== action.version) {\n action.blockers.push(\n blocker(\n action,\n 'DESIRED_PAYLOAD_IDENTITY_MISMATCH',\n 'Generated alias payload root id/version does not match the target row.',\n identity,\n ),\n );\n action.status = 'blocked';\n }\n }\n }\n\n const intendedRows = projectedRows({\n current: accountSnapshot.rows,\n actions: actionPlans,\n desiredPayloads,\n });\n const deleteActions = scope.actions.filter((action) => action.action === 'delete');\n const currentImpacts = referenceImpacts({\n rows: accountSnapshot.rows,\n deletes: deleteActions,\n phase: 'current',\n });\n const projectedImpacts = referenceImpacts({\n rows: intendedRows,\n deletes: deleteActions,\n phase: 'projected',\n });\n for (const action of actionPlans.filter((entry) => entry.action === 'delete')) {\n const impacts = projectedImpacts.filter(\n (impact) => impact.target_action_id === action.action_id,\n );\n if (impacts.length) {\n action.blockers.push(\n blocker(\n action,\n 'PROJECTED_INBOUND_REFERENCES',\n `Projected state still contains ${impacts.length} inbound reference(s) to this delete target.`,\n impacts,\n ),\n );\n action.status = 'blocked';\n }\n }\n const allBlockers = actionPlans.flatMap((action) => action.blockers);\n const protectedRowList = protectedRows({ snapshot: snapshotRows, actions: actionPlans });\n const scopeSha256 = sha256Json(scope);\n const plan: DatasetMaintenancePlan = {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n task_id: scope.task_id,\n operation: scope.operation,\n operation_id: `maintenance-${scopeSha256.slice(0, 20)}`,\n account: {\n user_id: scope.account.user_id,\n email: context.account.email,\n },\n source_import_run_id: scope.source_import_run_id ?? null,\n source_lineage: scope.source_lineage ?? null,\n target_mode: scope.target_mode ?? null,\n status: allBlockers.length ? 'blocked' : 'ready',\n scope_sha256: scopeSha256,\n visible_snapshot_sha256: sha256Json(snapshotRows),\n snapshot_completeness: accountSnapshot.completeness,\n projected_reference_sha256: sha256Json(maintenanceProjectedReferenceFingerprint(intendedRows)),\n plan_sha256: '',\n summary: {\n actions: actionPlans.length,\n save_draft: actionPlans.filter((action) => action.action === 'save_draft').length,\n delete: actionPlans.filter((action) => action.action === 'delete').length,\n update_json_ordered: actionPlans.filter((action) => action.action === 'update_json_ordered')\n .length,\n atomic_batches: aliasBatches?.length ?? 0,\n scaled_exchanges: aliasBatches?.reduce((sum, batch) => sum + batch.summary.exchanges, 0) ?? 0,\n scaled_amount_fields:\n aliasBatches?.reduce((sum, batch) => sum + batch.summary.amount_fields, 0) ?? 0,\n unrelated_exchanges_preserved:\n aliasBatches?.reduce((sum, batch) => sum + batch.summary.unrelated_exchanges, 0) ?? 0,\n protected_rows: protectedRowList.length,\n blockers: allBlockers.length,\n current_reference_impacts: currentImpacts.length,\n projected_reference_impacts: projectedImpacts.length,\n },\n artifacts: {\n maintenance_scope: 'maintenance-scope.json',\n rls_visible_snapshot: 'rls-visible-snapshot.json',\n protected_rows: 'protected-rows.jsonl',\n reference_impact_report: 'reference-impact-report.json',\n maintenance_plan: 'maintenance-plan.json',\n dry_run_report: 'dry-run-report.json',\n payload_dir: 'payloads',\n ...(aliasBatches ? { exchange_rewrite_plan: 'exchange-rewrite-plan.jsonl' } : {}),\n },\n actions: actionPlans,\n ...(aliasBatches ? { alias_batches: aliasBatches } : {}),\n protected_rows: protectedRowList,\n blockers: allBlockers,\n };\n plan.plan_sha256 = computePlanSha256(plan);\n\n writeImmutableJson(path.join(outDir, plan.artifacts.maintenance_scope), scope);\n writeImmutableJson(path.join(outDir, plan.artifacts.rls_visible_snapshot), {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n account: {\n user_id: scope.account.user_id,\n email: context.account.email,\n session_source: context.account.session_source,\n },\n page_size: pageSize,\n completeness: accountSnapshot.completeness,\n source_urls: accountSnapshot.source_urls,\n row_count: snapshotRows.length,\n snapshot_sha256: plan.visible_snapshot_sha256,\n rows: snapshotRows,\n });\n writeImmutableJsonLines(path.join(outDir, plan.artifacts.protected_rows), protectedRowList);\n if (plan.artifacts.exchange_rewrite_plan) {\n writeImmutableJsonLines(\n path.join(outDir, plan.artifacts.exchange_rewrite_plan),\n plan.alias_batches!.flatMap((batch) =>\n batch.exchange_rewrites.map((rewrite) => ({\n schema_version: 1,\n plan_sha256: plan.plan_sha256,\n operation_id: plan.operation_id,\n batch_id: batch.batch_id,\n factor: batch.factor,\n ...rewrite,\n })),\n ),\n );\n }\n writeImmutableJson(path.join(outDir, plan.artifacts.reference_impact_report), {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n plan_sha256: plan.plan_sha256,\n status: plan.status,\n current: currentImpacts,\n projected: projectedImpacts,\n projected_reference_sha256: plan.projected_reference_sha256,\n });\n writeImmutableJson(path.join(outDir, plan.artifacts.dry_run_report), {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n status: plan.status,\n operation: plan.operation,\n target_mode: plan.target_mode,\n task_id: plan.task_id,\n plan_sha256: plan.plan_sha256,\n account: plan.account,\n snapshot_completeness: accountSnapshot.completeness,\n summary: plan.summary,\n actions: plan.actions.map((action) => ({\n action_id: action.action_id,\n action: action.action,\n table: action.table,\n id: action.id,\n version: action.version,\n status: action.status,\n blockers: action.blockers,\n })),\n alias_batches: plan.alias_batches?.map((batch) => ({\n batch_id: batch.batch_id,\n dimension: batch.dimension,\n factor: batch.factor,\n summary: batch.summary,\n postconditions: batch.postconditions,\n target_snapshots: batch.target_snapshots,\n })),\n blockers: plan.blockers,\n });\n writeImmutableJson(path.join(outDir, plan.artifacts.maintenance_plan), plan);\n return plan;\n}\n\nexport const __testInternals = {\n desiredPayloadIdentity: maintenancePayloadIdentity,\n maintenanceProjectedReferenceFingerprint,\n projectedRows,\n protectedRows,\n referenceImpacts,\n};\n"]}
|