supatool 0.3.1 → 0.3.2
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 +38 -34
- package/dist/generator/sqlGenerator.js +19 -5
- package/dist/sync/definitionExtractor.js +41 -11
- package/package.json +1 -1
package/README.md
CHANGED
@@ -285,6 +285,43 @@ supatool extract --all -c "postgresql://..." -o supabase/schemas
|
|
285
285
|
|
286
286
|
## Changelog
|
287
287
|
|
288
|
+
### v0.3.2
|
289
|
+
|
290
|
+
- **ENHANCED**: Adjust for extensions(vector, geometry etc.)
|
291
|
+
- **FIXED**: USER-DEFINED column types are now rendered with full type definitions (e.g. `vector(1536)`, `geometry(Point,4326)`).
|
292
|
+
- **ADDED**: `FOREIGN KEY` constraints are now included as `CONSTRAINT ... FOREIGN KEY ... REFERENCES ...` inside generated `CREATE TABLE` statements.
|
293
|
+
|
294
|
+
### v0.3.0
|
295
|
+
|
296
|
+
**NEW Features:**
|
297
|
+
- **NEW**: `extract` command for database schema extraction
|
298
|
+
- **NEW**: Full compliance with Supabase declarative database schemas workflow
|
299
|
+
- **NEW**: AI-friendly index.md and llms.txt generation for better schema understanding
|
300
|
+
- **NEW**: Database comment extraction and integration
|
301
|
+
- **NEW**: Organized directory structure (tables/, views/, rls/, rpc/)
|
302
|
+
- **NEW**: Pattern matching for selective extraction
|
303
|
+
- **ENHANCED**: Support for all database object types (RLS, functions, triggers, cron jobs, custom types)
|
304
|
+
- **ENHANCED**: Flexible output options with --no-separate compatibility
|
305
|
+
|
306
|
+
**Enhanced Error Handling:**
|
307
|
+
- Comprehensive try-catch blocks for all CRUD operations
|
308
|
+
- Enhanced null/undefined checks with proper fallbacks
|
309
|
+
- Detailed error messages with contextual information
|
310
|
+
- Special handling for PGRST116 errors (record not found)
|
311
|
+
- Parameter validation for required fields
|
312
|
+
- Proper error logging and debugging support
|
313
|
+
|
314
|
+
**Breaking Changes:**
|
315
|
+
- **Function Parameter Format**: All CRUD functions now use destructuring assignment
|
316
|
+
- Before: `selectTableRowById(id: string)`
|
317
|
+
- After: `selectTableRowById({ id }: { id: string })`
|
318
|
+
- **Type Safety**: Enhanced TypeScript type annotations for all functions
|
319
|
+
|
320
|
+
### v0.2.0
|
321
|
+
- Added `gen:` commands for code and schema generation
|
322
|
+
- Enhanced `create` command
|
323
|
+
- Introduced model schema support (`schemas/supatool-data.schema.ts`)
|
324
|
+
|
288
325
|
## Database Comments
|
289
326
|
|
290
327
|
Supatool automatically extracts and includes PostgreSQL comments in all generated files. Comments enhance documentation and AI understanding of your schema.
|
@@ -317,37 +354,4 @@ Comments appear in:
|
|
317
354
|
## Tables
|
318
355
|
- [users](tables/users.sql) - User account information and authentication data
|
319
356
|
- [posts](tables/posts.sql) - User-generated content and blog posts
|
320
|
-
```
|
321
|
-
|
322
|
-
## Changelog
|
323
|
-
|
324
|
-
### v0.3.0
|
325
|
-
|
326
|
-
**NEW Features:**
|
327
|
-
- **NEW**: `extract` command for database schema extraction
|
328
|
-
- **NEW**: Full compliance with Supabase declarative database schemas workflow
|
329
|
-
- **NEW**: AI-friendly index.md and llms.txt generation for better schema understanding
|
330
|
-
- **NEW**: Database comment extraction and integration
|
331
|
-
- **NEW**: Organized directory structure (tables/, views/, rls/, rpc/)
|
332
|
-
- **NEW**: Pattern matching for selective extraction
|
333
|
-
- **ENHANCED**: Support for all database object types (RLS, functions, triggers, cron jobs, custom types)
|
334
|
-
- **ENHANCED**: Flexible output options with --no-separate compatibility
|
335
|
-
|
336
|
-
**Enhanced Error Handling:**
|
337
|
-
- Comprehensive try-catch blocks for all CRUD operations
|
338
|
-
- Enhanced null/undefined checks with proper fallbacks
|
339
|
-
- Detailed error messages with contextual information
|
340
|
-
- Special handling for PGRST116 errors (record not found)
|
341
|
-
- Parameter validation for required fields
|
342
|
-
- Proper error logging and debugging support
|
343
|
-
|
344
|
-
**Breaking Changes:**
|
345
|
-
- **Function Parameter Format**: All CRUD functions now use destructuring assignment
|
346
|
-
- Before: `selectTableRowById(id: string)`
|
347
|
-
- After: `selectTableRowById({ id }: { id: string })`
|
348
|
-
- **Type Safety**: Enhanced TypeScript type annotations for all functions
|
349
|
-
|
350
|
-
### v0.2.0
|
351
|
-
- Added `gen:` commands for code and schema generation
|
352
|
-
- Enhanced `create` command
|
353
|
-
- Introduced model schema support (`schemas/supatool-data.schema.ts`)
|
357
|
+
```
|
@@ -49,6 +49,7 @@ function generateSqlFromModel(model, outPath) {
|
|
49
49
|
let userIdCount = userIdFields.length;
|
50
50
|
sql += `CREATE TABLE ${tableName} (\n`;
|
51
51
|
const colDefs = [];
|
52
|
+
const tableConstraints = [];
|
52
53
|
for (const [colName, col] of Object.entries(t.fields || {})) {
|
53
54
|
const c = col;
|
54
55
|
let actualColName = colName;
|
@@ -66,18 +67,23 @@ function generateSqlFromModel(model, outPath) {
|
|
66
67
|
let def = ` ${actualColName} ${toSqlType(c.type, actualColName)}`;
|
67
68
|
if (c.primary)
|
68
69
|
def += ' PRIMARY KEY';
|
70
|
+
if (c.unique)
|
71
|
+
def += ' UNIQUE';
|
69
72
|
if (c.notNull)
|
70
73
|
def += ' NOT NULL';
|
71
74
|
if (c.default)
|
72
75
|
def += ` DEFAULT ${c.default}`;
|
73
|
-
//
|
76
|
+
// 外部キー制約はテーブル末尾に付与するため保留
|
77
|
+
colDefs.push(def);
|
78
|
+
// 外部キー制約をテーブル末尾に格納
|
74
79
|
if (c.ref) {
|
75
80
|
const refTable = c.ref.split('.')[0];
|
76
|
-
|
81
|
+
tableConstraints.push(` FOREIGN KEY (${actualColName}) REFERENCES ${refTable}(id)`);
|
77
82
|
}
|
78
|
-
colDefs.push(def);
|
79
83
|
}
|
80
|
-
|
84
|
+
// 列定義 + テーブルレベル制約を結合
|
85
|
+
const defs = [...colDefs, ...tableConstraints];
|
86
|
+
sql += defs.join(',\n') + '\n);\n\n';
|
81
87
|
// ALTER TABLEによる外部キー制約は出力しない
|
82
88
|
sql += '\n';
|
83
89
|
}
|
@@ -89,12 +95,20 @@ function toSqlType(type, colName) {
|
|
89
95
|
// 時刻列は必ずtimestamptz
|
90
96
|
if (type === 'timestamp' || type === 'timestamptz' || colName.endsWith('_at'))
|
91
97
|
return 'timestamptz';
|
98
|
+
// vector型サポート
|
99
|
+
if (/^vector(\(\d+\))?$/i.test(type))
|
100
|
+
return type;
|
101
|
+
// extensions.vector → vector へ変換
|
102
|
+
const extVectorMatch = type.match(/^extensions\.vector(\(\d+\))?$/i);
|
103
|
+
if (extVectorMatch) {
|
104
|
+
return `vector${extVectorMatch[1] || ''}`;
|
105
|
+
}
|
92
106
|
switch (type) {
|
93
107
|
case 'uuid': return 'uuid';
|
94
108
|
case 'text': return 'text';
|
95
109
|
case 'int':
|
96
110
|
case 'integer': return 'integer';
|
97
111
|
case 'boolean': return 'boolean';
|
98
|
-
default: return
|
112
|
+
default: return type; // 指定が未知の場合はそのまま返す
|
99
113
|
}
|
100
114
|
}
|
@@ -717,19 +717,24 @@ async function fetchTableDefinitions(client, spinner, progress, schemas = ['publ
|
|
717
717
|
*/
|
718
718
|
async function generateCreateTableDDL(client, tableName, schemaName = 'public') {
|
719
719
|
// 全てのクエリを並行実行
|
720
|
-
const [columnsResult, primaryKeyResult, tableCommentResult, columnCommentsResult, uniqueConstraintResult] = await Promise.all([
|
720
|
+
const [columnsResult, primaryKeyResult, tableCommentResult, columnCommentsResult, uniqueConstraintResult, foreignKeyResult] = await Promise.all([
|
721
721
|
// カラム情報を取得
|
722
722
|
client.query(`
|
723
723
|
SELECT
|
724
|
-
column_name,
|
725
|
-
data_type,
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
724
|
+
c.column_name,
|
725
|
+
c.data_type,
|
726
|
+
c.udt_name,
|
727
|
+
c.character_maximum_length,
|
728
|
+
c.is_nullable,
|
729
|
+
c.column_default,
|
730
|
+
pg_catalog.format_type(a.atttypid, a.atttypmod) AS full_type
|
731
|
+
FROM information_schema.columns c
|
732
|
+
JOIN pg_class cl ON cl.relname = c.table_name
|
733
|
+
JOIN pg_namespace ns ON ns.nspname = c.table_schema AND ns.oid = cl.relnamespace
|
734
|
+
JOIN pg_attribute a ON a.attrelid = cl.oid AND a.attname = c.column_name
|
735
|
+
WHERE c.table_schema = $1
|
736
|
+
AND c.table_name = $2
|
737
|
+
ORDER BY c.ordinal_position
|
733
738
|
`, [schemaName, tableName]),
|
734
739
|
// 主キー情報を取得
|
735
740
|
client.query(`
|
@@ -776,6 +781,25 @@ async function generateCreateTableDDL(client, tableName, schemaName = 'public')
|
|
776
781
|
AND tc.constraint_type = 'UNIQUE'
|
777
782
|
GROUP BY tc.constraint_name
|
778
783
|
ORDER BY tc.constraint_name
|
784
|
+
`, [schemaName, tableName]),
|
785
|
+
// FOREIGN KEY制約を取得
|
786
|
+
client.query(`
|
787
|
+
SELECT
|
788
|
+
tc.constraint_name,
|
789
|
+
string_agg(kcu.column_name, ', ' ORDER BY kcu.ordinal_position) as columns,
|
790
|
+
ccu.table_schema AS foreign_table_schema,
|
791
|
+
ccu.table_name AS foreign_table_name,
|
792
|
+
string_agg(ccu.column_name, ', ' ORDER BY kcu.ordinal_position) as foreign_columns
|
793
|
+
FROM information_schema.table_constraints tc
|
794
|
+
JOIN information_schema.key_column_usage kcu
|
795
|
+
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
796
|
+
JOIN information_schema.constraint_column_usage ccu
|
797
|
+
ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema
|
798
|
+
WHERE tc.table_schema = $1
|
799
|
+
AND tc.table_name = $2
|
800
|
+
AND tc.constraint_type = 'FOREIGN KEY'
|
801
|
+
GROUP BY tc.constraint_name, ccu.table_schema, ccu.table_name
|
802
|
+
ORDER BY tc.constraint_name
|
779
803
|
`, [schemaName, tableName])
|
780
804
|
]);
|
781
805
|
const columnComments = new Map();
|
@@ -798,7 +822,9 @@ async function generateCreateTableDDL(client, tableName, schemaName = 'public')
|
|
798
822
|
ddl += `CREATE TABLE IF NOT EXISTS ${tableName} (\n`;
|
799
823
|
const columnDefs = [];
|
800
824
|
for (const col of columnsResult.rows) {
|
801
|
-
|
825
|
+
const rawType = col.full_type ||
|
826
|
+
((col.data_type === 'USER-DEFINED' && col.udt_name) ? col.udt_name : col.data_type);
|
827
|
+
let colDef = ` ${col.column_name} ${rawType}`;
|
802
828
|
// 長さ指定
|
803
829
|
if (col.character_maximum_length) {
|
804
830
|
colDef += `(${col.character_maximum_length})`;
|
@@ -823,6 +849,10 @@ async function generateCreateTableDDL(client, tableName, schemaName = 'public')
|
|
823
849
|
for (const unique of uniqueConstraintResult.rows) {
|
824
850
|
ddl += `,\n CONSTRAINT ${unique.constraint_name} UNIQUE (${unique.columns})`;
|
825
851
|
}
|
852
|
+
// FOREIGN KEY制約をCREATE TABLE内に追加
|
853
|
+
for (const fk of foreignKeyResult.rows) {
|
854
|
+
ddl += `,\n CONSTRAINT ${fk.constraint_name} FOREIGN KEY (${fk.columns}) REFERENCES ${fk.foreign_table_schema}.${fk.foreign_table_name} (${fk.foreign_columns})`;
|
855
|
+
}
|
826
856
|
ddl += '\n);\n';
|
827
857
|
ddl += '\n';
|
828
858
|
// カラムコメントを追加(スキーマ名を含む)
|
package/package.json
CHANGED