@tiangong-lca/cli 0.0.21 → 0.0.22

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.
@@ -0,0 +1,485 @@
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
+ projected_reference_sha256: sha256Json(maintenanceProjectedReferenceFingerprint(intendedRows)),
379
+ plan_sha256: '',
380
+ summary: {
381
+ actions: actionPlans.length,
382
+ save_draft: actionPlans.filter((action) => action.action === 'save_draft').length,
383
+ delete: actionPlans.filter((action) => action.action === 'delete').length,
384
+ update_json_ordered: actionPlans.filter((action) => action.action === 'update_json_ordered')
385
+ .length,
386
+ atomic_batches: aliasBatches?.length ?? 0,
387
+ scaled_exchanges: aliasBatches?.reduce((sum, batch) => sum + batch.summary.exchanges, 0) ?? 0,
388
+ scaled_amount_fields: aliasBatches?.reduce((sum, batch) => sum + batch.summary.amount_fields, 0) ?? 0,
389
+ unrelated_exchanges_preserved: aliasBatches?.reduce((sum, batch) => sum + batch.summary.unrelated_exchanges, 0) ?? 0,
390
+ protected_rows: protectedRowList.length,
391
+ blockers: allBlockers.length,
392
+ current_reference_impacts: currentImpacts.length,
393
+ projected_reference_impacts: projectedImpacts.length,
394
+ },
395
+ artifacts: {
396
+ maintenance_scope: 'maintenance-scope.json',
397
+ rls_visible_snapshot: 'rls-visible-snapshot.json',
398
+ protected_rows: 'protected-rows.jsonl',
399
+ reference_impact_report: 'reference-impact-report.json',
400
+ maintenance_plan: 'maintenance-plan.json',
401
+ dry_run_report: 'dry-run-report.json',
402
+ payload_dir: 'payloads',
403
+ ...(aliasBatches ? { exchange_rewrite_plan: 'exchange-rewrite-plan.jsonl' } : {}),
404
+ },
405
+ actions: actionPlans,
406
+ ...(aliasBatches ? { alias_batches: aliasBatches } : {}),
407
+ protected_rows: protectedRowList,
408
+ blockers: allBlockers,
409
+ };
410
+ plan.plan_sha256 = computePlanSha256(plan);
411
+ writeImmutableJson(path.join(outDir, plan.artifacts.maintenance_scope), scope);
412
+ writeImmutableJson(path.join(outDir, plan.artifacts.rls_visible_snapshot), {
413
+ schema_version: 1,
414
+ generated_at_utc: generatedAtUtc,
415
+ account: {
416
+ user_id: scope.account.user_id,
417
+ email: context.account.email,
418
+ session_source: context.account.session_source,
419
+ },
420
+ page_size: pageSize,
421
+ source_urls: accountSnapshot.source_urls,
422
+ row_count: snapshotRows.length,
423
+ snapshot_sha256: plan.visible_snapshot_sha256,
424
+ rows: snapshotRows,
425
+ });
426
+ writeImmutableJsonLines(path.join(outDir, plan.artifacts.protected_rows), protectedRowList);
427
+ if (plan.artifacts.exchange_rewrite_plan) {
428
+ writeImmutableJsonLines(path.join(outDir, plan.artifacts.exchange_rewrite_plan), plan.alias_batches.flatMap((batch) => batch.exchange_rewrites.map((rewrite) => ({
429
+ schema_version: 1,
430
+ plan_sha256: plan.plan_sha256,
431
+ operation_id: plan.operation_id,
432
+ batch_id: batch.batch_id,
433
+ factor: batch.factor,
434
+ ...rewrite,
435
+ }))));
436
+ }
437
+ writeImmutableJson(path.join(outDir, plan.artifacts.reference_impact_report), {
438
+ schema_version: 1,
439
+ generated_at_utc: generatedAtUtc,
440
+ plan_sha256: plan.plan_sha256,
441
+ status: plan.status,
442
+ current: currentImpacts,
443
+ projected: projectedImpacts,
444
+ projected_reference_sha256: plan.projected_reference_sha256,
445
+ });
446
+ writeImmutableJson(path.join(outDir, plan.artifacts.dry_run_report), {
447
+ schema_version: 1,
448
+ generated_at_utc: generatedAtUtc,
449
+ status: plan.status,
450
+ operation: plan.operation,
451
+ target_mode: plan.target_mode,
452
+ task_id: plan.task_id,
453
+ plan_sha256: plan.plan_sha256,
454
+ account: plan.account,
455
+ summary: plan.summary,
456
+ actions: plan.actions.map((action) => ({
457
+ action_id: action.action_id,
458
+ action: action.action,
459
+ table: action.table,
460
+ id: action.id,
461
+ version: action.version,
462
+ status: action.status,
463
+ blockers: action.blockers,
464
+ })),
465
+ alias_batches: plan.alias_batches?.map((batch) => ({
466
+ batch_id: batch.batch_id,
467
+ dimension: batch.dimension,
468
+ factor: batch.factor,
469
+ summary: batch.summary,
470
+ postconditions: batch.postconditions,
471
+ target_snapshots: batch.target_snapshots,
472
+ })),
473
+ blockers: plan.blockers,
474
+ });
475
+ writeImmutableJson(path.join(outDir, plan.artifacts.maintenance_plan), plan);
476
+ return plan;
477
+ }
478
+ export const __testInternals = {
479
+ desiredPayloadIdentity: maintenancePayloadIdentity,
480
+ maintenanceProjectedReferenceFingerprint,
481
+ projectedRows,
482
+ protectedRows,
483
+ referenceImpacts,
484
+ };
485
+ //# 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 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 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 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"]}