nexusql 0.6.2 → 0.8.0
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 +19 -14
- package/dist/cli.js +52 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +47 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,16 +60,16 @@ nexusql migrate
|
|
|
60
60
|
|
|
61
61
|
## Commands
|
|
62
62
|
|
|
63
|
-
| Command
|
|
64
|
-
|
|
65
|
-
| `nexusql init`
|
|
66
|
-
| `nexusql gen`
|
|
67
|
-
| `nexusql migrate`
|
|
68
|
-
| `nexusql up`
|
|
69
|
-
| `nexusql down`
|
|
70
|
-
| `nexusql status`
|
|
71
|
-
| `nexusql types`
|
|
72
|
-
| `nexusql mark-applied <version>` | Mark migration as applied without running
|
|
63
|
+
| Command | Description |
|
|
64
|
+
| -------------------------------- | ------------------------------------------- |
|
|
65
|
+
| `nexusql init` | Initialize configuration and directories |
|
|
66
|
+
| `nexusql gen` | Generate migration SQL from DBML schema |
|
|
67
|
+
| `nexusql migrate` | Interactive: generate → create file → apply |
|
|
68
|
+
| `nexusql up` | Apply all pending migrations |
|
|
69
|
+
| `nexusql down` | Rollback applied migrations |
|
|
70
|
+
| `nexusql status` | Show migration status |
|
|
71
|
+
| `nexusql types` | Generate TypeScript definitions |
|
|
72
|
+
| `nexusql mark-applied <version>` | Mark migration as applied without running |
|
|
73
73
|
|
|
74
74
|
### `nexusql gen`
|
|
75
75
|
|
|
@@ -79,6 +79,7 @@ Generate migration SQL by diffing your current database against the DBML schema.
|
|
|
79
79
|
nexusql gen # Output to stdout
|
|
80
80
|
nexusql gen -o migration.sql # Write to file
|
|
81
81
|
nexusql gen -v # Verbose output
|
|
82
|
+
nexusql gen -v # Verbose output
|
|
82
83
|
```
|
|
83
84
|
|
|
84
85
|
### `nexusql types`
|
|
@@ -142,19 +143,23 @@ nexusql mark-applied 20240101120000
|
|
|
142
143
|
Create `nexusql.config.js` in your project root:
|
|
143
144
|
|
|
144
145
|
```js
|
|
145
|
-
|
|
146
|
+
module.exports = {
|
|
146
147
|
// Path to DBML schema file
|
|
147
|
-
|
|
148
|
+
dbmlFile: "./schema.dbml",
|
|
148
149
|
|
|
149
150
|
// Migration files directory
|
|
150
|
-
migrations:
|
|
151
|
+
migrations: "./db/migrations",
|
|
151
152
|
|
|
152
153
|
// PostgreSQL extensions to install in temp database
|
|
153
|
-
extensions: [
|
|
154
|
+
extensions: ["uuid-ossp", "vector"],
|
|
155
|
+
|
|
156
|
+
// Restrict to specific PostgreSQL schemas (optional, default: ['public'])
|
|
157
|
+
// targetDbSchemas: ['public'],
|
|
154
158
|
};
|
|
155
159
|
```
|
|
156
160
|
|
|
157
161
|
Environment variables:
|
|
162
|
+
|
|
158
163
|
- `DATABASE_URL` - PostgreSQL connection string
|
|
159
164
|
|
|
160
165
|
## Migration Format
|
package/dist/cli.js
CHANGED
|
@@ -3155,6 +3155,12 @@ var Database = class _Database {
|
|
|
3155
3155
|
* Drop a database if it exists.
|
|
3156
3156
|
*/
|
|
3157
3157
|
async dropDatabase(name) {
|
|
3158
|
+
await this.exec(`
|
|
3159
|
+
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
3160
|
+
FROM pg_stat_activity
|
|
3161
|
+
WHERE pg_stat_activity.datname = '${name}'
|
|
3162
|
+
AND pid <> pg_backend_pid();
|
|
3163
|
+
`);
|
|
3158
3164
|
await this.exec(`DROP DATABASE IF EXISTS "${name}";`);
|
|
3159
3165
|
}
|
|
3160
3166
|
/**
|
|
@@ -3224,6 +3230,11 @@ function runMigra(fromUrl, toUrl, options = {}) {
|
|
|
3224
3230
|
if (options.unsafe) {
|
|
3225
3231
|
args.push("--unsafe");
|
|
3226
3232
|
}
|
|
3233
|
+
if (options.schemas?.length) {
|
|
3234
|
+
for (const schema of options.schemas) {
|
|
3235
|
+
args.push("--schema", schema);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3227
3238
|
try {
|
|
3228
3239
|
const output = execFileSync("migra", args, {
|
|
3229
3240
|
encoding: "utf-8",
|
|
@@ -3251,9 +3262,10 @@ import { join } from "path";
|
|
|
3251
3262
|
import { pathToFileURL } from "url";
|
|
3252
3263
|
import { config as loadDotenv } from "dotenv";
|
|
3253
3264
|
var DEFAULT_CONFIG = {
|
|
3254
|
-
|
|
3265
|
+
dbmlFile: "./schema.dbml",
|
|
3255
3266
|
migrations: "./db/migrations",
|
|
3256
|
-
extensions: ["uuid-ossp"]
|
|
3267
|
+
extensions: ["uuid-ossp"],
|
|
3268
|
+
targetDbSchemas: ["public"]
|
|
3257
3269
|
};
|
|
3258
3270
|
async function loadConfig(cwd = process.cwd()) {
|
|
3259
3271
|
loadDotenv();
|
|
@@ -3267,30 +3279,42 @@ async function loadConfig(cwd = process.cwd()) {
|
|
|
3267
3279
|
} catch {
|
|
3268
3280
|
}
|
|
3269
3281
|
}
|
|
3270
|
-
|
|
3282
|
+
const finalConfig = {
|
|
3271
3283
|
...DEFAULT_CONFIG,
|
|
3272
3284
|
...userConfig,
|
|
3273
3285
|
databaseUrl: process.env.DATABASE_URL || userConfig.databaseUrl
|
|
3274
3286
|
};
|
|
3287
|
+
if (userConfig.schema && !userConfig.dbmlFile) {
|
|
3288
|
+
console.warn(
|
|
3289
|
+
"Warning: 'schema' config option is deprecated. Use 'dbmlFile' instead."
|
|
3290
|
+
);
|
|
3291
|
+
finalConfig.dbmlFile = userConfig.schema;
|
|
3292
|
+
}
|
|
3293
|
+
return finalConfig;
|
|
3275
3294
|
}
|
|
3276
3295
|
function getDatabaseUrl(config) {
|
|
3277
3296
|
const url = config.databaseUrl || process.env.DATABASE_URL;
|
|
3278
3297
|
if (!url) {
|
|
3279
|
-
throw new Error(
|
|
3298
|
+
throw new Error(
|
|
3299
|
+
"DATABASE_URL not set. Set it in .env or nexusql.config.js"
|
|
3300
|
+
);
|
|
3280
3301
|
}
|
|
3281
3302
|
return url;
|
|
3282
3303
|
}
|
|
3283
3304
|
function generateConfigTemplate() {
|
|
3284
3305
|
return `/** @type {import('nexusql').NexusqlConfig} */
|
|
3285
|
-
|
|
3306
|
+
module.exports = {
|
|
3286
3307
|
// Path to your DBML schema file
|
|
3287
|
-
|
|
3308
|
+
dbmlFile: './schema.dbml',
|
|
3288
3309
|
|
|
3289
3310
|
// Directory for migration files
|
|
3290
3311
|
migrations: './db/migrations',
|
|
3291
3312
|
|
|
3292
3313
|
// PostgreSQL extensions to install in temp database
|
|
3293
3314
|
extensions: ['uuid-ossp'],
|
|
3315
|
+
|
|
3316
|
+
// Restrict to specific PostgreSQL schemas (optional, default: ['public'])
|
|
3317
|
+
// targetDbSchemas: ['public'],
|
|
3294
3318
|
};
|
|
3295
3319
|
`;
|
|
3296
3320
|
}
|
|
@@ -3323,7 +3347,7 @@ async function gen(options = {}) {
|
|
|
3323
3347
|
const db = new Database(databaseUrl);
|
|
3324
3348
|
const spinner = ora();
|
|
3325
3349
|
spinner.start("Reading DBML schema...");
|
|
3326
|
-
const schemaPath = resolve(config.
|
|
3350
|
+
const schemaPath = resolve(config.dbmlFile);
|
|
3327
3351
|
let dbmlContent;
|
|
3328
3352
|
try {
|
|
3329
3353
|
dbmlContent = readFileSync(schemaPath, "utf-8");
|
|
@@ -3359,10 +3383,17 @@ async function gen(options = {}) {
|
|
|
3359
3383
|
Tables in temp database: ${tables.join(", ")}`));
|
|
3360
3384
|
}
|
|
3361
3385
|
spinner.start("Generating migration diff...");
|
|
3386
|
+
const targetDbSchemas = config.targetDbSchemas;
|
|
3387
|
+
if (targetDbSchemas?.length) {
|
|
3388
|
+
console.log(
|
|
3389
|
+
source_default.dim(`
|
|
3390
|
+
Restricting to schemas: ${targetDbSchemas.join(", ")}`)
|
|
3391
|
+
);
|
|
3392
|
+
}
|
|
3362
3393
|
const migraResult = runMigra(
|
|
3363
3394
|
db.getConnectionUrl(),
|
|
3364
3395
|
tempDb.getConnectionUrl(),
|
|
3365
|
-
{ unsafe: true }
|
|
3396
|
+
{ unsafe: true, schemas: targetDbSchemas }
|
|
3366
3397
|
);
|
|
3367
3398
|
migrationSql = filterSchemaMigrations(migraResult.sql);
|
|
3368
3399
|
if (migraResult.hasChanges) {
|
|
@@ -3371,7 +3402,8 @@ Tables in temp database: ${tables.join(", ")}`));
|
|
|
3371
3402
|
spinner.info("No schema changes detected");
|
|
3372
3403
|
}
|
|
3373
3404
|
spinner.start("Checking comment changes...");
|
|
3374
|
-
|
|
3405
|
+
spinner.start("Checking comment changes...");
|
|
3406
|
+
const commentSql = await getCommentChanges(db, tempDb, targetDbSchemas);
|
|
3375
3407
|
if (commentSql) {
|
|
3376
3408
|
migrationSql = migrationSql ? `${migrationSql}
|
|
3377
3409
|
|
|
@@ -3385,8 +3417,8 @@ ${commentSql}`;
|
|
|
3385
3417
|
try {
|
|
3386
3418
|
await db.dropDatabase(tempDbName);
|
|
3387
3419
|
spinner.succeed("Cleaned up temp database");
|
|
3388
|
-
} catch {
|
|
3389
|
-
spinner.warn(
|
|
3420
|
+
} catch (error) {
|
|
3421
|
+
spinner.warn(`Failed to cleanup temp database: ${error}`);
|
|
3390
3422
|
}
|
|
3391
3423
|
}
|
|
3392
3424
|
const finalSql = migrationSql || "-- No changes detected";
|
|
@@ -3400,7 +3432,8 @@ Migration SQL written to: ${options.output}`));
|
|
|
3400
3432
|
}
|
|
3401
3433
|
return finalSql;
|
|
3402
3434
|
}
|
|
3403
|
-
async function getCommentChanges(currentDb, targetDb) {
|
|
3435
|
+
async function getCommentChanges(currentDb, targetDb, schemas) {
|
|
3436
|
+
const schemaFilter = schemas && schemas.length > 0 ? `AND c.table_schema IN (${schemas.map((s) => `'${s}'`).join(", ")})` : "";
|
|
3404
3437
|
const commentQuery = `
|
|
3405
3438
|
SELECT
|
|
3406
3439
|
format('COMMENT ON COLUMN %I.%I IS %L;',
|
|
@@ -3415,8 +3448,8 @@ async function getCommentChanges(currentDb, targetDb) {
|
|
|
3415
3448
|
JOIN pg_catalog.pg_class pc ON pc.relname = c.table_name
|
|
3416
3449
|
JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace AND pn.nspname = c.table_schema
|
|
3417
3450
|
LEFT JOIN pg_catalog.pg_description pgd ON pgd.objoid = pc.oid AND pgd.objsubid = c.ordinal_position
|
|
3418
|
-
WHERE c.
|
|
3419
|
-
|
|
3451
|
+
WHERE c.table_name != 'schema_migrations'
|
|
3452
|
+
${schemaFilter}
|
|
3420
3453
|
ORDER BY c.table_name, c.ordinal_position;
|
|
3421
3454
|
`;
|
|
3422
3455
|
const [currentComments, targetComments] = await Promise.all([
|
|
@@ -4074,7 +4107,7 @@ async function types(options = {}) {
|
|
|
4074
4107
|
const config = await loadConfig();
|
|
4075
4108
|
const spinner = ora("Generating TypeScript definitions...").start();
|
|
4076
4109
|
try {
|
|
4077
|
-
const schemaPath = resolve2(config.
|
|
4110
|
+
const schemaPath = resolve2(config.dbmlFile);
|
|
4078
4111
|
const dbmlContent = readFileSync3(schemaPath, "utf-8");
|
|
4079
4112
|
const tsContent = generateTypes(dbmlContent);
|
|
4080
4113
|
const outputPath = options.output || "src/types/schema.d.ts";
|
|
@@ -4156,7 +4189,10 @@ program.command("mark-applied <version>").description("Mark a migration as appli
|
|
|
4156
4189
|
process.exit(1);
|
|
4157
4190
|
}
|
|
4158
4191
|
});
|
|
4159
|
-
program.command("types").description("Generate TypeScript definitions from DBML").option(
|
|
4192
|
+
program.command("types").description("Generate TypeScript definitions from DBML").option(
|
|
4193
|
+
"-o, --output <file>",
|
|
4194
|
+
"Output file path (default: src/types/schema.d.ts)"
|
|
4195
|
+
).action(async (options) => {
|
|
4160
4196
|
try {
|
|
4161
4197
|
await types(options);
|
|
4162
4198
|
} catch (error) {
|