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.
- package/README.md +410 -330
- package/dist/commands/cutoff.d.ts +5 -0
- package/dist/commands/cutoff.js +49 -0
- package/dist/commands/cutoff.js.map +1 -0
- package/dist/commands/db-reset.d.ts +1 -0
- package/dist/commands/db-reset.js +101 -182
- package/dist/commands/db-reset.js.map +1 -1
- package/dist/commands/init.js +9 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migration.d.ts +16 -0
- package/dist/commands/migration.js +234 -0
- package/dist/commands/migration.js.map +1 -0
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +50 -2
- package/dist/commands/sync.js.map +1 -1
- package/dist/index.js +111 -90
- package/dist/index.js.map +1 -1
- package/dist/lib/constants.d.ts +3 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/migration-classifier.d.ts +22 -0
- package/dist/lib/migration-classifier.js +240 -0
- package/dist/lib/migration-classifier.js.map +1 -0
- package/dist/lib/post-seed-cutoff.d.ts +24 -0
- package/dist/lib/post-seed-cutoff.js +195 -0
- package/dist/lib/post-seed-cutoff.js.map +1 -0
- package/dist/lib/subprocess.js +7 -7
- package/package.json +54 -53
- package/supabee.config.example.json +34 -29
|
@@ -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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"}
|
package/dist/commands/sync.d.ts
CHANGED
package/dist/commands/sync.js
CHANGED
|
@@ -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":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,GAEzB,MAAM,4BAA4B,CAAC;AAWpC,SAAS,iBAAiB,CAAC,SAAiB,EAAE,OAA2B;IACvE,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACpC,IAAI,OAAO,CAAC,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC3B,CAAC;SAAM,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IAClD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,yCAAyC,QAAQ,mEAAmE,CACrH,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAwB,EAAE,GAAuB;IAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,uBAAuB,CAAC,KAAK,CAAC;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,IAAI,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7D,MAAM,GAAG,OAAO,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,qCAAqC,CAAC,KAAc;IACjE,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,iBAAiB,CAAC,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;QAC1F,MAAM,IAAI,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,mFAAmF,CAAC,CAAC,CAAC;gBACvG,OAAO;YACT,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,YAAY,GAAG,qBAAqB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC3D,IAAI,WAAW,KAAK,YAAY;YAAE,OAAO;QAEzC,MAAM,OAAO,GAAG,wDAAwD,WAAW,IAAI,QAAQ,iBAAiB,YAAY,IAAI,QAAQ,IAAI,CAAC;QAC7I,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,6BAA6B,CAAC,CAAC,CAAC;YAC3D,OAAO;QACT,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,mCAAmC,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,sFAAsF,OAAO,EAAE,CAAC,CAAC,CAAC;YACnH,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAkB,EAAE,UAA8B,EAAE;IACvF,MAAM,qCAAqC,CAAC,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IAEpE,MAAM,QAAQ,GACZ,MAAM,KAAK,QAAQ;QACjB,CAAC,CAAC,oBAAoB,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QAClF,CAAC,CAAC,oBAAoB,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAErF,MAAM,QAAQ,GAAG,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACtF,MAAM,aAAa,GAAG,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC;IAExE,OAAO,CAAC,GAAG,CACT,eAAe,CACb,KAAK,CAAC,aAAa,CAAC,EACpB,MAAM,KAAK,QAAQ;QACjB,CAAC,CAAC,sFAAsF;QACxF,CAAC,CAAC,sFAAsF,CAC3F,CACF,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,qBAAqB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,gBAAgB,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,oBAAoB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC1D,MAAM,oBAAoB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAE/C,MAAM,YAAY,GAAG,iBAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACpE,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,cAAc,CAAC,YAAY,CAAC,CAAC;AACrC,CAAC"}
|
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
|
-
|
|
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
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
.
|
|
185
|
-
|
|
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
|
-
.
|
|
195
|
-
|
|
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
|
-
.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
supabee db reset
|
|
214
|
-
supabee db reset
|
|
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(
|
|
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
|
-
.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
supabee start
|
|
235
|
-
supabee start
|
|
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(
|
|
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);
|