longcelot-sheet-db
Version:
Google Sheets-backed staging database adapter for Node.js with schema-first design
285 lines (204 loc) • 11.4 kB
Markdown
name: cli
description: Use the longcelot-sheet-db CLI (lsdb). Use when running lsdb init, generate, sync, validate, seed, mock-users, doctor, status, migrate, migrate-data, drop-table, drop-column, or rename-column commands — or when scaffolding a new project, generating schema files, syncing schemas to Google Sheets (including CI-friendly --token-file), seeding with --skip-existing or --upsert, diagnosing configuration issues, exporting schemas to Prisma/SQL, pushing schema changes to all user sheets, or removing/renaming a table or column safely (schema file + live sheet together).
license: MIT
metadata:
package: longcelot-sheet-db
version: "0.1.29"
# longcelot-sheet-db — CLI Reference (`lsdb`)
All commands are available as `lsdb <command>` (global install) or via:
```bash
npx lsdb <command>
pnpm dlx lsdb <command>
yarn dlx lsdb <command>
bunx lsdb <command>
```
## init — Scaffold a new project
```bash
npx lsdb init
npx lsdb init --integrate # merge into existing project without overwriting
```
**What it creates:**
- `lsdb.config.ts` — Project configuration (project name, actors with env var mapping)
- `.env` — Environment variable template (one `DEV_*_SHEET_ID` per actor)
- `schemas/` — Schemas directory with default admin schemas (`users`, `credentials`, `schema_versions`)
Run `init` **once** when first adding the package. Use `--integrate` to add it to an existing project without overwriting existing files.
## generate — Interactive schema generator
```bash
npx lsdb generate bookings
```
Launches an interactive prompt to define columns (name, type, modifiers). Writes a new schema file to `schemas/<actor>/`.
## sync — Sync schemas to Google Sheets
```bash
npx lsdb sync # sync all actor sheets
npx lsdb sync --all-users # also push to all registered user sheets
npx lsdb sync --all-users --dry-run # preview --all-users changes without writing
npx lsdb sync --token-file /tmp/t.json # CI/CD: load tokens from file, skip OAuth prompt
```
**What it does:**
1. Loads all schemas from `schemas/`
2. Validates environment variables
3. **Auth**: reads `.lsdb-tokens.json`, refreshes if stale, prompts for browser auth if absent
4. Calls `syncSchema()` for every schema — creates missing tabs and adds missing headers
5. Prints a per-actor status table:
```
Actor │ Sheet ID │ Tables │ Status
───────────┼────────────────────────────┼──────────┼────────────
admin │ 1ABCyourAdminSheetId │ 3 │ ✅ synced
student │ 1DEFyourStudentSheetId │ 5 │ ✅ synced
teacher │ (not set) │ 4 │ ⚠ skipped
```
**`--all-users`** — reads all rows from admin `users` table and pushes schema changes to every registered user sheet. Uses exponential backoff (1 s → 32 s) to handle Google Sheets API rate limits.
**`--token-file <path>`** — CI/CD-friendly. Load a pre-stored tokens JSON instead of the interactive browser prompt:
```bash
# GitHub Actions example
echo "$LSDB_TOKENS" > /tmp/tokens.json
npx lsdb sync --token-file /tmp/tokens.json
```
> `.lsdb-tokens.json` is written to the project root on first interactive auth. Add it to `.gitignore`. Never commit OAuth tokens.
## validate — Validate all schemas
```bash
npx lsdb validate
```
Checks all schema files in `schemas/` for:
- Duplicate table names within the same actor
- Invalid column modifiers
- Unknown actor references (actor not listed in `lsdb.config.ts`)
- Missing required schema fields (`name`, `actor`, `columns`)
Use in CI to catch schema problems before `sync`.
## seed — Load initial/test data
```bash
npx lsdb seed <seed-file>
npx lsdb seed seeds/admin.ts --skip-existing # skip on unique conflict (idempotent)
npx lsdb seed seeds/admin.ts --upsert # update on unique conflict
npx lsdb seed seeds/users.ts --all-actors # distribute data across all user sheets
```
### Seed file formats
**Static (plain object):**
```typescript
// seeds/admin.ts
export default {
users: [
{ email: 'admin@example.com', role: 'admin', status: 'active' },
],
}
```
**Dynamic (function — receives `process.env`):**
```typescript
// seeds/admin.ts
export default async function(env: NodeJS.ProcessEnv) {
return {
users: [
{ email: env.SUPER_ADMIN_EMAIL, role: 'admin', status: 'active' },
],
}
}
```
Use the dynamic form when seed data depends on environment variables or CLI arguments.
**Flags:**
| Flag | Effect |
|---|---|
| `--skip-existing` | Skip rows where a unique column already matches — no error, no overwrite |
| `--upsert` | Update existing row on unique conflict instead of throwing |
| `--all-actors` | Distribute seed data across all registered user sheets (reads from admin `users` table) |
## mock-users — Create test user sheets
```bash
npx lsdb mock-users
npx lsdb mock-users 5 # create 5 mock users
```
Generates mock Google Sheets for development/testing. Lets you inspect what real users see without registering real accounts.
## doctor — Diagnostics and health checks
```bash
npx lsdb doctor
```
Checks:
- All required environment variables are set
- Google OAuth credentials are valid
- `lsdb.config.ts` structure is correct
- `.lsdb-tokens.json` exists and is readable
Run `doctor` first when debugging mysterious adapter errors.
## status — Show project status
```bash
npx lsdb status
```
Displays registered tables, actors, sheet IDs, schema counts, and token info.
## migrate — Export schemas to SQL/Prisma
```bash
npx lsdb migrate --prisma --output ./prisma # generate schema.prisma
npx lsdb migrate --sql --output ./migrations # generate CREATE TABLE DDL
```
Exports all table schemas to a target format. Use as a starting point when migrating from Google Sheets to a production database. For the full migration guide see `skills/migrations/SKILL.md`.
> Deprecated alias: `lsdb export` still works but emits a deprecation warning.
## migrate-data — Generate data migration script
```bash
npx lsdb migrate-data
npx lsdb migrate-data --table users --output ./scripts
npx lsdb migrate-data --all-users # also export every registered user sheet
npx lsdb migrate-data --dry-run # preview plan without writing files
```
Generates a `migrate-data.js` script with `insertRow()` stubs. Replace the stubs with your production DB client to transfer data row-by-row.
> Deprecated alias: `lsdb export-data` still works but emits a deprecation warning.
## drop-table — Delete a table's schema file + Google Sheet tab
```bash
npx lsdb drop-table bookings
npx lsdb drop-table bookings old_notifications --all-users
npx lsdb drop-table # interactive checkbox over every table
npx lsdb drop-table --dry-run
```
`sync` never deletes anything on its own (see `skills/migrations/SKILL.md`) — this is the explicit way to remove a table from both the schema file and the live sheet. Deletes the schema file, then the corresponding Google Sheet tab (and, with `--all-users`, the tab in every registered user's personal sheet). Prints a plan and asks for confirmation first (`--yes` to skip). Warns if another table's `ref()` points at the one being dropped.
## drop-column — Delete column(s) from a table
```bash
npx lsdb drop-column bookings notes
npx lsdb drop-column bookings # interactive checkbox over that table's columns
npx lsdb drop-column # interactive: pick table, then columns
npx lsdb drop-column bookings notes --all-users --yes
```
Removes column(s) from the schema file and deletes the matching column(s) — and all data in them — from the live Google Sheet. Resolves each column's *current* position from the sheet's header row, not the schema file's declared order (`sync` appends new columns at the end, so they can differ). Refuses to drop reserved columns (`_id`, `_created_at`, `_updated_at`, `_deleted_at`) or a table's primary key column — drop the whole table instead if that's really the intent.
## rename-column — Rename a column without losing data
```bash
npx lsdb rename-column bookings notes remarks
npx lsdb rename-column # fully interactive: table, column, new name
npx lsdb rename-column bookings notes remarks --all-users
```
Renames the column's key in the schema file and overwrites the Google Sheet **header cell in place** — existing row data is preserved, unlike a drop-and-re-add. This is what prevents the real risk it closes: renaming a column in the schema file alone desyncs every existing row from that point on, and a later `sync`/`auto-sync` has no way to recover data under the old header name. Warns (doesn't block) if another table's `ref()` points at the old name — those `ref()` strings in other schema files aren't rewritten automatically.
## lsdb.config.ts structure
```typescript
import type { SheetDBConfig } from 'longcelot-sheet-db';
export default {
projectName: 'my-app',
superAdminEmail: 'admin@example.com',
actors: [
{ name: 'admin', sheetIdEnv: 'ADMIN_SHEET_ID' },
{ name: 'student', sheetIdEnv: 'DEV_STUDENT_SHEET_ID' },
{ name: 'teacher', sheetIdEnv: 'DEV_TEACHER_SHEET_ID' },
],
onSchemaMismatch: 'warn', // 'warn' | 'error' | 'auto-sync'
} satisfies SheetDBConfig;
```
The `sheetIdEnv` field tells each CLI command which env var holds the sheet ID for that actor.
## Common Mistakes
- **Running `sync` without `.env` configured** — `sync` fails at OAuth if env vars are missing. Run `doctor` first.
- **Committing `.lsdb-tokens.json`** — Contains OAuth refresh tokens. Verify it is in `.gitignore` before any push.
- **Not running `sync` after schema changes** — Schema files are the source of truth. New columns don't appear in Sheets until `sync` runs.
- **Re-seeding without `--skip-existing`** — Running `seed` twice throws `ValidationError: Unique constraint violation` for any unique column. Use `--skip-existing` for idempotent seeds or `--upsert` to update.
- **Forgetting `--token-file` in CI** — Without it, `sync` blocks waiting for interactive input and the CI job hangs.
- **Using a dynamic seed file without `export default async function`** — Named exports or non-function defaults are treated as the static plain-object format and must return `Record<string, unknown[]>` directly.
- **Expecting `drop-column`/`drop-table` to be additive-safe like `sync`** — They're destructive by design: real Google Sheets columns/tabs (and all data in them) are deleted. Always run with `--dry-run` first if unsure, especially with `--all-users`.
- **Renaming a column in the schema file by hand instead of `rename-column`** — A manual rename leaves the live sheet's header on the old name, silently desyncing every existing row. Use `lsdb rename-column` so the schema file and sheet header change together.
- **Expecting `rename-column`/`drop-column` to update `ref()` strings in other schema files** — They warn if another table's `ref('table.column')` points at what's being renamed/dropped, but don't rewrite it for you. Update those `ref()` calls manually.