nexusql 0.6.1 → 0.7.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
@@ -3224,6 +3224,11 @@ function runMigra(fromUrl, toUrl, options = {}) {
3224
3224
  if (options.unsafe) {
3225
3225
  args.push("--unsafe");
3226
3226
  }
3227
+ if (options.schemas?.length) {
3228
+ for (const schema of options.schemas) {
3229
+ args.push("--schema", schema);
3230
+ }
3231
+ }
3227
3232
  try {
3228
3233
  const output = execFileSync("migra", args, {
3229
3234
  encoding: "utf-8",
@@ -3251,9 +3256,10 @@ import { join } from "path";
3251
3256
  import { pathToFileURL } from "url";
3252
3257
  import { config as loadDotenv } from "dotenv";
3253
3258
  var DEFAULT_CONFIG = {
3254
- schema: "./schema.dbml",
3259
+ dbmlFile: "./schema.dbml",
3255
3260
  migrations: "./db/migrations",
3256
- extensions: ["uuid-ossp"]
3261
+ extensions: ["uuid-ossp"],
3262
+ targetDbSchemas: ["public"]
3257
3263
  };
3258
3264
  async function loadConfig(cwd = process.cwd()) {
3259
3265
  loadDotenv();
@@ -3267,30 +3273,42 @@ async function loadConfig(cwd = process.cwd()) {
3267
3273
  } catch {
3268
3274
  }
3269
3275
  }
3270
- return {
3276
+ const finalConfig = {
3271
3277
  ...DEFAULT_CONFIG,
3272
3278
  ...userConfig,
3273
3279
  databaseUrl: process.env.DATABASE_URL || userConfig.databaseUrl
3274
3280
  };
3281
+ if (userConfig.schema && !userConfig.dbmlFile) {
3282
+ console.warn(
3283
+ "Warning: 'schema' config option is deprecated. Use 'dbmlFile' instead."
3284
+ );
3285
+ finalConfig.dbmlFile = userConfig.schema;
3286
+ }
3287
+ return finalConfig;
3275
3288
  }
3276
3289
  function getDatabaseUrl(config) {
3277
3290
  const url = config.databaseUrl || process.env.DATABASE_URL;
3278
3291
  if (!url) {
3279
- throw new Error("DATABASE_URL not set. Set it in .env or nexusql.config.js");
3292
+ throw new Error(
3293
+ "DATABASE_URL not set. Set it in .env or nexusql.config.js"
3294
+ );
3280
3295
  }
3281
3296
  return url;
3282
3297
  }
3283
3298
  function generateConfigTemplate() {
3284
3299
  return `/** @type {import('nexusql').NexusqlConfig} */
3285
- export default {
3300
+ module.exports = {
3286
3301
  // Path to your DBML schema file
3287
- schema: './schema.dbml',
3302
+ dbmlFile: './schema.dbml',
3288
3303
 
3289
3304
  // Directory for migration files
3290
3305
  migrations: './db/migrations',
3291
3306
 
3292
3307
  // PostgreSQL extensions to install in temp database
3293
3308
  extensions: ['uuid-ossp'],
3309
+
3310
+ // Restrict to specific PostgreSQL schemas (optional, default: ['public'])
3311
+ // targetDbSchemas: ['public'],
3294
3312
  };
3295
3313
  `;
3296
3314
  }
@@ -3323,7 +3341,7 @@ async function gen(options = {}) {
3323
3341
  const db = new Database(databaseUrl);
3324
3342
  const spinner = ora();
3325
3343
  spinner.start("Reading DBML schema...");
3326
- const schemaPath = resolve(config.schema);
3344
+ const schemaPath = resolve(config.dbmlFile);
3327
3345
  let dbmlContent;
3328
3346
  try {
3329
3347
  dbmlContent = readFileSync(schemaPath, "utf-8");
@@ -3359,10 +3377,17 @@ async function gen(options = {}) {
3359
3377
  Tables in temp database: ${tables.join(", ")}`));
3360
3378
  }
3361
3379
  spinner.start("Generating migration diff...");
3380
+ const targetDbSchemas = config.targetDbSchemas;
3381
+ if (targetDbSchemas?.length) {
3382
+ console.log(
3383
+ source_default.dim(`
3384
+ Restricting to schemas: ${targetDbSchemas.join(", ")}`)
3385
+ );
3386
+ }
3362
3387
  const migraResult = runMigra(
3363
3388
  db.getConnectionUrl(),
3364
3389
  tempDb.getConnectionUrl(),
3365
- { unsafe: true }
3390
+ { unsafe: true, schemas: targetDbSchemas }
3366
3391
  );
3367
3392
  migrationSql = filterSchemaMigrations(migraResult.sql);
3368
3393
  if (migraResult.hasChanges) {
@@ -3371,7 +3396,8 @@ Tables in temp database: ${tables.join(", ")}`));
3371
3396
  spinner.info("No schema changes detected");
3372
3397
  }
3373
3398
  spinner.start("Checking comment changes...");
3374
- const commentSql = await getCommentChanges(db, tempDb);
3399
+ spinner.start("Checking comment changes...");
3400
+ const commentSql = await getCommentChanges(db, tempDb, targetDbSchemas);
3375
3401
  if (commentSql) {
3376
3402
  migrationSql = migrationSql ? `${migrationSql}
3377
3403
 
@@ -3385,8 +3411,8 @@ ${commentSql}`;
3385
3411
  try {
3386
3412
  await db.dropDatabase(tempDbName);
3387
3413
  spinner.succeed("Cleaned up temp database");
3388
- } catch {
3389
- spinner.warn("Failed to cleanup temp database");
3414
+ } catch (error) {
3415
+ spinner.warn(`Failed to cleanup temp database: ${error}`);
3390
3416
  }
3391
3417
  }
3392
3418
  const finalSql = migrationSql || "-- No changes detected";
@@ -3400,7 +3426,8 @@ Migration SQL written to: ${options.output}`));
3400
3426
  }
3401
3427
  return finalSql;
3402
3428
  }
3403
- async function getCommentChanges(currentDb, targetDb) {
3429
+ async function getCommentChanges(currentDb, targetDb, schemas) {
3430
+ const schemaFilter = schemas && schemas.length > 0 ? `AND c.table_schema IN (${schemas.map((s) => `'${s}'`).join(", ")})` : "";
3404
3431
  const commentQuery = `
3405
3432
  SELECT
3406
3433
  format('COMMENT ON COLUMN %I.%I IS %L;',
@@ -3415,8 +3442,8 @@ async function getCommentChanges(currentDb, targetDb) {
3415
3442
  JOIN pg_catalog.pg_class pc ON pc.relname = c.table_name
3416
3443
  JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace AND pn.nspname = c.table_schema
3417
3444
  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'
3445
+ WHERE c.table_name != 'schema_migrations'
3446
+ ${schemaFilter}
3420
3447
  ORDER BY c.table_name, c.ordinal_position;
3421
3448
  `;
3422
3449
  const [currentComments, targetComments] = await Promise.all([
@@ -3685,7 +3712,11 @@ function getReadline() {
3685
3712
  if (!rl) {
3686
3713
  rl = readline.createInterface({
3687
3714
  input: process.stdin,
3688
- output: process.stdout
3715
+ output: process.stdout,
3716
+ terminal: process.stdin.isTTY ?? false
3717
+ });
3718
+ rl.on("close", () => {
3719
+ rl = null;
3689
3720
  });
3690
3721
  }
3691
3722
  return rl;
@@ -3697,8 +3728,14 @@ function closePrompts() {
3697
3728
  }
3698
3729
  }
3699
3730
  function question(query) {
3700
- return new Promise((resolve3) => {
3701
- getReadline().question(query, (answer) => {
3731
+ return new Promise((resolve3, reject) => {
3732
+ const readline2 = getReadline();
3733
+ const closeHandler = () => {
3734
+ reject(new Error("Readline closed unexpectedly"));
3735
+ };
3736
+ readline2.once("close", closeHandler);
3737
+ readline2.question(query, (answer) => {
3738
+ readline2.removeListener("close", closeHandler);
3702
3739
  resolve3(answer);
3703
3740
  });
3704
3741
  });
@@ -4064,7 +4101,7 @@ async function types(options = {}) {
4064
4101
  const config = await loadConfig();
4065
4102
  const spinner = ora("Generating TypeScript definitions...").start();
4066
4103
  try {
4067
- const schemaPath = resolve2(config.schema);
4104
+ const schemaPath = resolve2(config.dbmlFile);
4068
4105
  const dbmlContent = readFileSync3(schemaPath, "utf-8");
4069
4106
  const tsContent = generateTypes(dbmlContent);
4070
4107
  const outputPath = options.output || "src/types/schema.d.ts";
@@ -4146,7 +4183,10 @@ program.command("mark-applied <version>").description("Mark a migration as appli
4146
4183
  process.exit(1);
4147
4184
  }
4148
4185
  });
4149
- 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) => {
4186
+ program.command("types").description("Generate TypeScript definitions from DBML").option(
4187
+ "-o, --output <file>",
4188
+ "Output file path (default: src/types/schema.d.ts)"
4189
+ ).action(async (options) => {
4150
4190
  try {
4151
4191
  await types(options);
4152
4192
  } catch (error) {