UNPKG

longcelot-sheet-db

Version:

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

172 lines 8.29 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.renameColumnCommand = renameColumnCommand; 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"); const NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/; 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; } function validateNewName(newName, table) { if (!NAME_PATTERN.test(newName)) { return `'${newName}' is not a valid column name — use letters, numbers, and underscores, starting with a letter or underscore`; } if (reservedColumns_1.RESERVED_COLUMN_NAMES.includes(newName)) { return `'${newName}' is a reserved column name and cannot be used`; } if (newName in table.schema.columns) { return `'${newName}' already exists on '${table.schema.name}'`; } return undefined; } async function renameColumnCommand(tableName, oldName, newName, options) { console.log(chalk_1.default.blue.bold('✏️ Rename 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 rename.')); return; } const table = await (0, resolveTable_1.promptOrResolveTable)(all, tableName); const selectable = Object.keys(table.schema.columns).filter((name) => !reservedColumns_1.RESERVED_COLUMN_NAMES.includes(name)); if (selectable.length === 0) { console.log(chalk_1.default.yellow(`⚠️ '${table.schema.name}' has no renameable columns (only reserved columns present).`)); return; } let resolvedOldName; if (oldName) { if (reservedColumns_1.RESERVED_COLUMN_NAMES.includes(oldName)) { console.error(chalk_1.default.red(`❌ '${oldName}' is a reserved auto-generated column and cannot be renamed`)); process.exit(1); } if (!(oldName in table.schema.columns)) { const suggestion = (0, suggest_1.closestMatch)(oldName, selectable); console.error(chalk_1.default.red(`❌ Column '${oldName}' not found on '${table.schema.name}'.${suggestion ? ` Did you mean '${suggestion}'?` : ''} Available: ${selectable.join(', ')}`)); process.exit(1); } resolvedOldName = oldName; } else { const { chosen } = await inquirer_1.default.prompt([ { type: 'list', name: 'chosen', message: `Which column on '${table.schema.name}'?`, choices: selectable }, ]); resolvedOldName = chosen; } let resolvedNewName; if (newName) { const error = validateNewName(newName, table); if (error) { console.error(chalk_1.default.red(`❌ ${error}`)); process.exit(1); } resolvedNewName = newName; } else { const { chosen } = await inquirer_1.default.prompt([ { type: 'input', name: 'chosen', message: `New name for '${resolvedOldName}':`, validate: (v) => validateNewName(v.trim(), table) ?? true, }, ]); resolvedNewName = chosen.trim(); } if (resolvedOldName === resolvedNewName) { console.log(chalk_1.default.yellow('New name is the same as the old name. Nothing to do.')); return; } console.log(chalk_1.default.bold('Plan:\n')); console.log(` Table: ${chalk_1.default.white(`${table.actor}/${table.schema.name}`)}`); console.log(` Rename: ${chalk_1.default.white(resolvedOldName)}${chalk_1.default.white(resolvedNewName)}`); const referencing = findReferencingColumns(all, table.schema.name, resolvedOldName); if (referencing.length > 0) { console.log(chalk_1.default.yellow(` ⚠ referenced by ref('${table.schema.name}.${resolvedOldName}') in: ${referencing.join(', ')} — update those ref() strings manually`)); } 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: `Rename '${resolvedOldName}' to '${resolvedNewName}' on '${table.schema.name}'? This updates the schema file and the live Google Sheet header — existing row data is preserved.`, default: true }, ]); if (!confirmed) { console.log(chalk_1.default.gray('Cancelled.')); return; } } const fileResult = (0, schemaFileMutator_1.renameColumnKey)(table.filePath, resolvedOldName, resolvedNewName); if (!fileResult.ok) { console.error(chalk_1.default.red(`❌ ${fileResult.reason}`)); process.exit(1); } const { adminCtx, adminSheetId } = await (0, adminAdapter_1.buildAdminAdapter)({ config, schemas: all.map((s) => s.schema), tokenFile: options.tokenFile, }); const updatedColumns = {}; for (const [key, value] of Object.entries(table.schema.columns)) { updatedColumns[key === resolvedOldName ? resolvedNewName : key] = value; } const updatedSchema = { ...table.schema, columns: updatedColumns, pkColumn: table.schema.pkColumn === resolvedOldName ? resolvedNewName : table.schema.pkColumn, }; 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 rename') }); continue; } const headers = rows[0]; const index = headers.indexOf(resolvedOldName); if (index < 0) { resultRows.push({ sheet: target.label, status: chalk_1.default.gray('– column already absent from sheet') }); continue; } await adminCtx.getClient().updateHeaderCell(target.sheetId, table.schema.name, index, resolvedNewName); await adminCtx.upsertSchemaVersion(target.sheetId, table.schema.name, newHash, Object.keys(updatedColumns).length); resultRows.push({ sheet: target.label, status: chalk_1.default.green('✅ renamed (data preserved)') }); } 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}: ${resolvedOldName}${resolvedNewName}):\n`)); for (const row of resultRows) { console.log(` ${chalk_1.default.gray(row.sheet.padEnd(28))} ${row.status}`); } console.log(); } exports.default = renameColumnCommand; //# sourceMappingURL=rename-column.js.map