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/dist/sync/sync.js CHANGED
@@ -7,102 +7,90 @@ const writeSchema_1 = require("./writeSchema");
7
7
  const generateMigration_1 = require("./generateMigration");
8
8
  const diff_1 = require("diff");
9
9
  const utils_1 = require("./utils");
10
- // グローバル承認状態(writeSchema.tsと共有)
10
+ // Global approval state (shared with writeSchema.ts)
11
11
  let globalApproveAll = false;
12
12
  /**
13
- * DDL文字列を正規化(空白・改行・タブを統一)
13
+ * Normalize DDL string (unify spaces, newlines, tabs)
14
14
  */
15
15
  function normalizeDDL(ddl) {
16
16
  return ddl
17
- .replace(/\s+/g, ' ') // 連続する空白文字を1つのスペースに
18
- .replace(/;\s+/g, ';\n') // セミコロン後に改行
19
- .trim(); // 前後の空白を削除
17
+ .replace(/\s+/g, ' ') // Replace consecutive whitespace with single space
18
+ .replace(/;\s+/g, ';\n') // Add newline after semicolon
19
+ .trim(); // Remove leading/trailing whitespace
20
20
  }
21
21
  /**
22
- * SQLを見やすく整形
22
+ * Format SQL for readability
23
23
  */
24
24
  function formatSQL(sql) {
25
25
  return sql
26
- .replace(/,\s*/g, ',\n ') // カンマ後に改行とインデント
27
- .replace(/\(\s*/g, ' (\n ') // 開き括弧後に改行とインデント
28
- .replace(/\s*\)/g, '\n)') // 閉じ括弧前に改行
29
- .replace(/\bCREATE\s+TABLE\b/g, '\nCREATE TABLE') // CREATE TABLE前に改行
30
- .replace(/\bPRIMARY\s+KEY\b/g, '\n PRIMARY KEY') // PRIMARY KEY前に改行とインデント
31
- .replace(/;\s*/g, ';\n') // セミコロン後に改行
26
+ .replace(/,\s*/g, ',\n ') // Add newline and indent after comma
27
+ .replace(/\(\s*/g, ' (\n ') // Add newline and indent after opening parenthesis
28
+ .replace(/\s*\)/g, '\n)') // Add newline before closing parenthesis
29
+ .replace(/\bCREATE\s+TABLE\b/g, '\nCREATE TABLE') // Add newline before CREATE TABLE
30
+ .replace(/\bPRIMARY\s+KEY\b/g, '\n PRIMARY KEY') // Add newline and indent before PRIMARY KEY
31
+ .replace(/;\s*/g, ';\n') // Add newline after semicolon
32
32
  .split('\n')
33
33
  .map(line => line.trim())
34
34
  .filter(line => line)
35
35
  .join('\n');
36
36
  }
37
37
  /**
38
- * すべてのテーブルスキーマを同期
38
+ * Synchronize all table schemas
39
39
  */
40
- async function syncAllTables({ connectionString, schemaDir, tablePattern = '*', force = false }) {
41
- // 承認状態をリセット
40
+ async function syncAllTables({ connectionString, schemaDir, tablePattern = '*', force = false, dryRun = false, generateOnly = false, requireConfirmation = false }) {
41
+ // Reset approval state
42
42
  (0, writeSchema_1.resetApprovalState)();
43
43
  const localSchemas = await (0, parseLocalSchemas_1.parseLocalSchemas)(schemaDir);
44
- const remoteSchemas = await (0, fetchRemoteSchemas_1.fetchRemoteSchemas)(connectionString);
45
- const allTables = new Set([...Object.keys(localSchemas), ...Object.keys(remoteSchemas)]);
46
- const remoteTables = new Set(Object.keys(remoteSchemas));
47
- // 存在しないテーブルのファイルをバックアップ
48
- await (0, writeSchema_1.backupOrphanedFiles)(schemaDir, remoteTables, force);
49
- for (const tableName of allTables) {
50
- if (!(0, utils_1.wildcardMatch)(tableName, tablePattern)) {
51
- continue;
52
- }
44
+ // Filter local tables based on pattern first
45
+ const targetLocalTables = Object.keys(localSchemas).filter(tableName => (0, utils_1.wildcardMatch)(tableName, tablePattern));
46
+ console.log(`Pattern: ${tablePattern}`);
47
+ console.log(`Schema directory: ${schemaDir}`);
48
+ console.log(`Available local tables: ${Object.keys(localSchemas).join(', ') || 'none'}`);
49
+ console.log(`Matched local tables: ${targetLocalTables.join(', ') || 'none'}`);
50
+ if (targetLocalTables.length === 0) {
51
+ console.log('No matching local tables found for the specified pattern.');
52
+ return;
53
+ }
54
+ // Fetch only the remote schemas for target tables
55
+ const remoteSchemas = await (0, fetchRemoteSchemas_1.fetchRemoteSchemas)(connectionString, targetLocalTables);
56
+ for (const tableName of targetLocalTables) {
53
57
  const local = localSchemas[tableName];
54
58
  const remote = remoteSchemas[tableName];
59
+ // Always use local as the source (never overwrite local files)
55
60
  if (local && !remote) {
56
- console.log(`[${tableName}] ローカルのみ - リモートに存在しません(バックアップ済み)`);
57
- }
58
- else if (!local && remote) {
59
- console.log(`[${tableName}] リモートのみ - スキーマを取得`);
60
- const success = await (0, writeSchema_1.writeSchemaToFile)(remote.ddl, schemaDir, remote.timestamp, `${tableName}.sql`, tableName, force);
61
- if (success) {
62
- console.log(`[${tableName}] スキーマファイルを作成しました`);
61
+ // Local only: generate CREATE TABLE migration
62
+ console.log(`[${tableName}] Local only - will generate CREATE TABLE migration`);
63
+ if (dryRun) {
64
+ console.log(`[${tableName}] 🔍 CREATE TABLE migration will be generated`);
65
+ console.log(`[${tableName}] 📄 Migration file: supabase/migrations/YYYYMMDDHHMMSS_create_${tableName}.sql`);
66
+ }
67
+ else if (generateOnly) {
68
+ console.log(`[${tableName}] 📝 generateOnly mode: skipping migration generation for local-only table`);
69
+ }
70
+ else {
71
+ // Generate CREATE TABLE migration
72
+ // For now, skip actual migration generation for local-only tables
73
+ console.log(`[${tableName}] CREATE TABLE migration generation not yet implemented`);
63
74
  }
64
75
  }
65
76
  else if (local && remote) {
66
- // 最初にDDLの差分をチェック
77
+ // Both local and remote exist: generate UPDATE migration (local → remote)
67
78
  const normalizedLocal = local.normalizedDdl;
68
79
  const normalizedRemote = normalizeDDL(remote.ddl);
69
80
  const diff = (0, diff_1.diffLines)(normalizedLocal, normalizedRemote);
70
81
  const hasDiff = diff.some(part => part.added || part.removed);
71
- // 差分がある場合のみ処理・表示
72
82
  if (hasDiff) {
73
- // タイムスタンプ比較(ローカルファイルの実際の更新時刻も考慮)
74
- const isRemoteNewer = remote.timestamp > local.timestamp;
75
- const isLocalFileNewer = local.fileTimestamp > remote.timestamp;
76
- const timeDiff = remote.timestamp - local.timestamp;
77
- const fileDiff = local.fileTimestamp - remote.timestamp;
78
- const timeDiffHours = Math.abs(timeDiff) / 3600;
79
- const fileDiffHours = Math.abs(fileDiff) / 3600;
80
- // ローカルファイルの方が新しい場合はリモートへマイグレーション提案
81
- if (isLocalFileNewer) {
82
- console.log(`[${tableName}] ローカルが ${fileDiffHours.toFixed(1)}時間新しい - マイグレーション生成`);
83
- }
84
- else if (isRemoteNewer) {
85
- console.log(`[${tableName}] リモートが ${timeDiffHours.toFixed(1)}時間新しい - ローカル更新`);
86
- }
87
- else {
88
- console.log(`[${tableName}] 差分を検出 - 確認が必要`);
89
- }
90
- // 整形されたSQLで行単位の差分を表示
83
+ console.log(`[${tableName}] Differences detected - will generate UPDATE migration`);
84
+ // Show formatted diff (local → remote)
91
85
  const formattedLocal = formatSQL(normalizedLocal);
92
86
  const formattedRemote = formatSQL(normalizedRemote);
93
- // ローカルが新しい場合は、ローカル→リモートの差分(マイグレーション用)
94
- // リモートが新しい場合は、リモート→ローカルの差分(ローカル更新用)
95
- const lineDiff = isLocalFileNewer
96
- ? (0, diff_1.diffLines)(formattedRemote, formattedLocal) // ローカル→リモート(マイグレーション用)
97
- : (0, diff_1.diffLines)(formattedLocal, formattedRemote); // リモート→ローカル(ローカル更新用)
98
- // 前後のコンテキスト行数
87
+ const lineDiff = (0, diff_1.diffLines)(formattedRemote, formattedLocal);
88
+ // Display diff for confirmation
99
89
  const contextLines = 1;
100
- let lineNumber = 0;
101
90
  let outputLines = [];
102
91
  lineDiff.forEach((part, index) => {
103
92
  const lines = part.value.split('\n').filter(line => line.trim() || index === lineDiff.length - 1);
104
93
  if (part.added) {
105
- // リモートから追加される行
106
94
  lines.forEach(line => {
107
95
  if (line.trim()) {
108
96
  outputLines.push(`\x1b[32m+ ${line}\x1b[0m`);
@@ -110,99 +98,46 @@ async function syncAllTables({ connectionString, schemaDir, tablePattern = '*',
110
98
  });
111
99
  }
112
100
  else if (part.removed) {
113
- // ローカルから削除される行
114
101
  lines.forEach(line => {
115
102
  if (line.trim()) {
116
103
  outputLines.push(`\x1b[31m- ${line}\x1b[0m`);
117
104
  }
118
105
  });
119
106
  }
120
- else {
121
- // 変更されていない行(コンテキスト)
122
- const unchangedLines = lines.filter(line => line.trim());
123
- // 差分の前後で適切にコンテキストを表示
124
- const hasChangeBefore = index > 0 && (lineDiff[index - 1].added || lineDiff[index - 1].removed);
125
- const hasChangeAfter = index < lineDiff.length - 1 && (lineDiff[index + 1].added || lineDiff[index + 1].removed);
126
- if (hasChangeBefore && hasChangeAfter) {
127
- // 前後に変更がある場合は全行表示
128
- unchangedLines.forEach(line => {
129
- outputLines.push(` ${line}`);
130
- });
131
- }
132
- else if (hasChangeBefore) {
133
- // 前に変更がある場合は最初の数行のみ表示
134
- const showLines = Math.min(contextLines, unchangedLines.length);
135
- for (let i = 0; i < showLines; i++) {
136
- outputLines.push(` ${unchangedLines[i]}`);
137
- }
138
- if (unchangedLines.length > contextLines) {
139
- outputLines.push(` \x1b[36m...(${unchangedLines.length - contextLines} more lines)...\x1b[0m`);
140
- }
141
- }
142
- else if (hasChangeAfter) {
143
- // 後に変更がある場合は最後の数行のみ表示
144
- const showLines = Math.min(contextLines, unchangedLines.length);
145
- const startIndex = unchangedLines.length - showLines;
146
- // 冒頭のCREATE TABLE行は常に表示
147
- const hasCreateTable = unchangedLines.some(line => line.trim().toUpperCase().startsWith('CREATE TABLE'));
148
- if (hasCreateTable && startIndex > 0) {
149
- // CREATE TABLE行を探して表示
150
- const createTableIndex = unchangedLines.findIndex(line => line.trim().toUpperCase().startsWith('CREATE TABLE'));
151
- if (createTableIndex >= 0) {
152
- outputLines.push(` ${unchangedLines[createTableIndex]}`);
153
- if (createTableIndex < startIndex - 1) {
154
- outputLines.push(` \x1b[36m...(${startIndex - createTableIndex - 1} lines)...\x1b[0m`);
155
- }
156
- }
157
- }
158
- else if (startIndex > 0) {
159
- outputLines.push(` \x1b[36m...(${startIndex} lines)...\x1b[0m`);
160
- }
161
- for (let i = startIndex; i < unchangedLines.length; i++) {
162
- outputLines.push(` ${unchangedLines[i]}`);
163
- }
164
- }
165
- else if (unchangedLines.length <= contextLines * 2) {
166
- // 短い場合は全部表示
167
- unchangedLines.forEach(line => {
168
- outputLines.push(` ${line}`);
169
- });
170
- }
171
- else {
172
- // 長い場合でも冒頭のCREATE TABLE行は表示
173
- const hasCreateTable = unchangedLines.some(line => line.trim().toUpperCase().startsWith('CREATE TABLE'));
174
- if (hasCreateTable) {
175
- const createTableIndex = unchangedLines.findIndex(line => line.trim().toUpperCase().startsWith('CREATE TABLE'));
176
- if (createTableIndex >= 0) {
177
- outputLines.push(` ${unchangedLines[createTableIndex]}`);
178
- if (unchangedLines.length > 1) {
179
- outputLines.push(` \x1b[36m...(${unchangedLines.length - 1} unchanged lines)...\x1b[0m`);
180
- }
181
- }
182
- }
183
- else {
184
- outputLines.push(` \x1b[36m...(${unchangedLines.length} unchanged lines)...\x1b[0m`);
185
- }
186
- }
187
- }
188
107
  });
189
- outputLines.forEach(line => console.log(line));
190
- if (isLocalFileNewer) {
191
- // マイグレーションファイルを生成(ローカル→リモートの差分)
192
- const migrationPath = await (0, generateMigration_1.generateMigrationFile)(tableName, normalizedRemote, // from(現在のリモート状態)
193
- normalizedLocal, // to(ローカルの目標状態)
194
- process.cwd());
108
+ if (outputLines.length > 0) {
109
+ console.log(`[${tableName}] Changes to apply:`);
110
+ outputLines.forEach(line => console.log(line));
111
+ }
112
+ // Generate or preview migration
113
+ if (dryRun) {
114
+ console.log(`[${tableName}] 🔍 UPDATE migration will be generated`);
115
+ console.log(`[${tableName}] 📄 File to be generated: supabase/migrations/YYYYMMDDHHMMSS_update_${tableName}.sql`);
116
+ }
117
+ else if (generateOnly) {
118
+ console.log(`[${tableName}] 📝 generateOnly mode: skipping migration generation`);
195
119
  }
196
120
  else {
197
- // リモートが新しいかタイムスタンプが同じ場合:ローカルを更新
198
- const shouldAutoUpdate = isRemoteNewer && !isLocalFileNewer;
199
- const success = await (0, writeSchema_1.writeSchemaToFile)(remote.ddl, schemaDir, remote.timestamp, `${tableName}.sql`, tableName, shouldAutoUpdate);
200
- if (success) {
201
- console.log(`[${tableName}] ローカルファイルを更新しました`);
121
+ // Confirmation check for confirmation mode
122
+ if (requireConfirmation && !force) {
123
+ const confirmed = await (0, utils_1.askUserConfirmation)(`Generate UPDATE migration for table ${tableName}?`);
124
+ if (!confirmed) {
125
+ console.log(`[${tableName}] Skipped by user confirmation`);
126
+ continue;
127
+ }
128
+ }
129
+ // Generate migration file (local → remote diff)
130
+ const migrationPath = await (0, generateMigration_1.generateMigrationFile)(tableName, normalizedRemote, // from (current remote state)
131
+ normalizedLocal, // to (local target state)
132
+ process.cwd());
133
+ if (migrationPath) {
134
+ console.log(`[${tableName}] 📝 UPDATE migration generated: ${migrationPath}`);
202
135
  }
203
136
  }
204
137
  }
205
- // 差分がない場合は何も表示せず、何もしない
138
+ else {
139
+ console.log(`[${tableName}] No differences found`);
140
+ }
206
141
  }
207
142
  }
208
143
  }
@@ -3,23 +3,35 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.wildcardMatch = wildcardMatch;
5
5
  exports.askUserConfirmation = askUserConfirmation;
6
- // ワイルドカードマッチング関数
6
+ // Wildcard matching function
7
7
  function wildcardMatch(str, pattern) {
8
- return pattern === '*' || str.includes(pattern);
8
+ if (pattern === '*') {
9
+ return true;
10
+ }
11
+ // Exact match if pattern contains no wildcards
12
+ if (!pattern.includes('*') && !pattern.includes('?')) {
13
+ return str === pattern;
14
+ }
15
+ // Simple wildcard pattern matching
16
+ const regexPattern = pattern
17
+ .replace(/\*/g, '.*')
18
+ .replace(/\?/g, '.');
19
+ const regex = new RegExp(`^${regexPattern}$`);
20
+ return regex.test(str);
9
21
  }
10
22
  /**
11
- * ユーザーに確認を求める(1文字入力で即座に判定)
23
+ * Ask user for confirmation (immediate single character input)
12
24
  */
13
25
  function askUserConfirmation(message, options) {
14
26
  return new Promise((resolve) => {
15
27
  const promptMessage = options?.message || '(y/N): ';
16
28
  process.stdout.write(`${message} ${promptMessage}`);
17
- // rawモードを有効にして1文字入力を受け取る
29
+ // Enable raw mode to receive single character input
18
30
  process.stdin.setRawMode(true);
19
31
  process.stdin.resume();
20
32
  process.stdin.setEncoding('utf8');
21
33
  const onData = (key) => {
22
- // クリーンアップ
34
+ // Cleanup
23
35
  process.stdin.setRawMode(false);
24
36
  process.stdin.pause();
25
37
  process.stdin.removeListener('data', onData);
@@ -33,7 +45,7 @@ function askUserConfirmation(message, options) {
33
45
  resolve('all');
34
46
  }
35
47
  else if (lowerKey === 'n' || key === '\r' || key === '\n' || key === '\u0003') {
36
- // nEnter、または Ctrl+C
48
+ // n, Enter, or Ctrl+C
37
49
  if (key === '\u0003') {
38
50
  console.log('\nOperation cancelled');
39
51
  process.exit(0);
@@ -42,7 +54,7 @@ function askUserConfirmation(message, options) {
42
54
  resolve(false);
43
55
  }
44
56
  else {
45
- // 無効なキー:デフォルトでfalse
57
+ // Invalid key: default to false
46
58
  console.log(`${key} (invalid input, treating as N)`);
47
59
  resolve(false);
48
60
  }
@@ -40,16 +40,16 @@ exports.moveToBackup = moveToBackup;
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const utils_1 = require("./utils");
43
- // グローバルな「全て承認」状態を管理
43
+ // Manage global "approve all" state
44
44
  let globalApproveAll = false;
45
45
  /**
46
- * 全て承認モードをリセット
46
+ * Reset approve all mode
47
47
  */
48
48
  function resetApprovalState() {
49
49
  globalApproveAll = false;
50
50
  }
51
51
  /**
52
- * 既存ファイルをバックアップディレクトリに移動
52
+ * Move existing file to backup directory
53
53
  */
54
54
  async function backupExistingFile(filePath, schemaDir, force = false) {
55
55
  if (!fs.existsSync(filePath)) {
@@ -57,16 +57,16 @@ async function backupExistingFile(filePath, schemaDir, force = false) {
57
57
  }
58
58
  if (!force && !globalApproveAll) {
59
59
  const fileName = path.basename(filePath);
60
- const response = await (0, utils_1.askUserConfirmation)(`↓ 既存ファイル ${fileName} を上書きしますか?`, {
60
+ const response = await (0, utils_1.askUserConfirmation)(`↓ Overwrite existing file ${fileName}?`, {
61
61
  allowAll: true,
62
62
  message: '(y/N/a[all]): '
63
63
  });
64
64
  if (response === 'all') {
65
65
  globalApproveAll = true;
66
- console.log('✅ すべてのファイルを承認します');
66
+ console.log('✅ Approving all files');
67
67
  }
68
68
  else if (response === false) {
69
- console.log('スキップしました');
69
+ console.log('Skipped');
70
70
  return false;
71
71
  }
72
72
  }
@@ -79,11 +79,11 @@ async function backupExistingFile(filePath, schemaDir, force = false) {
79
79
  const backupFileName = `${timestamp}_${fileName}`;
80
80
  const backupPath = path.join(backupDir, backupFileName);
81
81
  fs.renameSync(filePath, backupPath);
82
- console.log(`既存ファイルをバックアップしました: ${backupPath}`);
82
+ console.log(`Existing file backed up: ${backupPath}`);
83
83
  return true;
84
84
  }
85
85
  /**
86
- * 存在しないテーブルのファイルをバックアップに移動
86
+ * Move files for non-existent tables to backup
87
87
  */
88
88
  async function backupOrphanedFiles(schemaDir, existingTables, force = false) {
89
89
  if (!fs.existsSync(schemaDir)) {
@@ -103,48 +103,48 @@ async function backupOrphanedFiles(schemaDir, existingTables, force = false) {
103
103
  return;
104
104
  }
105
105
  if (!force) {
106
- console.log('\n以下のファイルは存在しないテーブルに対応しています:');
106
+ console.log('\nThe following files correspond to non-existent tables:');
107
107
  orphanedFiles.forEach(file => console.log(` - ${file}`));
108
- const confirmed = await (0, utils_1.askUserConfirmation)('これらのファイルをバックアップフォルダに移動しますか?');
108
+ const confirmed = await (0, utils_1.askUserConfirmation)('Move these files to backup folder?');
109
109
  if (!confirmed) {
110
- console.log('孤児ファイルの移動をスキップしました');
110
+ console.log('Skipped moving orphaned files');
111
111
  return;
112
112
  }
113
113
  }
114
114
  for (const file of orphanedFiles) {
115
115
  const tableName = path.basename(file, '.sql');
116
116
  const filePath = path.join(schemaDir, file);
117
- console.log(`[${tableName}] 存在しないテーブルのファイルをバックアップ`);
118
- await backupExistingFile(filePath, schemaDir, true); // 確認済みなのでforce=true
117
+ console.log(`[${tableName}] Backing up file for non-existent table`);
118
+ await backupExistingFile(filePath, schemaDir, true); // force=true since already confirmed
119
119
  }
120
120
  }
121
121
  /**
122
- * スキーマをファイルに書き込み
122
+ * Write schema to file
123
123
  */
124
124
  async function writeSchemaToFile(ddl, schemaDir, timestamp, fileName, tableName, force = false) {
125
125
  if (!fs.existsSync(schemaDir)) {
126
126
  fs.mkdirSync(schemaDir, { recursive: true });
127
127
  }
128
128
  const filePath = path.join(schemaDir, fileName);
129
- // 既存ファイルがある場合はバックアップ
129
+ // Backup existing file if exists
130
130
  const canProceed = await backupExistingFile(filePath, schemaDir, force);
131
131
  if (!canProceed) {
132
132
  return false;
133
133
  }
134
- // リモートデータベースの最終更新時刻を記録(比較用メタデータ - 編集禁止)
134
+ // Record remote database last updated time (metadata for comparison - do not edit)
135
135
  const remoteTimestamp = `-- Remote last updated: ${new Date(timestamp * 1000).toISOString()}\n`;
136
- // 同期実行時刻(参考情報)
136
+ // Sync execution time (reference info)
137
137
  const syncTimestamp = `-- Synced by supatool at: ${new Date().toISOString()}\n`;
138
- // 警告コメント
139
- const warningComment = `-- ⚠️ このファイルはsupatoolによって自動生成されます。手動編集は同期時に失われる可能性があります。\n`;
138
+ // Warning comment
139
+ const warningComment = `-- ⚠️ This file is auto-generated by supatool. Manual edits may be lost during sync.\n`;
140
140
  const tableComment = `-- Table: ${tableName}\n\n`;
141
141
  const finalContent = remoteTimestamp + syncTimestamp + warningComment + tableComment + ddl;
142
142
  fs.writeFileSync(filePath, finalContent, 'utf-8');
143
- console.log(`📁 ${tableName}.sql に保存しました`);
143
+ console.log(`📁 Saved to ${tableName}.sql`);
144
144
  return true;
145
145
  }
146
146
  /**
147
- * 孤立したファイルをバックアップフォルダに移動
147
+ * Move orphaned files to backup folder
148
148
  */
149
149
  function moveToBackup(schemaDir, fileName) {
150
150
  const backupDir = path.join(schemaDir, 'backup');
@@ -156,6 +156,6 @@ function moveToBackup(schemaDir, fileName) {
156
156
  const backupFile = path.join(backupDir, `${path.basename(fileName, '.sql')}_${timestamp}.sql`);
157
157
  if (fs.existsSync(sourceFile)) {
158
158
  fs.renameSync(sourceFile, backupFile);
159
- console.log(`📦 ${fileName} ${backupFile} にバックアップしました`);
159
+ console.log(`📦 Backed up ${fileName} to ${backupFile}`);
160
160
  }
161
161
  }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "supatool",
3
- "version": "0.3.7",
4
- "description": "A CLI tool for Supabase schema extraction and TypeScript CRUD generation with declarative database schema support.",
3
+ "version": "0.4.0",
4
+ "description": "CLI for Supabase: extract schema (tables, views, RLS, RPC) to files + llms.txt for LLM, deploy local schema, seed export. CRUD code gen deprecated.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "bin": {
8
8
  "supatool": "dist/bin/supatool.js"
9
9
  },
10
- "scripts": {
11
- "build": "tsc",
12
- "postbuild": "npm link",
13
- "start": "tsx bin/supatool.ts"
14
- },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "tsx src/bin/supatool.ts",
13
+ "local": "tsx src/bin/supatool.ts"
14
+ },
15
15
  "files": [
16
16
  "dist",
17
17
  "bin"
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@types/diff": "^7.0.2",
45
45
  "@types/js-yaml": "^4.0.9",
46
- "@types/node": "^20.17.30",
46
+ "@types/node": "^20.19.22",
47
47
  "@types/pg": "^8.11.0"
48
48
  },
49
49
  "exports": {