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 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
- // 外部キー制約はREFERENCESで付与
76
+ // 外部キー制約はテーブル末尾に付与するため保留
77
+ colDefs.push(def);
78
+ // 外部キー制約をテーブル末尾に格納
74
79
  if (c.ref) {
75
80
  const refTable = c.ref.split('.')[0];
76
- def += ` REFERENCES ${refTable}(id)`;
81
+ tableConstraints.push(` FOREIGN KEY (${actualColName}) REFERENCES ${refTable}(id)`);
77
82
  }
78
- colDefs.push(def);
79
83
  }
80
- sql += colDefs.join(',\n') + '\n);\n\n';
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 'text';
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
- character_maximum_length,
727
- is_nullable,
728
- column_default
729
- FROM information_schema.columns
730
- WHERE table_schema = $1
731
- AND table_name = $2
732
- ORDER BY ordinal_position
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
- let colDef = ` ${col.column_name} ${col.data_type.toUpperCase()}`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supatool",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "A CLI tool for Supabase schema extraction and TypeScript CRUD generation with declarative database schema support.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",