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
package/dist/bin/supatool.js
CHANGED
|
@@ -1,8 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
6
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
37
|
// CLI entry point
|
|
8
38
|
// Subcommand support with commander
|
|
@@ -19,8 +49,8 @@ const rlsGenerator_1 = require("../generator/rlsGenerator");
|
|
|
19
49
|
const sync_1 = require("../sync");
|
|
20
50
|
const definitionExtractor_1 = require("../sync/definitionExtractor");
|
|
21
51
|
const seedGenerator_1 = require("../sync/seedGenerator");
|
|
22
|
-
const
|
|
23
|
-
const
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
24
54
|
const program = new commander_1.Command();
|
|
25
55
|
program
|
|
26
56
|
.name('supatool')
|
|
@@ -53,8 +83,8 @@ program
|
|
|
53
83
|
process.exit(1);
|
|
54
84
|
}
|
|
55
85
|
try {
|
|
56
|
-
// --schema
|
|
57
|
-
let schemas = ['public']; //
|
|
86
|
+
// Handle --schema option
|
|
87
|
+
let schemas = ['public']; // default
|
|
58
88
|
if (options.schema) {
|
|
59
89
|
schemas = options.schema.split(',').map((s) => s.trim());
|
|
60
90
|
}
|
|
@@ -67,7 +97,8 @@ program
|
|
|
67
97
|
all: options.all,
|
|
68
98
|
tablePattern: options.tables,
|
|
69
99
|
force: options.force,
|
|
70
|
-
schemas: schemas
|
|
100
|
+
schemas: schemas,
|
|
101
|
+
version: package_json_1.version
|
|
71
102
|
});
|
|
72
103
|
}
|
|
73
104
|
catch (error) {
|
|
@@ -75,135 +106,142 @@ program
|
|
|
75
106
|
process.exit(1);
|
|
76
107
|
}
|
|
77
108
|
});
|
|
78
|
-
// config:init
|
|
109
|
+
// config:init subcommand
|
|
79
110
|
program
|
|
80
111
|
.command('config:init')
|
|
81
|
-
.description('
|
|
82
|
-
.option('-o, --out <path>', '
|
|
112
|
+
.description('Generate config file template')
|
|
113
|
+
.option('-o, --out <path>', 'Output path', 'supatool.config.json')
|
|
83
114
|
.action((options) => {
|
|
84
115
|
(0, sync_1.createConfigTemplate)(options.out);
|
|
85
116
|
});
|
|
86
|
-
// gen:types
|
|
117
|
+
// gen:types subcommand
|
|
87
118
|
program
|
|
88
119
|
.command('gen:types <modelPath>')
|
|
89
|
-
.description('
|
|
90
|
-
.option('-o, --out <path>', '
|
|
120
|
+
.description('Generate TypeScript types from model YAML')
|
|
121
|
+
.option('-o, --out <path>', 'Output path', 'docs/generated/types.ts')
|
|
91
122
|
.action((modelPath, options) => {
|
|
92
123
|
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
93
124
|
(0, typeGenerator_1.generateTypesFromModel)(model, options.out);
|
|
94
|
-
console.log('TypeScript
|
|
125
|
+
console.log('TypeScript types output:', options.out);
|
|
95
126
|
});
|
|
96
|
-
// gen:crud
|
|
127
|
+
// gen:crud subcommand
|
|
97
128
|
program
|
|
98
129
|
.command('gen:crud <modelPath>')
|
|
99
|
-
.description('Generate CRUD TypeScript code from model YAML')
|
|
100
|
-
.option('-o, --out <dir>', '
|
|
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')
|
|
101
132
|
.action((modelPath, options) => {
|
|
133
|
+
console.warn('⚠️ gen:crud is deprecated. With LLM development, writing code as needed is often more efficient.');
|
|
102
134
|
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
103
135
|
(0, crudGenerator_1.generateCrudFromModel)(model, options.out);
|
|
104
136
|
console.log('Generated CRUD TypeScript code:', options.out);
|
|
105
137
|
});
|
|
106
|
-
// gen:docs
|
|
138
|
+
// gen:docs subcommand
|
|
107
139
|
program
|
|
108
140
|
.command('gen:docs <modelPath>')
|
|
109
|
-
.description('
|
|
110
|
-
.option('-o, --out <path>', '
|
|
141
|
+
.description('Generate documentation (Markdown) from model YAML')
|
|
142
|
+
.option('-o, --out <path>', 'Table doc output path', 'docs/generated/table-doc.md')
|
|
111
143
|
.action((modelPath, options) => {
|
|
112
144
|
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
113
145
|
(0, docGenerator_1.generateTableDocMarkdown)(model, options.out);
|
|
114
146
|
(0, docGenerator_1.generateRelationsMarkdown)(model, 'docs/generated/relations.md');
|
|
115
|
-
console.log('
|
|
116
|
-
console.log('
|
|
147
|
+
console.log('Table doc output:', options.out);
|
|
148
|
+
console.log('Relations list output: docs/generated/relations.md');
|
|
117
149
|
});
|
|
118
|
-
// gen:sql
|
|
150
|
+
// gen:sql subcommand
|
|
119
151
|
program
|
|
120
152
|
.command('gen:sql <modelPath>')
|
|
121
|
-
.description('
|
|
122
|
-
.option('-o, --out <path>', '
|
|
153
|
+
.description('Generate table, relation, RLS/security SQL from model YAML')
|
|
154
|
+
.option('-o, --out <path>', 'Output path', 'docs/generated/schema.sql')
|
|
123
155
|
.action((modelPath, options) => {
|
|
124
156
|
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
125
|
-
//
|
|
157
|
+
// Write to temp files first
|
|
126
158
|
const tmpSchema = 'docs/generated/.tmp_schema.sql';
|
|
127
159
|
const tmpRls = 'docs/generated/.tmp_rls.sql';
|
|
128
160
|
(0, sqlGenerator_1.generateSqlFromModel)(model, tmpSchema);
|
|
129
161
|
(0, rlsGenerator_1.generateRlsSqlFromModel)(model, tmpRls);
|
|
130
|
-
//
|
|
131
|
-
const schema =
|
|
132
|
-
const rls =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
console.log('
|
|
162
|
+
// Merge into single file
|
|
163
|
+
const schema = fs.readFileSync(tmpSchema, 'utf-8');
|
|
164
|
+
const rls = fs.readFileSync(tmpRls, 'utf-8');
|
|
165
|
+
fs.writeFileSync(options.out, schema + '\n' + rls);
|
|
166
|
+
fs.unlinkSync(tmpSchema);
|
|
167
|
+
fs.unlinkSync(tmpRls);
|
|
168
|
+
console.log('Table, relation, RLS/security SQL output:', options.out);
|
|
137
169
|
});
|
|
138
|
-
// gen:rls
|
|
170
|
+
// gen:rls subcommand
|
|
139
171
|
program
|
|
140
172
|
.command('gen:rls <modelPath>')
|
|
141
|
-
.description('
|
|
142
|
-
.option('-o, --out <path>', '
|
|
173
|
+
.description('Generate RLS/security policy SQL from model YAML')
|
|
174
|
+
.option('-o, --out <path>', 'Output path', 'docs/generated/rls.sql')
|
|
143
175
|
.action((modelPath, options) => {
|
|
144
176
|
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
145
177
|
(0, rlsGenerator_1.generateRlsSqlFromModel)(model, options.out);
|
|
146
|
-
console.log('RLS
|
|
178
|
+
console.log('RLS/security policy SQL output:', options.out);
|
|
147
179
|
});
|
|
148
|
-
// gen:all
|
|
180
|
+
// gen:all subcommand
|
|
149
181
|
program
|
|
150
182
|
.command('gen:all <modelPath>')
|
|
151
|
-
.description('
|
|
183
|
+
.description('Generate all from model YAML')
|
|
152
184
|
.action((modelPath) => {
|
|
153
185
|
const model = (0, modelParser_1.parseModelYaml)(modelPath);
|
|
154
186
|
(0, typeGenerator_1.generateTypesFromModel)(model, 'docs/generated/types.ts');
|
|
155
187
|
(0, crudGenerator_1.generateCrudFromModel)(model, 'docs/generated/crud');
|
|
156
188
|
(0, docGenerator_1.generateTableDocMarkdown)(model, 'docs/generated/table-doc.md');
|
|
157
189
|
(0, docGenerator_1.generateRelationsMarkdown)(model, 'docs/generated/relations.md');
|
|
158
|
-
console.log('TypeScript
|
|
159
|
-
console.log('CRUD
|
|
160
|
-
console.log('
|
|
161
|
-
console.log('
|
|
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');
|
|
162
194
|
});
|
|
163
|
-
// create
|
|
195
|
+
// create subcommand
|
|
164
196
|
program
|
|
165
197
|
.command('create <template>')
|
|
166
|
-
.description('
|
|
167
|
-
.option('-o, --out <path>', '
|
|
198
|
+
.description('Generate template YAML')
|
|
199
|
+
.option('-o, --out <path>', 'Output path', 'docs/model-schema-example.yaml')
|
|
168
200
|
.action((template, options) => {
|
|
169
|
-
const srcPath =
|
|
201
|
+
const srcPath = path.join(__dirname, '../templates/yaml', `${template}.yaml`);
|
|
170
202
|
const destPath = options.out;
|
|
171
|
-
if (!
|
|
172
|
-
console.error(
|
|
203
|
+
if (!fs.existsSync(srcPath)) {
|
|
204
|
+
console.error(`Template not found: ${srcPath}`);
|
|
173
205
|
process.exit(1);
|
|
174
206
|
}
|
|
175
|
-
|
|
176
|
-
console.log(
|
|
207
|
+
fs.copyFileSync(srcPath, destPath);
|
|
208
|
+
console.log(`Template generated: ${destPath}`);
|
|
177
209
|
});
|
|
178
|
-
// crud
|
|
210
|
+
// crud command (Supabase types -> CRUD generation)
|
|
179
211
|
program
|
|
180
212
|
.command('crud')
|
|
181
|
-
.description('Supabase
|
|
182
|
-
.option('-i, --input <path>', '
|
|
183
|
-
.option('-o, --output <path>', 'CRUD
|
|
184
|
-
.option('-t, --tables <names>', '
|
|
185
|
-
.option('-f, --force', '
|
|
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')
|
|
186
218
|
.action((options) => {
|
|
187
|
-
|
|
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
|
|
188
221
|
(0, index_1.main)();
|
|
189
222
|
});
|
|
190
|
-
// help
|
|
223
|
+
// help subcommand
|
|
191
224
|
program
|
|
192
225
|
.command('help')
|
|
193
226
|
.description('Show help')
|
|
194
227
|
.action(() => {
|
|
195
228
|
console.log(helptext_1.helpText);
|
|
196
229
|
});
|
|
197
|
-
// sync
|
|
230
|
+
// sync command (deprecated)
|
|
198
231
|
program
|
|
199
232
|
.command('sync')
|
|
200
|
-
.description('
|
|
233
|
+
.description('Synchronize local and remote schemas [deprecated]')
|
|
201
234
|
.option('-c, --connection <string>', 'Supabase connection string')
|
|
202
|
-
.option('-s, --schema-dir <path>', '
|
|
203
|
-
.option('-t, --tables <pattern>', '
|
|
204
|
-
.option('-f, --force', '
|
|
205
|
-
.option('--config <path>', '
|
|
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')
|
|
206
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('');
|
|
207
245
|
const config = (0, sync_1.resolveConfig)({
|
|
208
246
|
connectionString: options.connection
|
|
209
247
|
}, options.config);
|
|
@@ -228,16 +266,86 @@ program
|
|
|
228
266
|
process.exit(1);
|
|
229
267
|
}
|
|
230
268
|
});
|
|
231
|
-
//
|
|
269
|
+
// deploy command (recommended)
|
|
270
|
+
program
|
|
271
|
+
.command('deploy')
|
|
272
|
+
.description('Deploy local schema to remote (diff detection, migration generation, confirm before apply)')
|
|
273
|
+
.option('-c, --connection <string>', 'Supabase connection string')
|
|
274
|
+
.option('-s, --schema-dir <path>', 'Local schema directory', './supabase/schemas')
|
|
275
|
+
.option('-t, --table <name>', 'Target table name (specify "all" for all tables)')
|
|
276
|
+
.option('--auto-apply', 'Auto-apply to remote (no confirmation)')
|
|
277
|
+
.option('--dry-run', 'Preview changes only (recommended)')
|
|
278
|
+
.option('--generate-only', 'Generate migration files only (no apply)')
|
|
279
|
+
.option('--config <path>', 'Configuration file path')
|
|
280
|
+
.action(async (options) => {
|
|
281
|
+
const config = (0, sync_1.resolveConfig)({
|
|
282
|
+
connectionString: options.connection
|
|
283
|
+
}, options.config);
|
|
284
|
+
if (!config.connectionString) {
|
|
285
|
+
console.error('Connection string is required. Set it using one of:');
|
|
286
|
+
console.error('1. --connection option');
|
|
287
|
+
console.error('2. SUPABASE_CONNECTION_STRING environment variable');
|
|
288
|
+
console.error('3. DATABASE_URL environment variable');
|
|
289
|
+
console.error('4. supatool.config.json configuration file');
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
// Validate table specification
|
|
293
|
+
if (!options.table) {
|
|
294
|
+
console.error('❌ Table name is required. Use --table <table-name>');
|
|
295
|
+
console.error(' Example: supatool deploy --table users --dry-run');
|
|
296
|
+
console.error(' Example: supatool deploy --table all --dry-run # all tables');
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
const tablePattern = options.table === 'all' ? '*' : options.table;
|
|
300
|
+
// Option processing
|
|
301
|
+
const isDryRun = options.dryRun || false;
|
|
302
|
+
const isAutoApply = options.autoApply || false;
|
|
303
|
+
const isGenerateOnly = options.generateOnly || false;
|
|
304
|
+
// Conflict check
|
|
305
|
+
const activeOptions = [isDryRun, isAutoApply, isGenerateOnly].filter(Boolean).length;
|
|
306
|
+
if (activeOptions > 1) {
|
|
307
|
+
console.error('❌ --dry-run, --auto-apply, --generate-only cannot be specified simultaneously');
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
if (isDryRun) {
|
|
311
|
+
console.log('🔍 Preview mode: showing changes');
|
|
312
|
+
}
|
|
313
|
+
else if (isAutoApply) {
|
|
314
|
+
console.log('⚡ Auto-apply mode: executing without confirmation');
|
|
315
|
+
}
|
|
316
|
+
else if (isGenerateOnly) {
|
|
317
|
+
console.log('📝 Migration generation mode: file generation only');
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
console.log('✅ Confirmation mode: generating migration then confirming before execution');
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
console.log(`Target tables: ${tablePattern}`);
|
|
324
|
+
await (0, sync_1.syncAllTables)({
|
|
325
|
+
connectionString: config.connectionString,
|
|
326
|
+
schemaDir: options.schemaDir,
|
|
327
|
+
tablePattern: tablePattern,
|
|
328
|
+
force: isAutoApply,
|
|
329
|
+
dryRun: isDryRun,
|
|
330
|
+
generateOnly: isGenerateOnly,
|
|
331
|
+
requireConfirmation: !isDryRun && !isAutoApply && !isGenerateOnly
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
console.error('⚠️ Deploy error:', error);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
// seed command
|
|
232
340
|
program
|
|
233
341
|
.command('seed')
|
|
234
|
-
.description('
|
|
235
|
-
.option('-c, --connection <string>', 'Supabase
|
|
236
|
-
.option('-t, --tables <path>', '
|
|
237
|
-
.option('-o, --out <dir>', '
|
|
238
|
-
.option('--config <path>', '
|
|
342
|
+
.description('Fetch table data from remote DB and generate AI seed JSON')
|
|
343
|
+
.option('-c, --connection <string>', 'Supabase connection string')
|
|
344
|
+
.option('-t, --tables <path>', 'Tables list YAML', 'tables.yaml')
|
|
345
|
+
.option('-o, --out <dir>', 'Output directory', 'supabase/seeds')
|
|
346
|
+
.option('--config <path>', 'Configuration file path')
|
|
239
347
|
.action(async (options) => {
|
|
240
|
-
//
|
|
348
|
+
// Resolve connection
|
|
241
349
|
const config = (0, sync_1.resolveConfig)({
|
|
242
350
|
connectionString: options.connection
|
|
243
351
|
}, options.config);
|
|
@@ -257,7 +365,7 @@ program
|
|
|
257
365
|
});
|
|
258
366
|
}
|
|
259
367
|
catch (error) {
|
|
260
|
-
console.error('⚠️ Seed
|
|
368
|
+
console.error('⚠️ Seed fetch error:', error);
|
|
261
369
|
process.exit(1);
|
|
262
370
|
}
|
|
263
371
|
});
|
package/dist/generator/client.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.supabase = void 0;
|
|
4
|
-
// Supabase
|
|
5
|
-
// 日本語コメント
|
|
4
|
+
// Supabase client definition (auto-generated / editable)
|
|
6
5
|
const supabase_js_1 = require("@supabase/supabase-js");
|
|
7
6
|
const supabaseUrl = process.env.SUPABASE_URL || '';
|
|
8
7
|
const supabaseKey = process.env.SUPABASE_KEY || '';
|
|
@@ -4,14 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateCrudFromModel = generateCrudFromModel;
|
|
7
|
-
// CRUD
|
|
8
|
-
// 日本語コメント
|
|
7
|
+
// CRUD TypeScript code auto-generation (Supabase-compatible)
|
|
9
8
|
const path_1 = __importDefault(require("path"));
|
|
10
9
|
const fs_1 = __importDefault(require("fs"));
|
|
11
10
|
/**
|
|
12
|
-
* dataSchema
|
|
13
|
-
* @param model
|
|
14
|
-
* @param outDir
|
|
11
|
+
* Generate CRUD TypeScript file per table from dataSchema or models/tables
|
|
12
|
+
* @param model Model object
|
|
13
|
+
* @param outDir Output directory
|
|
15
14
|
*/
|
|
16
15
|
function generateCrudFromModel(model, outDir) {
|
|
17
16
|
if (!fs_1.default.existsSync(outDir)) {
|
|
@@ -35,15 +34,15 @@ function generateCrudFromModel(model, outDir) {
|
|
|
35
34
|
continue;
|
|
36
35
|
const tableName = tableObj.tableName;
|
|
37
36
|
const capitalizedName = capitalize(tableName);
|
|
38
|
-
let code = `//
|
|
37
|
+
let code = `// Auto-generated: CRUD for ${tableName}\n\n`;
|
|
39
38
|
code += `import { supabase } from '../client';\n`;
|
|
40
39
|
code += `import type { ${tableName} } from '../types';\n\n`;
|
|
41
|
-
//
|
|
42
|
-
code += `//
|
|
40
|
+
// Type definitions
|
|
41
|
+
code += `// Filter type definition\n`;
|
|
43
42
|
code += `type FilterValue = string | number | boolean | null;\n`;
|
|
44
43
|
code += `type Filters = Record<string, FilterValue | FilterValue[]>;\n\n`;
|
|
45
|
-
//
|
|
46
|
-
code += `/**
|
|
44
|
+
// Select all (improved)
|
|
45
|
+
code += `/** Select all */\n`;
|
|
47
46
|
code += `export async function select${capitalizedName}Rows(): Promise<${tableName}[]> {\n`;
|
|
48
47
|
code += ` try {\n`;
|
|
49
48
|
code += ` const { data, error } = await supabase.from('${tableName}').select('*');\n`;
|
|
@@ -60,8 +59,8 @@ function generateCrudFromModel(model, outDir) {
|
|
|
60
59
|
code += ` throw error;\n`;
|
|
61
60
|
code += ` }\n`;
|
|
62
61
|
code += `}\n\n`;
|
|
63
|
-
// ID
|
|
64
|
-
code += `/** ID
|
|
62
|
+
// Select by ID (improved)
|
|
63
|
+
code += `/** Select by ID */\n`;
|
|
65
64
|
code += `export async function select${capitalizedName}RowById({ id }: { id: string }): Promise<${tableName} | null> {\n`;
|
|
66
65
|
code += ` if (!id) {\n`;
|
|
67
66
|
code += ` throw new Error('ID is required');\n`;
|
|
@@ -69,7 +68,7 @@ function generateCrudFromModel(model, outDir) {
|
|
|
69
68
|
code += ` try {\n`;
|
|
70
69
|
code += ` const { data, error } = await supabase.from('${tableName}').select('*').eq('id', id).single();\n`;
|
|
71
70
|
code += ` if (error) {\n`;
|
|
72
|
-
code += ` //
|
|
71
|
+
code += ` // Return null when record not found (PGRST116)\n`;
|
|
73
72
|
code += ` if (error.code === 'PGRST116') {\n`;
|
|
74
73
|
code += ` return null;\n`;
|
|
75
74
|
code += ` }\n`;
|
|
@@ -82,15 +81,15 @@ function generateCrudFromModel(model, outDir) {
|
|
|
82
81
|
code += ` throw error;\n`;
|
|
83
82
|
code += ` }\n`;
|
|
84
83
|
code += `}\n\n`;
|
|
85
|
-
//
|
|
86
|
-
code += `/**
|
|
84
|
+
// Select by filters
|
|
85
|
+
code += `/** Select multiple by filters */\n`;
|
|
87
86
|
code += `export async function select${capitalizedName}RowsWithFilters({ filters }: { filters: Filters }): Promise<${tableName}[]> {\n`;
|
|
88
|
-
code += ` // filters
|
|
87
|
+
code += ` // Guard for filters\n`;
|
|
89
88
|
code += ` if (!filters || typeof filters !== 'object') return [];\n`;
|
|
90
89
|
code += ` try {\n`;
|
|
91
90
|
code += ` let query = supabase.from('${tableName}').select('*');\n`;
|
|
92
91
|
code += ` \n`;
|
|
93
|
-
code += ` //
|
|
92
|
+
code += ` // Apply filters\n`;
|
|
94
93
|
code += ` for (const [key, value] of Object.entries(filters)) {\n`;
|
|
95
94
|
code += ` if (Array.isArray(value)) {\n`;
|
|
96
95
|
code += ` query = query.in(key, value);\n`;
|
|
@@ -110,8 +109,8 @@ function generateCrudFromModel(model, outDir) {
|
|
|
110
109
|
code += ` throw error;\n`;
|
|
111
110
|
code += ` }\n`;
|
|
112
111
|
code += `}\n\n`;
|
|
113
|
-
//
|
|
114
|
-
code += `/**
|
|
112
|
+
// Insert
|
|
113
|
+
code += `/** Insert */\n`;
|
|
115
114
|
code += `export async function insert${capitalizedName}Row({ data }: { data: Omit<${tableName}, 'id' | 'created_at' | 'updated_at'> }): Promise<${tableName}> {\n`;
|
|
116
115
|
code += ` if (!data) {\n`;
|
|
117
116
|
code += ` throw new Error('Data is required for creation');\n`;
|
|
@@ -135,8 +134,8 @@ function generateCrudFromModel(model, outDir) {
|
|
|
135
134
|
code += ` throw error;\n`;
|
|
136
135
|
code += ` }\n`;
|
|
137
136
|
code += `}\n\n`;
|
|
138
|
-
//
|
|
139
|
-
code += `/**
|
|
137
|
+
// Update
|
|
138
|
+
code += `/** Update */\n`;
|
|
140
139
|
code += `export async function update${capitalizedName}Row({ id, data }: { id: string; data: Partial<Omit<${tableName}, 'id' | 'created_at'>> }): Promise<${tableName}> {\n`;
|
|
141
140
|
code += ` if (!id) {\n`;
|
|
142
141
|
code += ` throw new Error('ID is required for update');\n`;
|
|
@@ -167,8 +166,8 @@ function generateCrudFromModel(model, outDir) {
|
|
|
167
166
|
code += ` throw error;\n`;
|
|
168
167
|
code += ` }\n`;
|
|
169
168
|
code += `}\n\n`;
|
|
170
|
-
//
|
|
171
|
-
code += `/**
|
|
169
|
+
// Delete
|
|
170
|
+
code += `/** Delete */\n`;
|
|
172
171
|
code += `export async function delete${capitalizedName}Row({ id }: { id: string }): Promise<boolean> {\n`;
|
|
173
172
|
code += ` if (!id) {\n`;
|
|
174
173
|
code += ` throw new Error('ID is required for deletion');\n`;
|
|
@@ -5,31 +5,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateTableDocMarkdown = generateTableDocMarkdown;
|
|
7
7
|
exports.generateRelationsMarkdown = generateRelationsMarkdown;
|
|
8
|
-
//
|
|
9
|
-
// 日本語コメント
|
|
8
|
+
// Document generation (minimal template)
|
|
10
9
|
const path_1 = __importDefault(require("path"));
|
|
11
10
|
const fs_1 = __importDefault(require("fs"));
|
|
12
11
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param model
|
|
15
|
-
* @param outPath
|
|
12
|
+
* Generate and save table definition doc (Markdown)
|
|
13
|
+
* @param model Model object
|
|
14
|
+
* @param outPath Output path
|
|
16
15
|
*/
|
|
17
16
|
function generateTableDocMarkdown(model, outPath) {
|
|
18
|
-
//
|
|
17
|
+
// Create output directory
|
|
19
18
|
const dir = path_1.default.dirname(outPath);
|
|
20
19
|
if (!fs_1.default.existsSync(dir)) {
|
|
21
20
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
22
21
|
}
|
|
23
|
-
let md = '#
|
|
22
|
+
let md = '# Table definition doc\n\n';
|
|
24
23
|
for (const m of model.models) {
|
|
25
24
|
const tables = m.tables || {};
|
|
26
25
|
for (const [tableName, table] of Object.entries(tables)) {
|
|
27
26
|
const t = table;
|
|
28
|
-
let skipNote = t.skipCreate ? '
|
|
27
|
+
let skipNote = t.skipCreate ? ' (skip: Supabase built-in)' : '';
|
|
29
28
|
md += `## ${tableName}${skipNote}\n`;
|
|
30
29
|
if (t.description)
|
|
31
30
|
md += `${t.description}\n`;
|
|
32
|
-
md += '\n|
|
|
31
|
+
md += '\n| Column | Type | PK | NotNull | Default | Label |\n|---|---|---|---|---|---|\n';
|
|
33
32
|
for (const [colName, col] of Object.entries(t.fields || {})) {
|
|
34
33
|
const c = col;
|
|
35
34
|
md += `| ${colName} | ${c.type || ''} | ${c.primary ? '★' : ''} | ${c.notNull ? '○' : ''} | ${c.default || ''} | ${c.label || ''} |\n`;
|
|
@@ -40,16 +39,16 @@ function generateTableDocMarkdown(model, outPath) {
|
|
|
40
39
|
fs_1.default.writeFileSync(outPath, md);
|
|
41
40
|
}
|
|
42
41
|
/**
|
|
43
|
-
*
|
|
44
|
-
* @param model
|
|
45
|
-
* @param outPath
|
|
42
|
+
* Generate and save relations list (Markdown)
|
|
43
|
+
* @param model Model object
|
|
44
|
+
* @param outPath Output path
|
|
46
45
|
*/
|
|
47
46
|
function generateRelationsMarkdown(model, outPath) {
|
|
48
47
|
const dir = path_1.default.dirname(outPath);
|
|
49
48
|
if (!fs_1.default.existsSync(dir)) {
|
|
50
49
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
51
50
|
}
|
|
52
|
-
let md = '#
|
|
51
|
+
let md = '# Relations list\n\n| Table | Relation | Target | Foreign key |\n|---|---|---|---|\n';
|
|
53
52
|
for (const m of model.models) {
|
|
54
53
|
const tables = m.tables || {};
|
|
55
54
|
for (const [tableName, table] of Object.entries(tables)) {
|