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 CHANGED
@@ -60,16 +60,16 @@ nexusql migrate
60
60
 
61
61
  ## Commands
62
62
 
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 |
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
- export default {
146
+ module.exports = {
146
147
  // Path to DBML schema file
147
- schema: './schema.dbml',
148
+ dbmlFile: "./schema.dbml",
148
149
 
149
150
  // Migration files directory
150
- migrations: './db/migrations',
151
+ migrations: "./db/migrations",
151
152
 
152
153
  // PostgreSQL extensions to install in temp database
153
- extensions: ['uuid-ossp', 'vector'],
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
- schema: "./schema.dbml",
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
- return {
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("DATABASE_URL not set. Set it in .env or nexusql.config.js");
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
- export default {
3306
+ module.exports = {
3286
3307
  // Path to your DBML schema file
3287
- schema: './schema.dbml',
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.schema);
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
- const commentSql = await getCommentChanges(db, tempDb);
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("Failed to cleanup temp database");
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.table_schema = 'public'
3419
- AND c.table_name != 'schema_migrations'
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.schema);
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("-o, --output <file>", "Output file path (default: src/types/schema.d.ts)").action(async (options) => {
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) {