supatool 0.3.0 → 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 +55 -31
- package/dist/generator/crudGenerator.js +146 -9
- package/dist/generator/sqlGenerator.js +19 -5
- package/dist/index.js +123 -37
- package/dist/integrations/supabase/crud-autogen/tasks.js +220 -0
- package/dist/integrations/supabase/crud-autogen/workflows.js +220 -0
- package/dist/sync/definitionExtractor.js +41 -11
- package/package.json +1 -1
package/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
A CLI tool that automatically generates TypeScript CRUD code from Supabase type definitions.
|
4
4
|
|
5
|
+
|
5
6
|
## Install
|
6
7
|
|
7
8
|
```
|
@@ -155,7 +156,7 @@ CRUD utility files for the `apps` table will be generated in `src/integrations/s
|
|
155
156
|
You can import and use these functions in your application as follows:
|
156
157
|
|
157
158
|
```ts
|
158
|
-
// Example: Using CRUD functions for the apps table
|
159
|
+
// Example: Using CRUD functions for the apps table (v0.3.0+)
|
159
160
|
import {
|
160
161
|
selectAppsRowsWithFilters,
|
161
162
|
selectAppsSingleRowWithFilters,
|
@@ -165,23 +166,26 @@ import {
|
|
165
166
|
deleteAppsRow,
|
166
167
|
} from 'src/integrations/supabase/crud-autogen/apps';
|
167
168
|
|
168
|
-
//
|
169
|
-
const apps = await selectAppsRowsWithFilters({ status: 'active' });
|
169
|
+
// Select multiple rows with filters (NEW: destructuring parameters)
|
170
|
+
const apps = await selectAppsRowsWithFilters({ filters: { status: 'active' } });
|
170
171
|
|
171
|
-
//
|
172
|
-
const app = await selectAppsSingleRowWithFilters({ id: 'your-app-id' });
|
172
|
+
// Select a single row with filters
|
173
|
+
const app = await selectAppsSingleRowWithFilters({ filters: { id: 'your-app-id' } });
|
173
174
|
|
174
|
-
//
|
175
|
-
const appById = await selectAppsRowById('your-app-id');
|
175
|
+
// Select by ID (NEW: destructuring parameters)
|
176
|
+
const appById = await selectAppsRowById({ id: 'your-app-id' });
|
176
177
|
|
177
|
-
//
|
178
|
-
const newApp = await insertAppsRow({ name: 'New App', status: 'active' });
|
178
|
+
// Insert new row (NEW: destructuring parameters)
|
179
|
+
const newApp = await insertAppsRow({ data: { name: 'New App', status: 'active' } });
|
179
180
|
|
180
|
-
// Update row
|
181
|
-
const updatedApp = await updateAppsRow({
|
181
|
+
// Update row (NEW: destructuring parameters)
|
182
|
+
const updatedApp = await updateAppsRow({
|
183
|
+
id: 'your-app-id',
|
184
|
+
data: { name: 'Updated Name' }
|
185
|
+
});
|
182
186
|
|
183
|
-
// Delete row
|
184
|
-
const
|
187
|
+
// Delete row (NEW: destructuring parameters)
|
188
|
+
const success = await deleteAppsRow({ id: 'your-app-id' });
|
185
189
|
```
|
186
190
|
|
187
191
|
- All functions are async and return the corresponding row type.
|
@@ -281,6 +285,43 @@ supatool extract --all -c "postgresql://..." -o supabase/schemas
|
|
281
285
|
|
282
286
|
## Changelog
|
283
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
|
+
|
284
325
|
## Database Comments
|
285
326
|
|
286
327
|
Supatool automatically extracts and includes PostgreSQL comments in all generated files. Comments enhance documentation and AI understanding of your schema.
|
@@ -313,21 +354,4 @@ Comments appear in:
|
|
313
354
|
## Tables
|
314
355
|
- [users](tables/users.sql) - User account information and authentication data
|
315
356
|
- [posts](tables/posts.sql) - User-generated content and blog posts
|
316
|
-
```
|
317
|
-
|
318
|
-
## Changelog
|
319
|
-
|
320
|
-
### v0.3.0
|
321
|
-
- **NEW**: `extract` command for database schema extraction
|
322
|
-
- **NEW**: Full compliance with Supabase declarative database schemas workflow
|
323
|
-
- **NEW**: AI-friendly index.md and llms.txt generation for better schema understanding
|
324
|
-
- **NEW**: Database comment extraction and integration
|
325
|
-
- **NEW**: Organized directory structure (tables/, views/, rls/, rpc/)
|
326
|
-
- **NEW**: Pattern matching for selective extraction
|
327
|
-
- **ENHANCED**: Support for all database object types (RLS, functions, triggers, cron jobs, custom types)
|
328
|
-
- **ENHANCED**: Flexible output options with --no-separate compatibility
|
329
|
-
|
330
|
-
### v0.2.0
|
331
|
-
- Added `gen:` commands for code and schema generation
|
332
|
-
- Enhanced `create` command
|
333
|
-
- Introduced model schema support (`schemas/supatool-data.schema.ts`)
|
357
|
+
```
|
@@ -34,21 +34,158 @@ function generateCrudFromModel(model, outDir) {
|
|
34
34
|
if (tableObj.skipCreate)
|
35
35
|
continue;
|
36
36
|
const tableName = tableObj.tableName;
|
37
|
+
const capitalizedName = capitalize(tableName);
|
37
38
|
let code = `// 自動生成: ${tableName}用CRUD関数\n\n`;
|
38
39
|
code += `import { supabase } from '../client';\n`;
|
39
40
|
code += `import type { ${tableName} } from '../types';\n\n`;
|
41
|
+
// 型定義
|
42
|
+
code += `// フィルター型定義\n`;
|
43
|
+
code += `type FilterValue = string | number | boolean | null;\n`;
|
44
|
+
code += `type Filters = Record<string, FilterValue | FilterValue[]>;\n\n`;
|
45
|
+
// 全件取得関数(改良版)
|
40
46
|
code += `/** 全件取得 */\n`;
|
41
|
-
code += `export async function
|
42
|
-
code += `
|
43
|
-
code += `
|
44
|
-
code += `
|
47
|
+
code += `export async function select${capitalizedName}Rows(): Promise<${tableName}[]> {\n`;
|
48
|
+
code += ` try {\n`;
|
49
|
+
code += ` const { data, error } = await supabase.from('${tableName}').select('*');\n`;
|
50
|
+
code += ` if (error) {\n`;
|
51
|
+
code += ` console.error('Error fetching all ${tableName}:', error);\n`;
|
52
|
+
code += ` throw new Error(\`Failed to fetch ${tableName}: \${error.message}\`);\n`;
|
53
|
+
code += ` }\n`;
|
54
|
+
code += ` if (!data) {\n`;
|
55
|
+
code += ` return [];\n`;
|
56
|
+
code += ` }\n`;
|
57
|
+
code += ` return data as ${tableName}[];\n`;
|
58
|
+
code += ` } catch (error) {\n`;
|
59
|
+
code += ` console.error('Unexpected error in select${capitalizedName}Rows:', error);\n`;
|
60
|
+
code += ` throw error;\n`;
|
61
|
+
code += ` }\n`;
|
45
62
|
code += `}\n\n`;
|
63
|
+
// IDで1件取得関数(改良版)
|
46
64
|
code += `/** IDで1件取得 */\n`;
|
47
|
-
code += `export async function
|
48
|
-
code += `
|
49
|
-
code += `
|
50
|
-
code += `
|
51
|
-
code += `
|
65
|
+
code += `export async function select${capitalizedName}RowById({ id }: { id: string }): Promise<${tableName} | null> {\n`;
|
66
|
+
code += ` if (!id) {\n`;
|
67
|
+
code += ` throw new Error('ID is required');\n`;
|
68
|
+
code += ` }\n`;
|
69
|
+
code += ` try {\n`;
|
70
|
+
code += ` const { data, error } = await supabase.from('${tableName}').select('*').eq('id', id).single();\n`;
|
71
|
+
code += ` if (error) {\n`;
|
72
|
+
code += ` // レコードが見つからない場合(PGRST116)は null を返す\n`;
|
73
|
+
code += ` if (error.code === 'PGRST116') {\n`;
|
74
|
+
code += ` return null;\n`;
|
75
|
+
code += ` }\n`;
|
76
|
+
code += ` console.error('Error fetching ${tableName} by ID:', error);\n`;
|
77
|
+
code += ` throw new Error(\`Failed to fetch ${tableName} with ID \${id}: \${error.message}\`);\n`;
|
78
|
+
code += ` }\n`;
|
79
|
+
code += ` return data as ${tableName} | null;\n`;
|
80
|
+
code += ` } catch (error) {\n`;
|
81
|
+
code += ` console.error('Unexpected error in select${capitalizedName}RowById:', error);\n`;
|
82
|
+
code += ` throw error;\n`;
|
83
|
+
code += ` }\n`;
|
84
|
+
code += `}\n\n`;
|
85
|
+
// フィルターで検索関数
|
86
|
+
code += `/** フィルターで複数件取得 */\n`;
|
87
|
+
code += `export async function select${capitalizedName}RowsWithFilters({ filters }: { filters: Filters }): Promise<${tableName}[]> {\n`;
|
88
|
+
code += ` try {\n`;
|
89
|
+
code += ` let query = supabase.from('${tableName}').select('*');\n`;
|
90
|
+
code += ` \n`;
|
91
|
+
code += ` // フィルターを適用\n`;
|
92
|
+
code += ` for (const [key, value] of Object.entries(filters)) {\n`;
|
93
|
+
code += ` if (Array.isArray(value)) {\n`;
|
94
|
+
code += ` query = query.in(key, value);\n`;
|
95
|
+
code += ` } else {\n`;
|
96
|
+
code += ` query = query.eq(key, value);\n`;
|
97
|
+
code += ` }\n`;
|
98
|
+
code += ` }\n`;
|
99
|
+
code += ` \n`;
|
100
|
+
code += ` const { data, error } = await query;\n`;
|
101
|
+
code += ` if (error) {\n`;
|
102
|
+
code += ` console.error('Error fetching ${tableName} by filters:', error);\n`;
|
103
|
+
code += ` throw new Error(\`Failed to fetch ${tableName}: \${error.message}\`);\n`;
|
104
|
+
code += ` }\n`;
|
105
|
+
code += ` return (data as unknown as ${tableName}[]) || [];\n`;
|
106
|
+
code += ` } catch (error) {\n`;
|
107
|
+
code += ` console.error('Unexpected error in select${capitalizedName}RowsWithFilters:', error);\n`;
|
108
|
+
code += ` throw error;\n`;
|
109
|
+
code += ` }\n`;
|
110
|
+
code += `}\n\n`;
|
111
|
+
// 作成関数
|
112
|
+
code += `/** 新規作成 */\n`;
|
113
|
+
code += `export async function insert${capitalizedName}Row({ data }: { data: Omit<${tableName}, 'id' | 'created_at' | 'updated_at'> }): Promise<${tableName}> {\n`;
|
114
|
+
code += ` if (!data) {\n`;
|
115
|
+
code += ` throw new Error('Data is required for creation');\n`;
|
116
|
+
code += ` }\n`;
|
117
|
+
code += ` try {\n`;
|
118
|
+
code += ` const { data: createdData, error } = await supabase\n`;
|
119
|
+
code += ` .from('${tableName}')\n`;
|
120
|
+
code += ` .insert([data])\n`;
|
121
|
+
code += ` .select()\n`;
|
122
|
+
code += ` .single();\n`;
|
123
|
+
code += ` if (error) {\n`;
|
124
|
+
code += ` console.error('Error creating ${tableName}:', error);\n`;
|
125
|
+
code += ` throw new Error(\`Failed to create ${tableName}: \${error.message}\`);\n`;
|
126
|
+
code += ` }\n`;
|
127
|
+
code += ` if (!createdData) {\n`;
|
128
|
+
code += ` throw new Error('No data returned after creation');\n`;
|
129
|
+
code += ` }\n`;
|
130
|
+
code += ` return createdData as ${tableName};\n`;
|
131
|
+
code += ` } catch (error) {\n`;
|
132
|
+
code += ` console.error('Unexpected error in insert${capitalizedName}Row:', error);\n`;
|
133
|
+
code += ` throw error;\n`;
|
134
|
+
code += ` }\n`;
|
135
|
+
code += `}\n\n`;
|
136
|
+
// 更新関数
|
137
|
+
code += `/** 更新 */\n`;
|
138
|
+
code += `export async function update${capitalizedName}Row({ id, data }: { id: string; data: Partial<Omit<${tableName}, 'id' | 'created_at'>> }): Promise<${tableName}> {\n`;
|
139
|
+
code += ` if (!id) {\n`;
|
140
|
+
code += ` throw new Error('ID is required for update');\n`;
|
141
|
+
code += ` }\n`;
|
142
|
+
code += ` if (!data || Object.keys(data).length === 0) {\n`;
|
143
|
+
code += ` throw new Error('Update data is required');\n`;
|
144
|
+
code += ` }\n`;
|
145
|
+
code += ` try {\n`;
|
146
|
+
code += ` const { data: updatedData, error } = await supabase\n`;
|
147
|
+
code += ` .from('${tableName}')\n`;
|
148
|
+
code += ` .update(data)\n`;
|
149
|
+
code += ` .eq('id', id)\n`;
|
150
|
+
code += ` .select()\n`;
|
151
|
+
code += ` .single();\n`;
|
152
|
+
code += ` if (error) {\n`;
|
153
|
+
code += ` if (error.code === 'PGRST116') {\n`;
|
154
|
+
code += ` throw new Error(\`${tableName} with ID \${id} not found\`);\n`;
|
155
|
+
code += ` }\n`;
|
156
|
+
code += ` console.error('Error updating ${tableName}:', error);\n`;
|
157
|
+
code += ` throw new Error(\`Failed to update ${tableName} with ID \${id}: \${error.message}\`);\n`;
|
158
|
+
code += ` }\n`;
|
159
|
+
code += ` if (!updatedData) {\n`;
|
160
|
+
code += ` throw new Error(\`${tableName} with ID \${id} not found\`);\n`;
|
161
|
+
code += ` }\n`;
|
162
|
+
code += ` return updatedData as ${tableName};\n`;
|
163
|
+
code += ` } catch (error) {\n`;
|
164
|
+
code += ` console.error('Unexpected error in update${capitalizedName}Row:', error);\n`;
|
165
|
+
code += ` throw error;\n`;
|
166
|
+
code += ` }\n`;
|
167
|
+
code += `}\n\n`;
|
168
|
+
// 削除関数
|
169
|
+
code += `/** 削除 */\n`;
|
170
|
+
code += `export async function delete${capitalizedName}Row({ id }: { id: string }): Promise<boolean> {\n`;
|
171
|
+
code += ` if (!id) {\n`;
|
172
|
+
code += ` throw new Error('ID is required for deletion');\n`;
|
173
|
+
code += ` }\n`;
|
174
|
+
code += ` try {\n`;
|
175
|
+
code += ` const { error } = await supabase\n`;
|
176
|
+
code += ` .from('${tableName}')\n`;
|
177
|
+
code += ` .delete()\n`;
|
178
|
+
code += ` .eq('id', id);\n`;
|
179
|
+
code += ` if (error) {\n`;
|
180
|
+
code += ` console.error('Error deleting ${tableName}:', error);\n`;
|
181
|
+
code += ` throw new Error(\`Failed to delete ${tableName} with ID \${id}: \${error.message}\`);\n`;
|
182
|
+
code += ` }\n`;
|
183
|
+
code += ` return true;\n`;
|
184
|
+
code += ` } catch (error) {\n`;
|
185
|
+
code += ` console.error('Unexpected error in delete${capitalizedName}Row:', error);\n`;
|
186
|
+
code += ` throw error;\n`;
|
187
|
+
code += ` }\n`;
|
188
|
+
code += `}\n\n`;
|
52
189
|
fs_1.default.writeFileSync(path_1.default.join(outDir, `${tableName}.ts`), code);
|
53
190
|
}
|
54
191
|
}
|
@@ -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
|
}
|
package/dist/index.js
CHANGED
@@ -161,13 +161,13 @@ const toUpperCamelCase = (str) => {
|
|
161
161
|
// CRUD template
|
162
162
|
const crudTemplate = (typeName, fields, isView) => {
|
163
163
|
const upperCamelTypeName = toUpperCamelCase(typeName);
|
164
|
-
const
|
165
|
-
const
|
166
|
-
const
|
167
|
-
const
|
164
|
+
const getByIdFunctionName = 'select' + upperCamelTypeName + 'RowById';
|
165
|
+
const getByFiltersFunctionName = 'select' + upperCamelTypeName + 'RowsWithFilters';
|
166
|
+
const getSingleByFiltersFunctionName = 'select' + upperCamelTypeName + 'SingleRowWithFilters';
|
167
|
+
const createFunctionName = 'insert' + upperCamelTypeName + 'Row';
|
168
168
|
const updateFunctionName = 'update' + upperCamelTypeName + 'Row';
|
169
169
|
const deleteFunctionName = 'delete' + upperCamelTypeName + 'Row';
|
170
|
-
const idType = fields.find((field) => field.name === 'id')?.type;
|
170
|
+
const idType = fields.find((field) => field.name === 'id')?.type || 'string';
|
171
171
|
const hasIdColumn = idType !== undefined; // Check if 'id' column exists
|
172
172
|
const exportHeaders = `// Supabase CRUD operations for ${typeName}
|
173
173
|
// This file is automatically generated. Do not edit it directly.
|
@@ -180,17 +180,29 @@ type Filters = Record<string, FilterTypesValue | FilterTypesValue[]>;
|
|
180
180
|
`;
|
181
181
|
const exportSelectById = `
|
182
182
|
// Read single row using id
|
183
|
-
async function ${
|
184
|
-
if (id
|
183
|
+
export async function ${getByIdFunctionName}({ id }: { id: ${idType} }): Promise<${typeName} | null> {
|
184
|
+
if (!id) {
|
185
|
+
throw new Error('ID is required');
|
186
|
+
}
|
187
|
+
try {
|
185
188
|
const result = await supabase
|
186
189
|
.from('${typeName.toLowerCase()}')
|
187
190
|
.select('*')
|
188
191
|
.eq('id', id)
|
189
192
|
.single();
|
193
|
+
|
194
|
+
if (result.error) {
|
195
|
+
if (result.error.code === 'PGRST116') {
|
196
|
+
return null;
|
197
|
+
}
|
198
|
+
throw new Error(\`Failed to fetch ${typeName}: \${result.error.message}\`);
|
199
|
+
}
|
200
|
+
|
190
201
|
return result.data as ${typeName};
|
202
|
+
} catch (error) {
|
203
|
+
console.error('Error in ${getByIdFunctionName}:', error);
|
204
|
+
throw error;
|
191
205
|
}
|
192
|
-
const result = await supabase.from('${typeName.toLowerCase()}').select('*');
|
193
|
-
return result.data as ${typeName}[];
|
194
206
|
}
|
195
207
|
`;
|
196
208
|
const exportSelectQueries = `
|
@@ -248,64 +260,138 @@ function applyFilters(query: any, filters: Filters): any {
|
|
248
260
|
}
|
249
261
|
|
250
262
|
// Read multiple rows with dynamic filters
|
251
|
-
async function ${
|
252
|
-
|
253
|
-
|
263
|
+
export async function ${getByFiltersFunctionName}({ filters }: { filters: Filters }): Promise<${typeName}[]> {
|
264
|
+
try {
|
265
|
+
let query = supabase.from('${typeName.toLowerCase()}').select('*');
|
266
|
+
query = applyFilters(query, filters);
|
254
267
|
|
255
|
-
|
256
|
-
|
268
|
+
const result = await query;
|
269
|
+
|
270
|
+
if (result.error) {
|
271
|
+
throw new Error(\`Failed to fetch ${typeName}: \${result.error.message}\`);
|
272
|
+
}
|
273
|
+
|
274
|
+
return (result.data as unknown as ${typeName}[]) || [];
|
275
|
+
} catch (error) {
|
276
|
+
console.error('Error in ${getByFiltersFunctionName}:', error);
|
277
|
+
throw error;
|
278
|
+
}
|
257
279
|
}
|
258
280
|
|
259
281
|
// Read a single row with dynamic filters
|
260
|
-
async function ${
|
261
|
-
|
262
|
-
|
282
|
+
export async function ${getSingleByFiltersFunctionName}({ filters }: { filters: Filters }): Promise<${typeName} | null> {
|
283
|
+
try {
|
284
|
+
let query = supabase.from('${typeName.toLowerCase()}').select('*');
|
285
|
+
query = applyFilters(query, filters).single();
|
263
286
|
|
264
|
-
|
265
|
-
|
287
|
+
const result = await query;
|
288
|
+
|
289
|
+
if (result.error) {
|
290
|
+
if (result.error.code === 'PGRST116') {
|
291
|
+
return null;
|
292
|
+
}
|
293
|
+
throw new Error(\`Failed to fetch ${typeName}: \${result.error.message}\`);
|
294
|
+
}
|
295
|
+
|
296
|
+
return result.data as unknown as ${typeName};
|
297
|
+
} catch (error) {
|
298
|
+
console.error('Error in ${getSingleByFiltersFunctionName}:', error);
|
299
|
+
throw error;
|
300
|
+
}
|
266
301
|
}
|
267
302
|
`;
|
268
303
|
const exportInsertOperation = isView ? '' :
|
269
304
|
`
|
270
305
|
// Create Function
|
271
|
-
async function ${
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
306
|
+
export async function ${createFunctionName}({ data }: { data: TablesInsert<'${typeName}'> }): Promise<${typeName}> {
|
307
|
+
if (!data) {
|
308
|
+
throw new Error('Data is required for creation');
|
309
|
+
}
|
310
|
+
try {
|
311
|
+
const result = await supabase
|
312
|
+
.from('${typeName}')
|
313
|
+
.insert([data])
|
314
|
+
.select()
|
315
|
+
.single();
|
277
316
|
|
278
|
-
|
317
|
+
if (result.error) {
|
318
|
+
throw new Error(\`Failed to create ${typeName}: \${result.error.message}\`);
|
319
|
+
}
|
320
|
+
|
321
|
+
if (!result.data) {
|
322
|
+
throw new Error('No data returned after creation');
|
323
|
+
}
|
324
|
+
|
279
325
|
return result.data as ${typeName};
|
326
|
+
} catch (error) {
|
327
|
+
console.error('Error in ${createFunctionName}:', error);
|
328
|
+
throw error;
|
280
329
|
}
|
281
|
-
throw new Error('Failed to insert data');
|
282
330
|
}
|
283
331
|
`;
|
284
332
|
const exportUpdateOperation = isView ? '' :
|
285
333
|
`
|
286
334
|
// Update Function
|
287
|
-
async function ${updateFunctionName}(data
|
288
|
-
|
289
|
-
|
335
|
+
export async function ${updateFunctionName}({ id, data }: { id: ${idType}; data: TablesUpdate<'${typeName}'> }): Promise<${typeName}> {
|
336
|
+
if (!id) {
|
337
|
+
throw new Error('ID is required for update');
|
338
|
+
}
|
339
|
+
if (!data || Object.keys(data).length === 0) {
|
340
|
+
throw new Error('Update data is required');
|
341
|
+
}
|
342
|
+
try {
|
343
|
+
const result = await supabase
|
344
|
+
.from('${typeName.toLowerCase()}')
|
345
|
+
.update(data)
|
346
|
+
.eq('id', id)
|
347
|
+
.select()
|
348
|
+
.single();
|
349
|
+
|
350
|
+
if (result.error) {
|
351
|
+
if (result.error.code === 'PGRST116') {
|
352
|
+
throw new Error(\`${typeName} with ID \${id} not found\`);
|
353
|
+
}
|
354
|
+
throw new Error(\`Failed to update ${typeName}: \${result.error.message}\`);
|
355
|
+
}
|
356
|
+
|
357
|
+
if (!result.data) {
|
358
|
+
throw new Error(\`${typeName} with ID \${id} not found\`);
|
359
|
+
}
|
360
|
+
|
290
361
|
return result.data as ${typeName};
|
362
|
+
} catch (error) {
|
363
|
+
console.error('Error in ${updateFunctionName}:', error);
|
364
|
+
throw error;
|
291
365
|
}
|
292
|
-
throw new Error('Failed to update data');
|
293
366
|
}
|
294
367
|
`;
|
295
368
|
const exportDeleteOperation = isView ? '' :
|
296
369
|
`
|
297
370
|
// Delete Function
|
298
|
-
async function ${deleteFunctionName}(id: ${idType}): Promise
|
299
|
-
|
300
|
-
|
301
|
-
|
371
|
+
export async function ${deleteFunctionName}({ id }: { id: ${idType} }): Promise<boolean> {
|
372
|
+
if (!id) {
|
373
|
+
throw new Error('ID is required for deletion');
|
374
|
+
}
|
375
|
+
try {
|
376
|
+
const result = await supabase
|
377
|
+
.from('${typeName.toLowerCase()}')
|
378
|
+
.delete()
|
379
|
+
.eq('id', id);
|
380
|
+
|
381
|
+
if (result.error) {
|
382
|
+
throw new Error(\`Failed to delete ${typeName}: \${result.error.message}\`);
|
383
|
+
}
|
384
|
+
|
385
|
+
return true;
|
386
|
+
} catch (error) {
|
387
|
+
console.error('Error in ${deleteFunctionName}:', error);
|
388
|
+
throw error;
|
302
389
|
}
|
303
|
-
throw new Error('Failed to delete data');
|
304
390
|
}
|
305
391
|
`;
|
306
392
|
// Export all functions
|
307
393
|
const exportAll = `
|
308
|
-
|
394
|
+
// All functions are exported individually above
|
309
395
|
`;
|
310
396
|
// Return all the code
|
311
397
|
return exportHeaders + exportSelectQueries + (hasIdColumn ? exportSelectById : '') + exportInsertOperation + exportUpdateOperation + exportDeleteOperation + exportAll;
|
@@ -0,0 +1,220 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getTasksByFilters = getTasksByFilters;
|
4
|
+
exports.getTasksSingleByFilters = getTasksSingleByFilters;
|
5
|
+
exports.getTasksById = getTasksById;
|
6
|
+
exports.createTasks = createTasks;
|
7
|
+
exports.updateTasks = updateTasks;
|
8
|
+
exports.deleteTasks = deleteTasks;
|
9
|
+
exports.queryTasks = queryTasks;
|
10
|
+
// Supabase CRUD operations for tasks
|
11
|
+
// This file is automatically generated. Do not edit it directly.
|
12
|
+
const client_1 = require("../client");
|
13
|
+
// Function to apply filters to a query
|
14
|
+
function applyFilters(query, filters) {
|
15
|
+
for (const [key, value] of Object.entries(filters)) {
|
16
|
+
if (Array.isArray(value)) {
|
17
|
+
query = query.in(key, value); // Use 'in' for array values
|
18
|
+
}
|
19
|
+
else if (typeof value === 'object' && value !== null) {
|
20
|
+
for (const [operator, val] of Object.entries(value)) {
|
21
|
+
switch (operator) {
|
22
|
+
case 'eq':
|
23
|
+
query = query.eq(key, val);
|
24
|
+
break;
|
25
|
+
case 'neq':
|
26
|
+
query = query.neq(key, val);
|
27
|
+
break;
|
28
|
+
case 'like':
|
29
|
+
query = query.like(key, val);
|
30
|
+
break;
|
31
|
+
case 'ilike':
|
32
|
+
query = query.ilike(key, val);
|
33
|
+
break;
|
34
|
+
case 'lt':
|
35
|
+
query = query.lt(key, val);
|
36
|
+
break;
|
37
|
+
case 'lte':
|
38
|
+
query = query.lte(key, val);
|
39
|
+
break;
|
40
|
+
case 'gte':
|
41
|
+
query = query.gte(key, val);
|
42
|
+
break;
|
43
|
+
case 'gt':
|
44
|
+
query = query.gt(key, val);
|
45
|
+
break;
|
46
|
+
case 'contains':
|
47
|
+
query = query.contains(key, val);
|
48
|
+
break;
|
49
|
+
case 'contains_any':
|
50
|
+
query = query.contains_any(key, val);
|
51
|
+
break;
|
52
|
+
case 'contains_all':
|
53
|
+
query = query.contains_all(key, val);
|
54
|
+
break;
|
55
|
+
// Add more operators as needed
|
56
|
+
default:
|
57
|
+
throw new Error('Unsupported operator: ' + operator);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
else {
|
62
|
+
query = query.eq(key, value); // Default to 'eq' for simple values
|
63
|
+
}
|
64
|
+
}
|
65
|
+
return query;
|
66
|
+
}
|
67
|
+
// Read multiple rows with dynamic filters
|
68
|
+
async function getTasksByFilters({ filters }) {
|
69
|
+
try {
|
70
|
+
let query = client_1.supabase.from('tasks').select('*');
|
71
|
+
query = applyFilters(query, filters);
|
72
|
+
const result = await query;
|
73
|
+
if (result.error) {
|
74
|
+
throw new Error(`Failed to fetch tasks: ${result.error.message}`);
|
75
|
+
}
|
76
|
+
return result.data || [];
|
77
|
+
}
|
78
|
+
catch (error) {
|
79
|
+
console.error('Error in getTasksByFilters:', error);
|
80
|
+
throw error;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
// Read a single row with dynamic filters
|
84
|
+
async function getTasksSingleByFilters({ filters }) {
|
85
|
+
try {
|
86
|
+
let query = client_1.supabase.from('tasks').select('*');
|
87
|
+
query = applyFilters(query, filters).single();
|
88
|
+
const result = await query;
|
89
|
+
if (result.error) {
|
90
|
+
if (result.error.code === 'PGRST116') {
|
91
|
+
return null;
|
92
|
+
}
|
93
|
+
throw new Error(`Failed to fetch tasks: ${result.error.message}`);
|
94
|
+
}
|
95
|
+
return result.data;
|
96
|
+
}
|
97
|
+
catch (error) {
|
98
|
+
console.error('Error in getTasksSingleByFilters:', error);
|
99
|
+
throw error;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
// Read single row using id
|
103
|
+
async function getTasksById({ id }) {
|
104
|
+
if (!id) {
|
105
|
+
throw new Error('ID is required');
|
106
|
+
}
|
107
|
+
try {
|
108
|
+
const result = await client_1.supabase
|
109
|
+
.from('tasks')
|
110
|
+
.select('*')
|
111
|
+
.eq('id', id)
|
112
|
+
.single();
|
113
|
+
if (result.error) {
|
114
|
+
if (result.error.code === 'PGRST116') {
|
115
|
+
return null;
|
116
|
+
}
|
117
|
+
throw new Error(`Failed to fetch tasks: ${result.error.message}`);
|
118
|
+
}
|
119
|
+
return result.data;
|
120
|
+
}
|
121
|
+
catch (error) {
|
122
|
+
console.error('Error in getTasksById:', error);
|
123
|
+
throw error;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
// Create Function
|
127
|
+
async function createTasks({ data }) {
|
128
|
+
if (!data) {
|
129
|
+
throw new Error('Data is required for creation');
|
130
|
+
}
|
131
|
+
try {
|
132
|
+
const result = await client_1.supabase
|
133
|
+
.from('tasks')
|
134
|
+
.insert([data])
|
135
|
+
.select()
|
136
|
+
.single();
|
137
|
+
if (result.error) {
|
138
|
+
throw new Error(`Failed to create tasks: ${result.error.message}`);
|
139
|
+
}
|
140
|
+
if (!result.data) {
|
141
|
+
throw new Error('No data returned after creation');
|
142
|
+
}
|
143
|
+
return result.data;
|
144
|
+
}
|
145
|
+
catch (error) {
|
146
|
+
console.error('Error in createTasks:', error);
|
147
|
+
throw error;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
// Update Function
|
151
|
+
async function updateTasks({ id, data }) {
|
152
|
+
if (!id) {
|
153
|
+
throw new Error('ID is required for update');
|
154
|
+
}
|
155
|
+
if (!data || Object.keys(data).length === 0) {
|
156
|
+
throw new Error('Update data is required');
|
157
|
+
}
|
158
|
+
try {
|
159
|
+
const result = await client_1.supabase
|
160
|
+
.from('tasks')
|
161
|
+
.update(data)
|
162
|
+
.eq('id', id)
|
163
|
+
.select()
|
164
|
+
.single();
|
165
|
+
if (result.error) {
|
166
|
+
if (result.error.code === 'PGRST116') {
|
167
|
+
throw new Error(`tasks with ID ${id} not found`);
|
168
|
+
}
|
169
|
+
throw new Error(`Failed to update tasks: ${result.error.message}`);
|
170
|
+
}
|
171
|
+
if (!result.data) {
|
172
|
+
throw new Error(`tasks with ID ${id} not found`);
|
173
|
+
}
|
174
|
+
return result.data;
|
175
|
+
}
|
176
|
+
catch (error) {
|
177
|
+
console.error('Error in updateTasks:', error);
|
178
|
+
throw error;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
// Delete Function
|
182
|
+
async function deleteTasks({ id }) {
|
183
|
+
if (!id) {
|
184
|
+
throw new Error('ID is required for deletion');
|
185
|
+
}
|
186
|
+
try {
|
187
|
+
const result = await client_1.supabase
|
188
|
+
.from('tasks')
|
189
|
+
.delete()
|
190
|
+
.eq('id', id);
|
191
|
+
if (result.error) {
|
192
|
+
throw new Error(`Failed to delete tasks: ${result.error.message}`);
|
193
|
+
}
|
194
|
+
return true;
|
195
|
+
}
|
196
|
+
catch (error) {
|
197
|
+
console.error('Error in deleteTasks:', error);
|
198
|
+
throw error;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
// Custom query function
|
202
|
+
async function queryTasks({ query }) {
|
203
|
+
if (!query) {
|
204
|
+
throw new Error('Query is required');
|
205
|
+
}
|
206
|
+
try {
|
207
|
+
const result = await client_1.supabase
|
208
|
+
.from('tasks')
|
209
|
+
.select(query);
|
210
|
+
if (result.error) {
|
211
|
+
throw new Error(`Failed to execute query: ${result.error.message}`);
|
212
|
+
}
|
213
|
+
return result.data || [];
|
214
|
+
}
|
215
|
+
catch (error) {
|
216
|
+
console.error('Error in queryTasks:', error);
|
217
|
+
throw error;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
// All functions are exported individually above
|
@@ -0,0 +1,220 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getWorkflowsByFilters = getWorkflowsByFilters;
|
4
|
+
exports.getWorkflowsSingleByFilters = getWorkflowsSingleByFilters;
|
5
|
+
exports.getWorkflowsById = getWorkflowsById;
|
6
|
+
exports.createWorkflows = createWorkflows;
|
7
|
+
exports.updateWorkflows = updateWorkflows;
|
8
|
+
exports.deleteWorkflows = deleteWorkflows;
|
9
|
+
exports.queryWorkflows = queryWorkflows;
|
10
|
+
// Supabase CRUD operations for workflows
|
11
|
+
// This file is automatically generated. Do not edit it directly.
|
12
|
+
const client_1 = require("../client");
|
13
|
+
// Function to apply filters to a query
|
14
|
+
function applyFilters(query, filters) {
|
15
|
+
for (const [key, value] of Object.entries(filters)) {
|
16
|
+
if (Array.isArray(value)) {
|
17
|
+
query = query.in(key, value); // Use 'in' for array values
|
18
|
+
}
|
19
|
+
else if (typeof value === 'object' && value !== null) {
|
20
|
+
for (const [operator, val] of Object.entries(value)) {
|
21
|
+
switch (operator) {
|
22
|
+
case 'eq':
|
23
|
+
query = query.eq(key, val);
|
24
|
+
break;
|
25
|
+
case 'neq':
|
26
|
+
query = query.neq(key, val);
|
27
|
+
break;
|
28
|
+
case 'like':
|
29
|
+
query = query.like(key, val);
|
30
|
+
break;
|
31
|
+
case 'ilike':
|
32
|
+
query = query.ilike(key, val);
|
33
|
+
break;
|
34
|
+
case 'lt':
|
35
|
+
query = query.lt(key, val);
|
36
|
+
break;
|
37
|
+
case 'lte':
|
38
|
+
query = query.lte(key, val);
|
39
|
+
break;
|
40
|
+
case 'gte':
|
41
|
+
query = query.gte(key, val);
|
42
|
+
break;
|
43
|
+
case 'gt':
|
44
|
+
query = query.gt(key, val);
|
45
|
+
break;
|
46
|
+
case 'contains':
|
47
|
+
query = query.contains(key, val);
|
48
|
+
break;
|
49
|
+
case 'contains_any':
|
50
|
+
query = query.contains_any(key, val);
|
51
|
+
break;
|
52
|
+
case 'contains_all':
|
53
|
+
query = query.contains_all(key, val);
|
54
|
+
break;
|
55
|
+
// Add more operators as needed
|
56
|
+
default:
|
57
|
+
throw new Error('Unsupported operator: ' + operator);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
else {
|
62
|
+
query = query.eq(key, value); // Default to 'eq' for simple values
|
63
|
+
}
|
64
|
+
}
|
65
|
+
return query;
|
66
|
+
}
|
67
|
+
// Read multiple rows with dynamic filters
|
68
|
+
async function getWorkflowsByFilters({ filters }) {
|
69
|
+
try {
|
70
|
+
let query = client_1.supabase.from('workflows').select('*');
|
71
|
+
query = applyFilters(query, filters);
|
72
|
+
const result = await query;
|
73
|
+
if (result.error) {
|
74
|
+
throw new Error(`Failed to fetch workflows: ${result.error.message}`);
|
75
|
+
}
|
76
|
+
return result.data || [];
|
77
|
+
}
|
78
|
+
catch (error) {
|
79
|
+
console.error('Error in getWorkflowsByFilters:', error);
|
80
|
+
throw error;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
// Read a single row with dynamic filters
|
84
|
+
async function getWorkflowsSingleByFilters({ filters }) {
|
85
|
+
try {
|
86
|
+
let query = client_1.supabase.from('workflows').select('*');
|
87
|
+
query = applyFilters(query, filters).single();
|
88
|
+
const result = await query;
|
89
|
+
if (result.error) {
|
90
|
+
if (result.error.code === 'PGRST116') {
|
91
|
+
return null;
|
92
|
+
}
|
93
|
+
throw new Error(`Failed to fetch workflows: ${result.error.message}`);
|
94
|
+
}
|
95
|
+
return result.data;
|
96
|
+
}
|
97
|
+
catch (error) {
|
98
|
+
console.error('Error in getWorkflowsSingleByFilters:', error);
|
99
|
+
throw error;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
// Read single row using id
|
103
|
+
async function getWorkflowsById({ id }) {
|
104
|
+
if (!id) {
|
105
|
+
throw new Error('ID is required');
|
106
|
+
}
|
107
|
+
try {
|
108
|
+
const result = await client_1.supabase
|
109
|
+
.from('workflows')
|
110
|
+
.select('*')
|
111
|
+
.eq('id', id)
|
112
|
+
.single();
|
113
|
+
if (result.error) {
|
114
|
+
if (result.error.code === 'PGRST116') {
|
115
|
+
return null;
|
116
|
+
}
|
117
|
+
throw new Error(`Failed to fetch workflows: ${result.error.message}`);
|
118
|
+
}
|
119
|
+
return result.data;
|
120
|
+
}
|
121
|
+
catch (error) {
|
122
|
+
console.error('Error in getWorkflowsById:', error);
|
123
|
+
throw error;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
// Create Function
|
127
|
+
async function createWorkflows({ data }) {
|
128
|
+
if (!data) {
|
129
|
+
throw new Error('Data is required for creation');
|
130
|
+
}
|
131
|
+
try {
|
132
|
+
const result = await client_1.supabase
|
133
|
+
.from('workflows')
|
134
|
+
.insert([data])
|
135
|
+
.select()
|
136
|
+
.single();
|
137
|
+
if (result.error) {
|
138
|
+
throw new Error(`Failed to create workflows: ${result.error.message}`);
|
139
|
+
}
|
140
|
+
if (!result.data) {
|
141
|
+
throw new Error('No data returned after creation');
|
142
|
+
}
|
143
|
+
return result.data;
|
144
|
+
}
|
145
|
+
catch (error) {
|
146
|
+
console.error('Error in createWorkflows:', error);
|
147
|
+
throw error;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
// Update Function
|
151
|
+
async function updateWorkflows({ id, data }) {
|
152
|
+
if (!id) {
|
153
|
+
throw new Error('ID is required for update');
|
154
|
+
}
|
155
|
+
if (!data || Object.keys(data).length === 0) {
|
156
|
+
throw new Error('Update data is required');
|
157
|
+
}
|
158
|
+
try {
|
159
|
+
const result = await client_1.supabase
|
160
|
+
.from('workflows')
|
161
|
+
.update(data)
|
162
|
+
.eq('id', id)
|
163
|
+
.select()
|
164
|
+
.single();
|
165
|
+
if (result.error) {
|
166
|
+
if (result.error.code === 'PGRST116') {
|
167
|
+
throw new Error(`workflows with ID ${id} not found`);
|
168
|
+
}
|
169
|
+
throw new Error(`Failed to update workflows: ${result.error.message}`);
|
170
|
+
}
|
171
|
+
if (!result.data) {
|
172
|
+
throw new Error(`workflows with ID ${id} not found`);
|
173
|
+
}
|
174
|
+
return result.data;
|
175
|
+
}
|
176
|
+
catch (error) {
|
177
|
+
console.error('Error in updateWorkflows:', error);
|
178
|
+
throw error;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
// Delete Function
|
182
|
+
async function deleteWorkflows({ id }) {
|
183
|
+
if (!id) {
|
184
|
+
throw new Error('ID is required for deletion');
|
185
|
+
}
|
186
|
+
try {
|
187
|
+
const result = await client_1.supabase
|
188
|
+
.from('workflows')
|
189
|
+
.delete()
|
190
|
+
.eq('id', id);
|
191
|
+
if (result.error) {
|
192
|
+
throw new Error(`Failed to delete workflows: ${result.error.message}`);
|
193
|
+
}
|
194
|
+
return true;
|
195
|
+
}
|
196
|
+
catch (error) {
|
197
|
+
console.error('Error in deleteWorkflows:', error);
|
198
|
+
throw error;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
// Custom query function
|
202
|
+
async function queryWorkflows({ query }) {
|
203
|
+
if (!query) {
|
204
|
+
throw new Error('Query is required');
|
205
|
+
}
|
206
|
+
try {
|
207
|
+
const result = await client_1.supabase
|
208
|
+
.from('workflows')
|
209
|
+
.select(query);
|
210
|
+
if (result.error) {
|
211
|
+
throw new Error(`Failed to execute query: ${result.error.message}`);
|
212
|
+
}
|
213
|
+
return result.data || [];
|
214
|
+
}
|
215
|
+
catch (error) {
|
216
|
+
console.error('Error in queryWorkflows:', error);
|
217
|
+
throw error;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
// All functions are exported individually above
|
@@ -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