@xubylele/schema-forge 1.8.1 → 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 +31 -10
- package/dist/api.d.ts +7 -2
- package/dist/api.js +41 -12
- package/dist/cli.js +45 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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
|
@@ -3389,7 +3389,21 @@ async function runImport(inputPath, options = {}) {
|
|
|
3389
3389
|
|
|
3390
3390
|
// src/commands/init.ts
|
|
3391
3391
|
var import_commander5 = require("commander");
|
|
3392
|
-
|
|
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) {
|
|
3393
3407
|
const root = getProjectRoot();
|
|
3394
3408
|
const schemaForgeDir = getSchemaForgeDir(root);
|
|
3395
3409
|
if (await fileExists(schemaForgeDir)) {
|
|
@@ -3407,6 +3421,7 @@ async function runInit() {
|
|
|
3407
3421
|
if (await fileExists(statePath)) {
|
|
3408
3422
|
throw new Error(`${statePath} already exists`);
|
|
3409
3423
|
}
|
|
3424
|
+
const provider = resolveInitProvider(options?.provider);
|
|
3410
3425
|
info("Initializing schema project...");
|
|
3411
3426
|
await ensureDir(schemaForgeDir);
|
|
3412
3427
|
const schemaContent = `# SchemaForge schema definition
|
|
@@ -3419,9 +3434,26 @@ table users {
|
|
|
3419
3434
|
`;
|
|
3420
3435
|
await writeTextFile(schemaFilePath, schemaContent);
|
|
3421
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
|
+
}
|
|
3422
3454
|
const config = {
|
|
3423
|
-
provider
|
|
3424
|
-
outputDir
|
|
3455
|
+
provider,
|
|
3456
|
+
outputDir,
|
|
3425
3457
|
schemaFile: "schemaforge/schema.sf",
|
|
3426
3458
|
stateFile: "schemaforge/state.json",
|
|
3427
3459
|
sql: {
|
|
@@ -3437,9 +3469,6 @@ table users {
|
|
|
3437
3469
|
};
|
|
3438
3470
|
await writeJsonFile(statePath, state);
|
|
3439
3471
|
success(`Created ${statePath}`);
|
|
3440
|
-
const outputDir = "migrations";
|
|
3441
|
-
await ensureDir(outputDir);
|
|
3442
|
-
success(`Created ${outputDir}`);
|
|
3443
3472
|
success("Project initialized successfully");
|
|
3444
3473
|
info("Next steps:");
|
|
3445
3474
|
info(" 1. Edit schemaforge/schema.sf to define your schema");
|
|
@@ -3449,9 +3478,9 @@ table users {
|
|
|
3449
3478
|
|
|
3450
3479
|
// src/commands/introspect.ts
|
|
3451
3480
|
var import_commander6 = require("commander");
|
|
3452
|
-
var
|
|
3481
|
+
var import_path12 = __toESM(require("path"));
|
|
3453
3482
|
function resolveOutputPath(root, outputPath) {
|
|
3454
|
-
return
|
|
3483
|
+
return import_path12.default.isAbsolute(outputPath) ? outputPath : import_path12.default.join(root, outputPath);
|
|
3455
3484
|
}
|
|
3456
3485
|
async function runIntrospect(options = {}) {
|
|
3457
3486
|
const connectionString = resolvePostgresConnectionString({ url: options.url });
|
|
@@ -3478,9 +3507,9 @@ async function runIntrospect(options = {}) {
|
|
|
3478
3507
|
|
|
3479
3508
|
// src/commands/validate.ts
|
|
3480
3509
|
var import_commander7 = require("commander");
|
|
3481
|
-
var
|
|
3510
|
+
var import_path13 = __toESM(require("path"));
|
|
3482
3511
|
function resolveConfigPath5(root, targetPath) {
|
|
3483
|
-
return
|
|
3512
|
+
return import_path13.default.isAbsolute(targetPath) ? targetPath : import_path13.default.join(root, targetPath);
|
|
3484
3513
|
}
|
|
3485
3514
|
async function runValidate(options = {}) {
|
|
3486
3515
|
const root = getProjectRoot();
|
|
@@ -3598,8 +3627,8 @@ async function runWithResult(fn) {
|
|
|
3598
3627
|
return { exitCode: EXIT_CODES.VALIDATION_ERROR };
|
|
3599
3628
|
}
|
|
3600
3629
|
}
|
|
3601
|
-
async function init() {
|
|
3602
|
-
return runWithResult(() => runInit());
|
|
3630
|
+
async function init(options = {}) {
|
|
3631
|
+
return runWithResult(() => runInit(options));
|
|
3603
3632
|
}
|
|
3604
3633
|
async function generate(options = {}) {
|
|
3605
3634
|
return runWithResult(() => runGenerate(options));
|
package/dist/cli.js
CHANGED
|
@@ -2733,7 +2733,7 @@ var import_commander8 = require("commander");
|
|
|
2733
2733
|
// package.json
|
|
2734
2734
|
var package_default = {
|
|
2735
2735
|
name: "@xubylele/schema-forge",
|
|
2736
|
-
version: "1.
|
|
2736
|
+
version: "1.9.0",
|
|
2737
2737
|
description: "Universal migration generator from schema DSL",
|
|
2738
2738
|
main: "dist/cli.js",
|
|
2739
2739
|
type: "commonjs",
|
|
@@ -3454,7 +3454,21 @@ async function runImport(inputPath, options = {}) {
|
|
|
3454
3454
|
|
|
3455
3455
|
// src/commands/init.ts
|
|
3456
3456
|
var import_commander5 = require("commander");
|
|
3457
|
-
|
|
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) {
|
|
3458
3472
|
const root = getProjectRoot();
|
|
3459
3473
|
const schemaForgeDir = getSchemaForgeDir(root);
|
|
3460
3474
|
if (await fileExists(schemaForgeDir)) {
|
|
@@ -3472,6 +3486,7 @@ async function runInit() {
|
|
|
3472
3486
|
if (await fileExists(statePath)) {
|
|
3473
3487
|
throw new Error(`${statePath} already exists`);
|
|
3474
3488
|
}
|
|
3489
|
+
const provider = resolveInitProvider(options?.provider);
|
|
3475
3490
|
info("Initializing schema project...");
|
|
3476
3491
|
await ensureDir(schemaForgeDir);
|
|
3477
3492
|
const schemaContent = `# SchemaForge schema definition
|
|
@@ -3484,9 +3499,26 @@ table users {
|
|
|
3484
3499
|
`;
|
|
3485
3500
|
await writeTextFile(schemaFilePath, schemaContent);
|
|
3486
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
|
+
}
|
|
3487
3519
|
const config = {
|
|
3488
|
-
provider
|
|
3489
|
-
outputDir
|
|
3520
|
+
provider,
|
|
3521
|
+
outputDir,
|
|
3490
3522
|
schemaFile: "schemaforge/schema.sf",
|
|
3491
3523
|
stateFile: "schemaforge/state.json",
|
|
3492
3524
|
sql: {
|
|
@@ -3502,9 +3534,6 @@ table users {
|
|
|
3502
3534
|
};
|
|
3503
3535
|
await writeJsonFile(statePath, state);
|
|
3504
3536
|
success(`Created ${statePath}`);
|
|
3505
|
-
const outputDir = "migrations";
|
|
3506
|
-
await ensureDir(outputDir);
|
|
3507
|
-
success(`Created ${outputDir}`);
|
|
3508
3537
|
success("Project initialized successfully");
|
|
3509
3538
|
info("Next steps:");
|
|
3510
3539
|
info(" 1. Edit schemaforge/schema.sf to define your schema");
|
|
@@ -3514,9 +3543,9 @@ table users {
|
|
|
3514
3543
|
|
|
3515
3544
|
// src/commands/introspect.ts
|
|
3516
3545
|
var import_commander6 = require("commander");
|
|
3517
|
-
var
|
|
3546
|
+
var import_path12 = __toESM(require("path"));
|
|
3518
3547
|
function resolveOutputPath(root, outputPath) {
|
|
3519
|
-
return
|
|
3548
|
+
return import_path12.default.isAbsolute(outputPath) ? outputPath : import_path12.default.join(root, outputPath);
|
|
3520
3549
|
}
|
|
3521
3550
|
async function runIntrospect(options = {}) {
|
|
3522
3551
|
const connectionString = resolvePostgresConnectionString({ url: options.url });
|
|
@@ -3543,9 +3572,9 @@ async function runIntrospect(options = {}) {
|
|
|
3543
3572
|
|
|
3544
3573
|
// src/commands/validate.ts
|
|
3545
3574
|
var import_commander7 = require("commander");
|
|
3546
|
-
var
|
|
3575
|
+
var import_path13 = __toESM(require("path"));
|
|
3547
3576
|
function resolveConfigPath5(root, targetPath) {
|
|
3548
|
-
return
|
|
3577
|
+
return import_path13.default.isAbsolute(targetPath) ? targetPath : import_path13.default.join(root, targetPath);
|
|
3549
3578
|
}
|
|
3550
3579
|
async function runValidate(options = {}) {
|
|
3551
3580
|
const root = getProjectRoot();
|
|
@@ -3710,9 +3739,12 @@ async function handleError(error2) {
|
|
|
3710
3739
|
}
|
|
3711
3740
|
process.exitCode = EXIT_CODES.VALIDATION_ERROR;
|
|
3712
3741
|
}
|
|
3713
|
-
program.command("init").description(
|
|
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) => {
|
|
3714
3745
|
try {
|
|
3715
|
-
|
|
3746
|
+
const provider = options.provider ?? providerArg;
|
|
3747
|
+
await runInit({ provider });
|
|
3716
3748
|
} catch (error2) {
|
|
3717
3749
|
await handleError(error2);
|
|
3718
3750
|
}
|