@tiangong-lca/cli 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +18 -7
  2. package/assets/tidas-schemas/tidas_contacts.json +312 -0
  3. package/assets/tidas-schemas/tidas_contacts_category.json +126 -0
  4. package/assets/tidas-schemas/tidas_data_types.json +359 -0
  5. package/assets/tidas-schemas/tidas_flowproperties.json +303 -0
  6. package/assets/tidas-schemas/tidas_flowproperties_category.json +61 -0
  7. package/assets/tidas-schemas/tidas_flows.json +809 -0
  8. package/assets/tidas-schemas/tidas_flows_elementary_category.json +720 -0
  9. package/assets/tidas-schemas/tidas_flows_product_category.json +59623 -0
  10. package/assets/tidas-schemas/tidas_lciamethods.json +1326 -0
  11. package/assets/tidas-schemas/tidas_lciamethods_category.json +685 -0
  12. package/assets/tidas-schemas/tidas_lifecyclemodels.json +1374 -0
  13. package/assets/tidas-schemas/tidas_locations_category.json +2593 -0
  14. package/assets/tidas-schemas/tidas_processes.json +1646 -0
  15. package/assets/tidas-schemas/tidas_processes_category.json +10795 -0
  16. package/assets/tidas-schemas/tidas_sources.json +228 -0
  17. package/assets/tidas-schemas/tidas_sources_category.json +100 -0
  18. package/assets/tidas-schemas/tidas_unitgroups.json +338 -0
  19. package/assets/tidas-schemas/tidas_unitgroups_category.json +61 -0
  20. package/dist/src/cli.js +847 -13
  21. package/dist/src/cli.js.map +1 -1
  22. package/dist/src/lib/dataset-classification.js +947 -0
  23. package/dist/src/lib/dataset-classification.js.map +1 -0
  24. package/dist/src/lib/dataset-command.js +11 -0
  25. package/dist/src/lib/dataset-command.js.map +1 -1
  26. package/dist/src/lib/dataset-contract.js +2 -0
  27. package/dist/src/lib/dataset-contract.js.map +1 -1
  28. package/dist/src/lib/dataset-curation-queue.js +359 -2
  29. package/dist/src/lib/dataset-curation-queue.js.map +1 -1
  30. package/dist/src/lib/dataset-import-lca.js +18 -0
  31. package/dist/src/lib/dataset-import-lca.js.map +1 -1
  32. package/dist/src/lib/dataset-local.js +94 -1
  33. package/dist/src/lib/dataset-local.js.map +1 -1
  34. package/dist/src/lib/dataset-maintenance-clear-account.js +506 -0
  35. package/dist/src/lib/dataset-maintenance-clear-account.js.map +1 -0
  36. package/dist/src/lib/dataset-patch.js +797 -0
  37. package/dist/src/lib/dataset-patch.js.map +1 -0
  38. package/dist/src/lib/dataset-remote-verify.js +188 -3
  39. package/dist/src/lib/dataset-remote-verify.js.map +1 -1
  40. package/dist/src/lib/dataset-save-draft-run.js +725 -0
  41. package/dist/src/lib/dataset-save-draft-run.js.map +1 -0
  42. package/dist/src/lib/dataset-validate.js +32 -2
  43. package/dist/src/lib/dataset-validate.js.map +1 -1
  44. package/dist/src/lib/flow-publish-version.js +7 -2
  45. package/dist/src/lib/flow-publish-version.js.map +1 -1
  46. package/dist/src/lib/flow-qa.js +8 -0
  47. package/dist/src/lib/flow-qa.js.map +1 -1
  48. package/dist/src/lib/identity-preflight.js +895 -117
  49. package/dist/src/lib/identity-preflight.js.map +1 -1
  50. package/dist/src/lib/lifecyclemodel-qa.js +159 -34
  51. package/dist/src/lib/lifecyclemodel-qa.js.map +1 -1
  52. package/dist/src/lib/process-required-fields.js +62 -12
  53. package/dist/src/lib/process-required-fields.js.map +1 -1
  54. package/dist/src/lib/process-save-draft-run.js +10 -0
  55. package/dist/src/lib/process-save-draft-run.js.map +1 -1
  56. package/dist/src/lib/process-save-draft.js +59 -3
  57. package/dist/src/lib/process-save-draft.js.map +1 -1
  58. package/dist/src/lib/supabase-client.js +19 -8
  59. package/dist/src/lib/supabase-client.js.map +1 -1
  60. package/package.json +2 -1
@@ -0,0 +1,506 @@
1
+ import path from 'node:path';
2
+ import { writeJsonArtifact } from './artifacts.js';
3
+ import { deleteDatasetRecord, deriveSupabaseFunctionsBaseUrl, } from './dataset-command.js';
4
+ import { CliError } from './errors.js';
5
+ import { buildSupabaseAuthHeaders, deriveSupabaseProjectBaseUrl, requireSupabaseRestRuntime, } from './supabase-client.js';
6
+ import { resolveSupabaseUserSession } from './supabase-session.js';
7
+ const CLEAR_ACCOUNT_TABLES = [
8
+ 'lifecyclemodels',
9
+ 'processes',
10
+ 'flows',
11
+ 'sources',
12
+ 'contacts',
13
+ ];
14
+ const DEFAULT_PAGE_SIZE = 1_000;
15
+ const DEFAULT_TIMEOUT_MS = 10_000;
16
+ function isRecord(value) {
17
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
18
+ }
19
+ function trimToken(value) {
20
+ if (typeof value !== 'string') {
21
+ return null;
22
+ }
23
+ const trimmed = value.trim();
24
+ return trimmed ? trimmed : null;
25
+ }
26
+ function normalizeEmail(value) {
27
+ return typeof value === 'string' ? value.trim().toLowerCase() : '';
28
+ }
29
+ function caughtErrorMessage(error) {
30
+ return error instanceof Error ? error.message : String(error);
31
+ }
32
+ function normalizeStateCode(value) {
33
+ if (typeof value === 'number' && Number.isInteger(value)) {
34
+ return value;
35
+ }
36
+ if (typeof value === 'string' && /^-?\d+$/u.test(value.trim())) {
37
+ return Number.parseInt(value.trim(), 10);
38
+ }
39
+ return null;
40
+ }
41
+ function normalizeStateCodes(values) {
42
+ const normalized = [...new Set((values ?? []).filter((value) => Number.isInteger(value)))].sort((left, right) => left - right);
43
+ return normalized.length > 0 ? normalized : null;
44
+ }
45
+ function normalizePageSize(value) {
46
+ if (value === undefined) {
47
+ return DEFAULT_PAGE_SIZE;
48
+ }
49
+ if (!Number.isInteger(value) || value < 1 || value > 5_000) {
50
+ throw new CliError('--page-size must be an integer between 1 and 5000.', {
51
+ code: 'DATASET_MAINTENANCE_PAGE_SIZE_INVALID',
52
+ exitCode: 2,
53
+ details: value,
54
+ });
55
+ }
56
+ return value;
57
+ }
58
+ function normalizeTimeoutMs(value) {
59
+ if (value === undefined) {
60
+ return DEFAULT_TIMEOUT_MS;
61
+ }
62
+ if (!Number.isInteger(value) || value < 1) {
63
+ throw new CliError('--timeout-ms must be a positive integer.', {
64
+ code: 'DATASET_MAINTENANCE_TIMEOUT_INVALID',
65
+ exitCode: 2,
66
+ details: value,
67
+ });
68
+ }
69
+ return value;
70
+ }
71
+ function normalizeRow(table, value) {
72
+ if (!isRecord(value)) {
73
+ return null;
74
+ }
75
+ const modifiedAt = trimToken(value.modified_at);
76
+ return {
77
+ table,
78
+ id: trimToken(value.id),
79
+ version: trimToken(value.version),
80
+ user_id: trimToken(value.user_id),
81
+ state_code: normalizeStateCode(value.state_code),
82
+ modified_at: modifiedAt,
83
+ };
84
+ }
85
+ function buildTableFilterUrl(options) {
86
+ const url = new URL(`${options.restBaseUrl}/${options.table}`);
87
+ url.searchParams.set('select', 'id,version,user_id,state_code,modified_at');
88
+ url.searchParams.set('user_id', `eq.${options.userId}`);
89
+ if (options.stateCodes) {
90
+ url.searchParams.set('state_code', `in.(${options.stateCodes.join(',')})`);
91
+ }
92
+ return url;
93
+ }
94
+ async function fetchJson(options) {
95
+ const response = await options.fetchImpl(options.url, {
96
+ ...options.init,
97
+ signal: AbortSignal.timeout(options.timeoutMs),
98
+ });
99
+ const rawText = await response.text();
100
+ if (!response.ok) {
101
+ throw new CliError(`HTTP ${response.status} returned from ${options.label}`, {
102
+ code: 'DATASET_MAINTENANCE_REMOTE_REQUEST_FAILED',
103
+ exitCode: 1,
104
+ details: {
105
+ url: options.url,
106
+ body: rawText,
107
+ },
108
+ });
109
+ }
110
+ const trimmed = rawText.trim();
111
+ if (!trimmed) {
112
+ return { body: null, headers: response.headers };
113
+ }
114
+ try {
115
+ return { body: JSON.parse(trimmed), headers: response.headers };
116
+ }
117
+ catch (error) {
118
+ throw new CliError(`Remote response was not valid JSON for ${options.label}`, {
119
+ code: 'DATASET_MAINTENANCE_REMOTE_INVALID_JSON',
120
+ exitCode: 1,
121
+ details: {
122
+ url: options.url,
123
+ error: String(error),
124
+ },
125
+ });
126
+ }
127
+ }
128
+ async function fetchCurrentUser(options) {
129
+ const url = `${options.projectBaseUrl}/auth/v1/user`;
130
+ const response = await fetchJson({
131
+ url,
132
+ init: {
133
+ method: 'GET',
134
+ headers: buildSupabaseAuthHeaders(options.publishableKey, options.accessToken),
135
+ },
136
+ fetchImpl: options.fetchImpl,
137
+ timeoutMs: options.timeoutMs,
138
+ label: 'supabase current-user lookup',
139
+ });
140
+ const body = isRecord(response.body) ? response.body : {};
141
+ const id = trimToken(body.id);
142
+ if (!id) {
143
+ throw new CliError('Supabase current-user lookup succeeded without a user id.', {
144
+ code: 'DATASET_MAINTENANCE_CURRENT_USER_ID_MISSING',
145
+ exitCode: 1,
146
+ });
147
+ }
148
+ return {
149
+ id,
150
+ email: trimToken(body.email),
151
+ };
152
+ }
153
+ 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,
178
+ });
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 };
191
+ }
192
+ async function deleteTableRows(options) {
193
+ const deleteUrl = `${options.transport.functionsBaseUrl}/app_dataset_delete`;
194
+ const deletedRows = [];
195
+ const failures = [];
196
+ for (const row of options.rows) {
197
+ if (!row.id || !row.version) {
198
+ failures.push({
199
+ row,
200
+ error: 'Row is missing id or version and cannot be deleted through app_dataset_delete.',
201
+ });
202
+ continue;
203
+ }
204
+ if (row.state_code !== 0) {
205
+ failures.push({
206
+ row,
207
+ error: `Protected non-draft row state_code=${String(row.state_code)}; cmd_dataset_delete only deletes draft rows.`,
208
+ });
209
+ continue;
210
+ }
211
+ try {
212
+ await deleteDatasetRecord({
213
+ transport: options.transport,
214
+ table: options.table,
215
+ id: row.id,
216
+ version: row.version,
217
+ });
218
+ deletedRows.push(row);
219
+ }
220
+ catch (error) {
221
+ failures.push({
222
+ row,
223
+ error: caughtErrorMessage(error),
224
+ });
225
+ }
226
+ }
227
+ return { deletedRows, deleteUrl, failures };
228
+ }
229
+ function buildSummary(tables) {
230
+ const byTable = Object.fromEntries(CLEAR_ACCOUNT_TABLES.map((table) => {
231
+ const report = tables.find((entry) => entry.table === table);
232
+ return [
233
+ table,
234
+ {
235
+ candidates: report?.candidates ?? 0,
236
+ deleted: report?.deleted ?? 0,
237
+ remaining: report?.remaining ?? null,
238
+ status: report?.status ?? 'skipped_after_failure',
239
+ },
240
+ ];
241
+ }));
242
+ return {
243
+ total_candidates: tables.reduce((sum, table) => sum + table.candidates, 0),
244
+ total_deleted: tables.reduce((sum, table) => sum + table.deleted, 0),
245
+ total_remaining: tables.reduce((sum, table) => sum + (table.remaining ?? 0), 0),
246
+ total_failures: tables.filter((table) => table.status === 'failed').length,
247
+ by_table: byTable,
248
+ };
249
+ }
250
+ export async function runDatasetMaintenanceClearAccount(options) {
251
+ const now = options.now ?? new Date();
252
+ const generatedAtUtc = now.toISOString();
253
+ const pageSize = normalizePageSize(options.pageSize);
254
+ const timeoutMs = normalizeTimeoutMs(options.timeoutMs);
255
+ const stateCodes = normalizeStateCodes(options.stateCodes);
256
+ const commit = Boolean(options.commit);
257
+ const outDir = path.resolve(options.outDir ?? 'dataset-maintenance/clear-account');
258
+ const runtime = requireSupabaseRestRuntime(options.env);
259
+ const projectBaseUrl = deriveSupabaseProjectBaseUrl(runtime.apiBaseUrl);
260
+ const restBaseUrl = `${projectBaseUrl}/rest/v1`;
261
+ const functionsBaseUrl = deriveSupabaseFunctionsBaseUrl(runtime.apiBaseUrl);
262
+ const session = await resolveSupabaseUserSession({
263
+ runtime,
264
+ fetchImpl: options.fetchImpl,
265
+ timeoutMs,
266
+ now,
267
+ });
268
+ const currentUser = await fetchCurrentUser({
269
+ projectBaseUrl,
270
+ publishableKey: runtime.publishableKey,
271
+ accessToken: session.accessToken,
272
+ fetchImpl: options.fetchImpl,
273
+ timeoutMs,
274
+ });
275
+ const accountEmail = currentUser.email ?? session.userEmail;
276
+ const commandTransport = {
277
+ functionsBaseUrl,
278
+ publishableKey: runtime.publishableKey,
279
+ accessToken: session.accessToken,
280
+ fetchImpl: options.fetchImpl,
281
+ timeoutMs,
282
+ };
283
+ if (commit && normalizeEmail(options.confirm) !== normalizeEmail(accountEmail)) {
284
+ throw new CliError('--commit requires --confirm to exactly match the current authenticated account email.', {
285
+ code: 'DATASET_MAINTENANCE_CONFIRMATION_REQUIRED',
286
+ exitCode: 2,
287
+ details: {
288
+ expected_email: accountEmail,
289
+ received_email: options.confirm ?? null,
290
+ },
291
+ });
292
+ }
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);
308
+ const artifacts = {
309
+ rls_visible_snapshot: path.join(outDir, 'rls-visible-snapshot.json'),
310
+ dry_run_report: path.join(outDir, 'dry-run-report.json'),
311
+ approval_record: commit ? path.join(outDir, 'approval-record.json') : null,
312
+ commit_report: commit ? path.join(outDir, 'commit-report.json') : null,
313
+ readback_verify_report: commit ? path.join(outDir, 'readback-verify-report.json') : null,
314
+ };
315
+ writeJsonArtifact(artifacts.rls_visible_snapshot, {
316
+ schema_version: 1,
317
+ generated_at_utc: generatedAtUtc,
318
+ account: {
319
+ email: accountEmail,
320
+ user_id: currentUser.id,
321
+ session_source: session.source,
322
+ },
323
+ filters: {
324
+ tables: CLEAR_ACCOUNT_TABLES,
325
+ state_codes: stateCodes,
326
+ page_size: pageSize,
327
+ },
328
+ row_count: snapshotRows.length,
329
+ rows: snapshotRows,
330
+ });
331
+ const tableReports = [];
332
+ let blockedByFailure = false;
333
+ for (const table of CLEAR_ACCOUNT_TABLES) {
334
+ const snapshot = snapshotTables.find((entry) => entry.table === table);
335
+ if (!commit) {
336
+ tableReports.push({
337
+ table,
338
+ status: 'dry_run',
339
+ candidates: snapshot.rows.length,
340
+ deleted: 0,
341
+ remaining: null,
342
+ source_urls: snapshot.sourceUrls,
343
+ delete_url: null,
344
+ error: null,
345
+ });
346
+ continue;
347
+ }
348
+ if (blockedByFailure) {
349
+ tableReports.push({
350
+ table,
351
+ status: 'skipped_after_failure',
352
+ candidates: snapshot.rows.length,
353
+ deleted: 0,
354
+ remaining: snapshot.rows.length,
355
+ source_urls: snapshot.sourceUrls,
356
+ delete_url: null,
357
+ error: 'Skipped because an earlier table delete or readback verification failed.',
358
+ });
359
+ continue;
360
+ }
361
+ if (snapshot.rows.length === 0) {
362
+ tableReports.push({
363
+ table,
364
+ status: 'skipped_empty',
365
+ candidates: 0,
366
+ deleted: 0,
367
+ remaining: 0,
368
+ source_urls: snapshot.sourceUrls,
369
+ delete_url: null,
370
+ error: null,
371
+ });
372
+ continue;
373
+ }
374
+ try {
375
+ const deleteResult = await deleteTableRows({
376
+ transport: commandTransport,
377
+ table,
378
+ rows: snapshot.rows,
379
+ });
380
+ const readback = await fetchTableRows({
381
+ restBaseUrl,
382
+ table,
383
+ userId: currentUser.id,
384
+ stateCodes,
385
+ pageSize,
386
+ publishableKey: runtime.publishableKey,
387
+ accessToken: session.accessToken,
388
+ fetchImpl: options.fetchImpl,
389
+ timeoutMs,
390
+ });
391
+ const remaining = readback.rows.length;
392
+ const failed = remaining > 0 || deleteResult.failures.length > 0;
393
+ blockedByFailure = failed;
394
+ const failureDetails = deleteResult.failures
395
+ .slice(0, 5)
396
+ .map((failure) => `${failure.row.table}:${failure.row.id ?? '?'}@${failure.row.version ?? '?'} ${failure.error}`);
397
+ tableReports.push({
398
+ table,
399
+ status: failed ? 'failed' : 'deleted',
400
+ candidates: snapshot.rows.length,
401
+ deleted: Math.max(snapshot.rows.length - remaining, deleteResult.deletedRows.length),
402
+ remaining,
403
+ source_urls: [...snapshot.sourceUrls, ...readback.sourceUrls],
404
+ delete_url: deleteResult.deleteUrl,
405
+ error: failed
406
+ ? [
407
+ deleteResult.failures.length > 0
408
+ ? `${deleteResult.failures.length} row delete request(s) failed.`
409
+ : null,
410
+ remaining > 0 ? `${remaining} row(s) remained after delete readback.` : null,
411
+ ...failureDetails,
412
+ ]
413
+ .filter((message) => typeof message === 'string')
414
+ .join(' ')
415
+ : null,
416
+ });
417
+ }
418
+ catch (error) {
419
+ blockedByFailure = true;
420
+ tableReports.push({
421
+ table,
422
+ status: 'failed',
423
+ candidates: snapshot.rows.length,
424
+ deleted: 0,
425
+ remaining: snapshot.rows.length,
426
+ source_urls: snapshot.sourceUrls,
427
+ delete_url: null,
428
+ error: caughtErrorMessage(error),
429
+ });
430
+ }
431
+ }
432
+ const summary = buildSummary(tableReports);
433
+ const report = {
434
+ schema_version: 1,
435
+ generated_at_utc: generatedAtUtc,
436
+ status: commit
437
+ ? summary.total_failures > 0 || summary.total_remaining > 0
438
+ ? 'completed_with_failures'
439
+ : 'cleared_account'
440
+ : 'planned_account_clear',
441
+ mode: commit ? 'commit' : 'dry-run',
442
+ account: {
443
+ email: accountEmail,
444
+ user_id: currentUser.id,
445
+ session_source: session.source,
446
+ },
447
+ filters: {
448
+ tables: CLEAR_ACCOUNT_TABLES,
449
+ state_codes: stateCodes,
450
+ page_size: pageSize,
451
+ },
452
+ summary,
453
+ tables: tableReports,
454
+ artifacts,
455
+ };
456
+ writeJsonArtifact(artifacts.dry_run_report, {
457
+ ...report,
458
+ mode: 'dry-run',
459
+ status: 'planned_account_clear',
460
+ });
461
+ if (commit &&
462
+ artifacts.approval_record &&
463
+ artifacts.commit_report &&
464
+ artifacts.readback_verify_report) {
465
+ writeJsonArtifact(artifacts.approval_record, {
466
+ schema_version: 1,
467
+ approved_at_utc: generatedAtUtc,
468
+ operation: 'clear-account',
469
+ account: report.account,
470
+ confirmed_email: options.confirm,
471
+ filters: report.filters,
472
+ candidate_count: summary.total_candidates,
473
+ });
474
+ writeJsonArtifact(artifacts.commit_report, report);
475
+ writeJsonArtifact(artifacts.readback_verify_report, {
476
+ schema_version: 1,
477
+ generated_at_utc: generatedAtUtc,
478
+ status: report.status,
479
+ tables: tableReports.map((table) => ({
480
+ table: table.table,
481
+ candidates: table.candidates,
482
+ deleted: table.deleted,
483
+ remaining: table.remaining,
484
+ status: table.status,
485
+ error: table.error,
486
+ })),
487
+ });
488
+ }
489
+ return report;
490
+ }
491
+ export const __testInternals = {
492
+ buildSummary,
493
+ buildTableFilterUrl,
494
+ caughtErrorMessage,
495
+ deleteTableRows,
496
+ fetchCurrentUser,
497
+ fetchJson,
498
+ fetchTableRows,
499
+ normalizeEmail,
500
+ normalizePageSize,
501
+ normalizeRow,
502
+ normalizeStateCode,
503
+ normalizeStateCodes,
504
+ normalizeTimeoutMs,
505
+ };
506
+ //# sourceMappingURL=dataset-maintenance-clear-account.js.map
@@ -0,0 +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"]}