supatool 0.5.0 → 0.6.1
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 +97 -23
- package/dist/bin/helptext.js +40 -31
- package/dist/bin/supatool.js +71 -149
- package/dist/sync/config.js +8 -7
- package/dist/sync/definitionExtractor.js +44 -8
- package/dist/sync/fetchRemoteSchemas.js +4 -2
- package/dist/sync/generateMigration.js +5 -5
- package/dist/sync/migrateRemote.js +114 -0
- package/dist/sync/seedGenerator.js +15 -4
- package/package.json +15 -6
- package/dist/integrations/supabase/crud-autogen/tasks.js +0 -220
- package/dist/integrations/supabase/crud-autogen/workflows.js +0 -220
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Supatool
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Schema Management CLI for PostgreSQL.** Works with Cloud SQL, Supabase, and any PostgreSQL database. Extract schemas into LLM-friendly structures, deploy diffs, apply migrations, and export seeds.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/supatool)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -9,10 +9,23 @@
|
|
|
9
9
|
|
|
10
10
|
Modern AI coding tools (Cursor, Claude, MCP) often struggle with large database schemas. Typical issues include:
|
|
11
11
|
- **Token Waste:** Reading the entire schema at once consumes 10k+ tokens.
|
|
12
|
-
- **Lost Context:** Frequent API calls to fetch table details
|
|
12
|
+
- **Lost Context:** Frequent API calls to fetch table details lead to fragmented reasoning.
|
|
13
13
|
- **Inaccuracy:** AI misses RLS policies or complex FK relations split across multiple files.
|
|
14
14
|
|
|
15
|
-
**Supatool solves this** by reorganizing your
|
|
15
|
+
**Supatool solves this** by reorganizing your schema into a highly searchable, indexed, and modular structure that helps AI "understand" your DB with minimal tokens.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Supported Databases
|
|
20
|
+
|
|
21
|
+
Any **PostgreSQL** database:
|
|
22
|
+
|
|
23
|
+
- Google Cloud SQL (PostgreSQL)
|
|
24
|
+
- Supabase
|
|
25
|
+
- Amazon RDS (PostgreSQL)
|
|
26
|
+
- Self-hosted PostgreSQL
|
|
27
|
+
|
|
28
|
+
Connection strings in both `postgresql://` and `postgres://` formats are accepted.
|
|
16
29
|
|
|
17
30
|
---
|
|
18
31
|
|
|
@@ -21,9 +34,9 @@ Modern AI coding tools (Cursor, Claude, MCP) often struggle with large database
|
|
|
21
34
|
- **Extract (AI-Optimized)** – DDL, RLS, and Triggers are bundled into **one file per table**. AI gets the full picture of a table by opening just one file.
|
|
22
35
|
- **llms.txt Catalog** – Automatically generates a standard `llms.txt` listing all OBJECTS, RELATIONS (FKs), and RPC dependencies. This serves as the "Map" for AI agents.
|
|
23
36
|
- **Multi-Schema Support** – Group objects by schema (e.g., `public`, `agent`, `auth`) with proper schema-qualification in SQL.
|
|
37
|
+
- **Migrate** – Apply pending `db/migrations/*.sql` files to remote, with tracking and transaction safety.
|
|
24
38
|
- **Seed for AI** – Export table data as JSON. Includes a dedicated `llms.txt` for seeds so AI can see real data structures.
|
|
25
39
|
- **Safe Deploy** – Push local schema changes with `--dry-run` to preview DDL before execution.
|
|
26
|
-
- **CRUD (Deprecated)** – Legacy code generation is still available but discouraged in favor of LLM-native development.
|
|
27
40
|
|
|
28
41
|
---
|
|
29
42
|
|
|
@@ -31,18 +44,21 @@ Modern AI coding tools (Cursor, Claude, MCP) often struggle with large database
|
|
|
31
44
|
|
|
32
45
|
```bash
|
|
33
46
|
npm install -g supatool
|
|
34
|
-
# Set your connection string
|
|
35
|
-
export SUPABASE_CONNECTION_STRING="postgresql://postgres:[password]@db.[ref].supabase.co:5432/postgres"
|
|
36
47
|
|
|
37
|
-
#
|
|
38
|
-
|
|
48
|
+
# Set connection string in .env.local
|
|
49
|
+
echo 'DB_CONNECTION_STRING=postgresql://user:password@host:5432/dbname' > .env.local
|
|
50
|
+
|
|
51
|
+
# Generate config
|
|
52
|
+
supatool config:init
|
|
39
53
|
|
|
54
|
+
# Extract schema and generate AI-ready docs
|
|
55
|
+
supatool extract --all -o db/schemas
|
|
40
56
|
```
|
|
41
57
|
|
|
42
58
|
### Output Structure
|
|
43
59
|
|
|
44
60
|
```text
|
|
45
|
-
|
|
61
|
+
db/schemas/
|
|
46
62
|
├── llms.txt # 🗺️ THE ENTRY POINT: Read this first to understand the DB map
|
|
47
63
|
├── schema_index.json # 🤖 For JSON-parsing agents
|
|
48
64
|
├── schema_summary.md # 📄 Single-file overview for quick human/AI scanning
|
|
@@ -51,18 +67,15 @@ supabase/schemas/
|
|
|
51
67
|
├── tables/ # table_name.sql (DDL + RLS + Triggers)
|
|
52
68
|
├── views/
|
|
53
69
|
└── rpc/
|
|
54
|
-
|
|
55
70
|
```
|
|
56
71
|
|
|
57
72
|
---
|
|
58
73
|
|
|
59
74
|
## Best Practices for AI Agents (Cursor / Claude / MCP)
|
|
60
75
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
2. **Targeted Reading:** Once the AI identifies the relevant tables from the catalog, instruct it to open only those specific `.sql` files.
|
|
65
|
-
3. **Understand Relations:** Use the `RELATIONS` section in `llms.txt` to help the AI write accurate JOINs without reading every file.
|
|
76
|
+
1. **Start with the Map:** Always ask the AI to read `db/schemas/llms.txt` first.
|
|
77
|
+
2. **Targeted Reading:** Once the AI identifies the relevant tables, instruct it to open only those specific `.sql` files.
|
|
78
|
+
3. **Understand Relations:** Use the `RELATIONS` section in `llms.txt` to help the AI write accurate JOINs.
|
|
66
79
|
4. **RPC Context:** If using functions, refer to `RPC_TABLES` in `llms.txt` to know which tables are affected.
|
|
67
80
|
|
|
68
81
|
---
|
|
@@ -71,33 +84,94 @@ To get the best results from your AI coding assistant, follow these steps:
|
|
|
71
84
|
|
|
72
85
|
### Extract
|
|
73
86
|
|
|
87
|
+
Pull schema from remote DB into local files:
|
|
88
|
+
|
|
74
89
|
```bash
|
|
75
|
-
supatool extract --all -o
|
|
90
|
+
supatool extract --all -o db/schemas
|
|
76
91
|
# Options:
|
|
77
|
-
# --schema public,agent Specify schemas
|
|
92
|
+
# --schema public,agent Specify schemas (explicit list)
|
|
93
|
+
# -e auth,storage Exclude schemas — targets all others automatically
|
|
78
94
|
# -t "user_*" Filter tables by pattern
|
|
79
|
-
# --force
|
|
95
|
+
# --force Delete .sql files for objects removed from DB
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Unchanged `.sql` files are never overwritten (content is compared excluding the generated header line). Use `--force` to also clean up `.sql` files whose corresponding DB objects have been dropped.
|
|
99
|
+
|
|
100
|
+
When you have many schemas and only want to exclude a few, use `-e` without `--schema`:
|
|
80
101
|
|
|
102
|
+
```bash
|
|
103
|
+
# Extract everything except auth and storage schemas
|
|
104
|
+
supatool extract --all -e auth,storage -o db/schemas
|
|
81
105
|
```
|
|
82
106
|
|
|
107
|
+
### Deploy
|
|
108
|
+
|
|
109
|
+
Push local schema changes to remote (diff → migration → apply):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
supatool deploy --table users --dry-run # preview
|
|
113
|
+
supatool deploy --table all --dry-run # all tables
|
|
114
|
+
supatool deploy --table users # confirm before apply
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Migrate
|
|
118
|
+
|
|
119
|
+
Apply pending SQL files from `db/migrations/` to remote:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
supatool migrate # apply pending migrations
|
|
123
|
+
supatool migrate --dry-run # preview only
|
|
124
|
+
supatool migrate -d path/to/dir # custom directory
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Migration files are applied in alphabetical order. Applied files are tracked in a `_supatool_migrations` table (auto-created).
|
|
128
|
+
|
|
83
129
|
### Seed
|
|
84
130
|
|
|
85
|
-
Export
|
|
131
|
+
Export table data as JSON for AI reference or testing:
|
|
86
132
|
|
|
87
133
|
```bash
|
|
88
134
|
supatool seed --tables tables.yaml
|
|
89
135
|
|
|
136
|
+
# tables.yaml format:
|
|
137
|
+
# public:
|
|
138
|
+
# - users
|
|
139
|
+
# - posts
|
|
90
140
|
```
|
|
91
141
|
|
|
92
|
-
*Outputs JSON files and a `llms.txt` index in `
|
|
142
|
+
*Outputs JSON files and a `llms.txt` index in `db/seeds/`.*
|
|
93
143
|
|
|
94
|
-
###
|
|
144
|
+
### Config
|
|
95
145
|
|
|
96
146
|
```bash
|
|
97
|
-
supatool
|
|
147
|
+
supatool config:init # generate supatool.config.json + .env.local template
|
|
148
|
+
```
|
|
98
149
|
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Configuration
|
|
153
|
+
|
|
154
|
+
`supatool.config.json`:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"schemaDir": "./db/schemas",
|
|
159
|
+
"tablePattern": "*",
|
|
160
|
+
"migration": {
|
|
161
|
+
"naming": "timestamp",
|
|
162
|
+
"dir": "db/migrations"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
99
165
|
```
|
|
100
166
|
|
|
167
|
+
`.env.local` (never commit):
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
DB_CONNECTION_STRING=postgresql://user:password@host:5432/dbname
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Legacy env vars are also accepted: `SUPABASE_CONNECTION_STRING`, `DATABASE_URL`.
|
|
174
|
+
|
|
101
175
|
---
|
|
102
176
|
|
|
103
177
|
## Repository
|
|
@@ -106,4 +180,4 @@ supatool deploy --dry-run
|
|
|
106
180
|
|
|
107
181
|
---
|
|
108
182
|
|
|
109
|
-
*
|
|
183
|
+
*Works with any PostgreSQL database. Always backup your DB before deployment.*
|
package/dist/bin/helptext.js
CHANGED
|
@@ -4,49 +4,62 @@ exports.modelSchemaHelp = exports.helpText = void 0;
|
|
|
4
4
|
// See: [src/bin/helptext.ts](./src/bin/helptext.ts) from project root
|
|
5
5
|
// Help text (command section from README, English only)
|
|
6
6
|
exports.helpText = `
|
|
7
|
-
Supatool CLI -
|
|
7
|
+
Supatool CLI - PostgreSQL schema management (Cloud SQL, Supabase, and any PostgreSQL)
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
10
|
supatool <command> [options]
|
|
11
11
|
|
|
12
12
|
Commands:
|
|
13
|
-
extract Extract database objects from
|
|
13
|
+
extract Extract database objects from remote DB into local files
|
|
14
|
+
deploy Deploy local schema changes to remote (diff → migration → apply)
|
|
15
|
+
migrate Apply pending db/migrations/*.sql files to remote DB
|
|
16
|
+
seed Export table data as AI-friendly seed JSON
|
|
17
|
+
config:init Generate supatool.config.json and .env.local template
|
|
14
18
|
gen:types Generate TypeScript types from model YAML
|
|
15
|
-
gen:crud Generate CRUD TypeScript code from model YAML [deprecated - prefer writing code with LLM]
|
|
16
19
|
gen:docs Generate Markdown documentation from model YAML
|
|
17
|
-
gen:sql Generate SQL (tables, relations, RLS
|
|
18
|
-
gen:rls Generate RLS
|
|
19
|
-
|
|
20
|
-
create Generate a template model YAML
|
|
21
|
-
crud Generate CRUD code from Supabase type definitions [deprecated - prefer writing code with LLM]
|
|
22
|
-
deploy Deploy local schema changes to remote (recommended)
|
|
23
|
-
sync Sync local and remote schemas [deprecated - use deploy]
|
|
24
|
-
seed Export selected table data as AI-friendly seed JSON
|
|
25
|
-
config:init Generate configuration template
|
|
26
|
-
help Show help
|
|
20
|
+
gen:sql Generate SQL (tables, relations, RLS) from model YAML
|
|
21
|
+
gen:rls Generate RLS policy SQL from model YAML
|
|
22
|
+
help Show this help
|
|
27
23
|
|
|
28
24
|
Common Options:
|
|
29
|
-
-c, --connection <string>
|
|
25
|
+
-c, --connection <string> Connection string (postgresql:// or postgres://)
|
|
30
26
|
-o, --output-dir <path> Output directory
|
|
31
|
-
|
|
32
|
-
--schema <schemas> Target schemas (comma-separated)
|
|
27
|
+
--schema <schemas> Target schemas (comma-separated, default: public)
|
|
33
28
|
--config <path> Configuration file path
|
|
34
29
|
-f, --force Force overwrite
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
supatool seed -c <connection> [-t tables.yaml] [-o supabase/seeds]
|
|
31
|
+
Examples:
|
|
38
32
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- users
|
|
42
|
-
- posts
|
|
43
|
-
admin:
|
|
44
|
-
- platforms
|
|
33
|
+
# Extract full schema
|
|
34
|
+
supatool extract --all -o db/schemas
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
# Multiple schemas and table filter
|
|
37
|
+
supatool extract --schema public,agent -t "user_*" -o db/schemas
|
|
48
38
|
|
|
49
|
-
|
|
39
|
+
# Deploy (preview first)
|
|
40
|
+
supatool deploy --table users --dry-run
|
|
41
|
+
supatool deploy --table all --dry-run
|
|
42
|
+
|
|
43
|
+
# Apply migrations
|
|
44
|
+
supatool migrate --dry-run
|
|
45
|
+
supatool migrate
|
|
46
|
+
|
|
47
|
+
# Seed export
|
|
48
|
+
supatool seed --tables tables.yaml -o db/seeds
|
|
49
|
+
|
|
50
|
+
# tables.yaml format:
|
|
51
|
+
# public:
|
|
52
|
+
# - users
|
|
53
|
+
# - posts
|
|
54
|
+
|
|
55
|
+
Connection string is read from (in priority order):
|
|
56
|
+
1. --connection option
|
|
57
|
+
2. DB_CONNECTION_STRING (.env.local)
|
|
58
|
+
3. SUPABASE_CONNECTION_STRING (legacy)
|
|
59
|
+
4. DATABASE_URL (legacy)
|
|
60
|
+
5. supatool.config.json
|
|
61
|
+
|
|
62
|
+
For details, see https://github.com/idea-garage/supatool
|
|
50
63
|
`;
|
|
51
64
|
// Model Schema Usage
|
|
52
65
|
exports.modelSchemaHelp = `
|
|
@@ -65,8 +78,4 @@ Model Schema Usage (schemas/supatool-data.schema.ts):
|
|
|
65
78
|
} else {
|
|
66
79
|
console.log('Valid!');
|
|
67
80
|
}
|
|
68
|
-
|
|
69
|
-
- Use with AI:
|
|
70
|
-
const schemaJson = JSON.stringify(SUPATOOL_MODEL_SCHEMA, null, 2);
|
|
71
|
-
// Pass schemaJson to your AI prompt or API
|
|
72
81
|
`;
|
package/dist/bin/supatool.js
CHANGED
|
@@ -37,57 +37,62 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
37
37
|
// CLI entry point
|
|
38
38
|
// Subcommand support with commander
|
|
39
39
|
const commander_1 = require("commander");
|
|
40
|
-
const
|
|
41
|
-
const helptext_1 = require("./helptext"); // Import help text from external file
|
|
40
|
+
const helptext_1 = require("./helptext");
|
|
42
41
|
const package_json_1 = require("../../package.json");
|
|
43
42
|
const modelParser_1 = require("../parser/modelParser");
|
|
44
43
|
const docGenerator_1 = require("../generator/docGenerator");
|
|
45
44
|
const typeGenerator_1 = require("../generator/typeGenerator");
|
|
46
|
-
const crudGenerator_1 = require("../generator/crudGenerator");
|
|
47
45
|
const sqlGenerator_1 = require("../generator/sqlGenerator");
|
|
48
46
|
const rlsGenerator_1 = require("../generator/rlsGenerator");
|
|
49
47
|
const sync_1 = require("../sync");
|
|
50
48
|
const definitionExtractor_1 = require("../sync/definitionExtractor");
|
|
51
49
|
const seedGenerator_1 = require("../sync/seedGenerator");
|
|
50
|
+
const migrateRemote_1 = require("../sync/migrateRemote");
|
|
52
51
|
const fs = __importStar(require("fs"));
|
|
53
|
-
const path = __importStar(require("path"));
|
|
54
52
|
const program = new commander_1.Command();
|
|
55
53
|
program
|
|
56
54
|
.name('supatool')
|
|
57
55
|
.description('Supatool CLI')
|
|
58
56
|
.version(package_json_1.version);
|
|
57
|
+
function connectionRequiredError() {
|
|
58
|
+
console.error('Connection string is required. Set it using one of:');
|
|
59
|
+
console.error('1. --connection option');
|
|
60
|
+
console.error('2. DB_CONNECTION_STRING environment variable');
|
|
61
|
+
console.error('3. SUPABASE_CONNECTION_STRING environment variable (legacy)');
|
|
62
|
+
console.error('4. DATABASE_URL environment variable (legacy)');
|
|
63
|
+
console.error('5. supatool.config.json configuration file');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
59
66
|
// extract command
|
|
60
67
|
program
|
|
61
68
|
.command('extract')
|
|
62
|
-
.description('Extract and categorize database objects
|
|
63
|
-
.option('-c, --connection <string>', '
|
|
64
|
-
.option('-o, --output-dir <path>', 'Output directory', './
|
|
69
|
+
.description('Extract and categorize database objects (tables, views, RLS, functions, triggers, types)')
|
|
70
|
+
.option('-c, --connection <string>', 'Connection string (postgresql:// or postgres://)')
|
|
71
|
+
.option('-o, --output-dir <path>', 'Output directory', './db/schemas')
|
|
65
72
|
.option('-t, --tables <pattern>', 'Table pattern with wildcards', '*')
|
|
66
73
|
.option('--tables-only', 'Extract only table definitions')
|
|
67
74
|
.option('--views-only', 'Extract only view definitions')
|
|
68
|
-
.option('--all', 'Extract all DB objects
|
|
75
|
+
.option('--all', 'Extract all DB objects')
|
|
69
76
|
.option('--no-separate', 'Output all objects in same directory')
|
|
70
77
|
.option('--schema <schemas>', 'Target schemas, comma-separated (default: public)')
|
|
78
|
+
.option('--all-schemas', 'Target all schemas in the DB (use with -e to exclude some)')
|
|
79
|
+
.option('-e, --exclude-schema <schemas>', 'Schemas to exclude, comma-separated. Without --schema, targets all schemas automatically.')
|
|
71
80
|
.option('--config <path>', 'Configuration file path')
|
|
72
81
|
.option('-f, --force', 'Force overwrite without confirmation')
|
|
73
82
|
.action(async (options) => {
|
|
74
83
|
const config = (0, sync_1.resolveConfig)({
|
|
75
84
|
connectionString: options.connection
|
|
76
85
|
}, options.config);
|
|
77
|
-
if (!config.connectionString)
|
|
78
|
-
|
|
79
|
-
console.error('1. --connection option');
|
|
80
|
-
console.error('2. SUPABASE_CONNECTION_STRING environment variable');
|
|
81
|
-
console.error('3. DATABASE_URL environment variable');
|
|
82
|
-
console.error('4. supatool.config.json configuration file');
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
86
|
+
if (!config.connectionString)
|
|
87
|
+
connectionRequiredError();
|
|
85
88
|
try {
|
|
86
|
-
|
|
87
|
-
let schemas = ['public']; // default
|
|
89
|
+
let schemas = ['public'];
|
|
88
90
|
if (options.schema) {
|
|
89
91
|
schemas = options.schema.split(',').map((s) => s.trim());
|
|
90
92
|
}
|
|
93
|
+
const excludeSchemas = options.excludeSchema
|
|
94
|
+
? options.excludeSchema.split(',').map((s) => s.trim())
|
|
95
|
+
: [];
|
|
91
96
|
await (0, definitionExtractor_1.extractDefinitions)({
|
|
92
97
|
connectionString: config.connectionString,
|
|
93
98
|
outputDir: options.outputDir,
|
|
@@ -98,6 +103,9 @@ program
|
|
|
98
103
|
tablePattern: options.tables,
|
|
99
104
|
force: options.force,
|
|
100
105
|
schemas: schemas,
|
|
106
|
+
allSchemas: options.allSchemas || false,
|
|
107
|
+
excludeSchemas,
|
|
108
|
+
schemasExplicit: !!options.schema,
|
|
101
109
|
version: package_json_1.version
|
|
102
110
|
});
|
|
103
111
|
}
|
|
@@ -124,17 +132,6 @@ program
|
|
|
124
132
|
(0, typeGenerator_1.generateTypesFromModel)(model, options.out);
|
|
125
133
|
console.log('TypeScript types output:', options.out);
|
|
126
134
|
});
|
|
127
|
-
// gen:crud subcommand
|
|
128
|
-
program
|
|
129
|
-
.command('gen:crud <modelPath>')
|
|
130
|
-
.description('Generate CRUD TypeScript code from model YAML [deprecated - prefer writing code with LLM]')
|
|
131
|
-
.option('-o, --out <dir>', 'Output directory', 'docs/generated/crud')
|
|
132
|
-
.action((modelPath, options) => {
|
|
133
|
-
console.warn('⚠️ gen:crud is deprecated. With LLM development, writing code as needed is often more efficient.');
|
|
134
|
-
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
135
|
-
(0, crudGenerator_1.generateCrudFromModel)(model, options.out);
|
|
136
|
-
console.log('Generated CRUD TypeScript code:', options.out);
|
|
137
|
-
});
|
|
138
135
|
// gen:docs subcommand
|
|
139
136
|
program
|
|
140
137
|
.command('gen:docs <modelPath>')
|
|
@@ -154,12 +151,10 @@ program
|
|
|
154
151
|
.option('-o, --out <path>', 'Output path', 'docs/generated/schema.sql')
|
|
155
152
|
.action((modelPath, options) => {
|
|
156
153
|
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
157
|
-
// Write to temp files first
|
|
158
154
|
const tmpSchema = 'docs/generated/.tmp_schema.sql';
|
|
159
155
|
const tmpRls = 'docs/generated/.tmp_rls.sql';
|
|
160
156
|
(0, sqlGenerator_1.generateSqlFromModel)(model, tmpSchema);
|
|
161
157
|
(0, rlsGenerator_1.generateRlsSqlFromModel)(model, tmpRls);
|
|
162
|
-
// Merge into single file
|
|
163
158
|
const schema = fs.readFileSync(tmpSchema, 'utf-8');
|
|
164
159
|
const rls = fs.readFileSync(tmpRls, 'utf-8');
|
|
165
160
|
fs.writeFileSync(options.out, schema + '\n' + rls);
|
|
@@ -177,102 +172,12 @@ program
|
|
|
177
172
|
(0, rlsGenerator_1.generateRlsSqlFromModel)(model, options.out);
|
|
178
173
|
console.log('RLS/security policy SQL output:', options.out);
|
|
179
174
|
});
|
|
180
|
-
//
|
|
181
|
-
program
|
|
182
|
-
.command('gen:all <modelPath>')
|
|
183
|
-
.description('Generate all from model YAML')
|
|
184
|
-
.action((modelPath) => {
|
|
185
|
-
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
186
|
-
(0, typeGenerator_1.generateTypesFromModel)(model, 'docs/generated/types.ts');
|
|
187
|
-
(0, crudGenerator_1.generateCrudFromModel)(model, 'docs/generated/crud');
|
|
188
|
-
(0, docGenerator_1.generateTableDocMarkdown)(model, 'docs/generated/table-doc.md');
|
|
189
|
-
(0, docGenerator_1.generateRelationsMarkdown)(model, 'docs/generated/relations.md');
|
|
190
|
-
console.log('TypeScript types output: docs/generated/types.ts');
|
|
191
|
-
console.log('CRUD code output: docs/generated/crud/');
|
|
192
|
-
console.log('Table doc output: docs/generated/table-doc.md');
|
|
193
|
-
console.log('Relations list output: docs/generated/relations.md');
|
|
194
|
-
});
|
|
195
|
-
// create subcommand
|
|
196
|
-
program
|
|
197
|
-
.command('create <template>')
|
|
198
|
-
.description('Generate template YAML')
|
|
199
|
-
.option('-o, --out <path>', 'Output path', 'docs/model-schema-example.yaml')
|
|
200
|
-
.action((template, options) => {
|
|
201
|
-
const srcPath = path.join(__dirname, '../templates/yaml', `${template}.yaml`);
|
|
202
|
-
const destPath = options.out;
|
|
203
|
-
if (!fs.existsSync(srcPath)) {
|
|
204
|
-
console.error(`Template not found: ${srcPath}`);
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
207
|
-
fs.copyFileSync(srcPath, destPath);
|
|
208
|
-
console.log(`Template generated: ${destPath}`);
|
|
209
|
-
});
|
|
210
|
-
// crud command (Supabase types -> CRUD generation)
|
|
211
|
-
program
|
|
212
|
-
.command('crud')
|
|
213
|
-
.description('Generate CRUD from Supabase type definitions [deprecated - prefer writing code with LLM]')
|
|
214
|
-
.option('-i, --input <path>', 'Type definition input path', 'shared/')
|
|
215
|
-
.option('-o, --output <path>', 'CRUD code output path', 'src/integrations/supabase/')
|
|
216
|
-
.option('-t, --tables <names>', 'Target tables (comma-separated)')
|
|
217
|
-
.option('-f, --force', 'Force overwrite output')
|
|
218
|
-
.action((options) => {
|
|
219
|
-
console.warn('⚠️ crud is deprecated. With LLM development, writing code as needed is often more efficient.');
|
|
220
|
-
// Pass argv to main() for CLI args
|
|
221
|
-
(0, index_1.main)();
|
|
222
|
-
});
|
|
223
|
-
// help subcommand
|
|
224
|
-
program
|
|
225
|
-
.command('help')
|
|
226
|
-
.description('Show help')
|
|
227
|
-
.action(() => {
|
|
228
|
-
console.log(helptext_1.helpText);
|
|
229
|
-
});
|
|
230
|
-
// sync command (deprecated)
|
|
231
|
-
program
|
|
232
|
-
.command('sync')
|
|
233
|
-
.description('Synchronize local and remote schemas [deprecated]')
|
|
234
|
-
.option('-c, --connection <string>', 'Supabase connection string')
|
|
235
|
-
.option('-s, --schema-dir <path>', 'Local schema directory', './supabase/schemas')
|
|
236
|
-
.option('-t, --tables <pattern>', 'Table pattern (wildcards supported)', '*')
|
|
237
|
-
.option('-f, --force', 'Force overwrite (no confirmation)')
|
|
238
|
-
.option('--config <path>', 'Configuration file path')
|
|
239
|
-
.action(async (options) => {
|
|
240
|
-
console.warn('⚠️ WARNING: sync command is deprecated.');
|
|
241
|
-
console.warn(' Please use `supatool deploy` command instead.');
|
|
242
|
-
console.warn(' Example: supatool deploy --table users --dry-run');
|
|
243
|
-
console.warn(' Example: supatool deploy --table all --dry-run # all tables');
|
|
244
|
-
console.warn('');
|
|
245
|
-
const config = (0, sync_1.resolveConfig)({
|
|
246
|
-
connectionString: options.connection
|
|
247
|
-
}, options.config);
|
|
248
|
-
if (!config.connectionString) {
|
|
249
|
-
console.error('Connection string is required. Set it using one of:');
|
|
250
|
-
console.error('1. --connection option');
|
|
251
|
-
console.error('2. SUPABASE_CONNECTION_STRING environment variable');
|
|
252
|
-
console.error('3. DATABASE_URL environment variable');
|
|
253
|
-
console.error('4. supatool.config.json configuration file');
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
try {
|
|
257
|
-
await (0, sync_1.syncAllTables)({
|
|
258
|
-
connectionString: config.connectionString,
|
|
259
|
-
schemaDir: options.schemaDir,
|
|
260
|
-
tablePattern: options.tables,
|
|
261
|
-
force: options.force,
|
|
262
|
-
migrationConfig: config.migration
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
catch (error) {
|
|
266
|
-
console.error('⚠️ Sync error:', error);
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
// deploy command (recommended)
|
|
175
|
+
// deploy command
|
|
271
176
|
program
|
|
272
177
|
.command('deploy')
|
|
273
178
|
.description('Deploy local schema to remote (diff detection, migration generation, confirm before apply)')
|
|
274
|
-
.option('-c, --connection <string>', '
|
|
275
|
-
.option('-s, --schema-dir <path>', 'Local schema directory', './
|
|
179
|
+
.option('-c, --connection <string>', 'Connection string (postgresql:// or postgres://)')
|
|
180
|
+
.option('-s, --schema-dir <path>', 'Local schema directory', './db/schemas')
|
|
276
181
|
.option('-t, --table <name>', 'Target table name (specify "all" for all tables)')
|
|
277
182
|
.option('--auto-apply', 'Auto-apply to remote (no confirmation)')
|
|
278
183
|
.option('--dry-run', 'Preview changes only (recommended)')
|
|
@@ -283,27 +188,18 @@ program
|
|
|
283
188
|
const config = (0, sync_1.resolveConfig)({
|
|
284
189
|
connectionString: options.connection
|
|
285
190
|
}, options.config);
|
|
286
|
-
if (!config.connectionString)
|
|
287
|
-
|
|
288
|
-
console.error('1. --connection option');
|
|
289
|
-
console.error('2. SUPABASE_CONNECTION_STRING environment variable');
|
|
290
|
-
console.error('3. DATABASE_URL environment variable');
|
|
291
|
-
console.error('4. supatool.config.json configuration file');
|
|
292
|
-
process.exit(1);
|
|
293
|
-
}
|
|
294
|
-
// Validate table specification
|
|
191
|
+
if (!config.connectionString)
|
|
192
|
+
connectionRequiredError();
|
|
295
193
|
if (!options.table) {
|
|
296
194
|
console.error('❌ Table name is required. Use --table <table-name>');
|
|
297
195
|
console.error(' Example: supatool deploy --table users --dry-run');
|
|
298
|
-
console.error(' Example: supatool deploy --table all --dry-run
|
|
196
|
+
console.error(' Example: supatool deploy --table all --dry-run');
|
|
299
197
|
process.exit(1);
|
|
300
198
|
}
|
|
301
199
|
const tablePattern = options.table === 'all' ? '*' : options.table;
|
|
302
|
-
// Option processing
|
|
303
200
|
const isDryRun = options.dryRun || false;
|
|
304
201
|
const isAutoApply = options.autoApply || false;
|
|
305
202
|
const isGenerateOnly = options.generateOnly || false;
|
|
306
|
-
// Conflict check
|
|
307
203
|
const activeOptions = [isDryRun, isAutoApply, isGenerateOnly].filter(Boolean).length;
|
|
308
204
|
if (activeOptions > 1) {
|
|
309
205
|
console.error('❌ --dry-run, --auto-apply, --generate-only cannot be specified simultaneously');
|
|
@@ -340,27 +236,46 @@ program
|
|
|
340
236
|
process.exit(1);
|
|
341
237
|
}
|
|
342
238
|
});
|
|
239
|
+
// migrate command — apply pending SQL migration files to remote DB
|
|
240
|
+
program
|
|
241
|
+
.command('migrate')
|
|
242
|
+
.description('Apply pending migration files from db/migrations/ to remote DB')
|
|
243
|
+
.option('-c, --connection <string>', 'Connection string (postgresql:// or postgres://)')
|
|
244
|
+
.option('-d, --dir <path>', 'Migrations directory', 'db/migrations')
|
|
245
|
+
.option('--dry-run', 'Preview pending migrations without applying')
|
|
246
|
+
.option('--config <path>', 'Configuration file path')
|
|
247
|
+
.action(async (options) => {
|
|
248
|
+
const config = (0, sync_1.resolveConfig)({
|
|
249
|
+
connectionString: options.connection
|
|
250
|
+
}, options.config);
|
|
251
|
+
if (!config.connectionString)
|
|
252
|
+
connectionRequiredError();
|
|
253
|
+
try {
|
|
254
|
+
await (0, migrateRemote_1.migrateRemote)({
|
|
255
|
+
connectionString: config.connectionString,
|
|
256
|
+
migrationsDir: options.dir,
|
|
257
|
+
dryRun: options.dryRun || false
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.error('⚠️ Migration error:', error);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
343
265
|
// seed command
|
|
344
266
|
program
|
|
345
267
|
.command('seed')
|
|
346
|
-
.description('Fetch table data from remote DB and generate
|
|
347
|
-
.option('-c, --connection <string>', '
|
|
268
|
+
.description('Fetch table data from remote DB and generate seed JSON')
|
|
269
|
+
.option('-c, --connection <string>', 'Connection string (postgresql:// or postgres://)')
|
|
348
270
|
.option('-t, --tables <path>', 'Tables list YAML', 'tables.yaml')
|
|
349
|
-
.option('-o, --out <dir>', 'Output directory', '
|
|
271
|
+
.option('-o, --out <dir>', 'Output directory', 'db/seeds')
|
|
350
272
|
.option('--config <path>', 'Configuration file path')
|
|
351
273
|
.action(async (options) => {
|
|
352
|
-
// Resolve connection
|
|
353
274
|
const config = (0, sync_1.resolveConfig)({
|
|
354
275
|
connectionString: options.connection
|
|
355
276
|
}, options.config);
|
|
356
|
-
if (!config.connectionString)
|
|
357
|
-
|
|
358
|
-
console.error('1. --connection option');
|
|
359
|
-
console.error('2. SUPABASE_CONNECTION_STRING environment variable');
|
|
360
|
-
console.error('3. DATABASE_URL environment variable');
|
|
361
|
-
console.error('4. supatool.config.json configuration file');
|
|
362
|
-
process.exit(1);
|
|
363
|
-
}
|
|
277
|
+
if (!config.connectionString)
|
|
278
|
+
connectionRequiredError();
|
|
364
279
|
try {
|
|
365
280
|
await (0, seedGenerator_1.generateSeedsFromRemote)({
|
|
366
281
|
connectionString: config.connectionString,
|
|
@@ -373,7 +288,14 @@ program
|
|
|
373
288
|
process.exit(1);
|
|
374
289
|
}
|
|
375
290
|
});
|
|
376
|
-
//
|
|
291
|
+
// help subcommand
|
|
292
|
+
program
|
|
293
|
+
.command('help')
|
|
294
|
+
.description('Show help')
|
|
295
|
+
.action(() => {
|
|
296
|
+
console.log(helptext_1.helpText);
|
|
297
|
+
});
|
|
298
|
+
// If no subcommand is specified, show helpText only
|
|
377
299
|
if (!process.argv.slice(2).length) {
|
|
378
300
|
console.log(helptext_1.helpText);
|
|
379
301
|
process.exit(0);
|