@xubylele/schema-forge 1.8.0 → 1.9.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
@@ -2,7 +2,7 @@
2
2
 
3
3
  A modern CLI tool for database schema management with a clean DSL and automatic SQL migration generation.
4
4
 
5
- **npm package:** [@xubylele/schema-forge](https://www.npmjs.com/package/@xubylele/schema-forge)
5
+ **Website:** [schemaforge.xuby.cl](https://schemaforge.xuby.cl/) · **npm package:** [@xubylele/schema-forge](https://www.npmjs.com/package/@xubylele/schema-forge)
6
6
 
7
7
  ## Features
8
8
 
@@ -40,7 +40,7 @@ const result = await generate({ name: 'MyMigration' });
40
40
  if (result.exitCode !== EXIT_CODES.SUCCESS) process.exit(result.exitCode);
41
41
  ```
42
42
 
43
- **Exports:** `init`, `generate`, `diff`, `doctor`, `validate`, `introspect`, `importSchema` (each returns `Promise<RunResult>`), `RunResult` (`{ exitCode: number }`), `EXIT_CODES`, and option types (`GenerateOptions`, `DiffOptions`, etc.). Entrypoint: `@xubylele/schema-forge/api`. Exit code semantics: [docs/exit-codes.json](docs/exit-codes.json).
43
+ **Exports:** `init`, `generate`, `diff`, `doctor`, `validate`, `introspect`, `importSchema` (each returns `Promise<RunResult>`), `RunResult` (`{ exitCode: number }`), `EXIT_CODES`, and option types (`InitOptions`, `GenerateOptions`, `DiffOptions`, etc.). Entrypoint: `@xubylele/schema-forge/api`. Exit code semantics: [docs/exit-codes.json](docs/exit-codes.json).
44
44
 
45
45
  ## Development
46
46
 
@@ -88,15 +88,20 @@ Here's a quick walkthrough to get started with SchemaForge:
88
88
  ### 1. Initialize a new project
89
89
 
90
90
  ```bash
91
- schema-forge init
91
+ schema-forge init [provider]
92
92
  ```
93
93
 
94
+ Optional `provider`: `postgres` (default) or `supabase`. You can also use `--provider <provider>`.
95
+
96
+ - **postgres** – Creates `migrations/` at the project root and sets `outputDir` to `migrations`.
97
+ - **supabase** – Uses `supabase/migrations/` for migrations. If `supabase/` does not exist, it is created; if it already exists (e.g. from `supabase init`), SchemaForge config is set to use `supabase/migrations/`.
98
+
94
99
  This creates:
95
100
 
96
101
  - `schemaforge/schema.sf` - Your schema definition file
97
- - `schemaforge/config.json` - Project configuration
102
+ - `schemaforge/config.json` - Project configuration (includes `provider` and `outputDir`)
98
103
  - `schemaforge/state.json` - State tracking file
99
- - `supabase/migrations/` - Directory for generated migrations
104
+ - `migrations/` or `supabase/migrations/` - Directory for generated migrations (depends on provider)
100
105
 
101
106
  ### 2. Define your schema
102
107
 
@@ -186,9 +191,18 @@ For common function-style defaults, comparisons are normalized to avoid obvious
186
191
  Initialize a new SchemaForge project in the current directory.
187
192
 
188
193
  ```bash
189
- schema-forge init
194
+ schema-forge init [provider]
195
+ schema-forge init --provider <provider>
190
196
  ```
191
197
 
198
+ - **`[provider]`** (optional) – `postgres` or `supabase`. Default is `postgres`.
199
+ - **`--provider <provider>`** – Same as above; overrides the positional argument if both are given.
200
+
201
+ **Behavior:**
202
+
203
+ - **postgres** (default): Creates `migrations/` at the project root. Config gets `provider: "postgres"` and `outputDir: "migrations"`.
204
+ - **supabase**: Uses `supabase/migrations/` for generated migrations. If the `supabase/` folder does not exist, it is created along with `supabase/migrations/`. If `supabase/` already exists (e.g. from an existing Supabase project), only `supabase/migrations/` is ensured and config is set to use it. Config gets `provider: "supabase"` and `outputDir: "supabase/migrations"`.
205
+
192
206
  Creates the necessary directory structure and configuration files.
193
207
 
194
208
  ### `schema-forge generate`
@@ -578,6 +592,8 @@ table profiles {
578
592
 
579
593
  ## Project Structure
580
594
 
595
+ With **postgres** (default), migrations live in `migrations/` at the project root. With **supabase**, they live in `supabase/migrations/`. Example for a Supabase-backed project:
596
+
581
597
  ```bash
582
598
  your-project/
583
599
  +-- schemaforge/
@@ -585,14 +601,16 @@ your-project/
585
601
  | +-- config.json # Project configuration
586
602
  | \-- state.json # State tracking (auto-generated)
587
603
  \-- supabase/
588
- \-- migrations/ # Generated SQL migrations
604
+ \-- migrations/ # Generated SQL migrations (when provider is supabase)
589
605
  +-- 20240101120000-initial.sql
590
606
  \-- 20240101120100-add-user-avatar.sql
591
607
  ```
592
608
 
609
+ For postgres, replace `supabase/migrations/` with a top-level `migrations/` directory.
610
+
593
611
  ## Configuration
594
612
 
595
- The `schemaforge/config.json` file contains project configuration:
613
+ The `schemaforge/config.json` file contains project configuration. The `provider` and `outputDir` values are set by `schema-forge init` based on the provider you choose:
596
614
 
597
615
  ```json
598
616
  {
@@ -607,18 +625,21 @@ The `schemaforge/config.json` file contains project configuration:
607
625
  }
608
626
  ```
609
627
 
628
+ - **postgres**: `provider: "postgres"`, `outputDir: "migrations"`.
629
+ - **supabase**: `provider: "supabase"`, `outputDir: "supabase/migrations"`.
630
+
610
631
  ## Supported Databases
611
632
 
612
633
  Currently supports:
613
634
 
614
- - PostgreSQL (`postgres`)
615
- - Supabase (`supabase`)
635
+ - PostgreSQL (`postgres`) – default; migrations in `migrations/`
636
+ - Supabase (`supabase`) – migrations in `supabase/migrations/`; choose at init with `schema-forge init supabase`
616
637
 
617
638
  ## Development Workflow
618
639
 
619
640
  A typical development workflow looks like this:
620
641
 
621
- 1. **Initialize** - `schema-forge init` (one time)
642
+ 1. **Initialize** - `schema-forge init` or `schema-forge init supabase` (one time)
622
643
  2. **Edit schema** - Modify `schemaforge/schema.sf`
623
644
  3. **Preview changes** - `schema-forge diff` (optional)
624
645
  4. **Generate migration** - `schema-forge generate --name "description"`
package/dist/api.d.ts CHANGED
@@ -21,6 +21,10 @@ interface ImportOptions {
21
21
  out?: string;
22
22
  }
23
23
 
24
+ interface InitOptions {
25
+ provider?: string;
26
+ }
27
+
24
28
  interface IntrospectOptions {
25
29
  url?: string;
26
30
  schema?: string;
@@ -70,8 +74,9 @@ interface RunResult {
70
74
  }
71
75
  /**
72
76
  * Initialize a new schema project in the current directory.
77
+ * @param options.provider - Database provider: 'postgres' (default) or 'supabase'. Supabase uses supabase/migrations for output.
73
78
  */
74
- declare function init(): Promise<RunResult>;
79
+ declare function init(options?: InitOptions): Promise<RunResult>;
75
80
  /**
76
81
  * Generate SQL migration from schema files.
77
82
  */
@@ -98,4 +103,4 @@ declare function introspect(options?: IntrospectOptions): Promise<RunResult>;
98
103
  */
99
104
  declare function importSchema(inputPath: string, options?: ImportOptions): Promise<RunResult>;
100
105
 
101
- export { type DiffOptions, type DoctorOptions, EXIT_CODES, type GenerateOptions, type ImportOptions, type IntrospectOptions, type RunResult, type ValidateOptions, diff, doctor, generate, importSchema, init, introspect, validate };
106
+ export { type DiffOptions, type DoctorOptions, EXIT_CODES, type GenerateOptions, type ImportOptions, type InitOptions, type IntrospectOptions, type RunResult, type ValidateOptions, diff, doctor, generate, importSchema, init, introspect, validate };
package/dist/api.js CHANGED
@@ -1206,7 +1206,7 @@ var init_fs = __esm({
1206
1206
  }
1207
1207
  });
1208
1208
 
1209
- // node_modules/@xubylele/schema-forge-core/dist/core/state-manager.js
1209
+ // node_modules/@xubylele/schema-forge-core/dist/core/state-transform.js
1210
1210
  async function schemaToState(schema) {
1211
1211
  const tables = {};
1212
1212
  for (const [tableName, table] of Object.entries(schema.tables)) {
@@ -1232,6 +1232,13 @@ async function schemaToState(schema) {
1232
1232
  tables
1233
1233
  };
1234
1234
  }
1235
+ var init_state_transform = __esm({
1236
+ "node_modules/@xubylele/schema-forge-core/dist/core/state-transform.js"() {
1237
+ "use strict";
1238
+ }
1239
+ });
1240
+
1241
+ // node_modules/@xubylele/schema-forge-core/dist/core/state-manager.js
1235
1242
  async function loadState(statePath) {
1236
1243
  return await readJsonFile2(statePath, { version: 1, tables: {} });
1237
1244
  }
@@ -1246,6 +1253,8 @@ var init_state_manager = __esm({
1246
1253
  "use strict";
1247
1254
  import_path4 = __toESM(require("path"), 1);
1248
1255
  init_fs();
1256
+ init_state_transform();
1257
+ init_state_transform();
1249
1258
  }
1250
1259
  });
1251
1260
 
@@ -3380,7 +3389,21 @@ async function runImport(inputPath, options = {}) {
3380
3389
 
3381
3390
  // src/commands/init.ts
3382
3391
  var import_commander5 = require("commander");
3383
- async function runInit() {
3392
+ var import_path11 = __toESM(require("path"));
3393
+ var ALLOWED_PROVIDERS = ["postgres", "supabase"];
3394
+ function resolveInitProvider(provider) {
3395
+ if (!provider) {
3396
+ return "postgres";
3397
+ }
3398
+ const normalized = provider.trim().toLowerCase();
3399
+ if (ALLOWED_PROVIDERS.includes(normalized)) {
3400
+ return normalized;
3401
+ }
3402
+ throw new Error(
3403
+ `Invalid provider "${provider}". Allowed values: ${ALLOWED_PROVIDERS.join(", ")}.`
3404
+ );
3405
+ }
3406
+ async function runInit(options) {
3384
3407
  const root = getProjectRoot();
3385
3408
  const schemaForgeDir = getSchemaForgeDir(root);
3386
3409
  if (await fileExists(schemaForgeDir)) {
@@ -3398,6 +3421,7 @@ async function runInit() {
3398
3421
  if (await fileExists(statePath)) {
3399
3422
  throw new Error(`${statePath} already exists`);
3400
3423
  }
3424
+ const provider = resolveInitProvider(options?.provider);
3401
3425
  info("Initializing schema project...");
3402
3426
  await ensureDir(schemaForgeDir);
3403
3427
  const schemaContent = `# SchemaForge schema definition
@@ -3410,9 +3434,26 @@ table users {
3410
3434
  `;
3411
3435
  await writeTextFile(schemaFilePath, schemaContent);
3412
3436
  success(`Created ${schemaFilePath}`);
3437
+ let outputDir;
3438
+ if (provider === "supabase") {
3439
+ const supabaseDir = import_path11.default.join(root, "supabase");
3440
+ const migrationsDir = import_path11.default.join(root, "supabase", "migrations");
3441
+ if (!await fileExists(supabaseDir)) {
3442
+ await ensureDir(migrationsDir);
3443
+ success(`Created supabase/migrations`);
3444
+ } else {
3445
+ await ensureDir(migrationsDir);
3446
+ success(`Using existing supabase/; migrations at supabase/migrations`);
3447
+ }
3448
+ outputDir = "supabase/migrations";
3449
+ } else {
3450
+ outputDir = "migrations";
3451
+ await ensureDir(import_path11.default.join(root, outputDir));
3452
+ success(`Created ${outputDir}`);
3453
+ }
3413
3454
  const config = {
3414
- provider: "postgres",
3415
- outputDir: "migrations",
3455
+ provider,
3456
+ outputDir,
3416
3457
  schemaFile: "schemaforge/schema.sf",
3417
3458
  stateFile: "schemaforge/state.json",
3418
3459
  sql: {
@@ -3428,9 +3469,6 @@ table users {
3428
3469
  };
3429
3470
  await writeJsonFile(statePath, state);
3430
3471
  success(`Created ${statePath}`);
3431
- const outputDir = "migrations";
3432
- await ensureDir(outputDir);
3433
- success(`Created ${outputDir}`);
3434
3472
  success("Project initialized successfully");
3435
3473
  info("Next steps:");
3436
3474
  info(" 1. Edit schemaforge/schema.sf to define your schema");
@@ -3440,9 +3478,9 @@ table users {
3440
3478
 
3441
3479
  // src/commands/introspect.ts
3442
3480
  var import_commander6 = require("commander");
3443
- var import_path11 = __toESM(require("path"));
3481
+ var import_path12 = __toESM(require("path"));
3444
3482
  function resolveOutputPath(root, outputPath) {
3445
- return import_path11.default.isAbsolute(outputPath) ? outputPath : import_path11.default.join(root, outputPath);
3483
+ return import_path12.default.isAbsolute(outputPath) ? outputPath : import_path12.default.join(root, outputPath);
3446
3484
  }
3447
3485
  async function runIntrospect(options = {}) {
3448
3486
  const connectionString = resolvePostgresConnectionString({ url: options.url });
@@ -3469,9 +3507,9 @@ async function runIntrospect(options = {}) {
3469
3507
 
3470
3508
  // src/commands/validate.ts
3471
3509
  var import_commander7 = require("commander");
3472
- var import_path12 = __toESM(require("path"));
3510
+ var import_path13 = __toESM(require("path"));
3473
3511
  function resolveConfigPath5(root, targetPath) {
3474
- return import_path12.default.isAbsolute(targetPath) ? targetPath : import_path12.default.join(root, targetPath);
3512
+ return import_path13.default.isAbsolute(targetPath) ? targetPath : import_path13.default.join(root, targetPath);
3475
3513
  }
3476
3514
  async function runValidate(options = {}) {
3477
3515
  const root = getProjectRoot();
@@ -3589,8 +3627,8 @@ async function runWithResult(fn) {
3589
3627
  return { exitCode: EXIT_CODES.VALIDATION_ERROR };
3590
3628
  }
3591
3629
  }
3592
- async function init() {
3593
- return runWithResult(() => runInit());
3630
+ async function init(options = {}) {
3631
+ return runWithResult(() => runInit(options));
3594
3632
  }
3595
3633
  async function generate(options = {}) {
3596
3634
  return runWithResult(() => runGenerate(options));
package/dist/cli.js CHANGED
@@ -1206,7 +1206,7 @@ var init_fs = __esm({
1206
1206
  }
1207
1207
  });
1208
1208
 
1209
- // node_modules/@xubylele/schema-forge-core/dist/core/state-manager.js
1209
+ // node_modules/@xubylele/schema-forge-core/dist/core/state-transform.js
1210
1210
  async function schemaToState(schema) {
1211
1211
  const tables = {};
1212
1212
  for (const [tableName, table] of Object.entries(schema.tables)) {
@@ -1232,6 +1232,13 @@ async function schemaToState(schema) {
1232
1232
  tables
1233
1233
  };
1234
1234
  }
1235
+ var init_state_transform = __esm({
1236
+ "node_modules/@xubylele/schema-forge-core/dist/core/state-transform.js"() {
1237
+ "use strict";
1238
+ }
1239
+ });
1240
+
1241
+ // node_modules/@xubylele/schema-forge-core/dist/core/state-manager.js
1235
1242
  async function loadState(statePath) {
1236
1243
  return await readJsonFile2(statePath, { version: 1, tables: {} });
1237
1244
  }
@@ -1246,6 +1253,8 @@ var init_state_manager = __esm({
1246
1253
  "use strict";
1247
1254
  import_path4 = __toESM(require("path"), 1);
1248
1255
  init_fs();
1256
+ init_state_transform();
1257
+ init_state_transform();
1249
1258
  }
1250
1259
  });
1251
1260
 
@@ -2724,7 +2733,7 @@ var import_commander8 = require("commander");
2724
2733
  // package.json
2725
2734
  var package_default = {
2726
2735
  name: "@xubylele/schema-forge",
2727
- version: "1.8.0",
2736
+ version: "1.9.0",
2728
2737
  description: "Universal migration generator from schema DSL",
2729
2738
  main: "dist/cli.js",
2730
2739
  type: "commonjs",
@@ -2767,7 +2776,7 @@ var package_default = {
2767
2776
  url: "git+https://github.com/xubylele/schema-forge.git"
2768
2777
  },
2769
2778
  bugs: "https://github.com/xubylele/schema-forge/issues",
2770
- homepage: "https://github.com/xubylele/schema-forge#readme",
2779
+ homepage: "https://schemaforge.xuby.cl/",
2771
2780
  files: [
2772
2781
  "dist"
2773
2782
  ],
@@ -2784,7 +2793,7 @@ var package_default = {
2784
2793
  "@changesets/cli": "^2.30.0",
2785
2794
  "@types/node": "^25.2.3",
2786
2795
  "@types/pg": "^8.18.0",
2787
- "@xubylele/schema-forge-core": "^1.3.0",
2796
+ "@xubylele/schema-forge-core": "^1.3.1",
2788
2797
  testcontainers: "^11.8.1",
2789
2798
  "ts-node": "^10.9.2",
2790
2799
  tsup: "^8.5.1",
@@ -3445,7 +3454,21 @@ async function runImport(inputPath, options = {}) {
3445
3454
 
3446
3455
  // src/commands/init.ts
3447
3456
  var import_commander5 = require("commander");
3448
- async function runInit() {
3457
+ var import_path11 = __toESM(require("path"));
3458
+ var ALLOWED_PROVIDERS = ["postgres", "supabase"];
3459
+ function resolveInitProvider(provider) {
3460
+ if (!provider) {
3461
+ return "postgres";
3462
+ }
3463
+ const normalized = provider.trim().toLowerCase();
3464
+ if (ALLOWED_PROVIDERS.includes(normalized)) {
3465
+ return normalized;
3466
+ }
3467
+ throw new Error(
3468
+ `Invalid provider "${provider}". Allowed values: ${ALLOWED_PROVIDERS.join(", ")}.`
3469
+ );
3470
+ }
3471
+ async function runInit(options) {
3449
3472
  const root = getProjectRoot();
3450
3473
  const schemaForgeDir = getSchemaForgeDir(root);
3451
3474
  if (await fileExists(schemaForgeDir)) {
@@ -3463,6 +3486,7 @@ async function runInit() {
3463
3486
  if (await fileExists(statePath)) {
3464
3487
  throw new Error(`${statePath} already exists`);
3465
3488
  }
3489
+ const provider = resolveInitProvider(options?.provider);
3466
3490
  info("Initializing schema project...");
3467
3491
  await ensureDir(schemaForgeDir);
3468
3492
  const schemaContent = `# SchemaForge schema definition
@@ -3475,9 +3499,26 @@ table users {
3475
3499
  `;
3476
3500
  await writeTextFile(schemaFilePath, schemaContent);
3477
3501
  success(`Created ${schemaFilePath}`);
3502
+ let outputDir;
3503
+ if (provider === "supabase") {
3504
+ const supabaseDir = import_path11.default.join(root, "supabase");
3505
+ const migrationsDir = import_path11.default.join(root, "supabase", "migrations");
3506
+ if (!await fileExists(supabaseDir)) {
3507
+ await ensureDir(migrationsDir);
3508
+ success(`Created supabase/migrations`);
3509
+ } else {
3510
+ await ensureDir(migrationsDir);
3511
+ success(`Using existing supabase/; migrations at supabase/migrations`);
3512
+ }
3513
+ outputDir = "supabase/migrations";
3514
+ } else {
3515
+ outputDir = "migrations";
3516
+ await ensureDir(import_path11.default.join(root, outputDir));
3517
+ success(`Created ${outputDir}`);
3518
+ }
3478
3519
  const config = {
3479
- provider: "postgres",
3480
- outputDir: "migrations",
3520
+ provider,
3521
+ outputDir,
3481
3522
  schemaFile: "schemaforge/schema.sf",
3482
3523
  stateFile: "schemaforge/state.json",
3483
3524
  sql: {
@@ -3493,9 +3534,6 @@ table users {
3493
3534
  };
3494
3535
  await writeJsonFile(statePath, state);
3495
3536
  success(`Created ${statePath}`);
3496
- const outputDir = "migrations";
3497
- await ensureDir(outputDir);
3498
- success(`Created ${outputDir}`);
3499
3537
  success("Project initialized successfully");
3500
3538
  info("Next steps:");
3501
3539
  info(" 1. Edit schemaforge/schema.sf to define your schema");
@@ -3505,9 +3543,9 @@ table users {
3505
3543
 
3506
3544
  // src/commands/introspect.ts
3507
3545
  var import_commander6 = require("commander");
3508
- var import_path11 = __toESM(require("path"));
3546
+ var import_path12 = __toESM(require("path"));
3509
3547
  function resolveOutputPath(root, outputPath) {
3510
- return import_path11.default.isAbsolute(outputPath) ? outputPath : import_path11.default.join(root, outputPath);
3548
+ return import_path12.default.isAbsolute(outputPath) ? outputPath : import_path12.default.join(root, outputPath);
3511
3549
  }
3512
3550
  async function runIntrospect(options = {}) {
3513
3551
  const connectionString = resolvePostgresConnectionString({ url: options.url });
@@ -3534,9 +3572,9 @@ async function runIntrospect(options = {}) {
3534
3572
 
3535
3573
  // src/commands/validate.ts
3536
3574
  var import_commander7 = require("commander");
3537
- var import_path12 = __toESM(require("path"));
3575
+ var import_path13 = __toESM(require("path"));
3538
3576
  function resolveConfigPath5(root, targetPath) {
3539
- return import_path12.default.isAbsolute(targetPath) ? targetPath : import_path12.default.join(root, targetPath);
3577
+ return import_path13.default.isAbsolute(targetPath) ? targetPath : import_path13.default.join(root, targetPath);
3540
3578
  }
3541
3579
  async function runValidate(options = {}) {
3542
3580
  const root = getProjectRoot();
@@ -3701,9 +3739,12 @@ async function handleError(error2) {
3701
3739
  }
3702
3740
  process.exitCode = EXIT_CODES.VALIDATION_ERROR;
3703
3741
  }
3704
- program.command("init").description("Initialize a new schema project").action(async () => {
3742
+ program.command("init").description(
3743
+ "Initialize a new schema project. Optional provider: postgres (default) or supabase. Supabase uses supabase/migrations for output."
3744
+ ).argument("[provider]", "Database provider: postgres or supabase").option("--provider <provider>", "Database provider: postgres or supabase (overrides argument)").action(async (providerArg, options) => {
3705
3745
  try {
3706
- await runInit();
3746
+ const provider = options.provider ?? providerArg;
3747
+ await runInit({ provider });
3707
3748
  } catch (error2) {
3708
3749
  await handleError(error2);
3709
3750
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xubylele/schema-forge",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Universal migration generator from schema DSL",
5
5
  "main": "dist/cli.js",
6
6
  "type": "commonjs",
@@ -43,7 +43,7 @@
43
43
  "url": "git+https://github.com/xubylele/schema-forge.git"
44
44
  },
45
45
  "bugs": "https://github.com/xubylele/schema-forge/issues",
46
- "homepage": "https://github.com/xubylele/schema-forge#readme",
46
+ "homepage": "https://schemaforge.xuby.cl/",
47
47
  "files": [
48
48
  "dist"
49
49
  ],
@@ -60,7 +60,7 @@
60
60
  "@changesets/cli": "^2.30.0",
61
61
  "@types/node": "^25.2.3",
62
62
  "@types/pg": "^8.18.0",
63
- "@xubylele/schema-forge-core": "^1.3.0",
63
+ "@xubylele/schema-forge-core": "^1.3.1",
64
64
  "testcontainers": "^11.8.1",
65
65
  "ts-node": "^10.9.2",
66
66
  "tsup": "^8.5.1",