turbine-orm 0.5.0 → 0.7.1
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 +292 -26
- package/dist/cjs/cli/config.js +5 -15
- package/dist/cjs/cli/index.js +311 -43
- package/dist/cjs/cli/loader.js +129 -0
- package/dist/cjs/cli/migrate.js +96 -47
- package/dist/cjs/cli/ui.js +5 -9
- package/dist/cjs/client.js +158 -49
- package/dist/cjs/errors.js +424 -0
- package/dist/cjs/generate.js +145 -14
- package/dist/cjs/index.js +43 -20
- package/dist/cjs/introspect.js +3 -5
- package/dist/cjs/pipeline.js +9 -2
- package/dist/cjs/query.js +544 -115
- package/dist/cjs/schema-builder.js +150 -30
- package/dist/cjs/schema-sql.js +241 -37
- package/dist/cjs/schema.js +5 -2
- package/dist/cjs/serverless.js +88 -176
- package/dist/cli/config.js +6 -16
- package/dist/cli/index.js +316 -48
- package/dist/cli/loader.d.ts +45 -0
- package/dist/cli/loader.js +91 -0
- package/dist/cli/migrate.d.ts +13 -2
- package/dist/cli/migrate.js +97 -48
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +5 -9
- package/dist/client.d.ts +92 -4
- package/dist/client.js +158 -49
- package/dist/errors.d.ts +225 -0
- package/dist/errors.js +405 -0
- package/dist/generate.d.ts +7 -1
- package/dist/generate.js +148 -18
- package/dist/index.d.ts +11 -9
- package/dist/index.js +16 -12
- package/dist/introspect.d.ts +1 -1
- package/dist/introspect.js +4 -6
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +9 -2
- package/dist/query.d.ts +374 -38
- package/dist/query.js +545 -116
- package/dist/schema-builder.d.ts +38 -5
- package/dist/schema-builder.js +150 -31
- package/dist/schema-sql.d.ts +7 -3
- package/dist/schema-sql.js +241 -37
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +5 -2
- package/dist/serverless.d.ts +92 -139
- package/dist/serverless.js +87 -173
- package/package.json +33 -16
- package/dist/types.d.ts +0 -93
- package/dist/types.js +0 -126
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm CLI — TypeScript loader registration
|
|
4
|
+
*
|
|
5
|
+
* The CLI loads user-supplied config and schema files via dynamic `import()`.
|
|
6
|
+
* Plain Node has no built-in `.ts` loader, so importing `turbine.config.ts`
|
|
7
|
+
* blows up with `ERR_UNKNOWN_FILE_EXTENSION` unless we register a TypeScript
|
|
8
|
+
* loader first.
|
|
9
|
+
*
|
|
10
|
+
* Strategy:
|
|
11
|
+
* 1. If the file we're about to import ends in `.ts` / `.mts` / `.cts`,
|
|
12
|
+
* probe whether `tsx/esm` is resolvable from the user's CWD.
|
|
13
|
+
* 2. If yes, call `module.register('tsx/esm', ...)` ONCE per process.
|
|
14
|
+
* 3. If no, surface an actionable error telling the user to install `tsx`.
|
|
15
|
+
*
|
|
16
|
+
* `tsx` is intentionally NOT a runtime dependency — many projects already
|
|
17
|
+
* have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
|
|
18
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.needsTsLoader = needsTsLoader;
|
|
54
|
+
exports.canResolveTsx = canResolveTsx;
|
|
55
|
+
exports.registerTsLoader = registerTsLoader;
|
|
56
|
+
exports._resetTsLoaderStateForTests = _resetTsLoaderStateForTests;
|
|
57
|
+
const node_module_1 = require("node:module");
|
|
58
|
+
const node_url_1 = require("node:url");
|
|
59
|
+
/**
|
|
60
|
+
* Detect whether a config / schema file path needs the tsx ESM loader.
|
|
61
|
+
* Returns true for `.ts`, `.mts`, and `.cts` files; false for `.js`, `.mjs`,
|
|
62
|
+
* `.cjs`, `.json`, missing paths, or anything else.
|
|
63
|
+
*/
|
|
64
|
+
function needsTsLoader(filePath) {
|
|
65
|
+
if (!filePath)
|
|
66
|
+
return false;
|
|
67
|
+
return /\.(ts|mts|cts)$/i.test(filePath);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Probe whether `tsx/esm` is resolvable from the user's current working
|
|
71
|
+
* directory. Returns true if `tsx` is installed in the user's project.
|
|
72
|
+
*
|
|
73
|
+
* Accepts an injected `resolver` so unit tests don't need a real filesystem.
|
|
74
|
+
*/
|
|
75
|
+
function canResolveTsx(resolver) {
|
|
76
|
+
try {
|
|
77
|
+
if (resolver) {
|
|
78
|
+
resolver('tsx/esm');
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
// Probe relative to the user's CWD, not Turbine's install location.
|
|
82
|
+
// This way we honour whatever `tsx` version the user has pinned.
|
|
83
|
+
const userRequire = (0, node_module_1.createRequire)(`${process.cwd()}/`);
|
|
84
|
+
userRequire.resolve('tsx/esm');
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
let tsLoaderState = null;
|
|
92
|
+
/**
|
|
93
|
+
* Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
|
|
94
|
+
* work. Safe to call multiple times — internal flag prevents double registration.
|
|
95
|
+
*
|
|
96
|
+
* Returns:
|
|
97
|
+
* - 'registered' loader was successfully registered this call
|
|
98
|
+
* - 'already' a loader was previously registered (idempotent)
|
|
99
|
+
* - 'unsupported' Node lacks `module.register()` (Node < 20.6)
|
|
100
|
+
* - 'missing' `tsx` is not installed in the user's project
|
|
101
|
+
*/
|
|
102
|
+
async function registerTsLoader() {
|
|
103
|
+
if (tsLoaderState === 'registered' || tsLoaderState === 'already') {
|
|
104
|
+
return 'already';
|
|
105
|
+
}
|
|
106
|
+
if (!canResolveTsx()) {
|
|
107
|
+
tsLoaderState = 'missing';
|
|
108
|
+
return 'missing';
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const mod = await Promise.resolve().then(() => __importStar(require('node:module')));
|
|
112
|
+
const register = mod.register;
|
|
113
|
+
if (typeof register !== 'function') {
|
|
114
|
+
tsLoaderState = 'unsupported';
|
|
115
|
+
return 'unsupported';
|
|
116
|
+
}
|
|
117
|
+
register('tsx/esm', (0, node_url_1.pathToFileURL)(`${process.cwd()}/`));
|
|
118
|
+
tsLoaderState = 'registered';
|
|
119
|
+
return 'registered';
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
tsLoaderState = 'missing';
|
|
123
|
+
return 'missing';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Reset the loader state — used by unit tests only. */
|
|
127
|
+
function _resetTsLoaderStateForTests() {
|
|
128
|
+
tsLoaderState = null;
|
|
129
|
+
}
|
package/dist/cjs/cli/migrate.js
CHANGED
|
@@ -27,15 +27,19 @@ exports.createMigration = createMigration;
|
|
|
27
27
|
exports.migrateUp = migrateUp;
|
|
28
28
|
exports.migrateDown = migrateDown;
|
|
29
29
|
exports.migrateStatus = migrateStatus;
|
|
30
|
+
const node_crypto_1 = require("node:crypto");
|
|
30
31
|
const node_fs_1 = require("node:fs");
|
|
31
32
|
const node_path_1 = require("node:path");
|
|
32
33
|
const pg_1 = __importDefault(require("pg"));
|
|
34
|
+
const errors_js_1 = require("../errors.js");
|
|
35
|
+
const query_js_1 = require("../query.js");
|
|
33
36
|
// ---------------------------------------------------------------------------
|
|
34
37
|
// Tracking table management
|
|
35
38
|
// ---------------------------------------------------------------------------
|
|
36
39
|
const TRACKING_TABLE = '_turbine_migrations';
|
|
40
|
+
const QUOTED_TRACKING_TABLE = (0, query_js_1.quoteIdent)(TRACKING_TABLE);
|
|
37
41
|
const CREATE_TRACKING_TABLE = `
|
|
38
|
-
CREATE TABLE IF NOT EXISTS ${
|
|
42
|
+
CREATE TABLE IF NOT EXISTS ${QUOTED_TRACKING_TABLE} (
|
|
39
43
|
id SERIAL PRIMARY KEY,
|
|
40
44
|
name TEXT NOT NULL UNIQUE,
|
|
41
45
|
checksum TEXT NOT NULL,
|
|
@@ -47,7 +51,7 @@ async function ensureTrackingTable(client) {
|
|
|
47
51
|
}
|
|
48
52
|
async function getAppliedMigrations(client) {
|
|
49
53
|
await ensureTrackingTable(client);
|
|
50
|
-
const result = await client.query(`SELECT id, name, applied_at, checksum FROM ${
|
|
54
|
+
const result = await client.query(`SELECT id, name, applied_at, checksum FROM ${QUOTED_TRACKING_TABLE} ORDER BY id ASC`);
|
|
51
55
|
return result.rows;
|
|
52
56
|
}
|
|
53
57
|
// ---------------------------------------------------------------------------
|
|
@@ -155,30 +159,45 @@ function parseMigrationSQL(filePath) {
|
|
|
155
159
|
return parseMigrationContent(content);
|
|
156
160
|
}
|
|
157
161
|
/**
|
|
158
|
-
*
|
|
162
|
+
* SHA-256 checksum for migration drift detection.
|
|
163
|
+
* Returns a hex-encoded hash of the file content.
|
|
159
164
|
*/
|
|
160
165
|
function checksum(content) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return Math.abs(hash).toString(36);
|
|
166
|
+
return (0, node_crypto_1.createHash)('sha256').update(content, 'utf-8').digest('hex');
|
|
167
|
+
}
|
|
168
|
+
/** Detect legacy djb2 checksums (short alphanumeric strings, pre-v0.6) */
|
|
169
|
+
function isLegacyChecksum(hash) {
|
|
170
|
+
return hash.length < 64;
|
|
167
171
|
}
|
|
168
172
|
// ---------------------------------------------------------------------------
|
|
169
173
|
// Commands
|
|
170
174
|
// ---------------------------------------------------------------------------
|
|
171
175
|
/**
|
|
172
176
|
* Create a new migration file.
|
|
177
|
+
* If `autoContent` is provided, the UP/DOWN sections are pre-populated with the given SQL.
|
|
173
178
|
*/
|
|
174
|
-
function createMigration(migrationsDir, name) {
|
|
179
|
+
function createMigration(migrationsDir, name, autoContent) {
|
|
175
180
|
(0, node_fs_1.mkdirSync)(migrationsDir, { recursive: true });
|
|
176
181
|
const now = new Date();
|
|
177
182
|
const ts = formatTimestamp(now);
|
|
178
183
|
const safeName = sanitizeName(name);
|
|
179
184
|
const filename = `${ts}_${safeName}.sql`;
|
|
180
185
|
const filePath = (0, node_path_1.join)(migrationsDir, filename);
|
|
181
|
-
|
|
186
|
+
let template;
|
|
187
|
+
if (autoContent) {
|
|
188
|
+
template = `-- Migration: ${name} (auto-generated from schema diff)
|
|
189
|
+
-- Created: ${now.toISOString()}
|
|
190
|
+
-- Review this file before running: npx turbine migrate up
|
|
191
|
+
|
|
192
|
+
-- UP
|
|
193
|
+
${autoContent.up}
|
|
194
|
+
|
|
195
|
+
-- DOWN
|
|
196
|
+
${autoContent.down}
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
template = `-- Migration: ${name}
|
|
182
201
|
-- Created: ${now.toISOString()}
|
|
183
202
|
|
|
184
203
|
-- UP
|
|
@@ -187,6 +206,7 @@ function createMigration(migrationsDir, name) {
|
|
|
187
206
|
-- DOWN
|
|
188
207
|
-- Write your rollback SQL here
|
|
189
208
|
`;
|
|
209
|
+
}
|
|
190
210
|
(0, node_fs_1.writeFileSync)(filePath, template, 'utf-8');
|
|
191
211
|
return {
|
|
192
212
|
filename,
|
|
@@ -201,17 +221,16 @@ function createMigration(migrationsDir, name) {
|
|
|
201
221
|
/** Fixed lock ID for Turbine migrations — prevents concurrent migrate runs */
|
|
202
222
|
const MIGRATION_LOCK_ID = 8_347_291; // arbitrary but stable
|
|
203
223
|
async function acquireLock(client) {
|
|
204
|
-
const result = await client.query(`SELECT pg_try_advisory_lock($1)`, [
|
|
224
|
+
const result = await client.query(`SELECT pg_try_advisory_lock($1)`, [
|
|
225
|
+
MIGRATION_LOCK_ID,
|
|
226
|
+
]);
|
|
205
227
|
return result.rows[0]?.pg_try_advisory_lock ?? false;
|
|
206
228
|
}
|
|
207
229
|
async function releaseLock(client) {
|
|
208
230
|
await client.query(`SELECT pg_advisory_unlock($1)`, [MIGRATION_LOCK_ID]);
|
|
209
231
|
}
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
// Checksum validation
|
|
212
|
-
// ---------------------------------------------------------------------------
|
|
213
232
|
/**
|
|
214
|
-
* Validate that applied migration files have not been modified since they were run.
|
|
233
|
+
* Validate that applied migration files have not been modified or deleted since they were run.
|
|
215
234
|
* Returns an array of mismatched migrations (empty if all are clean).
|
|
216
235
|
*/
|
|
217
236
|
async function validateChecksums(client, migrationsDir) {
|
|
@@ -221,15 +240,31 @@ async function validateChecksums(client, migrationsDir) {
|
|
|
221
240
|
const mismatches = [];
|
|
222
241
|
for (const migration of applied) {
|
|
223
242
|
const file = fileMap.get(migration.name);
|
|
224
|
-
if (!file)
|
|
225
|
-
|
|
243
|
+
if (!file) {
|
|
244
|
+
mismatches.push({
|
|
245
|
+
name: migration.name,
|
|
246
|
+
expected: migration.checksum,
|
|
247
|
+
actual: '',
|
|
248
|
+
type: 'missing',
|
|
249
|
+
});
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
226
252
|
const content = (0, node_fs_1.readFileSync)(file.path, 'utf-8');
|
|
227
253
|
const currentHash = checksum(content);
|
|
228
254
|
if (currentHash !== migration.checksum) {
|
|
255
|
+
// Auto-upgrade legacy djb2 checksums to SHA-256 without flagging as modified
|
|
256
|
+
if (isLegacyChecksum(migration.checksum)) {
|
|
257
|
+
await client.query(`UPDATE ${QUOTED_TRACKING_TABLE} SET checksum = $1 WHERE name = $2`, [
|
|
258
|
+
currentHash,
|
|
259
|
+
migration.name,
|
|
260
|
+
]);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
229
263
|
mismatches.push({
|
|
230
264
|
name: migration.name,
|
|
231
265
|
expected: migration.checksum,
|
|
232
266
|
actual: currentHash,
|
|
267
|
+
type: 'modified',
|
|
233
268
|
});
|
|
234
269
|
}
|
|
235
270
|
}
|
|
@@ -241,37 +276,57 @@ async function validateChecksums(client, migrationsDir) {
|
|
|
241
276
|
* Features:
|
|
242
277
|
* - Idempotent: running twice is safe (already-applied migrations are skipped)
|
|
243
278
|
* - Advisory lock: prevents concurrent migration runs
|
|
244
|
-
* - Checksum validation: detects modified migration files
|
|
279
|
+
* - Checksum validation: detects modified migration files (BLOCKING — use
|
|
280
|
+
* `allowDrift: true` to bypass when intentionally rewriting history)
|
|
245
281
|
* - Each migration runs in its own transaction
|
|
282
|
+
*
|
|
283
|
+
* Throws `MigrationError` if any applied migration has been modified or deleted
|
|
284
|
+
* on disk, listing the offending files. Pass `{ allowDrift: true }` to bypass
|
|
285
|
+
* this check (the CLI exposes this as `--allow-drift`).
|
|
246
286
|
*/
|
|
247
287
|
async function migrateUp(connectionString, migrationsDir, options) {
|
|
248
288
|
const client = new pg_1.default.Client({ connectionString });
|
|
249
289
|
await client.connect();
|
|
290
|
+
// Treat `force` as an alias for `allowDrift` for backwards compatibility.
|
|
291
|
+
const allowDrift = options?.allowDrift === true || options?.force === true;
|
|
250
292
|
try {
|
|
251
293
|
// Acquire advisory lock to prevent concurrent migrations
|
|
252
294
|
const gotLock = await acquireLock(client);
|
|
253
295
|
if (!gotLock) {
|
|
254
|
-
|
|
255
|
-
applied: [],
|
|
256
|
-
errors: [{
|
|
257
|
-
file: { filename: '', path: '', name: '', timestamp: '' },
|
|
258
|
-
error: 'Could not acquire migration lock — another migration is already running',
|
|
259
|
-
}],
|
|
260
|
-
};
|
|
296
|
+
throw new errors_js_1.MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
|
|
261
297
|
}
|
|
262
298
|
try {
|
|
263
299
|
await ensureTrackingTable(client);
|
|
264
|
-
// Validate checksums of already-applied migrations
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
300
|
+
// Validate checksums of already-applied migrations.
|
|
301
|
+
// Drift = an APPLIED migration's on-disk file has changed (or been deleted)
|
|
302
|
+
// since it was run. Either situation means the database state and the
|
|
303
|
+
// migration history no longer agree, so we BLOCK the run by default.
|
|
304
|
+
// Users can pass `allowDrift: true` (CLI: `--allow-drift`) to force past
|
|
305
|
+
// the block when they are intentionally rewriting history.
|
|
306
|
+
if (!allowDrift) {
|
|
307
|
+
const mismatches = await validateChecksums(client, migrationsDir);
|
|
308
|
+
if (mismatches.length > 0) {
|
|
309
|
+
const modified = mismatches.filter((m) => m.type === 'modified');
|
|
310
|
+
const missing = mismatches.filter((m) => m.type === 'missing');
|
|
311
|
+
const lines = [
|
|
312
|
+
'[turbine] Migration drift detected — refusing to apply pending migrations.',
|
|
313
|
+
'',
|
|
314
|
+
'Applied migrations should be immutable. The following files no longer match their applied state:',
|
|
315
|
+
'',
|
|
316
|
+
];
|
|
317
|
+
for (const m of modified) {
|
|
318
|
+
lines.push(` - ${m.name}.sql (modified on disk)`);
|
|
319
|
+
}
|
|
320
|
+
for (const m of missing) {
|
|
321
|
+
lines.push(` - ${m.name}.sql (deleted from disk)`);
|
|
322
|
+
}
|
|
323
|
+
lines.push('');
|
|
324
|
+
lines.push('Fix one of these:');
|
|
325
|
+
lines.push(' 1. Restore the file(s) to their original content, OR');
|
|
326
|
+
lines.push(' 2. Roll back the affected migrations with `npx turbine migrate down`, OR');
|
|
327
|
+
lines.push(' 3. Pass `--allow-drift` to bypass this check (advanced — make sure you know what you are doing).');
|
|
328
|
+
throw new errors_js_1.MigrationError(lines.join('\n'));
|
|
329
|
+
}
|
|
275
330
|
}
|
|
276
331
|
const applied = await getAppliedMigrations(client);
|
|
277
332
|
const appliedNames = new Set(applied.map((m) => m.name));
|
|
@@ -293,7 +348,7 @@ async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
293
348
|
try {
|
|
294
349
|
await client.query('BEGIN');
|
|
295
350
|
await client.query(up);
|
|
296
|
-
await client.query(`INSERT INTO ${
|
|
351
|
+
await client.query(`INSERT INTO ${QUOTED_TRACKING_TABLE} (name, checksum) VALUES ($1, $2) ON CONFLICT (name) DO NOTHING`, [file.name, hash]);
|
|
297
352
|
await client.query('COMMIT');
|
|
298
353
|
results.push(file);
|
|
299
354
|
}
|
|
@@ -329,13 +384,7 @@ async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
329
384
|
try {
|
|
330
385
|
const gotLock = await acquireLock(client);
|
|
331
386
|
if (!gotLock) {
|
|
332
|
-
|
|
333
|
-
rolledBack: [],
|
|
334
|
-
errors: [{
|
|
335
|
-
file: { filename: '', path: '', name: '', timestamp: '' },
|
|
336
|
-
error: 'Could not acquire migration lock — another migration is already running',
|
|
337
|
-
}],
|
|
338
|
-
};
|
|
387
|
+
throw new errors_js_1.MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
|
|
339
388
|
}
|
|
340
389
|
try {
|
|
341
390
|
await ensureTrackingTable(client);
|
|
@@ -353,7 +402,7 @@ async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
353
402
|
const file = fileMap.get(migration.name);
|
|
354
403
|
if (!file) {
|
|
355
404
|
errors.push({
|
|
356
|
-
file: { filename: migration.name
|
|
405
|
+
file: { filename: `${migration.name}.sql`, path: '', name: migration.name, timestamp: '' },
|
|
357
406
|
error: `Migration file not found for "${migration.name}"`,
|
|
358
407
|
});
|
|
359
408
|
continue;
|
|
@@ -366,7 +415,7 @@ async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
366
415
|
try {
|
|
367
416
|
await client.query('BEGIN');
|
|
368
417
|
await client.query(down);
|
|
369
|
-
await client.query(`DELETE FROM ${
|
|
418
|
+
await client.query(`DELETE FROM ${QUOTED_TRACKING_TABLE} WHERE name = $1`, [migration.name]);
|
|
370
419
|
await client.query('COMMIT');
|
|
371
420
|
results.push(file);
|
|
372
421
|
}
|
package/dist/cjs/cli/ui.js
CHANGED
|
@@ -24,9 +24,7 @@ exports.redactUrl = redactUrl;
|
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
25
25
|
// ANSI escape codes
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
|
-
const isColorSupported = process.env
|
|
28
|
-
process.env['TERM'] !== 'dumb' &&
|
|
29
|
-
(process.stdout.isTTY ?? false);
|
|
27
|
+
const isColorSupported = process.env.NO_COLOR == null && process.env.TERM !== 'dumb' && (process.stdout.isTTY ?? false);
|
|
30
28
|
function code(open, close) {
|
|
31
29
|
if (!isColorSupported)
|
|
32
30
|
return (s) => s;
|
|
@@ -113,9 +111,7 @@ function table(headers, rows) {
|
|
|
113
111
|
return ` ${(0, exports.bold)(h)}${' '.repeat(Math.max(0, w - stripAnsi(h).length))} `;
|
|
114
112
|
})
|
|
115
113
|
.join((0, exports.dim)(exports.symbols.vertLine));
|
|
116
|
-
const separator = colWidths
|
|
117
|
-
.map((w) => exports.symbols.line.repeat(w + 2))
|
|
118
|
-
.join((0, exports.dim)(exports.symbols.line));
|
|
114
|
+
const separator = colWidths.map((w) => exports.symbols.line.repeat(w + 2)).join((0, exports.dim)(exports.symbols.line));
|
|
119
115
|
const bodyLines = rows.map((row) => row
|
|
120
116
|
.map((cell, i) => {
|
|
121
117
|
const w = colWidths[i];
|
|
@@ -195,7 +191,7 @@ function info(msg) {
|
|
|
195
191
|
console.log(` ${(0, exports.blue)(exports.symbols.info)} ${msg}`);
|
|
196
192
|
}
|
|
197
193
|
function label(key, value) {
|
|
198
|
-
console.log(` ${(0, exports.dim)(key
|
|
194
|
+
console.log(` ${(0, exports.dim)(`${key}:`)} ${value}`);
|
|
199
195
|
}
|
|
200
196
|
function newline() {
|
|
201
197
|
console.log('');
|
|
@@ -209,7 +205,7 @@ function divider() {
|
|
|
209
205
|
// ---------------------------------------------------------------------------
|
|
210
206
|
function banner() {
|
|
211
207
|
console.log('');
|
|
212
|
-
console.log(` ${(0, exports.bold)((0, exports.cyan)('turbine'))}
|
|
208
|
+
console.log(` ${(0, exports.bold)((0, exports.cyan)('turbine-orm'))}`);
|
|
213
209
|
console.log(` ${(0, exports.dim)('TypeScript ORM with json_agg nested queries')}`);
|
|
214
210
|
console.log('');
|
|
215
211
|
}
|
|
@@ -226,7 +222,7 @@ function elapsed(startMs) {
|
|
|
226
222
|
// Strip ANSI codes (for width calculation)
|
|
227
223
|
// ---------------------------------------------------------------------------
|
|
228
224
|
function stripAnsi(s) {
|
|
229
|
-
//
|
|
225
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes require control characters
|
|
230
226
|
return s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
231
227
|
}
|
|
232
228
|
// ---------------------------------------------------------------------------
|