supabee 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,234 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import readline from 'node:readline/promises';
4
+ import { stdin as input, stdout as output } from 'node:process';
5
+ import { classifyMigrations, resolveMarkers, } from '../lib/migration-classifier.js';
6
+ import { info, ok, sectionWithNote, title, warn } from '../lib/ui.js';
7
+ const DEFAULT_MIGRATIONS_DIR = 'supabase/migrations';
8
+ function absoluteMigrationsDir(dir) {
9
+ return path.resolve(process.cwd(), dir ?? DEFAULT_MIGRATIONS_DIR);
10
+ }
11
+ async function ensureDirectoryExists(dirPath) {
12
+ const stats = await fs.promises.stat(dirPath).catch(() => null);
13
+ if (!stats || !stats.isDirectory()) {
14
+ throw new Error(`Migrations directory not found: ${dirPath}`);
15
+ }
16
+ }
17
+ function recommendationText(result) {
18
+ if (result.classification === 'mixed') {
19
+ return 'Split file into schema-only and data-only migrations';
20
+ }
21
+ if (result.classification === 'unknown') {
22
+ return 'No automatic action';
23
+ }
24
+ if (!result.recommendedMarker) {
25
+ return 'No marker change needed';
26
+ }
27
+ return `Add ${result.recommendedMarker} marker`;
28
+ }
29
+ function markerLine(markerType, markers) {
30
+ if (markerType === 'data')
31
+ return `-- ${markers.dataMarker}`;
32
+ if (markerType === 'schema')
33
+ return `-- ${markers.schemaMarker}`;
34
+ throw new Error('Invalid marker type');
35
+ }
36
+ function prependMarker(sql, marker) {
37
+ if (sql.startsWith('\uFEFF')) {
38
+ const withoutBom = sql.slice(1);
39
+ return `\uFEFF${marker}\n\n${withoutBom}`;
40
+ }
41
+ return `${marker}\n\n${sql}`;
42
+ }
43
+ function removeMarkerLines(sql, markers) {
44
+ const hadBom = sql.startsWith('\uFEFF');
45
+ const raw = hadBom ? sql.slice(1) : sql;
46
+ const lines = raw.split('\n');
47
+ const markerValues = [markers.dataMarker, markers.schemaMarker];
48
+ let removed = false;
49
+ const kept = lines.filter((line) => {
50
+ const trimmed = line.trim();
51
+ for (const marker of markerValues) {
52
+ if (trimmed === `-- ${marker}` || trimmed === `--${marker}`) {
53
+ removed = true;
54
+ return false;
55
+ }
56
+ }
57
+ return true;
58
+ });
59
+ const updated = kept.join('\n');
60
+ return { updated: hadBom ? `\uFEFF${updated}` : updated, removed };
61
+ }
62
+ function printClassificationRow(result) {
63
+ console.log(info(`${result.fileName}`));
64
+ console.log(info(` class = ${result.classification} (${result.source})`));
65
+ console.log(info(` reason = ${result.reasons.join('; ')}`));
66
+ console.log(info(` recommendation = ${recommendationText(result)}`));
67
+ }
68
+ export async function runMigrationAuditCommand(options = {}) {
69
+ const migrationsDir = absoluteMigrationsDir(options.migrationsDir);
70
+ await ensureDirectoryExists(migrationsDir);
71
+ const markers = resolveMarkers();
72
+ const results = await classifyMigrations({ migrationsDir });
73
+ console.log(sectionWithNote(title('Migration audit'), 'Classifies migration files as data/schema/mixed/unknown.'));
74
+ console.log(info(`migrationsDir = ${migrationsDir}`));
75
+ console.log(info(`dataMarker = ${markers.dataMarker}`));
76
+ console.log(info(`schemaMarker = ${markers.schemaMarker}`));
77
+ console.log('');
78
+ if (results.length === 0) {
79
+ console.log(warn('No migration files found.'));
80
+ return;
81
+ }
82
+ let dataCount = 0;
83
+ let schemaCount = 0;
84
+ let mixedCount = 0;
85
+ let unknownCount = 0;
86
+ let suggestedMarkerCount = 0;
87
+ for (const result of results) {
88
+ if (result.classification === 'data')
89
+ dataCount += 1;
90
+ if (result.classification === 'schema')
91
+ schemaCount += 1;
92
+ if (result.classification === 'mixed')
93
+ mixedCount += 1;
94
+ if (result.classification === 'unknown')
95
+ unknownCount += 1;
96
+ if (result.recommendedMarker)
97
+ suggestedMarkerCount += 1;
98
+ printClassificationRow(result);
99
+ }
100
+ console.log('');
101
+ console.log(ok(`Summary: data=${dataCount} schema=${schemaCount} mixed=${mixedCount} unknown=${unknownCount}`));
102
+ console.log(info(`Suggested marker updates: ${suggestedMarkerCount}`));
103
+ if (mixedCount > 0) {
104
+ console.log(warn('Mixed files detected. Split schema and data into separate migration files.'));
105
+ }
106
+ }
107
+ export async function runMigrationMarkCommand(options = {}) {
108
+ const migrationsDir = absoluteMigrationsDir(options.migrationsDir);
109
+ await ensureDirectoryExists(migrationsDir);
110
+ const markers = resolveMarkers();
111
+ const results = await classifyMigrations({ migrationsDir });
112
+ const mixed = results.filter((result) => result.classification === 'mixed');
113
+ if (mixed.length > 0) {
114
+ const names = mixed.map((m) => m.fileName).join(', ');
115
+ throw new Error(`Cannot mark migrations while mixed schema+DML files exist: ${names}\nSplit each mixed migration into schema-only and data-only files, then re-run.`);
116
+ }
117
+ const candidates = results.filter((result) => result.recommendedMarker !== null);
118
+ console.log(sectionWithNote(title('Migration mark'), 'Suggests and applies marker comments to migration files.'));
119
+ console.log(info(`migrationsDir = ${migrationsDir}`));
120
+ console.log(info(`candidates = ${candidates.length}`));
121
+ console.log('');
122
+ if (candidates.length === 0) {
123
+ console.log(ok('No marker updates needed.'));
124
+ return;
125
+ }
126
+ for (const candidate of candidates) {
127
+ const marker = markerLine(candidate.recommendedMarker, markers);
128
+ console.log(info(`${candidate.fileName}`));
129
+ console.log(info(` add = ${marker}`));
130
+ console.log(info(` reason = ${candidate.reasons.join('; ')}`));
131
+ }
132
+ if (options.dryRun === true) {
133
+ console.log('');
134
+ console.log(info('Dry run only. Re-run without `--dry-run` to confirm and write markers.'));
135
+ return;
136
+ }
137
+ const confirmed = [];
138
+ if (options.yes === true) {
139
+ confirmed.push(...candidates);
140
+ }
141
+ else {
142
+ const rl = readline.createInterface({ input, output });
143
+ try {
144
+ for (const candidate of candidates) {
145
+ const marker = markerLine(candidate.recommendedMarker, markers);
146
+ const answer = await rl.question(`Add "${marker}" to ${candidate.fileName}? [y/N] `);
147
+ const accepted = answer.trim().toLowerCase();
148
+ if (accepted === 'y' || accepted === 'yes') {
149
+ confirmed.push(candidate);
150
+ }
151
+ }
152
+ }
153
+ finally {
154
+ rl.close();
155
+ }
156
+ }
157
+ if (confirmed.length === 0) {
158
+ console.log('');
159
+ console.log(warn('No marker updates selected. Nothing written.'));
160
+ return;
161
+ }
162
+ for (const candidate of confirmed) {
163
+ const marker = markerLine(candidate.recommendedMarker, markers);
164
+ const original = await fs.promises.readFile(candidate.filePath, 'utf8');
165
+ const updated = prependMarker(original, marker);
166
+ await fs.promises.writeFile(candidate.filePath, updated, 'utf8');
167
+ console.log(ok(`Updated ${candidate.fileName}`));
168
+ }
169
+ }
170
+ export async function runMigrationUnmarkCommand(options = {}) {
171
+ const migrationsDir = absoluteMigrationsDir(options.migrationsDir);
172
+ await ensureDirectoryExists(migrationsDir);
173
+ const markers = resolveMarkers();
174
+ const results = await classifyMigrations({ migrationsDir });
175
+ const candidates = results.filter((result) => result.hasDataMarker || result.hasSchemaMarker);
176
+ console.log(sectionWithNote(title('Migration unmark'), 'Removes marker comments from migration files.'));
177
+ console.log(info(`migrationsDir = ${migrationsDir}`));
178
+ console.log(info(`candidates = ${candidates.length}`));
179
+ console.log('');
180
+ if (candidates.length === 0) {
181
+ console.log(ok('No marker lines found.'));
182
+ return;
183
+ }
184
+ for (const candidate of candidates) {
185
+ const removals = [];
186
+ if (candidate.hasDataMarker)
187
+ removals.push(`-- ${markers.dataMarker}`);
188
+ if (candidate.hasSchemaMarker)
189
+ removals.push(`-- ${markers.schemaMarker}`);
190
+ console.log(info(`${candidate.fileName}`));
191
+ console.log(info(` remove = ${removals.join(', ')}`));
192
+ console.log(info(` reason = explicit marker override cleanup`));
193
+ }
194
+ if (options.dryRun === true) {
195
+ console.log('');
196
+ console.log(info('Dry run only. Re-run without `--dry-run` to confirm and remove markers.'));
197
+ return;
198
+ }
199
+ const confirmed = [];
200
+ if (options.yes === true) {
201
+ confirmed.push(...candidates);
202
+ }
203
+ else {
204
+ const rl = readline.createInterface({ input, output });
205
+ try {
206
+ for (const candidate of candidates) {
207
+ const answer = await rl.question(`Remove markers from ${candidate.fileName}? [y/N] `);
208
+ const accepted = answer.trim().toLowerCase();
209
+ if (accepted === 'y' || accepted === 'yes') {
210
+ confirmed.push(candidate);
211
+ }
212
+ }
213
+ }
214
+ finally {
215
+ rl.close();
216
+ }
217
+ }
218
+ if (confirmed.length === 0) {
219
+ console.log('');
220
+ console.log(warn('No files selected. Nothing written.'));
221
+ return;
222
+ }
223
+ for (const candidate of confirmed) {
224
+ const original = await fs.promises.readFile(candidate.filePath, 'utf8');
225
+ const { updated, removed } = removeMarkerLines(original, markers);
226
+ if (!removed) {
227
+ console.log(warn(`Skipped ${candidate.fileName}; no marker line found at write time.`));
228
+ continue;
229
+ }
230
+ await fs.promises.writeFile(candidate.filePath, updated, 'utf8');
231
+ console.log(ok(`Updated ${candidate.fileName}`));
232
+ }
233
+ }
234
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.js","sourceRoot":"","sources":["../../src/commands/migration.ts"],"names":[],"mappings":"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"}
@@ -3,5 +3,6 @@ export type SyncCommandOptions = {
3
3
  input?: string;
4
4
  output?: string;
5
5
  backup?: boolean;
6
+ force?: boolean;
6
7
  };
7
8
  export declare function runSyncCommand(target: SyncTarget, options?: SyncCommandOptions): Promise<void>;
@@ -1,9 +1,10 @@
1
1
  import fs from 'node:fs';
2
2
  import { resolveCommandConfig } from '../lib/config.js';
3
- import { info, ok, sectionWithNote, title } from '../lib/ui.js';
3
+ import { info, ok, sectionWithNote, title, warn } from '../lib/ui.js';
4
4
  import { runSchemaCommand } from './schema.js';
5
5
  import { runDataCommand } from './data.js';
6
- import { runCommandToFile } from '../lib/subprocess.js';
6
+ import { runCommandCapture, runCommandToFile } from '../lib/subprocess.js';
7
+ import { compareMigrationVersions, isValidMigrationVersion, parseMigrationListOutput, } from '../lib/post-seed-cutoff.js';
7
8
  function buildWorkflowArgs(inputFile, options) {
8
9
  const args = ['--input', inputFile];
9
10
  if (options.output)
@@ -22,7 +23,54 @@ async function assertDumpHasContent(filePath) {
22
23
  throw new Error(`Supabase dump produced an empty file: ${filePath}\nAborting before split so existing output files are not cleared.`);
23
24
  }
24
25
  }
26
+ function latestVersionFromRows(rows, key) {
27
+ const versions = new Set();
28
+ for (const row of rows) {
29
+ const value = row[key];
30
+ if (isValidMigrationVersion(value))
31
+ versions.add(value);
32
+ }
33
+ let latest = null;
34
+ for (const version of versions) {
35
+ if (!latest || compareMigrationVersions(version, latest) > 0) {
36
+ latest = version;
37
+ }
38
+ }
39
+ return latest;
40
+ }
41
+ async function assertLinkedMigrationAlignmentForSync(force) {
42
+ try {
43
+ const { stdout } = await runCommandCapture('supabase', ['migration', 'list', '--linked']);
44
+ const rows = parseMigrationListOutput(stdout);
45
+ if (rows.length === 0) {
46
+ if (force) {
47
+ console.log(warn('Linked migration alignment preflight returned no rows. Continuing due to --force.'));
48
+ return;
49
+ }
50
+ throw new Error('Linked migration alignment preflight returned no rows.');
51
+ }
52
+ const latestLocal = latestVersionFromRows(rows, 'local');
53
+ const latestRemote = latestVersionFromRows(rows, 'remote');
54
+ if (latestLocal === latestRemote)
55
+ return;
56
+ const message = `Local/remote migrations are not aligned (latestLocal=${latestLocal ?? '(none)'} latestRemote=${latestRemote ?? '(none)'}).`;
57
+ if (force) {
58
+ console.log(warn(`${message} Continuing due to --force.`));
59
+ return;
60
+ }
61
+ throw new Error(`${message} Re-run with --force to override.`);
62
+ }
63
+ catch (error) {
64
+ if (force) {
65
+ const message = error instanceof Error ? error.message : String(error);
66
+ console.log(warn(`Could not validate linked migration alignment. Continuing due to --force. Details: ${message}`));
67
+ return;
68
+ }
69
+ throw error;
70
+ }
71
+ }
25
72
  export async function runSyncCommand(target, options = {}) {
73
+ await assertLinkedMigrationAlignmentForSync(options.force === true);
26
74
  const resolved = target === 'schema'
27
75
  ? resolveCommandConfig('schema', { input: options.input, output: options.output })
28
76
  : resolveCommandConfig('data', { input: options.input, output: options.output });
@@ -1 +1 @@
1
- {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'node:fs';
3
2
  import { Command } from 'commander';
4
3
  import { runSchemaCommand } from './commands/schema.js';
5
4
  import { runDataCommand } from './commands/data.js';
6
5
  import { runInitCommand } from './commands/init.js';
7
6
  import { runSyncCommand } from './commands/sync.js';
8
7
  import { runDbResetCommand, runStartCommand } from './commands/db-reset.js';
8
+ import { runCutoffDetectCommand } from './commands/cutoff.js';
9
+ import { runMigrationAuditCommand, runMigrationMarkCommand, runMigrationUnmarkCommand } from './commands/migration.js';
9
10
  import { runCommand } from './lib/subprocess.js';
10
11
  import { error as errText, info, warn } from './lib/ui.js';
11
12
  function buildStepArgs(step, reconstructed, options = {}) {
@@ -26,21 +27,9 @@ function buildStepArgs(step, reconstructed, options = {}) {
26
27
  args.push(reconstructed);
27
28
  return args;
28
29
  }
29
- function readCliVersion() {
30
- try {
31
- const packageJsonPath = new URL('../package.json', import.meta.url);
32
- const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
33
- if (typeof parsed.version === 'string' && parsed.version.trim() !== '') {
34
- return parsed.version;
35
- }
36
- }
37
- catch {
38
- // fallback below
39
- }
40
- return '0.0.0';
41
- }
42
- const KNOWN_TOP_LEVEL_COMMANDS = new Set(['init', 'schema', 'data', 'sync', 'db', 'start', 'help']);
30
+ const KNOWN_TOP_LEVEL_COMMANDS = new Set(['init', 'schema', 'data', 'sync', 'db', 'start', 'cutoff', 'migration', 'help']);
43
31
  const KNOWN_DB_SUBCOMMANDS = new Set(['reset', 'help']);
32
+ const KNOWN_MIGRATION_SUBCOMMANDS = new Set(['audit', 'mark', 'unmark', 'help']);
44
33
  function firstNonOptionToken(args) {
45
34
  for (let index = 0; index < args.length; index += 1) {
46
35
  const token = args[index];
@@ -94,15 +83,25 @@ async function maybePassthroughToSupabase(args) {
94
83
  if (!topLevel)
95
84
  return false;
96
85
  if (KNOWN_TOP_LEVEL_COMMANDS.has(topLevel.token)) {
97
- if (topLevel.token !== 'db')
98
- return false;
99
- const dbSubcommand = firstNonOptionToken(args.slice(topLevel.index + 1));
100
- if (!dbSubcommand || KNOWN_DB_SUBCOMMANDS.has(dbSubcommand.token)) {
101
- return false;
86
+ if (topLevel.token === 'db') {
87
+ const dbSubcommand = firstNonOptionToken(args.slice(topLevel.index + 1));
88
+ if (!dbSubcommand || KNOWN_DB_SUBCOMMANDS.has(dbSubcommand.token)) {
89
+ return false;
90
+ }
91
+ console.log(info(`Forwarding to Supabase CLI: supabase ${args.join(' ')}`));
92
+ await runCommand('supabase', args);
93
+ return true;
102
94
  }
103
- console.log(info(`Forwarding to Supabase CLI: supabase ${args.join(' ')}`));
104
- await runCommand('supabase', args);
105
- return true;
95
+ if (topLevel.token === 'migration') {
96
+ const migrationSubcommand = firstNonOptionToken(args.slice(topLevel.index + 1));
97
+ if (!migrationSubcommand || KNOWN_MIGRATION_SUBCOMMANDS.has(migrationSubcommand.token)) {
98
+ return false;
99
+ }
100
+ console.log(info(`Forwarding to Supabase CLI: supabase ${args.join(' ')}`));
101
+ await runCommand('supabase', args);
102
+ return true;
103
+ }
104
+ return false;
106
105
  }
107
106
  console.log(info(`Forwarding to Supabase CLI: supabase ${args.join(' ')}`));
108
107
  await runCommand('supabase', args);
@@ -111,29 +110,28 @@ async function maybePassthroughToSupabase(args) {
111
110
  const program = new Command();
112
111
  program
113
112
  .name('supabee')
114
- .version(readCliVersion(), '-V, --version', 'display version number')
115
113
  .description('Supabase sync and migration orchestration CLI.')
116
114
  .showHelpAfterError('(run with --help for usage)')
117
- .addHelpText('after', `
118
- Main Workflows
119
- supabee sync schema
120
- supabee sync data
121
- supabee db reset [cutoff_timestamp]
122
- supabee start [cutoff_timestamp]
123
-
124
- Setup (once per project)
125
- supabee init
126
- supabase link
127
-
128
- Examples
129
- supabee sync schema
130
- supabee sync data
131
- supabee db reset 20260309180959
132
- supabee start 20260309180959
133
-
134
- Manual Processing (existing dump files)
135
- supabee schema
136
- supabee data
115
+ .addHelpText('after', `
116
+ Main Workflows
117
+ supabee sync schema
118
+ supabee sync data
119
+ supabee db reset [cutoff_timestamp]
120
+ supabee start [cutoff_timestamp]
121
+
122
+ Setup (once per project)
123
+ supabee init
124
+ supabase link
125
+
126
+ Examples
127
+ supabee sync schema
128
+ supabee sync data
129
+ supabee db reset 20260309180959
130
+ supabee start 20260309180959
131
+
132
+ Manual Processing (existing dump files)
133
+ supabee schema
134
+ supabee data
137
135
  `);
138
136
  program
139
137
  .command('init')
@@ -161,18 +159,18 @@ for (const commandName of ['schema', 'data']) {
161
159
  const syncCommand = program
162
160
  .command('sync')
163
161
  .description('Dump from linked project, then split/reconstruct/validate')
164
- .addHelpText('after', `
165
- Main Usage
166
- supabee sync schema
167
- supabee sync data
168
-
169
- Examples
170
- supabee sync schema
171
- supabee sync data --input supabase/seeds/prod-data.sql
172
- supabee sync data --no-backup
173
-
174
- Note
175
- Requires \`supabase link\` to be configured for this project.
162
+ .addHelpText('after', `
163
+ Main Usage
164
+ supabee sync schema
165
+ supabee sync data
166
+
167
+ Examples
168
+ supabee sync schema
169
+ supabee sync data --input supabase/seeds/prod-data.sql
170
+ supabee sync data --no-backup
171
+
172
+ Note
173
+ Requires \`supabase link\` to be configured for this project.
176
174
  `);
177
175
  syncCommand
178
176
  .command('schema')
@@ -181,9 +179,8 @@ syncCommand
181
179
  .option('--output <path>', 'Split output path override for schema processing')
182
180
  .option('--backup', 'Backup existing split output before replacing')
183
181
  .option('--no-backup', 'Do not backup dirty split output folder before split')
184
- .action(async (options = {}) => {
185
- await runSyncCommand('schema', options);
186
- });
182
+ .option('--force', 'Skip linked migration alignment preflight for sync')
183
+ .action((options = {}) => runSyncCommand('schema', options));
187
184
  syncCommand
188
185
  .command('data')
189
186
  .description('Run `supabase db dump --data-only`, then process data files')
@@ -191,9 +188,35 @@ syncCommand
191
188
  .option('--output <path>', 'Split output path override for data processing')
192
189
  .option('--backup', 'Backup existing split output before replacing')
193
190
  .option('--no-backup', 'Do not backup dirty split output folder before split')
194
- .action(async (options = {}) => {
195
- await runSyncCommand('data', options);
196
- });
191
+ .option('--force', 'Skip linked migration alignment preflight for sync')
192
+ .action((options = {}) => runSyncCommand('data', options));
193
+ const cutoffCommand = program.command('cutoff').description('Cutoff detection helpers');
194
+ cutoffCommand
195
+ .command('detect [cutoffTimestamp]')
196
+ .description('Resolve cutoff from argument, linked migration alignment, or configured fallback')
197
+ .option('--env <name>', 'Environment key used for postSeedCutoffByEnv fallback lookup')
198
+ .option('--json', 'Print machine-readable JSON output')
199
+ .action((cutoffTimestamp, options = {}) => runCutoffDetectCommand(cutoffTimestamp, options));
200
+ const migrationCommand = program.command('migration').description('Migration classification helpers');
201
+ migrationCommand
202
+ .command('audit')
203
+ .description('Classify migrations as data/schema/mixed/unknown and show suggested marker actions')
204
+ .option('--migrations-dir <path>', 'Migrations directory', 'supabase/migrations')
205
+ .action((options = {}) => runMigrationAuditCommand(options));
206
+ migrationCommand
207
+ .command('mark')
208
+ .description('Add marker comments to classified migration files (interactive by default)')
209
+ .option('--migrations-dir <path>', 'Migrations directory', 'supabase/migrations')
210
+ .option('--dry-run', 'Preview marker updates without writing files')
211
+ .option('--yes', 'Apply all suggested marker updates without interactive prompts')
212
+ .action((options = {}) => runMigrationMarkCommand(options));
213
+ migrationCommand
214
+ .command('unmark')
215
+ .description('Remove marker comments from migration files (interactive by default)')
216
+ .option('--migrations-dir <path>', 'Migrations directory', 'supabase/migrations')
217
+ .option('--dry-run', 'Preview marker removals without writing files')
218
+ .option('--yes', 'Remove all found marker updates without interactive prompts')
219
+ .action((options = {}) => runMigrationUnmarkCommand(options));
197
220
  const dbCommand = program.command('db').description('Database orchestration commands');
198
221
  dbCommand
199
222
  .command('reset [cutoffTimestamp]')
@@ -201,42 +224,40 @@ dbCommand
201
224
  .option('--psql', 'Apply deferred migrations via raw psql instead of supabase migration up')
202
225
  .option('--migrations-dir <path>', 'Migrations directory', 'supabase/migrations')
203
226
  .option('--temp-dir <path>', 'Temporary directory for deferred migrations', 'supabase/.tmp-migrations')
204
- .addHelpText('after', `
205
- What This Command Does
206
- 1) Defers migrations with timestamps greater than <cutoffTimestamp>
207
- 2) Runs \`supabase db reset\`
208
- 3) Restores deferred migration files
209
- 4) Reapplies deferred migrations
210
-
211
- Examples
212
- supabee db reset 20260309180959
213
- supabee db reset
214
- supabee db reset 20260309180959 --psql
227
+ .option('--env <name>', 'Environment key used for postSeedCutoffByEnv fallback lookup')
228
+ .addHelpText('after', `
229
+ What This Command Does
230
+ 1) Defers migrations with timestamps greater than <cutoffTimestamp>
231
+ 2) Runs \`supabase db reset\`
232
+ 3) Restores deferred migration files
233
+ 4) Reapplies deferred migrations
234
+
235
+ Examples
236
+ supabee db reset 20260309180959
237
+ supabee db reset
238
+ supabee db reset 20260309180959 --psql
215
239
  `)
216
- .action(async (cutoffTimestamp, options = {}) => {
217
- await runDbResetCommand(cutoffTimestamp, options);
218
- });
240
+ .action((cutoffTimestamp, options = {}) => runDbResetCommand(cutoffTimestamp, options));
219
241
  program
220
242
  .command('start [cutoffTimestamp]')
221
243
  .description('Start Supabase with deferred post-seed migrations applied after startup')
222
244
  .option('--psql', 'Apply deferred migrations via raw psql instead of supabase migration up')
223
245
  .option('--migrations-dir <path>', 'Migrations directory', 'supabase/migrations')
224
246
  .option('--temp-dir <path>', 'Temporary directory for deferred migrations', 'supabase/.tmp-migrations')
225
- .addHelpText('after', `
226
- What This Command Does
227
- 1) Defers migrations with timestamps greater than [cutoffTimestamp]
228
- 2) Runs \`supabase start\`
229
- 3) Restores deferred migration files
230
- 4) Reapplies deferred migrations
231
-
232
- Examples
233
- supabee start 20260309180959
234
- supabee start
235
- supabee start --psql
247
+ .option('--env <name>', 'Environment key used for postSeedCutoffByEnv fallback lookup')
248
+ .addHelpText('after', `
249
+ What This Command Does
250
+ 1) Defers migrations with timestamps greater than [cutoffTimestamp]
251
+ 2) Runs \`supabase start\`
252
+ 3) Restores deferred migration files
253
+ 4) Reapplies deferred migrations
254
+
255
+ Examples
256
+ supabee start 20260309180959
257
+ supabee start
258
+ supabee start --psql
236
259
  `)
237
- .action(async (cutoffTimestamp, options = {}) => {
238
- await runStartCommand(cutoffTimestamp, options);
239
- });
260
+ .action((cutoffTimestamp, options = {}) => runStartCommand(cutoffTimestamp, options));
240
261
  async function main() {
241
262
  const args = process.argv.slice(2);
242
263
  maybePrintGlobalInstallHint(args);