d1s 0.2.4 → 0.2.6

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 (176) hide show
  1. package/dist/.next/.next/BUILD_ID +1 -1
  2. package/dist/.next/.next/app-build-manifest.json +12 -12
  3. package/dist/.next/.next/app-path-routes-manifest.json +1 -1
  4. package/dist/.next/.next/build-manifest.json +2 -2
  5. package/dist/.next/.next/cache/.rscinfo +1 -1
  6. package/dist/.next/.next/cache/.tsbuildinfo +1 -1
  7. package/dist/.next/.next/cache/webpack/client-production/0.pack +0 -0
  8. package/dist/.next/.next/cache/webpack/client-production/1.pack +0 -0
  9. package/dist/.next/.next/cache/webpack/client-production/2.pack +0 -0
  10. package/dist/.next/.next/cache/webpack/client-production/3.pack +0 -0
  11. package/dist/.next/.next/cache/webpack/client-production/index.pack +0 -0
  12. package/dist/.next/.next/cache/webpack/client-production/index.pack.old +0 -0
  13. package/dist/.next/.next/cache/webpack/edge-server-production/index.pack +0 -0
  14. package/dist/.next/.next/cache/webpack/server-production/0.pack +0 -0
  15. package/dist/.next/.next/cache/webpack/server-production/1.pack +0 -0
  16. package/dist/.next/.next/cache/webpack/server-production/2.pack +0 -0
  17. package/dist/.next/.next/cache/webpack/server-production/index.pack +0 -0
  18. package/dist/.next/.next/cache/webpack/server-production/index.pack.old +0 -0
  19. package/dist/.next/.next/next-minimal-server.js.nft.json +1 -1
  20. package/dist/.next/.next/next-server.js.nft.json +1 -1
  21. package/dist/.next/.next/prerender-manifest.json +1 -1
  22. package/dist/.next/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/dist/.next/.next/server/app/_not-found.html +1 -1
  24. package/dist/.next/.next/server/app/_not-found.rsc +2 -2
  25. package/dist/.next/.next/server/app/api/cloudflare/route.js +1 -1
  26. package/dist/.next/.next/server/app/api/cloudflare/route.js.nft.json +1 -1
  27. package/dist/.next/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
  28. package/dist/.next/.next/server/app/api/databases/route.js +1 -1
  29. package/dist/.next/.next/server/app/api/databases/route.js.nft.json +1 -1
  30. package/dist/.next/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
  31. package/dist/.next/.next/server/app/api/migrations/sync/route.js +3 -3
  32. package/dist/.next/.next/server/app/api/migrations/sync/route.js.nft.json +1 -1
  33. package/dist/.next/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
  34. package/dist/.next/.next/server/app/api/migrations/sync-files/route.js +1 -1
  35. package/dist/.next/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -1
  36. package/dist/.next/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
  37. package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js +1 -1
  38. package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -1
  39. package/dist/.next/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
  40. package/dist/.next/.next/server/app/api/pull-remote/preview/route.js +1 -1
  41. package/dist/.next/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -1
  42. package/dist/.next/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
  43. package/dist/.next/.next/server/app/api/push-remote/confirm/route.js +2 -2
  44. package/dist/.next/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -1
  45. package/dist/.next/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
  46. package/dist/.next/.next/server/app/api/push-remote/preview/route.js +2 -2
  47. package/dist/.next/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -1
  48. package/dist/.next/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
  49. package/dist/.next/.next/server/app/api/query/route.js +2 -2
  50. package/dist/.next/.next/server/app/api/query/route.js.nft.json +1 -1
  51. package/dist/.next/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
  52. package/dist/.next/.next/server/app/api/tables/[name]/route.js +4 -4
  53. package/dist/.next/.next/server/app/api/tables/[name]/route.js.nft.json +1 -1
  54. package/dist/.next/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
  55. package/dist/.next/.next/server/app/api/tables/route.js +2 -2
  56. package/dist/.next/.next/server/app/api/tables/route.js.nft.json +1 -1
  57. package/dist/.next/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
  58. package/dist/.next/.next/server/app/index.html +3 -3
  59. package/dist/.next/.next/server/app/index.rsc +3 -3
  60. package/dist/.next/.next/server/app/page.js +8 -8
  61. package/dist/.next/.next/server/app/page_client-reference-manifest.js +1 -1
  62. package/dist/.next/.next/server/app-paths-manifest.json +3 -3
  63. package/dist/.next/.next/server/chunks/{801.js → 603.js} +1 -1
  64. package/dist/.next/.next/server/chunks/686.js +23 -0
  65. package/dist/.next/.next/server/pages/404.html +1 -1
  66. package/dist/.next/.next/server/pages/500.html +1 -1
  67. package/dist/.next/.next/server/server-reference-manifest.json +1 -1
  68. package/dist/.next/.next/server/webpack-runtime.js +1 -1
  69. package/dist/.next/.next/static/chunks/app/page-79049bfa6cc07e11.js +1 -0
  70. package/dist/.next/.next/static/css/cf41da7a0d47f779.css +3 -0
  71. package/dist/.next/.next/trace +3 -3
  72. package/dist/.next/BUILD_ID +1 -1
  73. package/dist/.next/app-build-manifest.json +18 -18
  74. package/dist/.next/app-path-routes-manifest.json +1 -1
  75. package/dist/.next/build-manifest.json +2 -2
  76. package/dist/.next/cache/.rscinfo +1 -1
  77. package/dist/.next/cache/.tsbuildinfo +1 -1
  78. package/dist/.next/cache/webpack/client-production/0.pack +0 -0
  79. package/dist/.next/cache/webpack/client-production/index.pack +0 -0
  80. package/dist/.next/cache/webpack/edge-server-production/index.pack +0 -0
  81. package/dist/.next/cache/webpack/server-production/0.pack +0 -0
  82. package/dist/.next/cache/webpack/server-production/index.pack +0 -0
  83. package/dist/.next/next-minimal-server.js.nft.json +1 -1
  84. package/dist/.next/next-server.js.nft.json +1 -1
  85. package/dist/.next/prerender-manifest.json +1 -1
  86. package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  87. package/dist/.next/server/app/_not-found.html +1 -1
  88. package/dist/.next/server/app/_not-found.rsc +2 -2
  89. package/dist/.next/server/app/api/cloudflare/route.js +1 -1
  90. package/dist/.next/server/app/api/cloudflare/route.js.nft.json +1 -1
  91. package/dist/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
  92. package/dist/.next/server/app/api/databases/route.js +1 -1
  93. package/dist/.next/server/app/api/databases/route.js.nft.json +1 -1
  94. package/dist/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
  95. package/dist/.next/server/app/api/migrations/sync/route.js +3 -3
  96. package/dist/.next/server/app/api/migrations/sync/route.js.nft.json +1 -1
  97. package/dist/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
  98. package/dist/.next/server/app/api/migrations/sync-files/route.js +1 -1
  99. package/dist/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -1
  100. package/dist/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
  101. package/dist/.next/server/app/api/pull-remote/confirm/route.js +1 -1
  102. package/dist/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -1
  103. package/dist/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
  104. package/dist/.next/server/app/api/pull-remote/preview/route.js +1 -1
  105. package/dist/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -1
  106. package/dist/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
  107. package/dist/.next/server/app/api/push-remote/confirm/route.js +2 -2
  108. package/dist/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -1
  109. package/dist/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
  110. package/dist/.next/server/app/api/push-remote/preview/route.js +2 -2
  111. package/dist/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -1
  112. package/dist/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
  113. package/dist/.next/server/app/api/query/route.js +2 -2
  114. package/dist/.next/server/app/api/query/route.js.nft.json +1 -1
  115. package/dist/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
  116. package/dist/.next/server/app/api/tables/[name]/route.js +4 -4
  117. package/dist/.next/server/app/api/tables/[name]/route.js.nft.json +1 -1
  118. package/dist/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
  119. package/dist/.next/server/app/api/tables/route.js +2 -2
  120. package/dist/.next/server/app/api/tables/route.js.nft.json +1 -1
  121. package/dist/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
  122. package/dist/.next/server/app/index.html +3 -3
  123. package/dist/.next/server/app/index.rsc +3 -3
  124. package/dist/.next/server/app/page.js +8 -8
  125. package/dist/.next/server/app/page_client-reference-manifest.js +1 -1
  126. package/dist/.next/server/app-paths-manifest.json +7 -7
  127. package/dist/.next/server/chunks/{801.js → 603.js} +11 -11
  128. package/dist/.next/server/chunks/686.js +23 -0
  129. package/dist/.next/server/pages/404.html +1 -1
  130. package/dist/.next/server/pages/500.html +1 -1
  131. package/dist/.next/server/server-reference-manifest.json +1 -1
  132. package/dist/.next/server/webpack-runtime.js +1 -1
  133. package/dist/.next/static/chunks/app/page-79049bfa6cc07e11.js +1 -0
  134. package/dist/.next/static/css/cf41da7a0d47f779.css +3 -0
  135. package/dist/.next/trace +14 -13
  136. package/dist/app/api/databases/route.js +1 -1
  137. package/dist/app/api/pull-remote/confirm/route.js +20 -20
  138. package/dist/app/api/push-remote/confirm/route.js +1 -1
  139. package/dist/app/api/push-remote/preview/route.js +1 -1
  140. package/dist/app/api/query/route.js +7 -7
  141. package/dist/app/app/page.jsx +3 -6
  142. package/dist/app/page.jsx +3 -6
  143. package/dist/d1-studio.js +42 -87
  144. package/dist/lib/find-wrangler-config.js +10 -9
  145. package/dist/lib/logger.js +19 -0
  146. package/package.json +3 -2
  147. package/scripts/studio.js +42 -87
  148. package/src/app/page.jsx +3 -6
  149. package/src/components/sidebar.jsx +108 -92
  150. package/src/lib/find-wrangler-config.js +10 -9
  151. package/src/lib/logger.js +19 -0
  152. package/dist/.next/.next/cache/webpack/client-production/4.pack +0 -0
  153. package/dist/.next/.next/cache/webpack/client-production/5.pack +0 -0
  154. package/dist/.next/.next/cache/webpack/client-production/6.pack +0 -0
  155. package/dist/.next/.next/cache/webpack/server-production/3.pack +0 -0
  156. package/dist/.next/.next/cache/webpack/server-production/4.pack +0 -0
  157. package/dist/.next/.next/cache/webpack/server-production/5.pack +0 -0
  158. package/dist/.next/.next/cache/webpack/server-production/6.pack +0 -0
  159. package/dist/.next/.next/server/chunks/973.js +0 -2
  160. package/dist/.next/.next/static/chunks/app/page-75081ac7a88cb706.js +0 -1
  161. package/dist/.next/.next/static/css/c51a7ef325e30c16.css +0 -3
  162. package/dist/.next/.next/static/fSVNB2iNhKcZdXXgESWZk/_buildManifest.js +0 -1
  163. package/dist/.next/.next/static/fSVNB2iNhKcZdXXgESWZk/_ssgManifest.js +0 -1
  164. package/dist/.next/.next/static/xzPddcyKcodbwNX_sKljr/_buildManifest.js +0 -1
  165. package/dist/.next/.next/static/xzPddcyKcodbwNX_sKljr/_ssgManifest.js +0 -1
  166. package/dist/.next/server/chunks/973.js +0 -2
  167. package/dist/.next/static/OmPVuV0d1lrcjSBQzxkdH/_buildManifest.js +0 -1
  168. package/dist/.next/static/OmPVuV0d1lrcjSBQzxkdH/_ssgManifest.js +0 -1
  169. package/dist/.next/static/chunks/app/page-75081ac7a88cb706.js +0 -1
  170. package/dist/.next/static/css/c51a7ef325e30c16.css +0 -3
  171. /package/dist/.next/.next/static/{ODMSSwVqLoDcBgbFHUjr8 → HsZ8OanS5v86DqC9rErO-}/_buildManifest.js +0 -0
  172. /package/dist/.next/.next/static/{ODMSSwVqLoDcBgbFHUjr8 → HsZ8OanS5v86DqC9rErO-}/_ssgManifest.js +0 -0
  173. /package/dist/.next/.next/static/{YIuXI2CxMwg2fuuUEBsXL → q6WmQmyONe88bMN0_jrC2}/_buildManifest.js +0 -0
  174. /package/dist/.next/.next/static/{YIuXI2CxMwg2fuuUEBsXL → q6WmQmyONe88bMN0_jrC2}/_ssgManifest.js +0 -0
  175. /package/dist/.next/{.next/static/_t0S4u4KzUwKctaxLKvx4 → static/Tk1fnz_CXLavm23ZEeChG}/_buildManifest.js +0 -0
  176. /package/dist/.next/{.next/static/_t0S4u4KzUwKctaxLKvx4 → static/Tk1fnz_CXLavm23ZEeChG}/_ssgManifest.js +0 -0
@@ -0,0 +1,19 @@
1
+ import winston from 'winston';
2
+
3
+ const { combine, timestamp, printf, colorize } = winston.format;
4
+
5
+ const logFormat = printf(({ level, message, timestamp }) => {
6
+ return `${timestamp} ${level}: ${message}`;
7
+ });
8
+
9
+ const logger = winston.createLogger({
10
+ level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
11
+ format: combine(
12
+ timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
13
+ colorize(),
14
+ logFormat
15
+ ),
16
+ transports: [new winston.transports.Console()]
17
+ });
18
+
19
+ export default logger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "d1s",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "description": "D1 Studio – a native studio for managing Cloudflare D1 databases.",
6
6
  "main": "./dist/d1-studio.js",
@@ -81,7 +81,8 @@
81
81
  "react-dom": "^18.2.0",
82
82
  "strip-json-comments": "^5.0.1",
83
83
  "tailwind-merge": "^2.6.0",
84
- "tailwindcss-animate": "^1.0.7"
84
+ "tailwindcss-animate": "^1.0.7",
85
+ "winston": "^3.17.0"
85
86
  },
86
87
  "devDependencies": {
87
88
  "@eslint/eslintrc": "^3",
package/scripts/studio.js CHANGED
@@ -11,6 +11,7 @@ import express from 'express';
11
11
  import { createServer } from 'http';
12
12
  import next from 'next';
13
13
  import { findWranglerConfig } from '../src/lib/find-wrangler-config.js';
14
+ import logger from '../src/lib/logger.js';
14
15
 
15
16
  // Compute the absolute path of this file and its directory.
16
17
  const __filename = fileURLToPath(import.meta.url);
@@ -46,14 +47,8 @@ async function validateLicenseRemote(licenseKey, email, hardwareId) {
46
47
  try {
47
48
  const response = await fetch(`${baseUrl}/api/validate-license`, {
48
49
  method: 'POST',
49
- headers: {
50
- 'Content-Type': 'application/json',
51
- },
52
- body: JSON.stringify({
53
- licenseKey,
54
- email,
55
- hardwareId,
56
- }),
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify({ licenseKey, email, hardwareId }),
57
52
  });
58
53
 
59
54
  const result = await response.json();
@@ -65,7 +60,6 @@ async function validateLicenseRemote(licenseKey, email, hardwareId) {
65
60
  );
66
61
  }
67
62
 
68
- // Check for subscription expiration if end_date is provided.
69
63
  if (result.end_date && new Date(result.end_date) < new Date()) {
70
64
  throw new LicenseError(
71
65
  'Your subscription has expired. Please renew your license to continue using D1 Studio.',
@@ -78,7 +72,6 @@ async function validateLicenseRemote(licenseKey, email, hardwareId) {
78
72
  if (error instanceof LicenseError) {
79
73
  throw error;
80
74
  }
81
-
82
75
  if (error.name === 'TypeError' && error.message.includes('fetch')) {
83
76
  throw new LicenseError(
84
77
  process.env.NODE_ENV === 'development'
@@ -87,7 +80,6 @@ async function validateLicenseRemote(licenseKey, email, hardwareId) {
87
80
  'CONNECTION_ERROR'
88
81
  );
89
82
  }
90
-
91
83
  throw new LicenseError(
92
84
  'An unexpected error occurred during license validation',
93
85
  'VALIDATION_ERROR'
@@ -106,7 +98,6 @@ async function checkLicenseFile() {
106
98
  if (licenseData.hardwareId !== hardwareId) {
107
99
  return null;
108
100
  }
109
-
110
101
  return licenseData;
111
102
  } catch (error) {
112
103
  return null;
@@ -127,22 +118,19 @@ async function saveLicenseFile(licenseData) {
127
118
 
128
119
  async function startServer(port, projectDirectory) {
129
120
  return new Promise((resolvePromise, rejectPromise) => {
130
- const dev = process.env.NODE_ENV === 'development';
131
-
132
- // Determine the directory where Next.js build is located:
133
- // In production (published package), __dirname is in the 'dist' folder so we use it directly.
134
- // In development, we assume the structure is different and go one level up.
121
+ // Determine Next.js app directory based on environment:
122
+ // In production (published package) __dirname is used.
123
+ // In development (npm link), assume the build is in the parent folder.
135
124
  const isProduction = process.env.NODE_ENV !== 'development';
136
125
  const nextAppDir = isProduction ? __dirname : join(__dirname, '..');
137
126
 
127
+ const dev = process.env.NODE_ENV === 'development';
138
128
  const app = next({
139
129
  dev,
140
130
  dir: nextAppDir,
141
131
  conf: {
142
132
  distDir: '.next',
143
- experimental: {
144
- appDir: true
145
- }
133
+ experimental: { appDir: true }
146
134
  }
147
135
  });
148
136
  const handle = app.getRequestHandler();
@@ -150,41 +138,29 @@ async function startServer(port, projectDirectory) {
150
138
  app.prepare()
151
139
  .then(() => {
152
140
  const server = express();
153
-
154
- // Set environment variables
155
141
  process.env.PORT = port.toString();
156
142
  process.env.D1_PROJECT_DIR = projectDirectory;
143
+ const httpServer = createServer((req, res) => handle(req, res));
157
144
 
158
- // Create HTTP server
159
- const httpServer = createServer((req, res) => {
160
- handle(req, res);
161
- });
162
-
163
- // Handle all requests through Next.js
164
- server.all('*', (req, res) => {
165
- return handle(req, res);
166
- });
145
+ server.all('*', (req, res) => handle(req, res));
167
146
 
168
- // Start listening
169
147
  httpServer.listen(port, (err) => {
170
148
  if (err) {
171
- console.error(chalk.red('Failed to start server:'), err);
149
+ logger.error(chalk.red(`Failed to start server: ${err}`));
172
150
  rejectPromise(err);
173
151
  return;
174
152
  }
175
-
176
- console.log(chalk.green(`Server started on port ${port}`));
153
+ logger.info(chalk.green(`Server started on port ${port}`));
177
154
  resolvePromise(httpServer);
178
155
  });
179
156
 
180
- // Handle server errors
181
157
  httpServer.on('error', (err) => {
182
- console.error(chalk.red('Server error:'), err);
158
+ logger.error(chalk.red(`Server error: ${err}`));
183
159
  rejectPromise(err);
184
160
  });
185
161
  })
186
162
  .catch((err) => {
187
- console.error(chalk.red('Failed to prepare Next.js app:'), err);
163
+ logger.error(chalk.red(`Failed to prepare Next.js app: ${err}`));
188
164
  rejectPromise(err);
189
165
  });
190
166
  });
@@ -192,17 +168,15 @@ async function startServer(port, projectDirectory) {
192
168
 
193
169
  async function startStudio() {
194
170
  try {
195
- console.log(chalk.green('Initializing D1 Studio...'));
171
+ logger.info(chalk.green('Initializing D1 Studio...'));
196
172
 
197
173
  // Validate license
198
174
  let licenseData = await checkLicenseFile();
199
- console.log(chalk.blue('License data found:'), licenseData);
175
+ logger.debug(chalk.blue(`License data found: ${JSON.stringify(licenseData)}`));
200
176
 
201
177
  const TWELVE_HOURS_MS = 43200000; // 12 hours
202
178
  let shouldRevalidate = true;
203
-
204
179
  if (licenseData) {
205
- // Use lastValidatedAt if available; otherwise, fall back to activatedAt.
206
180
  const lastCheck = new Date(licenseData.lastValidatedAt || licenseData.activatedAt);
207
181
  if (Date.now() - lastCheck.getTime() < TWELVE_HOURS_MS) {
208
182
  shouldRevalidate = false;
@@ -211,7 +185,7 @@ async function startStudio() {
211
185
 
212
186
  if (!licenseData || shouldRevalidate) {
213
187
  const { default: inquirer } = await import('inquirer');
214
- console.log(chalk.blue('\nPlease enter your D1 Studio license information:'));
188
+ logger.info(chalk.yellow('\nPlease enter your D1 Studio license information:'));
215
189
 
216
190
  const answers = await inquirer.prompt([
217
191
  {
@@ -231,10 +205,9 @@ async function startStudio() {
231
205
  },
232
206
  ]);
233
207
 
234
- console.log(chalk.blue('Entered license information:'), answers);
208
+ logger.debug(chalk.blue(`Entered license information: ${JSON.stringify(answers)}`));
235
209
 
236
210
  const hardwareId = await generateHardwareId();
237
-
238
211
  try {
239
212
  const validation = await validateLicenseRemote(
240
213
  answers.licenseKey,
@@ -242,51 +215,42 @@ async function startStudio() {
242
215
  hardwareId
243
216
  );
244
217
 
245
- // Optionally, you can store the returned end_date for future checks.
246
218
  licenseData = {
247
219
  licenseKey: answers.licenseKey,
248
220
  email: answers.email,
249
- hardwareId,
250
- licenseType: validation.licenseType,
251
221
  activatedAt: new Date().toISOString(),
252
- lastValidatedAt: new Date().toISOString(),
253
222
  endDate: validation.end_date || null
254
223
  };
255
224
 
256
225
  await saveLicenseFile(licenseData);
257
- console.log(chalk.green('\nLicense activated successfully!'));
226
+ logger.info(chalk.green('\nLicense activated successfully!'));
258
227
  } catch (error) {
259
- console.error(chalk.red('\nLicense validation failed:'), error.message);
228
+ logger.error(chalk.red(`\nLicense validation failed: ${error.message}`));
260
229
  process.exit(1);
261
230
  }
262
- } else {
263
- // console.log(chalk.blue('Using cached license data.'));
264
231
  }
265
232
 
266
233
  // Get wrangler configuration
267
234
  try {
268
235
  const userProjectPath = process.cwd();
269
-
270
236
  if (!userProjectPath) {
271
- console.error(chalk.red('Error: Could not determine project directory'));
237
+ logger.error(chalk.red('Error: Could not determine project directory'));
272
238
  process.exit(1);
273
239
  }
274
-
240
+
275
241
  const wranglerResult = await findWranglerConfig(userProjectPath);
276
-
277
242
  if (!wranglerResult || !wranglerResult.config) {
278
- console.error(chalk.red('\nError: No wrangler.toml found in current directory'));
279
- console.log(chalk.yellow('Make sure you are in a Cloudflare Workers project directory'));
280
- console.log(chalk.yellow('with a valid wrangler.toml file.'));
243
+ logger.error(chalk.red('\nError: No wrangler.toml found in current directory'));
244
+ logger.info(chalk.yellow('Make sure you are in a Cloudflare Workers project directory'));
245
+ logger.info(chalk.yellow('with a valid wrangler.toml file.'));
281
246
  process.exit(1);
282
247
  }
283
-
248
+
284
249
  const { config, projectPath } = wranglerResult;
285
-
286
250
  if (!config.d1_databases?.length) {
287
- console.error(chalk.red('\nError: No D1 databases configured in wrangler.toml'));
288
- console.log(chalk.yellow('\nAdd D1 configuration to your wrangler.toml:'));
289
- console.log(chalk.gray(`
251
+ logger.error(chalk.red('\nError: No D1 databases configured in wrangler.toml'));
252
+ logger.info(chalk.yellow('\nAdd D1 configuration to your wrangler.toml:'));
253
+ logger.info(chalk.gray(`
290
254
  [[d1_databases]]
291
255
  binding = "DB"
292
256
  database_name = "my-database"
@@ -294,11 +258,10 @@ async function startStudio() {
294
258
  `));
295
259
  process.exit(1);
296
260
  }
297
-
298
- // Display found databases
299
- console.log(chalk.green('\nFound D1 databases:'));
261
+
262
+ logger.info(chalk.green('\nFound D1 databases:'));
300
263
  config.d1_databases.forEach((db) => {
301
- console.log(chalk.blue(` - ${db.database_name} (${db.binding})`));
264
+ logger.info(chalk.blue(` - ${db.database_name} (${db.binding})`));
302
265
  });
303
266
 
304
267
  const port = 4000;
@@ -306,7 +269,7 @@ async function startStudio() {
306
269
 
307
270
  await startServer(port, projectPath);
308
271
 
309
- console.log(chalk.green(`
272
+ logger.info(chalk.green(`
310
273
  ┌─────────────────────────────────────────┐
311
274
  │ │
312
275
  │ D1 Studio is running on port ${port} │
@@ -316,50 +279,42 @@ async function startStudio() {
316
279
  └─────────────────────────────────────────┘
317
280
  `));
318
281
 
319
- // Add delay before opening browser
320
282
  await new Promise(resolve => setTimeout(resolve, 2000));
321
283
 
322
284
  try {
323
285
  await open(url, {
324
286
  wait: false,
325
287
  url: true,
326
- app: {
327
- name: open.apps.chrome
328
- }
288
+ app: { name: open.apps.chrome }
329
289
  }).catch(() => {
330
- return open(url, {
331
- wait: false,
332
- url: true
333
- });
290
+ return open(url, { wait: false, url: true });
334
291
  });
335
292
  } catch (browserError) {
336
- console.log(chalk.yellow('\nCould not automatically open browser.'));
337
- console.log(chalk.blue('Please open your browser and navigate to:'), url);
293
+ logger.info(chalk.yellow('\nCould not automatically open browser.'));
294
+ logger.info(chalk.blue(`Please open your browser and navigate to: ${url}`));
338
295
  }
339
-
340
296
  } catch (configError) {
341
- console.error(chalk.red('\nConfiguration error:'), configError.message);
297
+ logger.error(chalk.red(`\nConfiguration error: ${configError.message}`));
342
298
  process.exit(1);
343
299
  }
344
-
345
300
  } catch (error) {
346
- console.error(chalk.red('\nFailed to start D1 Studio:'), error);
301
+ logger.error(chalk.red(`\nFailed to start D1 Studio: ${error}`));
347
302
  process.exit(1);
348
303
  }
349
304
  }
350
305
 
351
306
  // Handle termination
352
307
  process.on('SIGINT', () => {
353
- console.log(chalk.yellow('\nShutting down D1 Studio...'));
308
+ logger.info(chalk.yellow('\nShutting down D1 Studio...'));
354
309
  process.exit(0);
355
310
  });
356
311
 
357
312
  process.on('SIGTERM', () => {
358
- console.log(chalk.yellow('\nShutting down D1 Studio...'));
313
+ logger.info(chalk.yellow('\nShutting down D1 Studio...'));
359
314
  process.exit(0);
360
315
  });
361
316
 
362
317
  startStudio().catch((error) => {
363
- console.error(chalk.red('\nFailed to start D1 Studio:'), error);
318
+ logger.error(chalk.red(`\nFailed to start D1 Studio: ${error}`));
364
319
  process.exit(1);
365
320
  });
package/src/app/page.jsx CHANGED
@@ -10,23 +10,20 @@ export default function StudioPage() {
10
10
  const [currentView, setCurrentView] = useState('sql-editor');
11
11
  const [selectedTable, setSelectedTable] = useState(null);
12
12
 
13
- // We don't need to handle push errors here anymore since we're using toasts
14
- // The error handling is now managed by the useEnvironmentController hook
15
-
16
13
  const handleTableSelect = (tableName) => {
17
14
  setSelectedTable(tableName);
18
15
  setCurrentView('table-view');
19
16
  };
20
17
 
21
18
  return (
22
- <div className="flex h-screen bg-background">
19
+ <div className="min-h-screen bg-background">
23
20
  <AppSidebar
24
- className="w-64 border-r flex-shrink-0"
21
+ className="w-64 border-r"
25
22
  currentView={currentView}
26
23
  onNavigate={setCurrentView}
27
24
  onTableSelect={handleTableSelect}
28
25
  />
29
- <main className="flex-1 overflow-hidden">
26
+ <main className="ml-64 h-screen">
30
27
  {currentView === 'sql-editor' && <SQLEditor />}
31
28
  {currentView === 'table-view' && selectedTable && (
32
29
  <TableView tableName={selectedTable} />
@@ -1,7 +1,7 @@
1
1
  // components/sidebar.jsx
2
2
  'use client';
3
3
 
4
- import React, { useEffect, useState } from 'react';
4
+ import React, { useEffect, useState, useMemo } from 'react';
5
5
  import {
6
6
  Table2,
7
7
  Search,
@@ -42,12 +42,25 @@ export function AppSidebar({
42
42
  PreviewUI,
43
43
  } = useEnvironmentController();
44
44
 
45
- // Filter tables based on search term
46
- const filteredTables =
47
- tables?.filter((table) =>
45
+ // Updated filtering logic with d1_migrations priority
46
+ const filteredTables = useMemo(() => {
47
+ const filtered = tables?.filter((table) =>
48
48
  table.toLowerCase().includes(searchTerm.toLowerCase())
49
49
  ) || [];
50
50
 
51
+ // Sort all tables alphabetically first
52
+ const sortedTables = [...filtered].sort((a, b) => a.localeCompare(b));
53
+
54
+ // If d1_migrations exists in the filtered list, move it to the top
55
+ const d1MigrationsIndex = sortedTables.indexOf('d1_migrations');
56
+ if (d1MigrationsIndex !== -1) {
57
+ sortedTables.splice(d1MigrationsIndex, 1);
58
+ sortedTables.unshift('d1_migrations');
59
+ }
60
+
61
+ return sortedTables;
62
+ }, [tables, searchTerm]);
63
+
51
64
  const handleTableClick = async (table) => {
52
65
  try {
53
66
  setIsTableLoading(true);
@@ -120,23 +133,36 @@ export function AppSidebar({
120
133
 
121
134
  return (
122
135
  <>
123
- <aside className={`${className} bg-neutral-50 p-4`}>
124
- <div className="font-sans">
125
- {/* Header */}
126
- <div className="flex items-center gap-2 mb-6">
127
- <Table2 className="h-5 w-5" />
128
- <span className="font-mono font-bold text-sm">d1-studio</span>
129
- </div>
136
+ <aside className={`${className} w-64 fixed top-0 left-0 h-full bg-neutral-50 p-4`}>
137
+ <div className="flex flex-col h-full font-sans">
138
+ {/* Header */}
139
+ <div className="flex items-center gap-2 mb-6">
140
+ <Table2 className="h-5 w-5" />
141
+ <span className="font-mono font-bold text-sm">d1-studio</span>
142
+ </div>
130
143
 
131
- {/* Error Message */}
132
- {error && (
133
- <Alert variant="destructive" className="mb-4">
134
- <AlertCircle className="h-4 w-4" />
135
- <AlertDescription className="text-sm ml-2">{error}</AlertDescription>
136
- </Alert>
137
- )}
144
+ {/* SQL Editor Section */}
145
+ <div className="mb-6">
146
+ <button
147
+ className={`w-full flex items-center gap-x-1 border rounded-md text-sm font-medium transition-colors px-2 py-2
148
+ ${currentView === 'sql-editor' ? 'bg-neutral-900 text-white' : 'bg-white hover:bg-neutral-100'}`}
149
+ onClick={() => onNavigate('sql-editor')}
150
+ disabled={isTableLoading}
151
+ >
152
+ <TerminalSquare className="h-[16px] w-[16px]" />
153
+ SQL editor
154
+ </button>
155
+ </div>
138
156
 
139
- {/* Database Section */}
157
+ {/* Error Message */}
158
+ {error && (
159
+ <Alert variant="destructive" className="mb-4">
160
+ <AlertCircle className="h-4 w-4" />
161
+ <AlertDescription className="text-sm ml-2">{error}</AlertDescription>
162
+ </Alert>
163
+ )}
164
+
165
+ {/* Database Section */}
140
166
  <div className="flex items-center justify-between mb-6">
141
167
  <div>
142
168
  <div className="text-xs text-muted-foreground mb-1">Database name</div>
@@ -144,7 +170,7 @@ export function AppSidebar({
144
170
  {databases[0]?.database_name || 'No database found'}
145
171
  </div>
146
172
  </div>
147
-
173
+
148
174
  <EnvironmentSelect
149
175
  value={environment}
150
176
  onValueChange={handleEnvironmentChange}
@@ -153,84 +179,74 @@ export function AppSidebar({
153
179
  showPreview={showPreview}
154
180
  />
155
181
  </div>
156
-
157
- {/* SQL Editor Section */}
158
- <div className="mb-6">
159
- <button
160
- className={`w-full flex items-center gap-x-1 border rounded-md text-sm font-medium transition-colors px-2 py-2
161
- ${currentView === 'sql-editor' ? 'bg-neutral-900 text-white' : 'bg-white hover:bg-neutral-100'}`}
162
- onClick={() => onNavigate('sql-editor')}
163
- disabled={isTableLoading}
164
- >
165
- <TerminalSquare className="h-[16px] w-[16px]" />
166
- SQL editor
167
- </button>
168
- </div>
169
-
170
- {/* Search Section */}
171
- <div className="relative mb-6 bg-white">
172
- <Search className="absolute left-2 top-2.5 h-4 w-4 text-neutral-500" />
173
- <Input
174
- placeholder="Search tables"
175
- value={searchTerm}
176
- onChange={(e) => setSearchTerm(e.target.value)}
177
- className="pl-7"
178
- />
179
- </div>
180
-
181
- {/* Tables Section */}
182
- <div>
183
- <div className="flex items-center justify-between mb-1">
184
- <h3 className="text-sm font-medium text-muted-foreground">
185
- Tables {tables?.length ? `(${tables.length})` : ''}
186
- </h3>
187
- {isTablesLoading && <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />}
182
+
183
+ {/* Search Section */}
184
+ <div className="relative mb-6 bg-white">
185
+ <Search className="absolute left-2 top-2.5 h-4 w-4 text-neutral-500" />
186
+ <Input
187
+ placeholder="Search tables"
188
+ value={searchTerm}
189
+ onChange={(e) => setSearchTerm(e.target.value)}
190
+ className="pl-7"
191
+ />
188
192
  </div>
193
+
194
+ {/* Scrollable Tables Section - Updated */}
195
+ <div className="flex-1 min-h-0 flex flex-col">
196
+ {/* Tables Header */}
197
+ <div className="flex items-center justify-between mb-1 flex-none">
198
+ <h3 className="text-sm font-medium text-muted-foreground">
199
+ Tables {tables?.length ? `(${tables.length})` : ''}
200
+ </h3>
201
+ {isTablesLoading && <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />}
202
+ </div>
189
203
 
190
- {/* Tables List */}
191
- <div className="space-y-1 font-sans">
192
- {!isTablesLoading && filteredTables.length === 0 && (
193
- <div className="text-sm font-medium text-muted-foreground px-2 py-2">
194
- {searchTerm
195
- ? 'No matching tables found'
196
- : error
197
- ? 'Error loading tables'
198
- : 'No tables available'
199
- }
204
+ {/* Tables List - Scrollable - Updated with padding bottom */}
205
+ <div className="overflow-y-auto flex-1">
206
+ <div className="pr-2 pb-4"> {/* Added pb-4 for bottom padding */}
207
+ {!isTablesLoading && filteredTables.length === 0 && (
208
+ <div className="text-sm font-medium text-muted-foreground px-2 py-2">
209
+ {searchTerm
210
+ ? 'No matching tables found'
211
+ : error
212
+ ? 'Error loading tables'
213
+ : 'No tables available'
214
+ }
215
+ </div>
216
+ )}
217
+
218
+ {filteredTables.map((table) => (
219
+ <button
220
+ key={table}
221
+ className={`w-full text-left text-sm font-medium rounded-md transition-colors px-2 py-2
222
+ ${currentView === 'table-view' && selectedTable === table
223
+ ? 'bg-[#F0F0F0] text-neutral-900 font-semibold'
224
+ : 'text-neutral-700 hover:bg-[#F2F2F2]'}
225
+ ${isTableLoading && 'opacity-50 cursor-not-allowed'}`}
226
+ onClick={() => handleTableClick(table)}
227
+ disabled={isTableLoading}
228
+ >
229
+ {table}
230
+ </button>
231
+ ))}
200
232
  </div>
201
- )}
202
-
203
- {filteredTables.map((table) => (
204
- <button
205
- key={table}
206
- className={`w-full text-left text-sm font-medium rounded-md transition-colors px-2 py-2
207
- ${currentView === 'table-view' && selectedTable === table
208
- ? 'bg-[#F0F0F0] text-neutral-900 font-semibold'
209
- : 'text-neutral-700 hover:bg-[#F2F2F2]'}
210
- ${isTableLoading && 'opacity-50 cursor-not-allowed'}`}
211
- onClick={() => handleTableClick(table)}
212
- disabled={isTableLoading}
213
- >
214
- {table}
215
- </button>
216
- ))}
233
+ </div>
217
234
  </div>
218
235
  </div>
219
- </div>
220
-
221
- {/* Cloudflare Credentials Modal */}
222
- <CloudflareCredentialsModal
223
- open={showCredentialsModal}
224
- onClose={() => {
225
- setShowCredentialsModal(false);
226
- setEnvironment('local');
227
- }}
228
- onSubmit={connectToCloudflare}
229
- />
230
- </aside>
231
-
236
+
237
+ {/* Cloudflare Credentials Modal */}
238
+ <CloudflareCredentialsModal
239
+ open={showCredentialsModal}
240
+ onClose={() => {
241
+ setShowCredentialsModal(false);
242
+ setEnvironment('local');
243
+ }}
244
+ onSubmit={connectToCloudflare}
245
+ />
246
+ </aside>
247
+
232
248
  {/* Preview UI */}
233
249
  {PreviewUI}
234
250
  </>
235
- );
251
+ );
236
252
  }