UNPKG

longcelot-sheet-db

Version:

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

110 lines 4.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.removeColumnLine = removeColumnLine; exports.renameColumnKey = renameColumnKey; const fs_1 = __importDefault(require("fs")); const reservedColumns_1 = require("../schema/reservedColumns"); function escapeRegExp(value) { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } /** Checks that every bracket opened on this line is closed on this line, in order. */ function isBalanced(line) { const stack = []; const closers = { ')': '(', ']': '[', '}': '{' }; for (const ch of line) { if (ch === '(' || ch === '[' || ch === '{') stack.push(ch); else if (ch === ')' || ch === ']' || ch === '}') { if (stack.pop() !== closers[ch]) return false; } } return stack.length === 0; } function isReserved(name) { return reservedColumns_1.RESERVED_COLUMN_NAMES.includes(name); } function findKeyLine(lines, columnName) { const keyPattern = new RegExp(`^\\s*${escapeRegExp(columnName)}\\s*:`); const matches = []; lines.forEach((line, i) => { if (keyPattern.test(line)) matches.push(i); }); return { index: matches[0] ?? -1, count: matches.length }; } /** * Removes a single-line `columnName: builder()...,` entry from a schema file's `columns` block. * Only ever removes a line it can confirm is a complete, self-contained property (balanced * brackets, and either a trailing comma or it's the last property before the closing `}`) — * anything it can't confirm returns `ok: false` instead of guessing and corrupting the file. */ function removeColumnLine(filePath, columnName) { if (isReserved(columnName)) { return { ok: false, reason: `'${columnName}' is a reserved auto-generated column and cannot be dropped` }; } let content; try { content = fs_1.default.readFileSync(filePath, 'utf-8'); } catch { return { ok: false, reason: `Could not read schema file: ${filePath}` }; } const lines = content.split('\n'); const { index, count } = findKeyLine(lines, columnName); if (count === 0) { return { ok: false, reason: `Column '${columnName}' not found in ${filePath}` }; } if (count > 1) { return { ok: false, reason: `Column '${columnName}' matched ${count} lines in ${filePath} — edit manually` }; } const trimmed = lines[index].trim(); const endsWithComma = /,\s*(\/\/.*)?$/.test(trimmed); const nextNonEmpty = lines.slice(index + 1).find((l) => l.trim().length > 0); const isLastProperty = !!nextNonEmpty && /^\s*\}/.test(nextNonEmpty); if (!isBalanced(trimmed) || !(endsWithComma || isLastProperty)) { return { ok: false, reason: `Column '${columnName}' definition in ${filePath} spans multiple lines — remove it manually`, }; } lines.splice(index, 1); fs_1.default.writeFileSync(filePath, lines.join('\n'), 'utf-8'); return { ok: true }; } /** * Renames the object key of a `columns` entry in place, leaving everything else on/after that * line untouched. Safe even for multi-line column definitions — only the key text on the * opening line changes, nothing is deleted or re-parsed. */ function renameColumnKey(filePath, oldName, newName) { if (isReserved(oldName)) { return { ok: false, reason: `'${oldName}' is a reserved auto-generated column and cannot be renamed` }; } if (isReserved(newName)) { return { ok: false, reason: `'${newName}' is a reserved column name and cannot be used` }; } let content; try { content = fs_1.default.readFileSync(filePath, 'utf-8'); } catch { return { ok: false, reason: `Could not read schema file: ${filePath}` }; } const lines = content.split('\n'); const { index, count } = findKeyLine(lines, oldName); if (count === 0) { return { ok: false, reason: `Column '${oldName}' not found in ${filePath}` }; } if (count > 1) { return { ok: false, reason: `Column '${oldName}' matched ${count} lines in ${filePath} — edit manually` }; } const keyPattern = new RegExp(`^(\\s*)${escapeRegExp(oldName)}(\\s*:)`); lines[index] = lines[index].replace(keyPattern, `$1${newName}$2`); fs_1.default.writeFileSync(filePath, lines.join('\n'), 'utf-8'); return { ok: true }; } //# sourceMappingURL=schemaFileMutator.js.map