supatool 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -285
- package/dist/bin/helptext.js +4 -3
- package/dist/bin/supatool.js +182 -74
- package/dist/generator/client.js +1 -2
- package/dist/generator/crudGenerator.js +22 -23
- package/dist/generator/docGenerator.js +12 -13
- package/dist/generator/rlsGenerator.js +21 -21
- package/dist/generator/sqlGenerator.js +20 -21
- package/dist/generator/typeGenerator.js +6 -7
- package/dist/index.js +23 -23
- package/dist/parser/modelParser.js +3 -4
- package/dist/sync/config.js +10 -10
- package/dist/sync/definitionExtractor.js +489 -321
- package/dist/sync/fetchRemoteSchemas.js +59 -43
- package/dist/sync/generateMigration.js +42 -42
- package/dist/sync/parseLocalSchemas.js +14 -11
- package/dist/sync/seedGenerator.js +15 -14
- package/dist/sync/sync.js +75 -140
- package/dist/sync/utils.js +19 -7
- package/dist/sync/writeSchema.js +22 -22
- package/package.json +8 -8
|
@@ -4,21 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateRlsSqlFromModel = generateRlsSqlFromModel;
|
|
7
|
-
// RLS
|
|
8
|
-
// Todo:
|
|
7
|
+
// RLS/security policy SQL auto-generation (minimal template)
|
|
8
|
+
// Todo: make schema configurable
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param model
|
|
14
|
-
* @param outPath
|
|
12
|
+
* Generate RLS/security policy SQL from model
|
|
13
|
+
* @param model Model object
|
|
14
|
+
* @param outPath Output path
|
|
15
15
|
*/
|
|
16
16
|
function generateRlsSqlFromModel(model, outPath) {
|
|
17
17
|
const dir = path_1.default.dirname(outPath);
|
|
18
18
|
if (!fs_1.default.existsSync(dir)) {
|
|
19
19
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
20
20
|
}
|
|
21
|
-
// dataSchema/models
|
|
21
|
+
// dataSchema/models: get table list
|
|
22
22
|
let tables = [];
|
|
23
23
|
if (Array.isArray(model.dataSchema)) {
|
|
24
24
|
tables = model.dataSchema.map((t) => ({ tableName: t.tableName || t.raw, ...t }));
|
|
@@ -32,11 +32,11 @@ function generateRlsSqlFromModel(model, outPath) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
let sql = '--
|
|
35
|
+
let sql = '-- Auto-generated: RLS/security policy DDL\n\n';
|
|
36
36
|
const security = model.security || {};
|
|
37
|
-
// roles
|
|
37
|
+
// If roles defined, auto-generate role master and user_roles DDL
|
|
38
38
|
if (model.roles && Array.isArray(model.roles)) {
|
|
39
|
-
sql += '--
|
|
39
|
+
sql += '-- Role master and user_roles DDL (auto-generated)\n';
|
|
40
40
|
sql += `CREATE TABLE IF NOT EXISTS m_roles (\n id uuid PRIMARY KEY,\n name text NOT NULL\n);\n\n`;
|
|
41
41
|
sql += `CREATE TABLE IF NOT EXISTS user_roles (\n id uuid PRIMARY KEY,\n user_id uuid NOT NULL,\n role_id uuid NOT NULL\n);\n\n`;
|
|
42
42
|
for (const role of model.roles) {
|
|
@@ -44,48 +44,48 @@ function generateRlsSqlFromModel(model, outPath) {
|
|
|
44
44
|
}
|
|
45
45
|
sql += '\n';
|
|
46
46
|
}
|
|
47
|
-
// DB
|
|
47
|
+
// DB function
|
|
48
48
|
if (security.functions) {
|
|
49
49
|
for (const [fnName, fn] of Object.entries(security.functions)) {
|
|
50
50
|
const f = fn;
|
|
51
|
-
const useTemplate = f.use_template !== false; //
|
|
51
|
+
const useTemplate = f.use_template !== false; // default true
|
|
52
52
|
const templateType = f.template_type || 'simple';
|
|
53
53
|
let sqlBody = f.sql;
|
|
54
54
|
if (!sqlBody && useTemplate) {
|
|
55
|
-
// template_type
|
|
55
|
+
// template_type takes precedence
|
|
56
56
|
if (templateType) {
|
|
57
57
|
sqlBody = getFunctionTemplate(fnName, templateType, model);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
// fallback: roles
|
|
60
|
+
// fallback: if roles defined use user_roles template
|
|
61
61
|
if (!sqlBody) {
|
|
62
62
|
if (model.roles && Array.isArray(model.roles)) {
|
|
63
|
-
sqlBody = `CREATE FUNCTION ${fnName}() RETURNS text AS $$\nBEGIN\n --
|
|
63
|
+
sqlBody = `CREATE FUNCTION ${fnName}() RETURNS text AS $$\nBEGIN\n -- Return current user role (template)\n RETURN (SELECT r.name FROM user_roles ur JOIN m_roles r ON ur.role_id = r.id WHERE ur.user_id = current_setting('request.jwt.claim.sub', true)::uuid LIMIT 1);\nEND;\n$$ LANGUAGE plpgsql;`;
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
|
-
sqlBody = `CREATE FUNCTION ${fnName}() RETURNS text AS $$\nBEGIN\n -- TODO:
|
|
66
|
+
sqlBody = `CREATE FUNCTION ${fnName}() RETURNS text AS $$\nBEGIN\n -- TODO: implement logic\n RETURN 'admin';\nEND;\n$$ LANGUAGE plpgsql;`;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
sql += `-- ${fnName}
|
|
69
|
+
sql += `-- ${fnName} function: get user role\n`;
|
|
70
70
|
sql += `${sqlBody}\n\n`;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
// RLS
|
|
73
|
+
// RLS policies
|
|
74
74
|
if (security.policies) {
|
|
75
75
|
for (const [tableName, tablePolicies] of Object.entries(security.policies)) {
|
|
76
76
|
const p = tablePolicies;
|
|
77
77
|
for (const [action, policy] of Object.entries(p)) {
|
|
78
78
|
const pol = policy;
|
|
79
|
-
const useTemplate = pol.use_template !== false; //
|
|
79
|
+
const useTemplate = pol.use_template !== false; // default true
|
|
80
80
|
const templateType = pol.template_type || 'simple';
|
|
81
81
|
let usingCond = pol.using;
|
|
82
82
|
if (!usingCond && useTemplate && pol.role && Array.isArray(pol.role)) {
|
|
83
83
|
usingCond = getPolicyTemplate(pol.role, templateType);
|
|
84
84
|
}
|
|
85
85
|
if (!usingCond) {
|
|
86
|
-
usingCond = 'true -- TODO:
|
|
86
|
+
usingCond = 'true -- TODO: write appropriate condition';
|
|
87
87
|
}
|
|
88
|
-
sql += `-- ${tableName}
|
|
88
|
+
sql += `-- ${tableName} table RLS policy for ${action}\n`;
|
|
89
89
|
sql += `ALTER TABLE ${tableName} ENABLE ROW LEVEL SECURITY;\n`;
|
|
90
90
|
sql += `CREATE POLICY ${tableName}_${action}_policy ON ${tableName}\n FOR ${action.toUpperCase()}\n USING (${usingCond});\n\n`;
|
|
91
91
|
}
|
|
@@ -93,7 +93,7 @@ function generateRlsSqlFromModel(model, outPath) {
|
|
|
93
93
|
}
|
|
94
94
|
fs_1.default.writeFileSync(outPath, sql);
|
|
95
95
|
}
|
|
96
|
-
//
|
|
96
|
+
// Template switcher function
|
|
97
97
|
function getFunctionTemplate(fnName, type, model) {
|
|
98
98
|
const templatePath = path_1.default.join(__dirname, '../templates/rls', `function_${type}.sql`);
|
|
99
99
|
let template = fs_1.default.readFileSync(templatePath, 'utf-8');
|
|
@@ -4,21 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateSqlFromModel = generateSqlFromModel;
|
|
7
|
-
// SQL
|
|
8
|
-
// 日本語コメント
|
|
7
|
+
// SQL auto-generation (table・relation minimal template)
|
|
9
8
|
const path_1 = __importDefault(require("path"));
|
|
10
9
|
const fs_1 = __importDefault(require("fs"));
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param model
|
|
14
|
-
* @param outPath
|
|
11
|
+
* Generate table definitions and relation SQL from model
|
|
12
|
+
* @param model Model object
|
|
13
|
+
* @param outPath Output path
|
|
15
14
|
*/
|
|
16
15
|
function generateSqlFromModel(model, outPath) {
|
|
17
16
|
const dir = path_1.default.dirname(outPath);
|
|
18
17
|
if (!fs_1.default.existsSync(dir)) {
|
|
19
18
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
20
19
|
}
|
|
21
|
-
// dataSchema/models
|
|
20
|
+
// dataSchema/models: get table list
|
|
22
21
|
let tables = [];
|
|
23
22
|
if (Array.isArray(model.dataSchema)) {
|
|
24
23
|
tables = model.dataSchema.map((t) => ({ tableName: t.tableName || t.raw, ...t }));
|
|
@@ -32,16 +31,16 @@ function generateSqlFromModel(model, outPath) {
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
|
-
let sql = '--
|
|
34
|
+
let sql = '-- Auto-generated: table・relation DDL\n\n';
|
|
36
35
|
for (const tableObj of tables) {
|
|
37
36
|
const t = tableObj;
|
|
38
37
|
const tableName = tableObj.tableName;
|
|
39
38
|
if (t.skipCreate) {
|
|
40
|
-
// auth.users
|
|
41
|
-
sql += `-- [skip] ${tableName}
|
|
39
|
+
// Skip-create tables (e.g. auth.users) noted in comment
|
|
40
|
+
sql += `-- [skip] ${tableName} (skip: Supabase built-in etc.)\n`;
|
|
42
41
|
continue;
|
|
43
42
|
}
|
|
44
|
-
//
|
|
43
|
+
// Auto-naming for user_id columns
|
|
45
44
|
const userIdFields = Object.entries(t.fields || {}).filter(([colName, col]) => {
|
|
46
45
|
const c = col;
|
|
47
46
|
return (colName === 'user_id' || (c.ref && c.ref.endsWith('user_profiles.id')));
|
|
@@ -53,13 +52,13 @@ function generateSqlFromModel(model, outPath) {
|
|
|
53
52
|
for (const [colName, col] of Object.entries(t.fields || {})) {
|
|
54
53
|
const c = col;
|
|
55
54
|
let actualColName = colName;
|
|
56
|
-
//
|
|
55
|
+
// Apply user_id column naming rule
|
|
57
56
|
if ((colName !== 'user_id') && c.ref && c.ref.endsWith('user_profiles.id')) {
|
|
58
57
|
if (userIdCount === 1) {
|
|
59
58
|
actualColName = 'user_id';
|
|
60
59
|
}
|
|
61
60
|
else {
|
|
62
|
-
//
|
|
61
|
+
// ref_table_user_id
|
|
63
62
|
const refTable = c.ref.split('.')[0];
|
|
64
63
|
actualColName = `${refTable}_user_id`;
|
|
65
64
|
}
|
|
@@ -71,14 +70,14 @@ function generateSqlFromModel(model, outPath) {
|
|
|
71
70
|
def += ` DEFAULT ${c.default}`;
|
|
72
71
|
colDefs.push(def);
|
|
73
72
|
}
|
|
74
|
-
//
|
|
73
|
+
// Primary key constraint (with constraint name)
|
|
75
74
|
const pkCols = Object.entries(t.fields || {})
|
|
76
75
|
.filter(([_, col]) => col.primary)
|
|
77
76
|
.map(([colName, _]) => colName);
|
|
78
77
|
if (pkCols.length > 0) {
|
|
79
78
|
tableConstraints.push(` CONSTRAINT ${tableName}_pkey PRIMARY KEY (${pkCols.join(', ')})`);
|
|
80
79
|
}
|
|
81
|
-
//
|
|
80
|
+
// Unique constraint (with constraint name)
|
|
82
81
|
if (Array.isArray(t.uniques)) {
|
|
83
82
|
for (const unique of t.uniques) {
|
|
84
83
|
if (Array.isArray(unique.columns) && unique.name) {
|
|
@@ -86,7 +85,7 @@ function generateSqlFromModel(model, outPath) {
|
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
}
|
|
89
|
-
//
|
|
88
|
+
// Foreign key constraint (with constraint name)
|
|
90
89
|
if (Array.isArray(t.foreignKeys)) {
|
|
91
90
|
for (const fk of t.foreignKeys) {
|
|
92
91
|
if (fk.name && fk.columns && fk.refTable && fk.refColumns) {
|
|
@@ -99,7 +98,7 @@ function generateSqlFromModel(model, outPath) {
|
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
|
-
//
|
|
101
|
+
// Check constraint (with constraint name)
|
|
103
102
|
if (Array.isArray(t.checkConstraints)) {
|
|
104
103
|
for (const check of t.checkConstraints) {
|
|
105
104
|
if (check.name) {
|
|
@@ -110,7 +109,7 @@ function generateSqlFromModel(model, outPath) {
|
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
111
|
}
|
|
113
|
-
//
|
|
112
|
+
// Join column defs and table-level constraints
|
|
114
113
|
const defs = [...colDefs, ...tableConstraints];
|
|
115
114
|
sql += defs.join(',\n') + '\n);\n\n';
|
|
116
115
|
sql += '\n';
|
|
@@ -120,13 +119,13 @@ function generateSqlFromModel(model, outPath) {
|
|
|
120
119
|
function toSqlType(type, colName) {
|
|
121
120
|
if (!type)
|
|
122
121
|
return 'text';
|
|
123
|
-
//
|
|
122
|
+
// Timestamp columns always timestamptz
|
|
124
123
|
if (type === 'timestamp' || type === 'timestamptz' || colName.endsWith('_at'))
|
|
125
124
|
return 'timestamptz';
|
|
126
|
-
// vector
|
|
125
|
+
// vector type support
|
|
127
126
|
if (/^vector(\(\d+\))?$/i.test(type))
|
|
128
127
|
return type;
|
|
129
|
-
// extensions.vector
|
|
128
|
+
// extensions.vector -> vector
|
|
130
129
|
const extVectorMatch = type.match(/^extensions\.vector(\(\d+\))?$/i);
|
|
131
130
|
if (extVectorMatch) {
|
|
132
131
|
return `vector${extVectorMatch[1] || ''}`;
|
|
@@ -137,6 +136,6 @@ function toSqlType(type, colName) {
|
|
|
137
136
|
case 'int':
|
|
138
137
|
case 'integer': return 'integer';
|
|
139
138
|
case 'boolean': return 'boolean';
|
|
140
|
-
default: return type; //
|
|
139
|
+
default: return type; // unknown type: pass through
|
|
141
140
|
}
|
|
142
141
|
}
|
|
@@ -4,21 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateTypesFromModel = generateTypesFromModel;
|
|
7
|
-
// TypeScript
|
|
8
|
-
// 日本語コメント
|
|
7
|
+
// TypeScript type definition auto-generation (minimal template)
|
|
9
8
|
const path_1 = __importDefault(require("path"));
|
|
10
9
|
const fs_1 = __importDefault(require("fs"));
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param model
|
|
14
|
-
* @param outPath
|
|
11
|
+
* Generate TypeScript type definition file from model
|
|
12
|
+
* @param model Model object
|
|
13
|
+
* @param outPath Output path
|
|
15
14
|
*/
|
|
16
15
|
function generateTypesFromModel(model, outPath) {
|
|
17
16
|
const dir = path_1.default.dirname(outPath);
|
|
18
17
|
if (!fs_1.default.existsSync(dir)) {
|
|
19
18
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
20
19
|
}
|
|
21
|
-
let code = '//
|
|
20
|
+
let code = '// Auto-generated: model type definitions\n\n';
|
|
22
21
|
for (const m of model.models) {
|
|
23
22
|
const tables = m.tables || {};
|
|
24
23
|
for (const [tableName, table] of Object.entries(tables)) {
|
|
@@ -35,7 +34,7 @@ function generateTypesFromModel(model, outPath) {
|
|
|
35
34
|
}
|
|
36
35
|
fs_1.default.writeFileSync(outPath, code);
|
|
37
36
|
}
|
|
38
|
-
//
|
|
37
|
+
// Type conversion utility
|
|
39
38
|
function toTsType(type) {
|
|
40
39
|
if (!type)
|
|
41
40
|
return 'any';
|
package/dist/index.js
CHANGED
|
@@ -83,13 +83,13 @@ function main() {
|
|
|
83
83
|
const typeNode = typeAliasDecl.type;
|
|
84
84
|
if (typeNode.kind === typescript_1.SyntaxKind.TypeLiteral && typeName === 'Database') {
|
|
85
85
|
console.log('Found Database type, processing schemas...');
|
|
86
|
-
// Database
|
|
86
|
+
// Process all schemas inside Database type
|
|
87
87
|
return typeNode.members.flatMap((schemaMember) => {
|
|
88
88
|
if (schemaMember.name && schemaMember.type && schemaMember.type.kind === typescript_1.SyntaxKind.TypeLiteral) {
|
|
89
89
|
const schemaName = schemaMember.name.text;
|
|
90
90
|
console.log(`Processing schema: ${schemaName}`);
|
|
91
91
|
const schemaType = schemaMember.type;
|
|
92
|
-
//
|
|
92
|
+
// Process Tables and Views in schema
|
|
93
93
|
const tablesAndViewsType = schemaType.members.filter((member) => member.name && (member.name.text === 'Tables' || member.name.text === 'Views'));
|
|
94
94
|
return tablesAndViewsType.flatMap((tablesOrViewsType) => {
|
|
95
95
|
if (tablesOrViewsType.type.kind === typescript_1.SyntaxKind.TypeLiteral) {
|
|
@@ -143,13 +143,13 @@ function main() {
|
|
|
143
143
|
return true;
|
|
144
144
|
})
|
|
145
145
|
.forEach(type => {
|
|
146
|
-
//
|
|
146
|
+
// Group by schema folder
|
|
147
147
|
const schemaFolder = path_1.default.join(crudFolderPath, type.schema);
|
|
148
148
|
if (!(0, fs_1.existsSync)(schemaFolder)) {
|
|
149
149
|
(0, fs_1.mkdirSync)(schemaFolder, { recursive: true });
|
|
150
150
|
}
|
|
151
151
|
const fileName = toLowerCamelCase(type.typeName);
|
|
152
|
-
//
|
|
152
|
+
// Adjust import path when using schema folders
|
|
153
153
|
const hasSchemaFolders = types.some(t => t.schema !== type.schema);
|
|
154
154
|
const crudCode = crudTemplate(type.typeName, type.fields, type.isView, type.schema, hasSchemaFolders);
|
|
155
155
|
const filePath = path_1.default.join(schemaFolder, `${fileName}.ts`);
|
|
@@ -174,7 +174,7 @@ const toLowerCamelCase = (str) => {
|
|
|
174
174
|
const toUpperCamelCase = (str) => {
|
|
175
175
|
return str.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
|
|
176
176
|
};
|
|
177
|
-
// CRUD
|
|
177
|
+
// CRUD template body - string generation
|
|
178
178
|
const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
179
179
|
const upperCamelTypeName = toUpperCamelCase(typeName);
|
|
180
180
|
const getByIdFunctionName = 'select' + upperCamelTypeName + 'RowById';
|
|
@@ -184,12 +184,12 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
184
184
|
const updateFunctionName = 'update' + upperCamelTypeName + 'Row';
|
|
185
185
|
const deleteFunctionName = 'delete' + upperCamelTypeName + 'Row';
|
|
186
186
|
const idType = fields.find((field) => field.name === 'id')?.type || 'string';
|
|
187
|
-
//
|
|
187
|
+
// Adjust import path dynamically
|
|
188
188
|
const importPath = hasSchemaFolders ? '../../client' : '../client';
|
|
189
|
-
//
|
|
189
|
+
// Header section
|
|
190
190
|
const header = [
|
|
191
191
|
`// Supabase CRUD operations for ${typeName} (${schema} schema)`,
|
|
192
|
-
'//
|
|
192
|
+
'// Auto-generated file',
|
|
193
193
|
`import { supabase } from "${importPath}";`,
|
|
194
194
|
'import { Tables, TablesInsert, TablesUpdate } from "@shared/types";',
|
|
195
195
|
'',
|
|
@@ -198,10 +198,10 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
198
198
|
'type Filters = Record<string, FilterTypesValue | FilterTypesValue[]>;',
|
|
199
199
|
''
|
|
200
200
|
].join('\n');
|
|
201
|
-
//
|
|
201
|
+
// Filter application function
|
|
202
202
|
const filterFunction = [
|
|
203
203
|
'/**',
|
|
204
|
-
' *
|
|
204
|
+
' * Apply filters to query',
|
|
205
205
|
' */',
|
|
206
206
|
'function applyFilters(query: any, filters: Filters): any {',
|
|
207
207
|
' for (const [key, value] of Object.entries(filters)) {',
|
|
@@ -232,10 +232,10 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
232
232
|
'}',
|
|
233
233
|
''
|
|
234
234
|
].join('\n');
|
|
235
|
-
// ID
|
|
235
|
+
// Select by ID
|
|
236
236
|
const selectById = [
|
|
237
237
|
'/**',
|
|
238
|
-
' * ID
|
|
238
|
+
' * Select single row by ID',
|
|
239
239
|
' */',
|
|
240
240
|
`export async function ${getByIdFunctionName}({ id }: { id: ${idType} }): Promise<${typeName} | null> {`,
|
|
241
241
|
' if (!id) throw new Error("ID is required");',
|
|
@@ -258,10 +258,10 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
258
258
|
'}',
|
|
259
259
|
''
|
|
260
260
|
].join('\n');
|
|
261
|
-
//
|
|
261
|
+
// Select multiple by filters
|
|
262
262
|
const selectMultiple = [
|
|
263
263
|
'/**',
|
|
264
|
-
' *
|
|
264
|
+
' * Select multiple rows by filters',
|
|
265
265
|
' */',
|
|
266
266
|
`export async function ${getByFiltersFunctionName}({ filters }: { filters: Filters }): Promise<${typeName}[]> {`,
|
|
267
267
|
' if (!filters || typeof filters !== "object") return [];',
|
|
@@ -278,10 +278,10 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
278
278
|
'}',
|
|
279
279
|
''
|
|
280
280
|
].join('\n');
|
|
281
|
-
//
|
|
281
|
+
// Select single by filters
|
|
282
282
|
const selectSingle = [
|
|
283
283
|
'/**',
|
|
284
|
-
' *
|
|
284
|
+
' * Select single row by filters',
|
|
285
285
|
' */',
|
|
286
286
|
`export async function ${getSingleByFiltersFunctionName}({ filters }: { filters: Filters }): Promise<${typeName} | null> {`,
|
|
287
287
|
' if (!filters || typeof filters !== "object") return null;',
|
|
@@ -301,10 +301,10 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
301
301
|
'}',
|
|
302
302
|
''
|
|
303
303
|
].join('\n');
|
|
304
|
-
//
|
|
304
|
+
// Insert (only when not view)
|
|
305
305
|
const insertOperation = isView ? '' : [
|
|
306
306
|
'/**',
|
|
307
|
-
' *
|
|
307
|
+
' * Insert',
|
|
308
308
|
' */',
|
|
309
309
|
`export async function ${createFunctionName}({ data }: { data: TablesInsert<"${typeName}"> }): Promise<${typeName}> {`,
|
|
310
310
|
' if (!data) throw new Error("Data is required for creation");',
|
|
@@ -325,10 +325,10 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
325
325
|
'}',
|
|
326
326
|
''
|
|
327
327
|
].join('\n');
|
|
328
|
-
//
|
|
328
|
+
// Update (only when not view)
|
|
329
329
|
const updateOperation = isView ? '' : [
|
|
330
330
|
'/**',
|
|
331
|
-
' *
|
|
331
|
+
' * Update',
|
|
332
332
|
' */',
|
|
333
333
|
`export async function ${updateFunctionName}({ id, data }: { id: ${idType}; data: TablesUpdate<"${typeName}"> }): Promise<${typeName}> {`,
|
|
334
334
|
' if (!id) throw new Error("ID is required for update");',
|
|
@@ -354,10 +354,10 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
354
354
|
'}',
|
|
355
355
|
''
|
|
356
356
|
].join('\n');
|
|
357
|
-
//
|
|
357
|
+
// Delete (only when not view)
|
|
358
358
|
const deleteOperation = isView ? '' : [
|
|
359
359
|
'/**',
|
|
360
|
-
' *
|
|
360
|
+
' * Delete',
|
|
361
361
|
' */',
|
|
362
362
|
`export async function ${deleteFunctionName}({ id }: { id: ${idType} }): Promise<boolean> {`,
|
|
363
363
|
' if (!id) throw new Error("ID is required for deletion");',
|
|
@@ -376,6 +376,6 @@ const crudTemplate = (typeName, fields, isView, schema, hasSchemaFolders) => {
|
|
|
376
376
|
'}',
|
|
377
377
|
''
|
|
378
378
|
].join('\n');
|
|
379
|
-
//
|
|
379
|
+
// Concatenate all sections
|
|
380
380
|
return header + filterFunction + selectById + selectMultiple + selectSingle + insertOperation + updateOperation + deleteOperation;
|
|
381
381
|
};
|
|
@@ -4,13 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.parseModelYaml = parseModelYaml;
|
|
7
|
-
//
|
|
8
|
-
// 日本語コメント
|
|
7
|
+
// Model YAML parser (minimal template)
|
|
9
8
|
const fs_1 = __importDefault(require("fs"));
|
|
10
9
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param filePath YAML
|
|
11
|
+
* Parse model YAML file and return JS object
|
|
12
|
+
* @param filePath YAML file path
|
|
14
13
|
*/
|
|
15
14
|
function parseModelYaml(filePath) {
|
|
16
15
|
const file = fs_1.default.readFileSync(filePath, 'utf8');
|
package/dist/sync/config.js
CHANGED
|
@@ -39,10 +39,10 @@ exports.createConfigTemplate = createConfigTemplate;
|
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const dotenv_1 = require("dotenv");
|
|
42
|
-
//
|
|
42
|
+
// Load .env / .env.local in project
|
|
43
43
|
const envPath = path.join(process.cwd(), '.env');
|
|
44
44
|
const envLocalPath = path.join(process.cwd(), '.env.local');
|
|
45
|
-
// .env.local
|
|
45
|
+
// .env.local takes precedence
|
|
46
46
|
if (fs.existsSync(envLocalPath)) {
|
|
47
47
|
(0, dotenv_1.config)({ path: envLocalPath });
|
|
48
48
|
}
|
|
@@ -50,7 +50,7 @@ else if (fs.existsSync(envPath)) {
|
|
|
50
50
|
(0, dotenv_1.config)({ path: envPath });
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* Load settings from config file
|
|
54
54
|
*/
|
|
55
55
|
function loadConfig(configPath) {
|
|
56
56
|
const defaultConfigPath = path.join(process.cwd(), 'supatool.config.json');
|
|
@@ -61,14 +61,14 @@ function loadConfig(configPath) {
|
|
|
61
61
|
return JSON.parse(configData);
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
|
-
console.warn(
|
|
64
|
+
console.warn(`Configuration file load error: ${finalConfigPath}`);
|
|
65
65
|
return {};
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
return {};
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
71
|
+
* Resolve final settings from env and config file
|
|
72
72
|
*/
|
|
73
73
|
function resolveConfig(options, configPath) {
|
|
74
74
|
const fileConfig = loadConfig(configPath);
|
|
@@ -82,17 +82,17 @@ function resolveConfig(options, configPath) {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
85
|
+
* Generate config file template
|
|
86
86
|
*/
|
|
87
87
|
function createConfigTemplate(outputPath) {
|
|
88
88
|
const template = {
|
|
89
89
|
connectionString: "postgresql://user:password@host:port/database",
|
|
90
90
|
schemaDir: "./supabase/schemas",
|
|
91
91
|
tablePattern: "*",
|
|
92
|
-
"_comment": "
|
|
92
|
+
"_comment": "It is recommended to set connection string in .env or .env.local file using SUPABASE_CONNECTION_STRING or DATABASE_URL"
|
|
93
93
|
};
|
|
94
94
|
fs.writeFileSync(outputPath, JSON.stringify(template, null, 2), 'utf-8');
|
|
95
|
-
console.log(
|
|
96
|
-
console.log('⚠️
|
|
97
|
-
console.log('💡
|
|
95
|
+
console.log(`Configuration template generated: ${outputPath}`);
|
|
96
|
+
console.log('⚠️ Remember to add the configuration file to .gitignore!');
|
|
97
|
+
console.log('💡 Manage connection string in .env or .env.local file');
|
|
98
98
|
}
|