@tiangong-lca/cli 0.0.22 → 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.
@@ -2,6 +2,7 @@ import path from 'node:path';
2
2
  import { writeJsonArtifact } from './artifacts.js';
3
3
  import { deleteDatasetRecord, deriveSupabaseFunctionsBaseUrl, } from './dataset-command.js';
4
4
  import { CliError } from './errors.js';
5
+ import { buildSnapshotCompleteness, fetchCompletePostgrestPages, } from './dataset-maintenance-pagination.js';
5
6
  import { buildSupabaseAuthHeaders, deriveSupabaseProjectBaseUrl, requireSupabaseRestRuntime, } from './supabase-client.js';
6
7
  import { resolveSupabaseUserSession } from './supabase-session.js';
7
8
  const CLEAR_ACCOUNT_TABLES = [
@@ -151,43 +152,66 @@ async function fetchCurrentUser(options) {
151
152
  };
152
153
  }
153
154
  async function fetchTableRows(options) {
154
- const rows = [];
155
- const sourceUrls = [];
156
- let offset = 0;
157
- while (true) {
158
- const url = buildTableFilterUrl(options);
159
- url.searchParams.set('order', 'id.asc,version.asc');
160
- url.searchParams.set('limit', String(options.pageSize));
161
- url.searchParams.set('offset', String(offset));
162
- const sourceUrl = url.toString();
163
- const response = await fetchJson({
164
- url: sourceUrl,
165
- init: {
166
- method: 'GET',
167
- headers: buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),
168
- },
169
- fetchImpl: options.fetchImpl,
170
- timeoutMs: options.timeoutMs,
171
- label: `${options.table} RLS visible snapshot`,
172
- });
173
- if (!Array.isArray(response.body)) {
174
- throw new CliError(`Remote ${options.table} snapshot response was not an array.`, {
175
- code: 'DATASET_MAINTENANCE_SNAPSHOT_INVALID',
176
- exitCode: 1,
177
- details: response.body,
155
+ const result = await fetchCompletePostgrestPages({
156
+ table: options.table,
157
+ requestedPageSize: options.pageSize,
158
+ rowIdentity: (row) => `${row.id}\u0000${row.version}`,
159
+ fetchPage: async (offset) => {
160
+ const url = buildTableFilterUrl(options);
161
+ url.searchParams.set('order', 'id.asc,version.asc');
162
+ url.searchParams.set('limit', String(options.pageSize));
163
+ url.searchParams.set('offset', String(offset));
164
+ const sourceUrl = url.toString();
165
+ const response = await fetchJson({
166
+ url: sourceUrl,
167
+ init: {
168
+ method: 'GET',
169
+ headers: {
170
+ ...buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),
171
+ Prefer: 'count=exact',
172
+ },
173
+ },
174
+ fetchImpl: options.fetchImpl,
175
+ timeoutMs: options.timeoutMs,
176
+ label: `${options.table} RLS visible snapshot`,
178
177
  });
179
- }
180
- const pageRows = response.body
181
- .map((row) => normalizeRow(options.table, row))
182
- .filter((row) => row !== null);
183
- rows.push(...pageRows);
184
- sourceUrls.push(sourceUrl);
185
- if (response.body.length < options.pageSize) {
186
- break;
187
- }
188
- offset += options.pageSize;
189
- }
190
- return { rows, sourceUrls };
178
+ if (!Array.isArray(response.body)) {
179
+ throw new CliError(`Remote ${options.table} snapshot response was not an array.`, {
180
+ code: 'DATASET_MAINTENANCE_SNAPSHOT_INVALID',
181
+ exitCode: 1,
182
+ details: response.body,
183
+ });
184
+ }
185
+ const pageRows = response.body.map((row) => normalizeRow(options.table, row));
186
+ if (pageRows.some((row) => row === null || !row.id || !row.version || row.user_id !== options.userId)) {
187
+ throw new CliError(`Remote ${options.table} snapshot contained an invalid account row.`, {
188
+ code: 'DATASET_MAINTENANCE_SNAPSHOT_INVALID',
189
+ exitCode: 1,
190
+ });
191
+ }
192
+ return {
193
+ rows: pageRows,
194
+ source_url: sourceUrl,
195
+ content_range: response.headers.get('content-range'),
196
+ };
197
+ },
198
+ });
199
+ return { rows: result.rows, sourceUrls: result.source_urls, completeness: result.completeness };
200
+ }
201
+ async function fetchAccountSnapshot(options) {
202
+ const tables = await Promise.all(CLEAR_ACCOUNT_TABLES.map(async (table) => ({
203
+ table,
204
+ ...(await fetchTableRows({ ...options, table })),
205
+ })));
206
+ return {
207
+ tables,
208
+ rows: tables.flatMap((table) => table.rows),
209
+ completeness: buildSnapshotCompleteness({
210
+ tables: CLEAR_ACCOUNT_TABLES,
211
+ requestedPageSize: options.pageSize,
212
+ results: tables,
213
+ }),
214
+ };
191
215
  }
192
216
  async function deleteTableRows(options) {
193
217
  const deleteUrl = `${options.transport.functionsBaseUrl}/app_dataset_delete`;
@@ -226,7 +250,7 @@ async function deleteTableRows(options) {
226
250
  }
227
251
  return { deletedRows, deleteUrl, failures };
228
252
  }
229
- function buildSummary(tables) {
253
+ function buildSummary(tables, additionalFailures = 0) {
230
254
  const byTable = Object.fromEntries(CLEAR_ACCOUNT_TABLES.map((table) => {
231
255
  const report = tables.find((entry) => entry.table === table);
232
256
  return [
@@ -243,7 +267,7 @@ function buildSummary(tables) {
243
267
  total_candidates: tables.reduce((sum, table) => sum + table.candidates, 0),
244
268
  total_deleted: tables.reduce((sum, table) => sum + table.deleted, 0),
245
269
  total_remaining: tables.reduce((sum, table) => sum + (table.remaining ?? 0), 0),
246
- total_failures: tables.filter((table) => table.status === 'failed').length,
270
+ total_failures: tables.filter((table) => table.status === 'failed').length + additionalFailures,
247
271
  by_table: byTable,
248
272
  };
249
273
  }
@@ -290,21 +314,19 @@ export async function runDatasetMaintenanceClearAccount(options) {
290
314
  },
291
315
  });
292
316
  }
293
- const snapshotTables = await Promise.all(CLEAR_ACCOUNT_TABLES.map(async (table) => {
294
- const snapshot = await fetchTableRows({
295
- restBaseUrl,
296
- table,
297
- userId: currentUser.id,
298
- stateCodes,
299
- pageSize,
300
- publishableKey: runtime.publishableKey,
301
- accessToken: session.accessToken,
302
- fetchImpl: options.fetchImpl,
303
- timeoutMs,
304
- });
305
- return { table, ...snapshot };
306
- }));
307
- const snapshotRows = snapshotTables.flatMap((table) => table.rows);
317
+ const initialSnapshot = await fetchAccountSnapshot({
318
+ restBaseUrl,
319
+ userId: currentUser.id,
320
+ stateCodes,
321
+ pageSize,
322
+ publishableKey: runtime.publishableKey,
323
+ accessToken: session.accessToken,
324
+ fetchImpl: options.fetchImpl,
325
+ timeoutMs,
326
+ });
327
+ const snapshotTables = initialSnapshot.tables;
328
+ const snapshotRows = initialSnapshot.rows;
329
+ const snapshotCompleteness = initialSnapshot.completeness;
308
330
  const artifacts = {
309
331
  rls_visible_snapshot: path.join(outDir, 'rls-visible-snapshot.json'),
310
332
  dry_run_report: path.join(outDir, 'dry-run-report.json'),
@@ -325,6 +347,7 @@ export async function runDatasetMaintenanceClearAccount(options) {
325
347
  state_codes: stateCodes,
326
348
  page_size: pageSize,
327
349
  },
350
+ completeness: snapshotCompleteness,
328
351
  row_count: snapshotRows.length,
329
352
  rows: snapshotRows,
330
353
  });
@@ -393,7 +416,7 @@ export async function runDatasetMaintenanceClearAccount(options) {
393
416
  blockedByFailure = failed;
394
417
  const failureDetails = deleteResult.failures
395
418
  .slice(0, 5)
396
- .map((failure) => `${failure.row.table}:${failure.row.id ?? '?'}@${failure.row.version ?? '?'} ${failure.error}`);
419
+ .map((failure) => `${failure.row.table}:${failure.row.id}@${failure.row.version} ${failure.error}`);
397
420
  tableReports.push({
398
421
  table,
399
422
  status: failed ? 'failed' : 'deleted',
@@ -429,14 +452,46 @@ export async function runDatasetMaintenanceClearAccount(options) {
429
452
  });
430
453
  }
431
454
  }
432
- const summary = buildSummary(tableReports);
455
+ let readbackCompleteness = null;
456
+ let readbackError = null;
457
+ if (commit) {
458
+ try {
459
+ const finalReadback = await fetchAccountSnapshot({
460
+ restBaseUrl,
461
+ userId: currentUser.id,
462
+ stateCodes,
463
+ pageSize,
464
+ publishableKey: runtime.publishableKey,
465
+ accessToken: session.accessToken,
466
+ fetchImpl: options.fetchImpl,
467
+ timeoutMs,
468
+ });
469
+ readbackCompleteness = finalReadback.completeness;
470
+ for (const table of CLEAR_ACCOUNT_TABLES) {
471
+ const tableReadback = finalReadback.tables.find((entry) => entry.table === table);
472
+ const tableReport = tableReports.find((entry) => entry.table === table);
473
+ tableReport.remaining = tableReadback.rows.length;
474
+ tableReport.source_urls.push(...tableReadback.sourceUrls);
475
+ if (tableReadback.rows.length > 0) {
476
+ const message = `${tableReadback.rows.length} row(s) remained in the final all-table readback.`;
477
+ tableReport.status = 'failed';
478
+ tableReport.error = [tableReport.error, message].filter(Boolean).join(' ');
479
+ }
480
+ }
481
+ }
482
+ catch (error) {
483
+ readbackError = `Final all-table readback could not prove account state: ${caughtErrorMessage(error)}`;
484
+ }
485
+ }
486
+ const summary = buildSummary(tableReports, readbackError ? 1 : 0);
487
+ const finalReadbackPassed = readbackCompleteness !== null && readbackCompleteness.row_count === 0;
433
488
  const report = {
434
489
  schema_version: 1,
435
490
  generated_at_utc: generatedAtUtc,
436
491
  status: commit
437
- ? summary.total_failures > 0 || summary.total_remaining > 0
438
- ? 'completed_with_failures'
439
- : 'cleared_account'
492
+ ? summary.total_failures === 0 && summary.total_remaining === 0 && finalReadbackPassed
493
+ ? 'cleared_account'
494
+ : 'completed_with_failures'
440
495
  : 'planned_account_clear',
441
496
  mode: commit ? 'commit' : 'dry-run',
442
497
  account: {
@@ -449,6 +504,9 @@ export async function runDatasetMaintenanceClearAccount(options) {
449
504
  state_codes: stateCodes,
450
505
  page_size: pageSize,
451
506
  },
507
+ snapshot_completeness: snapshotCompleteness,
508
+ readback_completeness: readbackCompleteness,
509
+ readback_error: readbackError,
452
510
  summary,
453
511
  tables: tableReports,
454
512
  artifacts,
@@ -470,12 +528,16 @@ export async function runDatasetMaintenanceClearAccount(options) {
470
528
  confirmed_email: options.confirm,
471
529
  filters: report.filters,
472
530
  candidate_count: summary.total_candidates,
531
+ snapshot_completeness: snapshotCompleteness,
473
532
  });
474
533
  writeJsonArtifact(artifacts.commit_report, report);
475
534
  writeJsonArtifact(artifacts.readback_verify_report, {
476
535
  schema_version: 1,
477
536
  generated_at_utc: generatedAtUtc,
478
537
  status: report.status,
538
+ initial_snapshot_completeness: snapshotCompleteness,
539
+ readback_completeness: readbackCompleteness,
540
+ readback_error: readbackError,
479
541
  tables: tableReports.map((table) => ({
480
542
  table: table.table,
481
543
  candidates: table.candidates,
@@ -493,6 +555,7 @@ export const __testInternals = {
493
555
  buildTableFilterUrl,
494
556
  caughtErrorMessage,
495
557
  deleteTableRows,
558
+ fetchAccountSnapshot,
496
559
  fetchCurrentUser,
497
560
  fetchJson,
498
561
  fetchTableRows,
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-maintenance-clear-account.js","sourceRoot":"","sources":["../../../src/lib/dataset-maintenance-clear-account.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { writeJsonArtifact } from './artifacts.js';\nimport {\n deleteDatasetRecord,\n deriveSupabaseFunctionsBaseUrl,\n type DatasetCommandTransport,\n} from './dataset-command.js';\nimport { CliError } from './errors.js';\nimport type { FetchLike, ResponseLike } from './http.js';\nimport {\n buildSupabaseAuthHeaders,\n deriveSupabaseProjectBaseUrl,\n requireSupabaseRestRuntime,\n} from './supabase-client.js';\nimport { resolveSupabaseUserSession } from './supabase-session.js';\n\ntype JsonObject = Record<string, unknown>;\n\nexport type DatasetMaintenanceClearAccountTable =\n | 'lifecyclemodels'\n | 'processes'\n | 'flows'\n | 'sources'\n | 'contacts';\n\nexport type DatasetMaintenanceClearAccountRow = {\n table: DatasetMaintenanceClearAccountTable;\n id: string | null;\n version: string | null;\n user_id: string | null;\n state_code: number | null;\n modified_at: string | null;\n};\n\nexport type DatasetMaintenanceClearAccountTableReport = {\n table: DatasetMaintenanceClearAccountTable;\n status: 'dry_run' | 'skipped_empty' | 'deleted' | 'failed' | 'skipped_after_failure';\n candidates: number;\n deleted: number;\n remaining: number | null;\n source_urls: string[];\n delete_url: string | null;\n error: string | null;\n};\n\nexport type DatasetMaintenanceClearAccountReport = {\n schema_version: 1;\n generated_at_utc: string;\n status: 'planned_account_clear' | 'cleared_account' | 'completed_with_failures';\n mode: 'dry-run' | 'commit';\n account: {\n email: string;\n user_id: string;\n session_source: string;\n };\n filters: {\n tables: DatasetMaintenanceClearAccountTable[];\n state_codes: number[] | null;\n page_size: number;\n };\n summary: {\n total_candidates: number;\n total_deleted: number;\n total_remaining: number;\n total_failures: number;\n by_table: Record<\n DatasetMaintenanceClearAccountTable,\n {\n candidates: number;\n deleted: number;\n remaining: number | null;\n status: DatasetMaintenanceClearAccountTableReport['status'];\n }\n >;\n };\n tables: DatasetMaintenanceClearAccountTableReport[];\n artifacts: {\n rls_visible_snapshot: string;\n dry_run_report: string;\n approval_record: string | null;\n commit_report: string | null;\n readback_verify_report: string | null;\n };\n};\n\nexport type RunDatasetMaintenanceClearAccountOptions = {\n outDir?: string | null;\n stateCodes?: number[] | null;\n pageSize?: number;\n timeoutMs?: number;\n commit?: boolean;\n confirm?: string | null;\n env: NodeJS.ProcessEnv;\n fetchImpl: FetchLike;\n now?: Date;\n};\n\nconst CLEAR_ACCOUNT_TABLES: DatasetMaintenanceClearAccountTable[] = [\n 'lifecyclemodels',\n 'processes',\n 'flows',\n 'sources',\n 'contacts',\n];\n\nconst DEFAULT_PAGE_SIZE = 1_000;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction isRecord(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction trimToken(value: unknown): string | null {\n if (typeof value !== 'string') {\n return null;\n }\n\n const trimmed = value.trim();\n return trimmed ? trimmed : null;\n}\n\nfunction normalizeEmail(value: unknown): string {\n return typeof value === 'string' ? value.trim().toLowerCase() : '';\n}\n\nfunction caughtErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction normalizeStateCode(value: unknown): number | null {\n if (typeof value === 'number' && Number.isInteger(value)) {\n return value;\n }\n\n if (typeof value === 'string' && /^-?\\d+$/u.test(value.trim())) {\n return Number.parseInt(value.trim(), 10);\n }\n\n return null;\n}\n\nfunction normalizeStateCodes(values: number[] | null | undefined): number[] | null {\n const normalized = [...new Set((values ?? []).filter((value) => Number.isInteger(value)))].sort(\n (left, right) => left - right,\n );\n return normalized.length > 0 ? normalized : null;\n}\n\nfunction normalizePageSize(value: number | undefined): number {\n if (value === undefined) {\n return DEFAULT_PAGE_SIZE;\n }\n\n if (!Number.isInteger(value) || value < 1 || value > 5_000) {\n throw new CliError('--page-size must be an integer between 1 and 5000.', {\n code: 'DATASET_MAINTENANCE_PAGE_SIZE_INVALID',\n exitCode: 2,\n details: value,\n });\n }\n\n return value;\n}\n\nfunction normalizeTimeoutMs(value: number | undefined): number {\n if (value === undefined) {\n return DEFAULT_TIMEOUT_MS;\n }\n\n if (!Number.isInteger(value) || value < 1) {\n throw new CliError('--timeout-ms must be a positive integer.', {\n code: 'DATASET_MAINTENANCE_TIMEOUT_INVALID',\n exitCode: 2,\n details: value,\n });\n }\n\n return value;\n}\n\nfunction normalizeRow(\n table: DatasetMaintenanceClearAccountTable,\n value: unknown,\n): DatasetMaintenanceClearAccountRow | null {\n if (!isRecord(value)) {\n return null;\n }\n\n const modifiedAt = trimToken(value.modified_at);\n return {\n table,\n id: trimToken(value.id),\n version: trimToken(value.version),\n user_id: trimToken(value.user_id),\n state_code: normalizeStateCode(value.state_code),\n modified_at: modifiedAt,\n };\n}\n\nfunction buildTableFilterUrl(options: {\n restBaseUrl: string;\n table: DatasetMaintenanceClearAccountTable;\n userId: string;\n stateCodes: number[] | null;\n}): URL {\n const url = new URL(`${options.restBaseUrl}/${options.table}`);\n url.searchParams.set('select', 'id,version,user_id,state_code,modified_at');\n url.searchParams.set('user_id', `eq.${options.userId}`);\n if (options.stateCodes) {\n url.searchParams.set('state_code', `in.(${options.stateCodes.join(',')})`);\n }\n return url;\n}\n\nasync function fetchJson(options: {\n url: string;\n init: RequestInit;\n fetchImpl: FetchLike;\n timeoutMs: number;\n label: string;\n}): Promise<{ body: unknown; headers: ResponseLike['headers'] }> {\n const response = await options.fetchImpl(options.url, {\n ...options.init,\n signal: AbortSignal.timeout(options.timeoutMs),\n });\n const rawText = await response.text();\n\n if (!response.ok) {\n throw new CliError(`HTTP ${response.status} returned from ${options.label}`, {\n code: 'DATASET_MAINTENANCE_REMOTE_REQUEST_FAILED',\n exitCode: 1,\n details: {\n url: options.url,\n body: rawText,\n },\n });\n }\n\n const trimmed = rawText.trim();\n if (!trimmed) {\n return { body: null, headers: response.headers };\n }\n\n try {\n return { body: JSON.parse(trimmed), headers: response.headers };\n } catch (error) {\n throw new CliError(`Remote response was not valid JSON for ${options.label}`, {\n code: 'DATASET_MAINTENANCE_REMOTE_INVALID_JSON',\n exitCode: 1,\n details: {\n url: options.url,\n error: String(error),\n },\n });\n }\n}\n\nasync function fetchCurrentUser(options: {\n projectBaseUrl: string;\n publishableKey: string;\n accessToken: string;\n fetchImpl: FetchLike;\n timeoutMs: number;\n}): Promise<{ id: string; email: string | null }> {\n const url = `${options.projectBaseUrl}/auth/v1/user`;\n const response = await fetchJson({\n url,\n init: {\n method: 'GET',\n headers: buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),\n },\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n label: 'supabase current-user lookup',\n });\n\n const body = isRecord(response.body) ? response.body : {};\n const id = trimToken(body.id);\n if (!id) {\n throw new CliError('Supabase current-user lookup succeeded without a user id.', {\n code: 'DATASET_MAINTENANCE_CURRENT_USER_ID_MISSING',\n exitCode: 1,\n });\n }\n\n return {\n id,\n email: trimToken(body.email),\n };\n}\n\nasync function fetchTableRows(options: {\n restBaseUrl: string;\n table: DatasetMaintenanceClearAccountTable;\n userId: string;\n stateCodes: number[] | null;\n pageSize: number;\n publishableKey: string;\n accessToken: string;\n fetchImpl: FetchLike;\n timeoutMs: number;\n}): Promise<{ rows: DatasetMaintenanceClearAccountRow[]; sourceUrls: string[] }> {\n const rows: DatasetMaintenanceClearAccountRow[] = [];\n const sourceUrls: string[] = [];\n let offset = 0;\n\n while (true) {\n const url = buildTableFilterUrl(options);\n url.searchParams.set('order', 'id.asc,version.asc');\n url.searchParams.set('limit', String(options.pageSize));\n url.searchParams.set('offset', String(offset));\n\n const sourceUrl = url.toString();\n const response = await fetchJson({\n url: sourceUrl,\n init: {\n method: 'GET',\n headers: buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),\n },\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n label: `${options.table} RLS visible snapshot`,\n });\n\n if (!Array.isArray(response.body)) {\n throw new CliError(`Remote ${options.table} snapshot response was not an array.`, {\n code: 'DATASET_MAINTENANCE_SNAPSHOT_INVALID',\n exitCode: 1,\n details: response.body,\n });\n }\n\n const pageRows = response.body\n .map((row) => normalizeRow(options.table, row))\n .filter((row): row is DatasetMaintenanceClearAccountRow => row !== null);\n rows.push(...pageRows);\n sourceUrls.push(sourceUrl);\n\n if (response.body.length < options.pageSize) {\n break;\n }\n\n offset += options.pageSize;\n }\n\n return { rows, sourceUrls };\n}\n\nasync function deleteTableRows(options: {\n transport: DatasetCommandTransport;\n table: DatasetMaintenanceClearAccountTable;\n rows: DatasetMaintenanceClearAccountRow[];\n}): Promise<{\n deletedRows: DatasetMaintenanceClearAccountRow[];\n deleteUrl: string;\n failures: Array<{ row: DatasetMaintenanceClearAccountRow; error: string }>;\n}> {\n const deleteUrl = `${options.transport.functionsBaseUrl}/app_dataset_delete`;\n const deletedRows: DatasetMaintenanceClearAccountRow[] = [];\n const failures: Array<{ row: DatasetMaintenanceClearAccountRow; error: string }> = [];\n\n for (const row of options.rows) {\n if (!row.id || !row.version) {\n failures.push({\n row,\n error: 'Row is missing id or version and cannot be deleted through app_dataset_delete.',\n });\n continue;\n }\n\n if (row.state_code !== 0) {\n failures.push({\n row,\n error: `Protected non-draft row state_code=${String(\n row.state_code,\n )}; cmd_dataset_delete only deletes draft rows.`,\n });\n continue;\n }\n\n try {\n await deleteDatasetRecord({\n transport: options.transport,\n table: options.table,\n id: row.id,\n version: row.version,\n });\n deletedRows.push(row);\n } catch (error) {\n failures.push({\n row,\n error: caughtErrorMessage(error),\n });\n }\n }\n\n return { deletedRows, deleteUrl, failures };\n}\n\nfunction buildSummary(\n tables: DatasetMaintenanceClearAccountTableReport[],\n): DatasetMaintenanceClearAccountReport['summary'] {\n const byTable = Object.fromEntries(\n CLEAR_ACCOUNT_TABLES.map((table) => {\n const report = tables.find((entry) => entry.table === table);\n return [\n table,\n {\n candidates: report?.candidates ?? 0,\n deleted: report?.deleted ?? 0,\n remaining: report?.remaining ?? null,\n status: report?.status ?? 'skipped_after_failure',\n },\n ];\n }),\n ) as DatasetMaintenanceClearAccountReport['summary']['by_table'];\n\n return {\n total_candidates: tables.reduce((sum, table) => sum + table.candidates, 0),\n total_deleted: tables.reduce((sum, table) => sum + table.deleted, 0),\n total_remaining: tables.reduce((sum, table) => sum + (table.remaining ?? 0), 0),\n total_failures: tables.filter((table) => table.status === 'failed').length,\n by_table: byTable,\n };\n}\n\nexport async function runDatasetMaintenanceClearAccount(\n options: RunDatasetMaintenanceClearAccountOptions,\n): Promise<DatasetMaintenanceClearAccountReport> {\n const now = options.now ?? new Date();\n const generatedAtUtc = now.toISOString();\n const pageSize = normalizePageSize(options.pageSize);\n const timeoutMs = normalizeTimeoutMs(options.timeoutMs);\n const stateCodes = normalizeStateCodes(options.stateCodes);\n const commit = Boolean(options.commit);\n const outDir = path.resolve(options.outDir ?? 'dataset-maintenance/clear-account');\n const runtime = requireSupabaseRestRuntime(options.env);\n const projectBaseUrl = deriveSupabaseProjectBaseUrl(runtime.apiBaseUrl);\n const restBaseUrl = `${projectBaseUrl}/rest/v1`;\n const functionsBaseUrl = deriveSupabaseFunctionsBaseUrl(runtime.apiBaseUrl);\n const session = await resolveSupabaseUserSession({\n runtime,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n now,\n });\n const currentUser = await fetchCurrentUser({\n projectBaseUrl,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n });\n const accountEmail = currentUser.email ?? session.userEmail;\n const commandTransport: DatasetCommandTransport = {\n functionsBaseUrl,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n };\n\n if (commit && normalizeEmail(options.confirm) !== normalizeEmail(accountEmail)) {\n throw new CliError(\n '--commit requires --confirm to exactly match the current authenticated account email.',\n {\n code: 'DATASET_MAINTENANCE_CONFIRMATION_REQUIRED',\n exitCode: 2,\n details: {\n expected_email: accountEmail,\n received_email: options.confirm ?? null,\n },\n },\n );\n }\n\n const snapshotTables = await Promise.all(\n CLEAR_ACCOUNT_TABLES.map(async (table) => {\n const snapshot = await fetchTableRows({\n restBaseUrl,\n table,\n userId: currentUser.id,\n stateCodes,\n pageSize,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n });\n return { table, ...snapshot };\n }),\n );\n\n const snapshotRows = snapshotTables.flatMap((table) => table.rows);\n const artifacts: DatasetMaintenanceClearAccountReport['artifacts'] = {\n rls_visible_snapshot: path.join(outDir, 'rls-visible-snapshot.json'),\n dry_run_report: path.join(outDir, 'dry-run-report.json'),\n approval_record: commit ? path.join(outDir, 'approval-record.json') : null,\n commit_report: commit ? path.join(outDir, 'commit-report.json') : null,\n readback_verify_report: commit ? path.join(outDir, 'readback-verify-report.json') : null,\n };\n\n writeJsonArtifact(artifacts.rls_visible_snapshot, {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n account: {\n email: accountEmail,\n user_id: currentUser.id,\n session_source: session.source,\n },\n filters: {\n tables: CLEAR_ACCOUNT_TABLES,\n state_codes: stateCodes,\n page_size: pageSize,\n },\n row_count: snapshotRows.length,\n rows: snapshotRows,\n });\n\n const tableReports: DatasetMaintenanceClearAccountTableReport[] = [];\n let blockedByFailure = false;\n\n for (const table of CLEAR_ACCOUNT_TABLES) {\n const snapshot = snapshotTables.find((entry) => entry.table === table)!;\n\n if (!commit) {\n tableReports.push({\n table,\n status: 'dry_run',\n candidates: snapshot.rows.length,\n deleted: 0,\n remaining: null,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: null,\n });\n continue;\n }\n\n if (blockedByFailure) {\n tableReports.push({\n table,\n status: 'skipped_after_failure',\n candidates: snapshot.rows.length,\n deleted: 0,\n remaining: snapshot.rows.length,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: 'Skipped because an earlier table delete or readback verification failed.',\n });\n continue;\n }\n\n if (snapshot.rows.length === 0) {\n tableReports.push({\n table,\n status: 'skipped_empty',\n candidates: 0,\n deleted: 0,\n remaining: 0,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: null,\n });\n continue;\n }\n\n try {\n const deleteResult = await deleteTableRows({\n transport: commandTransport,\n table,\n rows: snapshot.rows,\n });\n const readback = await fetchTableRows({\n restBaseUrl,\n table,\n userId: currentUser.id,\n stateCodes,\n pageSize,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n });\n const remaining = readback.rows.length;\n const failed = remaining > 0 || deleteResult.failures.length > 0;\n blockedByFailure = failed;\n const failureDetails = deleteResult.failures\n .slice(0, 5)\n .map(\n (failure) =>\n `${failure.row.table}:${failure.row.id ?? '?'}@${failure.row.version ?? '?'} ${\n failure.error\n }`,\n );\n\n tableReports.push({\n table,\n status: failed ? 'failed' : 'deleted',\n candidates: snapshot.rows.length,\n deleted: Math.max(snapshot.rows.length - remaining, deleteResult.deletedRows.length),\n remaining,\n source_urls: [...snapshot.sourceUrls, ...readback.sourceUrls],\n delete_url: deleteResult.deleteUrl,\n error: failed\n ? [\n deleteResult.failures.length > 0\n ? `${deleteResult.failures.length} row delete request(s) failed.`\n : null,\n remaining > 0 ? `${remaining} row(s) remained after delete readback.` : null,\n ...failureDetails,\n ]\n .filter((message): message is string => typeof message === 'string')\n .join(' ')\n : null,\n });\n } catch (error) {\n blockedByFailure = true;\n tableReports.push({\n table,\n status: 'failed',\n candidates: snapshot.rows.length,\n deleted: 0,\n remaining: snapshot.rows.length,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: caughtErrorMessage(error),\n });\n }\n }\n\n const summary = buildSummary(tableReports);\n const report: DatasetMaintenanceClearAccountReport = {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n status: commit\n ? summary.total_failures > 0 || summary.total_remaining > 0\n ? 'completed_with_failures'\n : 'cleared_account'\n : 'planned_account_clear',\n mode: commit ? 'commit' : 'dry-run',\n account: {\n email: accountEmail,\n user_id: currentUser.id,\n session_source: session.source,\n },\n filters: {\n tables: CLEAR_ACCOUNT_TABLES,\n state_codes: stateCodes,\n page_size: pageSize,\n },\n summary,\n tables: tableReports,\n artifacts,\n };\n\n writeJsonArtifact(artifacts.dry_run_report, {\n ...report,\n mode: 'dry-run',\n status: 'planned_account_clear',\n });\n\n if (\n commit &&\n artifacts.approval_record &&\n artifacts.commit_report &&\n artifacts.readback_verify_report\n ) {\n writeJsonArtifact(artifacts.approval_record, {\n schema_version: 1,\n approved_at_utc: generatedAtUtc,\n operation: 'clear-account',\n account: report.account,\n confirmed_email: options.confirm,\n filters: report.filters,\n candidate_count: summary.total_candidates,\n });\n writeJsonArtifact(artifacts.commit_report, report);\n writeJsonArtifact(artifacts.readback_verify_report, {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n status: report.status,\n tables: tableReports.map((table) => ({\n table: table.table,\n candidates: table.candidates,\n deleted: table.deleted,\n remaining: table.remaining,\n status: table.status,\n error: table.error,\n })),\n });\n }\n\n return report;\n}\n\nexport const __testInternals = {\n buildSummary,\n buildTableFilterUrl,\n caughtErrorMessage,\n deleteTableRows,\n fetchCurrentUser,\n fetchJson,\n fetchTableRows,\n normalizeEmail,\n normalizePageSize,\n normalizeRow,\n normalizeStateCode,\n normalizeStateCodes,\n normalizeTimeoutMs,\n};\n"]}
1
+ {"version":3,"file":"dataset-maintenance-clear-account.js","sourceRoot":"","sources":["../../../src/lib/dataset-maintenance-clear-account.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { writeJsonArtifact } from './artifacts.js';\nimport {\n deleteDatasetRecord,\n deriveSupabaseFunctionsBaseUrl,\n type DatasetCommandTransport,\n} from './dataset-command.js';\nimport { CliError } from './errors.js';\nimport type { FetchLike, ResponseLike } from './http.js';\nimport {\n buildSnapshotCompleteness,\n fetchCompletePostgrestPages,\n type DatasetMaintenanceSnapshotCompleteness,\n type DatasetMaintenanceTableCompleteness,\n} from './dataset-maintenance-pagination.js';\nimport {\n buildSupabaseAuthHeaders,\n deriveSupabaseProjectBaseUrl,\n requireSupabaseRestRuntime,\n} from './supabase-client.js';\nimport { resolveSupabaseUserSession } from './supabase-session.js';\n\ntype JsonObject = Record<string, unknown>;\n\nexport type DatasetMaintenanceClearAccountTable =\n | 'lifecyclemodels'\n | 'processes'\n | 'flows'\n | 'sources'\n | 'contacts';\n\nexport type DatasetMaintenanceClearAccountRow = {\n table: DatasetMaintenanceClearAccountTable;\n id: string | null;\n version: string | null;\n user_id: string | null;\n state_code: number | null;\n modified_at: string | null;\n};\n\nexport type DatasetMaintenanceClearAccountTableReport = {\n table: DatasetMaintenanceClearAccountTable;\n status: 'dry_run' | 'skipped_empty' | 'deleted' | 'failed' | 'skipped_after_failure';\n candidates: number;\n deleted: number;\n remaining: number | null;\n source_urls: string[];\n delete_url: string | null;\n error: string | null;\n};\n\nexport type DatasetMaintenanceClearAccountReport = {\n schema_version: 1;\n generated_at_utc: string;\n status: 'planned_account_clear' | 'cleared_account' | 'completed_with_failures';\n mode: 'dry-run' | 'commit';\n account: {\n email: string;\n user_id: string;\n session_source: string;\n };\n filters: {\n tables: DatasetMaintenanceClearAccountTable[];\n state_codes: number[] | null;\n page_size: number;\n };\n snapshot_completeness: DatasetMaintenanceSnapshotCompleteness<DatasetMaintenanceClearAccountTable>;\n readback_completeness: DatasetMaintenanceSnapshotCompleteness<DatasetMaintenanceClearAccountTable> | null;\n readback_error: string | null;\n summary: {\n total_candidates: number;\n total_deleted: number;\n total_remaining: number;\n total_failures: number;\n by_table: Record<\n DatasetMaintenanceClearAccountTable,\n {\n candidates: number;\n deleted: number;\n remaining: number | null;\n status: DatasetMaintenanceClearAccountTableReport['status'];\n }\n >;\n };\n tables: DatasetMaintenanceClearAccountTableReport[];\n artifacts: {\n rls_visible_snapshot: string;\n dry_run_report: string;\n approval_record: string | null;\n commit_report: string | null;\n readback_verify_report: string | null;\n };\n};\n\nexport type RunDatasetMaintenanceClearAccountOptions = {\n outDir?: string | null;\n stateCodes?: number[] | null;\n pageSize?: number;\n timeoutMs?: number;\n commit?: boolean;\n confirm?: string | null;\n env: NodeJS.ProcessEnv;\n fetchImpl: FetchLike;\n now?: Date;\n};\n\nconst CLEAR_ACCOUNT_TABLES: DatasetMaintenanceClearAccountTable[] = [\n 'lifecyclemodels',\n 'processes',\n 'flows',\n 'sources',\n 'contacts',\n];\n\nconst DEFAULT_PAGE_SIZE = 1_000;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction isRecord(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction trimToken(value: unknown): string | null {\n if (typeof value !== 'string') {\n return null;\n }\n\n const trimmed = value.trim();\n return trimmed ? trimmed : null;\n}\n\nfunction normalizeEmail(value: unknown): string {\n return typeof value === 'string' ? value.trim().toLowerCase() : '';\n}\n\nfunction caughtErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction normalizeStateCode(value: unknown): number | null {\n if (typeof value === 'number' && Number.isInteger(value)) {\n return value;\n }\n\n if (typeof value === 'string' && /^-?\\d+$/u.test(value.trim())) {\n return Number.parseInt(value.trim(), 10);\n }\n\n return null;\n}\n\nfunction normalizeStateCodes(values: number[] | null | undefined): number[] | null {\n const normalized = [...new Set((values ?? []).filter((value) => Number.isInteger(value)))].sort(\n (left, right) => left - right,\n );\n return normalized.length > 0 ? normalized : null;\n}\n\nfunction normalizePageSize(value: number | undefined): number {\n if (value === undefined) {\n return DEFAULT_PAGE_SIZE;\n }\n\n if (!Number.isInteger(value) || value < 1 || value > 5_000) {\n throw new CliError('--page-size must be an integer between 1 and 5000.', {\n code: 'DATASET_MAINTENANCE_PAGE_SIZE_INVALID',\n exitCode: 2,\n details: value,\n });\n }\n\n return value;\n}\n\nfunction normalizeTimeoutMs(value: number | undefined): number {\n if (value === undefined) {\n return DEFAULT_TIMEOUT_MS;\n }\n\n if (!Number.isInteger(value) || value < 1) {\n throw new CliError('--timeout-ms must be a positive integer.', {\n code: 'DATASET_MAINTENANCE_TIMEOUT_INVALID',\n exitCode: 2,\n details: value,\n });\n }\n\n return value;\n}\n\nfunction normalizeRow(\n table: DatasetMaintenanceClearAccountTable,\n value: unknown,\n): DatasetMaintenanceClearAccountRow | null {\n if (!isRecord(value)) {\n return null;\n }\n\n const modifiedAt = trimToken(value.modified_at);\n return {\n table,\n id: trimToken(value.id),\n version: trimToken(value.version),\n user_id: trimToken(value.user_id),\n state_code: normalizeStateCode(value.state_code),\n modified_at: modifiedAt,\n };\n}\n\nfunction buildTableFilterUrl(options: {\n restBaseUrl: string;\n table: DatasetMaintenanceClearAccountTable;\n userId: string;\n stateCodes: number[] | null;\n}): URL {\n const url = new URL(`${options.restBaseUrl}/${options.table}`);\n url.searchParams.set('select', 'id,version,user_id,state_code,modified_at');\n url.searchParams.set('user_id', `eq.${options.userId}`);\n if (options.stateCodes) {\n url.searchParams.set('state_code', `in.(${options.stateCodes.join(',')})`);\n }\n return url;\n}\n\nasync function fetchJson(options: {\n url: string;\n init: RequestInit;\n fetchImpl: FetchLike;\n timeoutMs: number;\n label: string;\n}): Promise<{ body: unknown; headers: ResponseLike['headers'] }> {\n const response = await options.fetchImpl(options.url, {\n ...options.init,\n signal: AbortSignal.timeout(options.timeoutMs),\n });\n const rawText = await response.text();\n\n if (!response.ok) {\n throw new CliError(`HTTP ${response.status} returned from ${options.label}`, {\n code: 'DATASET_MAINTENANCE_REMOTE_REQUEST_FAILED',\n exitCode: 1,\n details: {\n url: options.url,\n body: rawText,\n },\n });\n }\n\n const trimmed = rawText.trim();\n if (!trimmed) {\n return { body: null, headers: response.headers };\n }\n\n try {\n return { body: JSON.parse(trimmed), headers: response.headers };\n } catch (error) {\n throw new CliError(`Remote response was not valid JSON for ${options.label}`, {\n code: 'DATASET_MAINTENANCE_REMOTE_INVALID_JSON',\n exitCode: 1,\n details: {\n url: options.url,\n error: String(error),\n },\n });\n }\n}\n\nasync function fetchCurrentUser(options: {\n projectBaseUrl: string;\n publishableKey: string;\n accessToken: string;\n fetchImpl: FetchLike;\n timeoutMs: number;\n}): Promise<{ id: string; email: string | null }> {\n const url = `${options.projectBaseUrl}/auth/v1/user`;\n const response = await fetchJson({\n url,\n init: {\n method: 'GET',\n headers: buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),\n },\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n label: 'supabase current-user lookup',\n });\n\n const body = isRecord(response.body) ? response.body : {};\n const id = trimToken(body.id);\n if (!id) {\n throw new CliError('Supabase current-user lookup succeeded without a user id.', {\n code: 'DATASET_MAINTENANCE_CURRENT_USER_ID_MISSING',\n exitCode: 1,\n });\n }\n\n return {\n id,\n email: trimToken(body.email),\n };\n}\n\nasync function fetchTableRows(options: {\n restBaseUrl: string;\n table: DatasetMaintenanceClearAccountTable;\n userId: string;\n stateCodes: number[] | null;\n pageSize: number;\n publishableKey: string;\n accessToken: string;\n fetchImpl: FetchLike;\n timeoutMs: number;\n}): Promise<{\n rows: DatasetMaintenanceClearAccountRow[];\n sourceUrls: string[];\n completeness: DatasetMaintenanceTableCompleteness;\n}> {\n const result = await fetchCompletePostgrestPages({\n table: options.table,\n requestedPageSize: options.pageSize,\n rowIdentity: (row: DatasetMaintenanceClearAccountRow) => `${row.id!}\\u0000${row.version!}`,\n fetchPage: async (offset) => {\n const url = buildTableFilterUrl(options);\n url.searchParams.set('order', 'id.asc,version.asc');\n url.searchParams.set('limit', String(options.pageSize));\n url.searchParams.set('offset', String(offset));\n\n const sourceUrl = url.toString();\n const response = await fetchJson({\n url: sourceUrl,\n init: {\n method: 'GET',\n headers: {\n ...buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),\n Prefer: 'count=exact',\n },\n },\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n label: `${options.table} RLS visible snapshot`,\n });\n\n if (!Array.isArray(response.body)) {\n throw new CliError(`Remote ${options.table} snapshot response was not an array.`, {\n code: 'DATASET_MAINTENANCE_SNAPSHOT_INVALID',\n exitCode: 1,\n details: response.body,\n });\n }\n\n const pageRows = response.body.map((row) => normalizeRow(options.table, row));\n if (\n pageRows.some(\n (row) => row === null || !row.id || !row.version || row.user_id !== options.userId,\n )\n ) {\n throw new CliError(`Remote ${options.table} snapshot contained an invalid account row.`, {\n code: 'DATASET_MAINTENANCE_SNAPSHOT_INVALID',\n exitCode: 1,\n });\n }\n return {\n rows: pageRows as DatasetMaintenanceClearAccountRow[],\n source_url: sourceUrl,\n content_range: response.headers.get('content-range'),\n };\n },\n });\n return { rows: result.rows, sourceUrls: result.source_urls, completeness: result.completeness };\n}\n\nasync function fetchAccountSnapshot(\n options: Omit<Parameters<typeof fetchTableRows>[0], 'table'>,\n): Promise<{\n tables: Array<{\n table: DatasetMaintenanceClearAccountTable;\n rows: DatasetMaintenanceClearAccountRow[];\n sourceUrls: string[];\n completeness: DatasetMaintenanceTableCompleteness;\n }>;\n rows: DatasetMaintenanceClearAccountRow[];\n completeness: DatasetMaintenanceSnapshotCompleteness<DatasetMaintenanceClearAccountTable>;\n}> {\n const tables = await Promise.all(\n CLEAR_ACCOUNT_TABLES.map(async (table) => ({\n table,\n ...(await fetchTableRows({ ...options, table })),\n })),\n );\n return {\n tables,\n rows: tables.flatMap((table) => table.rows),\n completeness: buildSnapshotCompleteness({\n tables: CLEAR_ACCOUNT_TABLES,\n requestedPageSize: options.pageSize,\n results: tables,\n }),\n };\n}\n\nasync function deleteTableRows(options: {\n transport: DatasetCommandTransport;\n table: DatasetMaintenanceClearAccountTable;\n rows: DatasetMaintenanceClearAccountRow[];\n}): Promise<{\n deletedRows: DatasetMaintenanceClearAccountRow[];\n deleteUrl: string;\n failures: Array<{ row: DatasetMaintenanceClearAccountRow; error: string }>;\n}> {\n const deleteUrl = `${options.transport.functionsBaseUrl}/app_dataset_delete`;\n const deletedRows: DatasetMaintenanceClearAccountRow[] = [];\n const failures: Array<{ row: DatasetMaintenanceClearAccountRow; error: string }> = [];\n\n for (const row of options.rows) {\n if (!row.id || !row.version) {\n failures.push({\n row,\n error: 'Row is missing id or version and cannot be deleted through app_dataset_delete.',\n });\n continue;\n }\n\n if (row.state_code !== 0) {\n failures.push({\n row,\n error: `Protected non-draft row state_code=${String(\n row.state_code,\n )}; cmd_dataset_delete only deletes draft rows.`,\n });\n continue;\n }\n\n try {\n await deleteDatasetRecord({\n transport: options.transport,\n table: options.table,\n id: row.id,\n version: row.version,\n });\n deletedRows.push(row);\n } catch (error) {\n failures.push({\n row,\n error: caughtErrorMessage(error),\n });\n }\n }\n\n return { deletedRows, deleteUrl, failures };\n}\n\nfunction buildSummary(\n tables: DatasetMaintenanceClearAccountTableReport[],\n additionalFailures = 0,\n): DatasetMaintenanceClearAccountReport['summary'] {\n const byTable = Object.fromEntries(\n CLEAR_ACCOUNT_TABLES.map((table) => {\n const report = tables.find((entry) => entry.table === table);\n return [\n table,\n {\n candidates: report?.candidates ?? 0,\n deleted: report?.deleted ?? 0,\n remaining: report?.remaining ?? null,\n status: report?.status ?? 'skipped_after_failure',\n },\n ];\n }),\n ) as DatasetMaintenanceClearAccountReport['summary']['by_table'];\n\n return {\n total_candidates: tables.reduce((sum, table) => sum + table.candidates, 0),\n total_deleted: tables.reduce((sum, table) => sum + table.deleted, 0),\n total_remaining: tables.reduce((sum, table) => sum + (table.remaining ?? 0), 0),\n total_failures: tables.filter((table) => table.status === 'failed').length + additionalFailures,\n by_table: byTable,\n };\n}\n\nexport async function runDatasetMaintenanceClearAccount(\n options: RunDatasetMaintenanceClearAccountOptions,\n): Promise<DatasetMaintenanceClearAccountReport> {\n const now = options.now ?? new Date();\n const generatedAtUtc = now.toISOString();\n const pageSize = normalizePageSize(options.pageSize);\n const timeoutMs = normalizeTimeoutMs(options.timeoutMs);\n const stateCodes = normalizeStateCodes(options.stateCodes);\n const commit = Boolean(options.commit);\n const outDir = path.resolve(options.outDir ?? 'dataset-maintenance/clear-account');\n const runtime = requireSupabaseRestRuntime(options.env);\n const projectBaseUrl = deriveSupabaseProjectBaseUrl(runtime.apiBaseUrl);\n const restBaseUrl = `${projectBaseUrl}/rest/v1`;\n const functionsBaseUrl = deriveSupabaseFunctionsBaseUrl(runtime.apiBaseUrl);\n const session = await resolveSupabaseUserSession({\n runtime,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n now,\n });\n const currentUser = await fetchCurrentUser({\n projectBaseUrl,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n });\n const accountEmail = currentUser.email ?? session.userEmail;\n const commandTransport: DatasetCommandTransport = {\n functionsBaseUrl,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n };\n\n if (commit && normalizeEmail(options.confirm) !== normalizeEmail(accountEmail)) {\n throw new CliError(\n '--commit requires --confirm to exactly match the current authenticated account email.',\n {\n code: 'DATASET_MAINTENANCE_CONFIRMATION_REQUIRED',\n exitCode: 2,\n details: {\n expected_email: accountEmail,\n received_email: options.confirm ?? null,\n },\n },\n );\n }\n\n const initialSnapshot = await fetchAccountSnapshot({\n restBaseUrl,\n userId: currentUser.id,\n stateCodes,\n pageSize,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n });\n const snapshotTables = initialSnapshot.tables;\n const snapshotRows = initialSnapshot.rows;\n const snapshotCompleteness = initialSnapshot.completeness;\n const artifacts: DatasetMaintenanceClearAccountReport['artifacts'] = {\n rls_visible_snapshot: path.join(outDir, 'rls-visible-snapshot.json'),\n dry_run_report: path.join(outDir, 'dry-run-report.json'),\n approval_record: commit ? path.join(outDir, 'approval-record.json') : null,\n commit_report: commit ? path.join(outDir, 'commit-report.json') : null,\n readback_verify_report: commit ? path.join(outDir, 'readback-verify-report.json') : null,\n };\n\n writeJsonArtifact(artifacts.rls_visible_snapshot, {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n account: {\n email: accountEmail,\n user_id: currentUser.id,\n session_source: session.source,\n },\n filters: {\n tables: CLEAR_ACCOUNT_TABLES,\n state_codes: stateCodes,\n page_size: pageSize,\n },\n completeness: snapshotCompleteness,\n row_count: snapshotRows.length,\n rows: snapshotRows,\n });\n\n const tableReports: DatasetMaintenanceClearAccountTableReport[] = [];\n let blockedByFailure = false;\n\n for (const table of CLEAR_ACCOUNT_TABLES) {\n const snapshot = snapshotTables.find((entry) => entry.table === table)!;\n\n if (!commit) {\n tableReports.push({\n table,\n status: 'dry_run',\n candidates: snapshot.rows.length,\n deleted: 0,\n remaining: null,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: null,\n });\n continue;\n }\n\n if (blockedByFailure) {\n tableReports.push({\n table,\n status: 'skipped_after_failure',\n candidates: snapshot.rows.length,\n deleted: 0,\n remaining: snapshot.rows.length,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: 'Skipped because an earlier table delete or readback verification failed.',\n });\n continue;\n }\n\n if (snapshot.rows.length === 0) {\n tableReports.push({\n table,\n status: 'skipped_empty',\n candidates: 0,\n deleted: 0,\n remaining: 0,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: null,\n });\n continue;\n }\n\n try {\n const deleteResult = await deleteTableRows({\n transport: commandTransport,\n table,\n rows: snapshot.rows,\n });\n const readback = await fetchTableRows({\n restBaseUrl,\n table,\n userId: currentUser.id,\n stateCodes,\n pageSize,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n });\n const remaining = readback.rows.length;\n const failed = remaining > 0 || deleteResult.failures.length > 0;\n blockedByFailure = failed;\n const failureDetails = deleteResult.failures\n .slice(0, 5)\n .map(\n (failure) =>\n `${failure.row.table}:${failure.row.id!}@${failure.row.version!} ${failure.error}`,\n );\n\n tableReports.push({\n table,\n status: failed ? 'failed' : 'deleted',\n candidates: snapshot.rows.length,\n deleted: Math.max(snapshot.rows.length - remaining, deleteResult.deletedRows.length),\n remaining,\n source_urls: [...snapshot.sourceUrls, ...readback.sourceUrls],\n delete_url: deleteResult.deleteUrl,\n error: failed\n ? [\n deleteResult.failures.length > 0\n ? `${deleteResult.failures.length} row delete request(s) failed.`\n : null,\n remaining > 0 ? `${remaining} row(s) remained after delete readback.` : null,\n ...failureDetails,\n ]\n .filter((message): message is string => typeof message === 'string')\n .join(' ')\n : null,\n });\n } catch (error) {\n blockedByFailure = true;\n tableReports.push({\n table,\n status: 'failed',\n candidates: snapshot.rows.length,\n deleted: 0,\n remaining: snapshot.rows.length,\n source_urls: snapshot.sourceUrls,\n delete_url: null,\n error: caughtErrorMessage(error),\n });\n }\n }\n\n let readbackCompleteness: DatasetMaintenanceClearAccountReport['readback_completeness'] = null;\n let readbackError: string | null = null;\n if (commit) {\n try {\n const finalReadback = await fetchAccountSnapshot({\n restBaseUrl,\n userId: currentUser.id,\n stateCodes,\n pageSize,\n publishableKey: runtime.publishableKey,\n accessToken: session.accessToken,\n fetchImpl: options.fetchImpl,\n timeoutMs,\n });\n readbackCompleteness = finalReadback.completeness;\n for (const table of CLEAR_ACCOUNT_TABLES) {\n const tableReadback = finalReadback.tables.find((entry) => entry.table === table)!;\n const tableReport = tableReports.find((entry) => entry.table === table)!;\n tableReport.remaining = tableReadback.rows.length;\n tableReport.source_urls.push(...tableReadback.sourceUrls);\n if (tableReadback.rows.length > 0) {\n const message = `${tableReadback.rows.length} row(s) remained in the final all-table readback.`;\n tableReport.status = 'failed';\n tableReport.error = [tableReport.error, message].filter(Boolean).join(' ');\n }\n }\n } catch (error) {\n readbackError = `Final all-table readback could not prove account state: ${caughtErrorMessage(\n error,\n )}`;\n }\n }\n\n const summary = buildSummary(tableReports, readbackError ? 1 : 0);\n const finalReadbackPassed = readbackCompleteness !== null && readbackCompleteness.row_count === 0;\n const report: DatasetMaintenanceClearAccountReport = {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n status: commit\n ? summary.total_failures === 0 && summary.total_remaining === 0 && finalReadbackPassed\n ? 'cleared_account'\n : 'completed_with_failures'\n : 'planned_account_clear',\n mode: commit ? 'commit' : 'dry-run',\n account: {\n email: accountEmail,\n user_id: currentUser.id,\n session_source: session.source,\n },\n filters: {\n tables: CLEAR_ACCOUNT_TABLES,\n state_codes: stateCodes,\n page_size: pageSize,\n },\n snapshot_completeness: snapshotCompleteness,\n readback_completeness: readbackCompleteness,\n readback_error: readbackError,\n summary,\n tables: tableReports,\n artifacts,\n };\n\n writeJsonArtifact(artifacts.dry_run_report, {\n ...report,\n mode: 'dry-run',\n status: 'planned_account_clear',\n });\n\n if (\n commit &&\n artifacts.approval_record &&\n artifacts.commit_report &&\n artifacts.readback_verify_report\n ) {\n writeJsonArtifact(artifacts.approval_record, {\n schema_version: 1,\n approved_at_utc: generatedAtUtc,\n operation: 'clear-account',\n account: report.account,\n confirmed_email: options.confirm,\n filters: report.filters,\n candidate_count: summary.total_candidates,\n snapshot_completeness: snapshotCompleteness,\n });\n writeJsonArtifact(artifacts.commit_report, report);\n writeJsonArtifact(artifacts.readback_verify_report, {\n schema_version: 1,\n generated_at_utc: generatedAtUtc,\n status: report.status,\n initial_snapshot_completeness: snapshotCompleteness,\n readback_completeness: readbackCompleteness,\n readback_error: readbackError,\n tables: tableReports.map((table) => ({\n table: table.table,\n candidates: table.candidates,\n deleted: table.deleted,\n remaining: table.remaining,\n status: table.status,\n error: table.error,\n })),\n });\n }\n\n return report;\n}\n\nexport const __testInternals = {\n buildSummary,\n buildTableFilterUrl,\n caughtErrorMessage,\n deleteTableRows,\n fetchAccountSnapshot,\n fetchCurrentUser,\n fetchJson,\n fetchTableRows,\n normalizeEmail,\n normalizePageSize,\n normalizeRow,\n normalizeStateCode,\n normalizeStateCodes,\n normalizeTimeoutMs,\n};\n"]}
@@ -2,6 +2,7 @@ import crypto from 'node:crypto';
2
2
  import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { inspectMaintenanceSupportPayload } from './dataset-maintenance-support-validation.js';
5
+ import { isDatasetMaintenanceSnapshotCompleteness, } from './dataset-maintenance-pagination.js';
5
6
  import { CliError } from './errors.js';
6
7
  export const MAINTENANCE_MUTABLE_TABLES = ['contacts', 'sources', 'flows', 'processes'];
7
8
  export const MAINTENANCE_SUPPORT_TABLES = ['unitgroups', 'flowproperties'];
@@ -487,6 +488,13 @@ export function parseMaintenancePlan(value) {
487
488
  });
488
489
  }
489
490
  const plan = value;
491
+ if (plan.snapshot_completeness !== undefined &&
492
+ !isDatasetMaintenanceSnapshotCompleteness(plan.snapshot_completeness, MAINTENANCE_SCAN_TABLES)) {
493
+ throw new CliError('Maintenance plan snapshot completeness proof is invalid.', {
494
+ code: 'DATASET_MAINTENANCE_PLAN_INVALID',
495
+ exitCode: 2,
496
+ });
497
+ }
490
498
  const expected = computePlanSha256(plan);
491
499
  if (plan.plan_sha256 !== expected) {
492
500
  throw new CliError('Maintenance plan hash does not match its canonical contents.', {