longcelot-sheet-db
Version:
Google Sheets-backed staging database adapter for Node.js with schema-first design
165 lines • 7.86 kB
JavaScript
;
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