UNPKG

longcelot-sheet-db

Version:

Google Sheets-backed staging database adapter for Node.js with schema-first design

165 lines 7.86 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.dropColumnCommand = dropColumnCommand; const chalk_1 = __importDefault(require("chalk")); const inquirer_1 = __importDefault(require("inquirer")); const adminAdapter_1 = require("../lib/adminAdapter"); const schemaLoader_1 = require("../lib/schemaLoader"); const resolveTable_1 = require("../lib/resolveTable"); const actorConfig_1 = require("../../utils/actorConfig"); const schemaFileMutator_1 = require("../../utils/schemaFileMutator"); const reservedColumns_1 = require("../../schema/reservedColumns"); const schemaHash_1 = require("../../utils/schemaHash"); const suggest_1 = require("../../utils/suggest"); function selectableColumns(table) { return Object.keys(table.schema.columns).filter((name) => !reservedColumns_1.RESERVED_COLUMN_NAMES.includes(name) && name !== table.schema.pkColumn); } function findReferencingColumns(all, tableName, columnName) { const referencing = []; for (const { schema } of all) { for (const col of Object.values(schema.columns)) { if (col.ref === `${tableName}.${columnName}`) { referencing.push(schema.name); break; } } } return referencing; } async function dropColumnCommand(tableName, columnNames, options) { console.log(chalk_1.default.blue.bold('🗑️ Drop column...\n')); const config = (0, adminAdapter_1.loadCLIConfig)(); const all = (0, schemaLoader_1.loadSchemasWithPaths)(config); if (all.length === 0) { console.log(chalk_1.default.yellow('⚠️ No schemas found. Nothing to drop.')); return; } const table = await (0, resolveTable_1.promptOrResolveTable)(all, tableName); const selectable = selectableColumns(table); if (selectable.length === 0) { console.log(chalk_1.default.yellow(`⚠️ '${table.schema.name}' has no droppable columns (only reserved/primary-key columns remain).`)); return; } let columns; if (columnNames.length === 0) { const { chosen } = await inquirer_1.default.prompt([ { type: 'checkbox', name: 'chosen', message: `Select column(s) to drop from '${table.schema.name}' (space to toggle, enter to confirm):`, choices: selectable, }, ]); if (chosen.length === 0) { console.log(chalk_1.default.yellow('No columns selected. Nothing to do.')); return; } columns = chosen; } else { const errors = []; columns = []; for (const name of columnNames) { if (reservedColumns_1.RESERVED_COLUMN_NAMES.includes(name)) { errors.push(`'${name}' is a reserved auto-generated column and cannot be dropped`); } else if (name === table.schema.pkColumn) { errors.push(`'${name}' is the primary key — drop the whole table instead of its PK column`); } else if (!(name in table.schema.columns)) { const suggestion = (0, suggest_1.closestMatch)(name, selectable); errors.push(`Column '${name}' not found on '${table.schema.name}'.${suggestion ? ` Did you mean '${suggestion}'?` : ''} Available: ${selectable.join(', ')}`); } else { columns.push(name); } } if (errors.length > 0) { console.error(chalk_1.default.red.bold('❌ Invalid column name(s):\n')); errors.forEach((e) => console.error(chalk_1.default.red(` - ${e}`))); process.exit(1); } } console.log(chalk_1.default.bold('Plan:\n')); console.log(` Table: ${chalk_1.default.white(`${table.actor}/${table.schema.name}`)}`); for (const col of columns) { console.log(` Drop column: ${chalk_1.default.white(col)}`); const referencing = findReferencingColumns(all, table.schema.name, col); if (referencing.length > 0) { console.log(chalk_1.default.yellow(` ⚠ referenced by ref('${table.schema.name}.${col}') in: ${referencing.join(', ')} — those FK columns will break`)); } } console.log(); if (options.dryRun) { console.log(chalk_1.default.yellow('[DRY RUN] No changes made.')); return; } if (!options.yes) { const { confirmed } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'confirmed', message: `Drop ${columns.length} column(s) from '${table.schema.name}'? This removes them from the schema file and deletes the live Google Sheet column(s), including all data in them.`, default: false }, ]); if (!confirmed) { console.log(chalk_1.default.gray('Cancelled.')); return; } } const removed = []; for (const col of columns) { const result = (0, schemaFileMutator_1.removeColumnLine)(table.filePath, col); if (result.ok) { removed.push(col); } else { console.log(chalk_1.default.yellow(` ⚠ Skipping '${col}' — ${result.reason}`)); } } if (removed.length === 0) { console.log(chalk_1.default.yellow('\nNo columns could be safely removed from the schema file. Nothing changed on Google Sheets.')); return; } const { adminCtx, adminSheetId } = await (0, adminAdapter_1.buildAdminAdapter)({ config, schemas: all.map((s) => s.schema), tokenFile: options.tokenFile, }); const updatedColumns = { ...table.schema.columns }; for (const col of removed) delete updatedColumns[col]; const updatedSchema = { ...table.schema, columns: updatedColumns }; const newHash = (0, schemaHash_1.computeSchemaHash)(updatedSchema); const actorCfg = config.actors.find((a) => (0, actorConfig_1.resolveActorName)(a) === table.actor); const targets = await (0, adminAdapter_1.resolveSheetTargets)(adminCtx, table.actor, adminSheetId, actorCfg, options.allUsers ?? false); const resultRows = []; for (const target of targets) { try { const rows = await adminCtx.getClient().getAllRows(target.sheetId, table.schema.name); if (rows.length === 0) { resultRows.push({ sheet: target.label, status: chalk_1.default.gray('– tab not synced yet, nothing to remove') }); continue; } const headers = rows[0]; const indexes = removed.map((col) => headers.indexOf(col)).filter((i) => i >= 0); if (indexes.length === 0) { resultRows.push({ sheet: target.label, status: chalk_1.default.gray('– column(s) already absent from sheet') }); continue; } await adminCtx.getClient().deleteColumns(target.sheetId, table.schema.name, indexes); await adminCtx.upsertSchemaVersion(target.sheetId, table.schema.name, newHash, Object.keys(updatedColumns).length); resultRows.push({ sheet: target.label, status: chalk_1.default.green(`✅ dropped ${indexes.length} column(s)`) }); } catch (err) { const message = err instanceof Error ? err.message : String(err); resultRows.push({ sheet: target.label, status: chalk_1.default.red(`❌ ${message}`) }); } } console.log(chalk_1.default.bold(`\nResult (${table.schema.name}: ${removed.join(', ')}):\n`)); for (const row of resultRows) { console.log(` ${chalk_1.default.gray(row.sheet.padEnd(28))} ${row.status}`); } console.log(); } exports.default = dropColumnCommand; //# sourceMappingURL=drop-column.js.map