sql-kite 1.0.6 → 1.0.8

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.
Files changed (89) hide show
  1. package/README.md +10 -2
  2. package/package.json +12 -4
  3. package/server/db/connections.js +114 -0
  4. package/server/db/meta-db.js +0 -0
  5. package/server/db/user-db.js +0 -0
  6. package/server/index.js +214 -0
  7. package/server/routes/branches.js +484 -0
  8. package/server/routes/compare.js +109 -0
  9. package/server/routes/export.js +201 -0
  10. package/server/routes/import.js +375 -0
  11. package/server/routes/migrations.js +332 -0
  12. package/server/routes/query.js +67 -0
  13. package/server/routes/schema.js +206 -0
  14. package/server/routes/snapshots.js +322 -0
  15. package/server/routes/tables.js +121 -0
  16. package/server/routes/timeline.js +108 -0
  17. package/server/server.js +0 -0
  18. package/src/commands/import-server.js +2 -5
  19. package/src/commands/import.js +5 -9
  20. package/src/commands/start.js +6 -7
  21. package/src/commands/stop.js +7 -2
  22. package/src/utils/paths.js +61 -1
  23. package/studio-out/404/index.html +1 -0
  24. package/studio-out/404.html +1 -0
  25. package/studio-out/__next.__PAGE__.txt +10 -0
  26. package/studio-out/__next._full.txt +20 -0
  27. package/studio-out/__next._head.txt +6 -0
  28. package/studio-out/__next._index.txt +6 -0
  29. package/studio-out/__next._tree.txt +3 -0
  30. package/studio-out/_next/static/LhecVBdPttfi1VZfXA-dL/_buildManifest.js +11 -0
  31. package/studio-out/_next/static/LhecVBdPttfi1VZfXA-dL/_clientMiddlewareManifest.json +1 -0
  32. package/studio-out/_next/static/LhecVBdPttfi1VZfXA-dL/_ssgManifest.js +1 -0
  33. package/studio-out/_next/static/chunks/118fc599da2f27aa.css +2 -0
  34. package/studio-out/_next/static/chunks/240f2fa81d4fb687.js +1 -0
  35. package/studio-out/_next/static/chunks/42c33ca704af9b68.js +1 -0
  36. package/studio-out/_next/static/chunks/99b69e65b599be96.js +5 -0
  37. package/studio-out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  38. package/studio-out/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  39. package/studio-out/_next/static/chunks/b20313408e970968.css +1 -0
  40. package/studio-out/_next/static/chunks/d104f42a7b0c57b2.js +2 -0
  41. package/studio-out/_next/static/chunks/d4aa9be9c80c98d6.js +1 -0
  42. package/studio-out/_next/static/chunks/f2f58a7e93290fbb.js +1 -0
  43. package/studio-out/_next/static/chunks/f547e106c8e2aa8e.js +1 -0
  44. package/studio-out/_next/static/chunks/f5cb054219e2eeb8.js +109 -0
  45. package/studio-out/_next/static/chunks/turbopack-1577480078e795df.js +4 -0
  46. package/studio-out/_not-found/__next._full.txt +15 -0
  47. package/studio-out/_not-found/__next._head.txt +6 -0
  48. package/studio-out/_not-found/__next._index.txt +6 -0
  49. package/studio-out/_not-found/__next._not-found/__PAGE__.txt +5 -0
  50. package/studio-out/_not-found/__next._not-found.txt +4 -0
  51. package/studio-out/_not-found/__next._tree.txt +2 -0
  52. package/studio-out/_not-found/index.html +1 -0
  53. package/studio-out/_not-found/index.txt +15 -0
  54. package/studio-out/favicon.ico +10 -0
  55. package/studio-out/index.html +37 -0
  56. package/studio-out/index.txt +20 -0
  57. package/studio-out/logo.svg +5 -0
  58. package/studio-out/snapshots/__next._full.txt +15 -0
  59. package/studio-out/snapshots/__next._head.txt +6 -0
  60. package/studio-out/snapshots/__next._index.txt +6 -0
  61. package/studio-out/snapshots/__next._tree.txt +2 -0
  62. package/studio-out/snapshots/__next.snapshots/__PAGE__.txt +5 -0
  63. package/studio-out/snapshots/__next.snapshots.txt +4 -0
  64. package/studio-out/snapshots/index.html +1 -0
  65. package/studio-out/snapshots/index.txt +15 -0
  66. package/studio-out/sql/__next._full.txt +15 -0
  67. package/studio-out/sql/__next._head.txt +6 -0
  68. package/studio-out/sql/__next._index.txt +6 -0
  69. package/studio-out/sql/__next._tree.txt +2 -0
  70. package/studio-out/sql/__next.sql/__PAGE__.txt +5 -0
  71. package/studio-out/sql/__next.sql.txt +4 -0
  72. package/studio-out/sql/index.html +1 -0
  73. package/studio-out/sql/index.txt +15 -0
  74. package/studio-out/tables/__next._full.txt +15 -0
  75. package/studio-out/tables/__next._head.txt +6 -0
  76. package/studio-out/tables/__next._index.txt +6 -0
  77. package/studio-out/tables/__next._tree.txt +2 -0
  78. package/studio-out/tables/__next.tables/__PAGE__.txt +5 -0
  79. package/studio-out/tables/__next.tables.txt +4 -0
  80. package/studio-out/tables/index.html +1 -0
  81. package/studio-out/tables/index.txt +15 -0
  82. package/studio-out/timeline/__next._full.txt +15 -0
  83. package/studio-out/timeline/__next._head.txt +6 -0
  84. package/studio-out/timeline/__next._index.txt +6 -0
  85. package/studio-out/timeline/__next._tree.txt +2 -0
  86. package/studio-out/timeline/__next.timeline/__PAGE__.txt +5 -0
  87. package/studio-out/timeline/__next.timeline.txt +4 -0
  88. package/studio-out/timeline/index.html +1 -0
  89. package/studio-out/timeline/index.txt +15 -0
package/README.md CHANGED
@@ -164,8 +164,16 @@ SQL Kite runs **entirely on your machine**.
164
164
 
165
165
  ### Requirements
166
166
 
167
- * Node.js 18+
168
- * npm
167
+ * **Node.js 18** (LTS recommended)
168
+ * **npm**
169
+
170
+ > **Important:** SQL Kite uses [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3), a native C++ module. Node.js 20+ may have compatibility issues with the current version. **Node.js 18 LTS is recommended** for the best experience.
171
+
172
+ **Windows users:** If installation fails, you may need native build tools:
173
+ 1. Re-run the Node.js installer and check **"Automatically install the necessary tools"**
174
+ 2. Or run `C:\Program Files\nodejs\install_tools.bat` to install Python and Visual Studio Build Tools
175
+
176
+ > **No special characters or spaces in your project path** — `node-gyp` may not handle them correctly.
169
177
 
170
178
  ### Option 1: Global Installation (Recommended)
171
179
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sql-kite",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "SQL-Kite CLI — Local-first SQLite workspace with branches, migrations and snapshots.",
5
5
  "type": "module",
6
6
  "preferGlobal": true,
@@ -9,14 +9,16 @@
9
9
  },
10
10
  "scripts": {
11
11
  "dev": "node bin/sql-kite.js",
12
- "start": "node bin/sql-kite.js"
12
+ "start": "node bin/sql-kite.js",
13
+ "prepublishOnly": "node scripts/prepare-publish.js"
13
14
  },
14
15
  "author": "D Krishna",
15
16
  "license": "MIT",
16
17
  "repository": {
17
18
  "type": "git",
18
19
  "url": "https://github.com/Ananta-V/sql-kite"
19
- }, "homepage": "https://github.com/Ananta-V/sql-kite#readme",
20
+ },
21
+ "homepage": "https://github.com/Ananta-V/sql-kite#readme",
20
22
  "bugs": {
21
23
  "url": "https://github.com/Ananta-V/sql-kite/issues"
22
24
  },
@@ -31,19 +33,25 @@
31
33
  "developer-tools"
32
34
  ],
33
35
  "engines": {
34
- "node": ">=18"
36
+ "node": ">=18 <=20"
35
37
  },
36
38
  "files": [
37
39
  "bin/",
38
40
  "src/",
41
+ "server/",
42
+ "studio-out/",
39
43
  "README.md"
40
44
  ],
41
45
  "dependencies": {
46
+ "@fastify/cors": "^10.0.1",
47
+ "@fastify/static": "^8.0.2",
42
48
  "better-sqlite3": "^9.2.2",
43
49
  "chalk": "^5.3.0",
44
50
  "commander": "^12.0.0",
51
+ "fastify": "^5.7.4",
45
52
  "find-free-port": "^2.0.0",
46
53
  "inquirer": "^9.2.12",
54
+ "nanoid": "^5.0.4",
47
55
  "open": "^10.0.3",
48
56
  "ora": "^8.0.1"
49
57
  }
@@ -0,0 +1,114 @@
1
+ import Database from 'better-sqlite3';
2
+ import { join } from 'path';
3
+
4
+ const connections = new Map();
5
+
6
+ /**
7
+ * Get the current active branch for a project
8
+ */
9
+ export function getCurrentBranch(projectPath) {
10
+ try {
11
+ const metaDb = getMetaDb(projectPath);
12
+ const result = metaDb.prepare(`
13
+ SELECT value FROM settings WHERE key = 'current_branch'
14
+ `).get();
15
+
16
+ return result ? result.value : 'main';
17
+ } catch (error) {
18
+ console.error('Error getting current branch:', error.message);
19
+ return 'main'; // Fallback to main
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Get user database for current branch
25
+ */
26
+ export function getUserDb(projectPath, branchName = null) {
27
+ const branch = branchName || getCurrentBranch(projectPath);
28
+ const key = `user-${projectPath}-${branch}`;
29
+
30
+ if (!connections.has(key)) {
31
+ const metaDb = getMetaDb(projectPath);
32
+
33
+ // Get the DB file for this branch
34
+ const branchInfo = metaDb.prepare(`
35
+ SELECT db_file FROM branches WHERE name = ?
36
+ `).get(branch);
37
+
38
+ if (!branchInfo) {
39
+ throw new Error(`Branch "${branch}" does not exist`);
40
+ }
41
+
42
+ const dbPath = join(projectPath, branchInfo.db_file);
43
+ const db = new Database(dbPath);
44
+ db.pragma('journal_mode = WAL');
45
+ connections.set(key, db);
46
+ }
47
+
48
+ return connections.get(key);
49
+ }
50
+
51
+ export function getMetaDb(projectPath) {
52
+ const key = `meta-${projectPath}`;
53
+
54
+ if (!connections.has(key)) {
55
+ const dbPath = join(projectPath, '.studio', 'meta.db');
56
+ const db = new Database(dbPath);
57
+ db.pragma('journal_mode = WAL');
58
+ connections.set(key, db);
59
+ }
60
+
61
+ return connections.get(key);
62
+ }
63
+
64
+ /**
65
+ * Close and remove connection for a specific branch
66
+ * Used when switching branches
67
+ */
68
+ export function closeBranchConnection(projectPath, branchName) {
69
+ const key = `user-${projectPath}-${branchName}`;
70
+
71
+ if (connections.has(key)) {
72
+ const db = connections.get(key);
73
+ db.close();
74
+ connections.delete(key);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Get a read-only user database connection for compare mode
80
+ */
81
+ export function getReadOnlyUserDb(projectPath, branchName) {
82
+ const branch = branchName || getCurrentBranch(projectPath);
83
+ const key = `readonly-${projectPath}-${branch}`;
84
+
85
+ if (!connections.has(key)) {
86
+ const metaDb = getMetaDb(projectPath);
87
+
88
+ const branchInfo = metaDb.prepare(`
89
+ SELECT db_file FROM branches WHERE name = ?
90
+ `).get(branch);
91
+
92
+ if (!branchInfo) {
93
+ throw new Error(`Branch "${branch}" does not exist`);
94
+ }
95
+
96
+ const dbPath = join(projectPath, branchInfo.db_file);
97
+ const db = new Database(dbPath, { readonly: true, fileMustExist: true });
98
+ db.pragma('query_only = ON');
99
+ db.pragma('busy_timeout = 2000');
100
+ connections.set(key, db);
101
+ }
102
+
103
+ return connections.get(key);
104
+ }
105
+
106
+ export function closeReadOnlyBranchConnection(projectPath, branchName) {
107
+ const key = `readonly-${projectPath}-${branchName}`;
108
+
109
+ if (connections.has(key)) {
110
+ const db = connections.get(key);
111
+ db.close();
112
+ connections.delete(key);
113
+ }
114
+ }
File without changes
File without changes
@@ -0,0 +1,214 @@
1
+ import Fastify from 'fastify';
2
+ import cors from '@fastify/cors';
3
+ import fastifyStatic from '@fastify/static';
4
+ import { join, dirname } from 'path';
5
+ import { fileURLToPath, pathToFileURL } from 'url';
6
+ import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
7
+ import { createRequire } from 'module';
8
+
9
+ import { getUserDb, getMetaDb, getCurrentBranch } from './db/connections.js';
10
+ import tablesRoutes from './routes/tables.js';
11
+ import queryRoutes from './routes/query.js';
12
+ import schemaRoutes from './routes/schema.js';
13
+ import timelineRoutes from './routes/timeline.js';
14
+ import migrationsRoutes from './routes/migrations.js';
15
+ import snapshotsRoutes from './routes/snapshots.js';
16
+ import branchesRoutes from './routes/branches.js';
17
+ import importRoutes from './routes/import.js';
18
+ import compareRoutes from './routes/compare.js';
19
+ import exportRoutes from './routes/export.js';
20
+
21
+ const require = createRequire(import.meta.url);
22
+ const __dirname = dirname(fileURLToPath(import.meta.url));
23
+
24
+ // Import meta migration - works in both layouts:
25
+ // monorepo: packages/server/src/ -> ../../cli/src/utils/meta-migration.js
26
+ // npm: sql-kite/server/ -> ../src/utils/meta-migration.js
27
+ let migrateMetaDb;
28
+ const metaMigrationPaths = [
29
+ join(__dirname, '..', 'src', 'utils', 'meta-migration.js'), // npm layout
30
+ join(__dirname, '..', '..', 'cli', 'src', 'utils', 'meta-migration.js') // monorepo
31
+ ];
32
+ for (const p of metaMigrationPaths) {
33
+ if (existsSync(p)) {
34
+ const mod = await import(pathToFileURL(p).href);
35
+ migrateMetaDb = mod.migrateMetaDb;
36
+ break;
37
+ }
38
+ }
39
+ if (!migrateMetaDb) {
40
+ console.error('Could not find meta-migration.js. Tried:', metaMigrationPaths);
41
+ process.exit(1);
42
+ }
43
+
44
+ const PROJECT_NAME = process.env.PROJECT_NAME;
45
+ const PROJECT_PATH = process.env.PROJECT_PATH;
46
+ const PORT = parseInt(process.env.PORT || '3000');
47
+ const IMPORT_MODE = process.env.IMPORT_MODE === 'true';
48
+
49
+ if (!IMPORT_MODE && (!PROJECT_NAME || !PROJECT_PATH)) {
50
+ console.error('Missing required environment variables: PROJECT_NAME, PROJECT_PATH');
51
+ console.error('Or set IMPORT_MODE=true to run in import-only mode');
52
+ process.exit(1);
53
+ }
54
+
55
+ // Run meta database migration before starting server (skip in import mode)
56
+ if (!IMPORT_MODE) {
57
+ const metaDbPath = join(PROJECT_PATH, '.studio', 'meta.db');
58
+ try {
59
+ migrateMetaDb(metaDbPath);
60
+ } catch (error) {
61
+ console.error('Failed to migrate meta database:', error);
62
+ process.exit(1);
63
+ }
64
+ }
65
+
66
+ const fastify = Fastify({
67
+ logger: {
68
+ level: 'info'
69
+ }
70
+ });
71
+
72
+ // CORS - Restrict to localhost origins only for security
73
+ // This prevents CSRF-style attacks from malicious websites
74
+ // Uses a function to allow any localhost port dynamically
75
+ await fastify.register(cors, {
76
+ origin: (origin, callback) => {
77
+ // Allow requests with no origin (same-origin, curl, Postman, etc.)
78
+ if (!origin) {
79
+ return callback(null, true);
80
+ }
81
+
82
+ // Allow any localhost or 127.0.0.1 origin (any port)
83
+ const localhostPattern = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/;
84
+ if (localhostPattern.test(origin)) {
85
+ return callback(null, true);
86
+ }
87
+
88
+ // Block all other origins
89
+ return callback(new Error('CORS not allowed'), false);
90
+ }
91
+ });
92
+
93
+ // Store project info in fastify instance (if not in import mode)
94
+ if (!IMPORT_MODE) {
95
+ fastify.decorate('projectName', PROJECT_NAME);
96
+ fastify.decorate('projectPath', PROJECT_PATH);
97
+ // API always queries 'main' branch only - branches are for Studio development only
98
+ fastify.decorate('getUserDb', () => getUserDb(PROJECT_PATH, 'main'));
99
+ fastify.decorate('getMetaDb', () => getMetaDb(PROJECT_PATH));
100
+ fastify.decorate('getCurrentBranch', () => getCurrentBranch(PROJECT_PATH));
101
+ }
102
+
103
+ // API Routes
104
+ fastify.register(importRoutes, { prefix: '/api/import' });
105
+
106
+ // Project-specific routes (only in project mode)
107
+ if (!IMPORT_MODE) {
108
+ fastify.register(branchesRoutes, { prefix: '/api/branches' });
109
+ fastify.register(tablesRoutes, { prefix: '/api/tables' });
110
+ fastify.register(queryRoutes, { prefix: '/api/query' });
111
+ fastify.register(schemaRoutes, { prefix: '/api/schema' });
112
+ fastify.register(timelineRoutes, { prefix: '/api/timeline' });
113
+ fastify.register(migrationsRoutes, { prefix: '/api/migrations' });
114
+ fastify.register(snapshotsRoutes, { prefix: '/api/snapshots' });
115
+ fastify.register(compareRoutes, { prefix: '/api/compare' });
116
+ fastify.register(exportRoutes, { prefix: '/api/export' });
117
+ }
118
+
119
+ // Project info endpoint (only in project mode)
120
+ if (!IMPORT_MODE) {
121
+ fastify.get('/api/project', async (request, reply) => {
122
+ const configPath = join(PROJECT_PATH, 'config.json');
123
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
124
+ const currentBranch = getCurrentBranch(PROJECT_PATH);
125
+
126
+ return {
127
+ name: PROJECT_NAME,
128
+ path: PROJECT_PATH,
129
+ port: PORT,
130
+ currentBranch,
131
+ ...config
132
+ };
133
+ });
134
+ } else {
135
+ // Import mode - minimal project info
136
+ fastify.get('/api/project', async (request, reply) => {
137
+ return {
138
+ name: 'Import Mode',
139
+ mode: 'import',
140
+ port: PORT
141
+ };
142
+ });
143
+ }
144
+
145
+ // Serve Studio static files - works in both layouts:
146
+ // monorepo: packages/server/src/ -> ../../studio/out
147
+ // npm: sql-kite/server/ -> ../studio-out
148
+ let studioPath = join(__dirname, '..', 'studio-out'); // npm layout
149
+ if (!existsSync(studioPath)) {
150
+ studioPath = join(__dirname, '..', '..', 'studio', 'out'); // monorepo layout
151
+ }
152
+
153
+ console.log('Looking for Studio at:', studioPath);
154
+ console.log('Studio exists:', existsSync(studioPath));
155
+
156
+ if (!existsSync(studioPath)) {
157
+ console.error('\n❌ ERROR: Studio build not found!');
158
+ console.error('Please run: cd packages/studio && npm run build\n');
159
+ process.exit(1);
160
+ }
161
+
162
+ await fastify.register(fastifyStatic, {
163
+ root: studioPath,
164
+ prefix: '/',
165
+ decorateReply: false,
166
+ index: 'index.html'
167
+ });
168
+
169
+ // Fallback to index.html for client-side routing
170
+ fastify.setNotFoundHandler((request, reply) => {
171
+ if (request.url.startsWith('/api')) {
172
+ reply.code(404).send({ error: 'API endpoint not found' });
173
+ } else {
174
+ reply.sendFile('index.html');
175
+ }
176
+ });
177
+
178
+ // Graceful shutdown
179
+ const closeGracefully = async (signal) => {
180
+ console.log(`\nReceived ${signal}, closing gracefully...`);
181
+
182
+ try {
183
+ // Close database connections
184
+ const userDb = getUserDb(PROJECT_PATH);
185
+ const metaDb = getMetaDb(PROJECT_PATH);
186
+ userDb.close();
187
+ metaDb.close();
188
+
189
+ // Remove server info file
190
+ const serverInfoPath = join(PROJECT_PATH, '.studio', 'server.json');
191
+ if (existsSync(serverInfoPath)) {
192
+ unlinkSync(serverInfoPath);
193
+ }
194
+ } catch (e) {
195
+ console.error('Error during shutdown:', e);
196
+ }
197
+
198
+ await fastify.close();
199
+ process.exit(0);
200
+ };
201
+
202
+ process.on('SIGTERM', closeGracefully);
203
+ process.on('SIGINT', closeGracefully);
204
+
205
+ // Start server
206
+ try {
207
+ await fastify.listen({ port: PORT, host: '0.0.0.0' });
208
+ console.log(`\n✓ Server started for project "${PROJECT_NAME}"`);
209
+ console.log(` URL: http://localhost:${PORT}`);
210
+ console.log(` Path: ${PROJECT_PATH}\n`);
211
+ } catch (err) {
212
+ fastify.log.error(err);
213
+ process.exit(1);
214
+ }