d1s 0.2.9 → 0.2.11
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/.next/.next/BUILD_ID +1 -1
- package/dist/.next/.next/app-build-manifest.json +28 -28
- package/dist/.next/.next/app-path-routes-manifest.json +1 -1
- package/dist/.next/.next/build-manifest.json +3 -3
- package/dist/.next/.next/cache/.rscinfo +1 -1
- package/dist/.next/.next/cache/.tsbuildinfo +1 -1
- package/dist/.next/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/2.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/3.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/dist/.next/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/2.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/dist/.next/.next/next-minimal-server.js.nft.json +1 -1
- package/dist/.next/.next/next-server.js.nft.json +1 -1
- package/dist/.next/.next/prerender-manifest.json +1 -1
- package/dist/.next/.next/required-server-files.json +1 -1
- package/dist/.next/.next/server/app/_not-found/page.js +1 -1
- package/dist/.next/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/.next/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/_not-found.html +1 -1
- package/dist/.next/.next/server/app/_not-found.rsc +4 -4
- package/dist/.next/.next/server/app/api/cloudflare/route.js +1 -1
- package/dist/.next/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/databases/route.js +1 -1
- package/dist/.next/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/migrations/sync/route.js +3 -3
- package/dist/.next/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/migrations/sync-files/route.js +8 -8
- package/dist/.next/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/pull-data/confirm/route.js +1 -1
- package/dist/.next/.next/server/app/api/pull-data/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js +2 -2
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/pull-remote/preview/route.js +2 -2
- package/dist/.next/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/push-remote/confirm/route.js +5 -5
- package/dist/.next/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/push-remote/preview/route.js +3 -3
- package/dist/.next/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/query/route.js +3 -3
- package/dist/.next/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/tables/[name]/route.js +2 -2
- package/dist/.next/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/tables/route.js +2 -2
- package/dist/.next/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/update/route.js +1 -5
- package/dist/.next/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/index.html +3 -3
- package/dist/.next/.next/server/app/index.rsc +5 -5
- package/dist/.next/.next/server/app/page.js +11 -13
- package/dist/.next/.next/server/app/page.js.nft.json +1 -1
- package/dist/.next/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app-paths-manifest.json +1 -1
- package/dist/.next/.next/server/chunks/226.js +6 -6
- package/dist/.next/.next/server/chunks/391.js +5 -5
- package/dist/.next/.next/server/chunks/452.js +1 -1
- package/dist/.next/.next/server/chunks/627.js +3 -3
- package/dist/.next/.next/server/chunks/638.js +3 -3
- package/dist/.next/.next/server/chunks/90.js +1 -0
- package/dist/.next/.next/server/chunks/992.js +3 -3
- package/dist/.next/.next/server/middleware-build-manifest.js +1 -1
- package/dist/.next/.next/server/next-font-manifest.js +1 -1
- package/dist/.next/.next/server/next-font-manifest.json +1 -1
- package/dist/.next/.next/server/pages/404.html +1 -1
- package/dist/.next/.next/server/pages/500.html +1 -1
- package/dist/.next/.next/server/pages/_app.js +1 -1
- package/dist/.next/.next/server/pages/_document.js +1 -1
- package/dist/.next/.next/server/pages/_error.js +1 -1
- package/dist/.next/.next/server/server-reference-manifest.json +1 -1
- package/dist/.next/.next/static/bHYW7fgIBDF8-Q33_WPoE/_buildManifest.js +1 -0
- package/dist/.next/.next/static/bHYW7fgIBDF8-Q33_WPoE/_ssgManifest.js +1 -0
- package/dist/.next/.next/static/chunks/576-c4daf274bb6469a1.js +1 -0
- package/dist/.next/.next/static/chunks/app/layout-be6ee67cf0c92654.js +1 -0
- package/dist/.next/.next/static/chunks/app/page-36ed30b2266617f3.js +1 -0
- package/dist/.next/.next/static/chunks/{main-app-d36cdfb402b40f13.js → main-app-fa35024de9bd9284.js} +1 -1
- package/dist/.next/.next/static/css/9f6c25c5400f83d3.css +3 -0
- package/dist/.next/.next/trace +3 -3
- package/dist/.next/.next/types/app/api/cloudflare/route.ts +1 -1
- package/dist/.next/.next/types/app/api/databases/route.ts +1 -1
- package/dist/.next/.next/types/app/api/migrations/sync/route.ts +1 -1
- package/dist/.next/.next/types/app/api/migrations/sync-files/route.ts +1 -1
- package/dist/.next/.next/types/app/api/pull-data/confirm/route.ts +1 -1
- package/dist/.next/.next/types/app/api/pull-remote/confirm/route.ts +1 -1
- package/dist/.next/.next/types/app/api/pull-remote/preview/route.ts +1 -1
- package/dist/.next/.next/types/app/api/push-remote/confirm/route.ts +1 -1
- package/dist/.next/.next/types/app/api/push-remote/preview/route.ts +1 -1
- package/dist/.next/.next/types/app/api/query/route.ts +1 -1
- package/dist/.next/.next/types/app/api/tables/[name]/route.ts +1 -1
- package/dist/.next/.next/types/app/api/tables/route.ts +1 -1
- package/dist/.next/.next/types/app/api/update/route.ts +1 -1
- package/dist/.next/.next/types/app/page.ts +1 -1
- package/dist/.next/BUILD_ID +1 -1
- package/dist/.next/app-build-manifest.json +41 -41
- package/dist/.next/app-path-routes-manifest.json +1 -1
- package/dist/.next/build-manifest.json +3 -3
- package/dist/.next/cache/.rscinfo +1 -1
- package/dist/.next/cache/.tsbuildinfo +1 -1
- package/dist/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/next-minimal-server.js.nft.json +1 -1
- package/dist/.next/next-server.js.nft.json +1 -1
- package/dist/.next/prerender-manifest.json +1 -1
- package/dist/.next/required-server-files.json +1 -1
- package/dist/.next/server/app/_not-found/page.js +1 -1
- package/dist/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/_not-found.html +1 -1
- package/dist/.next/server/app/_not-found.rsc +4 -4
- package/dist/.next/server/app/api/cloudflare/route.js +1 -1
- package/dist/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/databases/route.js +1 -1
- package/dist/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/migrations/sync/route.js +3 -3
- package/dist/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/migrations/sync-files/route.js +8 -8
- package/dist/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pull-data/confirm/route.js +1 -1
- package/dist/.next/server/app/api/pull-data/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pull-remote/confirm/route.js +2 -2
- package/dist/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pull-remote/preview/route.js +2 -2
- package/dist/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/push-remote/confirm/route.js +5 -5
- package/dist/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/push-remote/preview/route.js +3 -3
- package/dist/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/query/route.js +3 -3
- package/dist/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/tables/[name]/route.js +2 -2
- package/dist/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/tables/route.js +2 -2
- package/dist/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/update/route.js +1 -5
- package/dist/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/index.html +3 -3
- package/dist/.next/server/app/index.rsc +5 -5
- package/dist/.next/server/app/page.js +11 -13
- package/dist/.next/server/app/page.js.nft.json +1 -1
- package/dist/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app-paths-manifest.json +5 -5
- package/dist/.next/server/chunks/226.js +6 -6
- package/dist/.next/server/chunks/391.js +5 -5
- package/dist/.next/server/chunks/452.js +1 -1
- package/dist/.next/server/chunks/627.js +3 -3
- package/dist/.next/server/chunks/638.js +3 -3
- package/dist/.next/server/chunks/90.js +1 -0
- package/dist/.next/server/chunks/992.js +3 -3
- package/dist/.next/server/middleware-build-manifest.js +1 -1
- package/dist/.next/server/next-font-manifest.js +1 -1
- package/dist/.next/server/next-font-manifest.json +1 -1
- package/dist/.next/server/pages/404.html +1 -1
- package/dist/.next/server/pages/500.html +1 -1
- package/dist/.next/server/pages/_app.js +1 -1
- package/dist/.next/server/pages/_document.js +1 -1
- package/dist/.next/server/pages/_error.js +1 -1
- package/dist/.next/server/server-reference-manifest.json +1 -1
- package/dist/.next/static/ZBTCAyp1kO5FdEHMU-Dw2/_buildManifest.js +1 -0
- package/dist/.next/static/ZBTCAyp1kO5FdEHMU-Dw2/_ssgManifest.js +1 -0
- package/dist/.next/static/chunks/576-c4daf274bb6469a1.js +1 -0
- package/dist/.next/static/chunks/app/layout-be6ee67cf0c92654.js +1 -0
- package/dist/.next/static/chunks/app/page-36ed30b2266617f3.js +1 -0
- package/dist/.next/static/chunks/{main-app-d36cdfb402b40f13.js → main-app-fa35024de9bd9284.js} +1 -1
- package/dist/.next/static/css/9f6c25c5400f83d3.css +3 -0
- package/dist/.next/trace +14 -17
- package/dist/.next/types/app/api/cloudflare/route.ts +1 -1
- package/dist/.next/types/app/api/databases/route.ts +1 -1
- package/dist/.next/types/app/api/migrations/sync/route.ts +1 -1
- package/dist/.next/types/app/api/migrations/sync-files/route.ts +1 -1
- package/dist/.next/types/app/api/pull-data/confirm/route.ts +1 -1
- package/dist/.next/types/app/api/pull-remote/confirm/route.ts +1 -1
- package/dist/.next/types/app/api/pull-remote/preview/route.ts +1 -1
- package/dist/.next/types/app/api/push-remote/confirm/route.ts +1 -1
- package/dist/.next/types/app/api/push-remote/preview/route.ts +1 -1
- package/dist/.next/types/app/api/query/route.ts +1 -1
- package/dist/.next/types/app/api/tables/[name]/route.ts +1 -1
- package/dist/.next/types/app/api/tables/route.ts +1 -1
- package/dist/.next/types/app/api/update/route.ts +1 -1
- package/dist/.next/types/app/page.ts +1 -1
- package/dist/app/api/update/route.js +29 -20
- package/dist/app/app/api/update/route.js +29 -20
- package/dist/app/app/page.jsx +13 -23
- package/dist/app/page.jsx +13 -23
- package/dist/d1-studio.js +30 -194
- package/dist/lib/sql-operations.js +30 -24
- package/dist/lib/version-utils.js +34 -0
- package/package.json +2 -2
- package/scripts/studio.js +30 -194
- package/src/app/api/update/route.js +29 -20
- package/src/app/page.jsx +13 -23
- package/src/components/sidebar.jsx +2 -1
- package/src/components/table-view.jsx +1 -1
- package/src/components/update-banner.jsx +69 -38
- package/src/lib/sql-operations.js +30 -24
- package/src/lib/version-utils.js +34 -0
- package/dist/.next/.next/server/chunks/336.js +0 -1
- package/dist/.next/.next/static/chunks/176-9146975ef8699719.js +0 -1
- package/dist/.next/.next/static/chunks/app/layout-8360fab69b848283.js +0 -1
- package/dist/.next/.next/static/chunks/app/page-5990cb23b8bd27c4.js +0 -1
- package/dist/.next/.next/static/css/674bee6608c638e4.css +0 -3
- package/dist/.next/server/chunks/336.js +0 -1
- package/dist/.next/static/chunks/176-9146975ef8699719.js +0 -1
- package/dist/.next/static/chunks/app/layout-8360fab69b848283.js +0 -1
- package/dist/.next/static/chunks/app/page-5990cb23b8bd27c4.js +0 -1
- package/dist/.next/static/css/674bee6608c638e4.css +0 -3
- /package/dist/.next/.next/static/{7XBqETNmhSxMXhw8iUpEa → OUHnbB-64afrrZQL0Srn5}/_buildManifest.js +0 -0
- /package/dist/.next/.next/static/{7XBqETNmhSxMXhw8iUpEa → OUHnbB-64afrrZQL0Srn5}/_ssgManifest.js +0 -0
- /package/dist/.next/{static/MoZBULXFiTFzrY1JVK_Df → .next/static/ZBTCAyp1kO5FdEHMU-Dw2}/_buildManifest.js +0 -0
- /package/dist/.next/{static/MoZBULXFiTFzrY1JVK_Df → .next/static/ZBTCAyp1kO5FdEHMU-Dw2}/_ssgManifest.js +0 -0
|
@@ -112,26 +112,18 @@ export function parseStatement(sql) {
|
|
|
112
112
|
// Extract the index name.
|
|
113
113
|
const indexMatch = cleanSql.match(/INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?([\w$]+)/i);
|
|
114
114
|
const indexName = indexMatch ? indexMatch[1] : '';
|
|
115
|
-
// Extract the columns from the ON clause.
|
|
116
|
-
const onMatch = cleanSql.match(/ON\s+[\w$]+\s*\(([^)]+)\)/i);
|
|
117
|
-
const columns = onMatch ? onMatch[1].trim() : '';
|
|
118
|
-
// Remove spaces and replace commas with hyphens.
|
|
119
|
-
const formattedColumns = columns.replace(/\s+/g, '').replace(/,/g, '-').toLowerCase();
|
|
120
115
|
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
let description = indexName;
|
|
124
|
-
if (formattedColumns && !description.toLowerCase().includes(formattedColumns)) {
|
|
125
|
-
description += `-${formattedColumns}`;
|
|
126
|
-
}
|
|
116
|
+
// For index migrations, simply use the index name as the description.
|
|
117
|
+
const description = indexName;
|
|
127
118
|
|
|
128
119
|
return {
|
|
129
120
|
type: Operations.CREATE_INDEX,
|
|
130
121
|
tableName,
|
|
131
122
|
sql: cleanSql,
|
|
132
|
-
description // e.g. "
|
|
123
|
+
description // now simply the index name, e.g. "idx_technology_timestamp"
|
|
133
124
|
};
|
|
134
|
-
}
|
|
125
|
+
}
|
|
126
|
+
|
|
135
127
|
|
|
136
128
|
if (upperSql.startsWith('DROP INDEX')) {
|
|
137
129
|
const tableName = extractTableNameFromIndex(cleanSql);
|
|
@@ -559,14 +551,14 @@ export function formatMigrationContent(sql) {
|
|
|
559
551
|
* @returns {Promise<string>} The migration file content.
|
|
560
552
|
*/
|
|
561
553
|
|
|
562
|
-
|
|
554
|
+
export async function getMigrationFileContent(projectDir, databaseName, migrationFile, env) {
|
|
563
555
|
try {
|
|
564
556
|
// Extract table name using improved helper
|
|
565
557
|
const tableName = await extractTableNameFromMigration(migrationFile);
|
|
566
558
|
let sql;
|
|
567
559
|
|
|
568
|
-
|
|
569
|
-
|
|
560
|
+
// For non-index migrations, try getting SQL from sqlite_master for table
|
|
561
|
+
if (tableName && !migrationFile.includes('create-index')) {
|
|
570
562
|
const result = await executeSQL(
|
|
571
563
|
projectDir,
|
|
572
564
|
databaseName,
|
|
@@ -583,24 +575,38 @@ export function formatMigrationContent(sql) {
|
|
|
583
575
|
}
|
|
584
576
|
}
|
|
585
577
|
|
|
586
|
-
// If we couldn't get the SQL from table, try index
|
|
578
|
+
// If we couldn't get the SQL from table and it's an index migration, try fetching via index name.
|
|
587
579
|
if (!sql && migrationFile.includes('create-index')) {
|
|
588
|
-
const indexMatch = migrationFile.match(/create-index[-
|
|
580
|
+
const indexMatch = migrationFile.match(/create-index[_-](.+)\.sql$/i);
|
|
589
581
|
if (indexMatch) {
|
|
590
|
-
|
|
582
|
+
let indexName = indexMatch[1]; // e.g. "idx_technology_timestamp-technology-timestamp"
|
|
583
|
+
let result = await executeSQL(
|
|
591
584
|
projectDir,
|
|
592
585
|
databaseName,
|
|
593
|
-
`SELECT sql FROM sqlite_master WHERE type='index' AND name='${
|
|
586
|
+
`SELECT sql FROM sqlite_master WHERE type='index' AND name='${indexName}' COLLATE NOCASE;`,
|
|
594
587
|
env,
|
|
595
588
|
true
|
|
596
589
|
);
|
|
597
|
-
|
|
590
|
+
let parsed = JSON.parse(result);
|
|
598
591
|
sql = parsed[0]?.results?.[0]?.sql;
|
|
592
|
+
// Fallback: if no result, try using only the part before the first hyphen.
|
|
593
|
+
if (!sql) {
|
|
594
|
+
const fallbackIndexName = indexName.split('-')[0]; // "idx_technology_timestamp"
|
|
595
|
+
result = await executeSQL(
|
|
596
|
+
projectDir,
|
|
597
|
+
databaseName,
|
|
598
|
+
`SELECT sql FROM sqlite_master WHERE type='index' AND name='${fallbackIndexName}' COLLATE NOCASE;`,
|
|
599
|
+
env,
|
|
600
|
+
true
|
|
601
|
+
);
|
|
602
|
+
parsed = JSON.parse(result);
|
|
603
|
+
sql = parsed[0]?.results?.[0]?.sql;
|
|
604
|
+
}
|
|
599
605
|
}
|
|
600
|
-
}
|
|
606
|
+
}
|
|
601
607
|
|
|
602
|
-
// If still no SQL
|
|
603
|
-
if (!sql && tableName) {
|
|
608
|
+
// If still no SQL and it's not an index migration, try a fallback query
|
|
609
|
+
if (!sql && tableName && !migrationFile.includes('create-index')) {
|
|
604
610
|
const result = await executeSQL(
|
|
605
611
|
projectDir,
|
|
606
612
|
databaseName,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// lib/version-utils.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compares semantic versions to determine if v2 is newer than v1
|
|
5
|
+
* @param {string} v1 - First version (e.g. "1.0.0")
|
|
6
|
+
* @param {string} v2 - Second version (e.g. "1.0.1")
|
|
7
|
+
* @returns {boolean} True if v2 is newer than v1
|
|
8
|
+
*/
|
|
9
|
+
export function isNewerVersion(v1, v2) {
|
|
10
|
+
if (!v1 || !v2) return false;
|
|
11
|
+
|
|
12
|
+
// Split versions by dots
|
|
13
|
+
const v1Parts = v1.split('.').map(Number);
|
|
14
|
+
const v2Parts = v2.split('.').map(Number);
|
|
15
|
+
|
|
16
|
+
// Compare major, minor, and patch versions
|
|
17
|
+
for (let i = 0; i < 3; i++) {
|
|
18
|
+
if (v2Parts[i] > v1Parts[i]) return true;
|
|
19
|
+
if (v2Parts[i] < v1Parts[i]) return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// If we reach here, versions are equal
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Formats version strings for display by removing leading 'v' if present
|
|
28
|
+
* @param {string} version - The version string (e.g. "v1.0.0" or "1.0.0")
|
|
29
|
+
* @returns {string} Formatted version (e.g. "1.0.0")
|
|
30
|
+
*/
|
|
31
|
+
export function formatVersion(version) {
|
|
32
|
+
if (!version) return '';
|
|
33
|
+
return version.startsWith('v') ? version.substring(1) : version;
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "d1s",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "D1 Studio – a native studio for managing Cloudflare D1 databases.",
|
|
6
6
|
"main": "./dist/d1-studio.js",
|
|
@@ -97,4 +97,4 @@
|
|
|
97
97
|
"tailwindcss": "^3.4.1",
|
|
98
98
|
"wrangler": "^3.107.3"
|
|
99
99
|
}
|
|
100
|
-
}
|
|
100
|
+
}
|
package/scripts/studio.js
CHANGED
|
@@ -3,10 +3,6 @@ import chalk from 'chalk';
|
|
|
3
3
|
import open from 'open';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { dirname, join } from 'path';
|
|
6
|
-
import { createHash } from 'crypto';
|
|
7
|
-
import { homedir } from 'os';
|
|
8
|
-
import os from 'os';
|
|
9
|
-
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
10
6
|
import express from 'express';
|
|
11
7
|
import { createServer } from 'http';
|
|
12
8
|
import next from 'next';
|
|
@@ -22,139 +18,42 @@ if (hasDebugFlag) {
|
|
|
22
18
|
logger.debug('Debug mode enabled');
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
const notifier = updateNotifier({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
30
|
-
const __dirname = dirname(__filename);
|
|
31
|
-
|
|
32
|
-
// License validation utilities
|
|
33
|
-
class LicenseError extends Error {
|
|
34
|
-
constructor(message, type = 'VALIDATION_ERROR') {
|
|
35
|
-
super(message);
|
|
36
|
-
this.name = 'LicenseError';
|
|
37
|
-
this.type = type;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
class StudioLicenseManager {
|
|
42
|
-
constructor() {
|
|
43
|
-
this.licenseDir = join(homedir(), '.d1studio');
|
|
44
|
-
this.licensePath = join(this.licenseDir, 'license.json');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async checkLicenseFile() {
|
|
48
|
-
try {
|
|
49
|
-
const licenseData = JSON.parse(await readFile(this.licensePath, 'utf8'));
|
|
50
|
-
|
|
51
|
-
// If no end_date or subscription_status, the license is valid (perpetual license)
|
|
52
|
-
if (!licenseData.end_date || !licenseData.subscription_status) {
|
|
53
|
-
return {
|
|
54
|
-
licenseData,
|
|
55
|
-
isValid: true,
|
|
56
|
-
isExpiringSoon: false
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Check subscription status first
|
|
61
|
-
if (licenseData.subscription_status !== 'active') {
|
|
62
|
-
return {
|
|
63
|
-
licenseData,
|
|
64
|
-
isValid: false,
|
|
65
|
-
isExpiringSoon: false
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const endDate = new Date(licenseData.end_date);
|
|
70
|
-
const now = new Date();
|
|
71
|
-
|
|
72
|
-
// Check if subscription has ended
|
|
73
|
-
if (endDate < now) {
|
|
74
|
-
return {
|
|
75
|
-
licenseData,
|
|
76
|
-
isValid: false,
|
|
77
|
-
isExpiringSoon: false
|
|
78
|
-
};
|
|
79
|
-
}
|
|
21
|
+
const notifier = updateNotifier({
|
|
22
|
+
pkg,
|
|
23
|
+
updateCheckInterval: 1000 * 60 * 60 * 24,
|
|
24
|
+
});
|
|
80
25
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
26
|
+
// Check if update is available
|
|
27
|
+
if (notifier.update) {
|
|
28
|
+
// Get current version and latest version
|
|
29
|
+
const currentVersion = pkg.version;
|
|
30
|
+
const latestVersion = notifier.update.latest;
|
|
31
|
+
|
|
32
|
+
// Version-specific messages
|
|
33
|
+
const versionMessages = {
|
|
34
|
+
// Add specific version messages here
|
|
35
|
+
'0.2.11': 'Fixed table toolbar and table view horizontal/vertical scroll issues'
|
|
36
|
+
// Add more version messages as needed
|
|
37
|
+
};
|
|
84
38
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
39
|
+
// Create custom message
|
|
40
|
+
let message = `Update available ${chalk.dim(currentVersion)} → ${chalk.green(latestVersion)}`;
|
|
41
|
+
|
|
42
|
+
// Add version-specific message if available
|
|
43
|
+
if (versionMessages[latestVersion]) {
|
|
44
|
+
message += `\n${chalk.cyan('What\'s new:')} ${versionMessages[latestVersion]}`;
|
|
93
45
|
}
|
|
46
|
+
|
|
47
|
+
// Add installation instructions
|
|
48
|
+
message += `\nRun ${chalk.cyan('npm i -g d1s@latest')} to update`;
|
|
94
49
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
await mkdir(this.licenseDir, { recursive: true });
|
|
98
|
-
await writeFile(this.licensePath, JSON.stringify(licenseData, null, 2));
|
|
99
|
-
} catch (error) {
|
|
100
|
-
throw new LicenseError('Failed to save license information', 'SAVE_ERROR');
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function generateHardwareId() {
|
|
106
|
-
const osInfo = {
|
|
107
|
-
platform: process.platform,
|
|
108
|
-
arch: process.arch,
|
|
109
|
-
cpus: os.cpus().length,
|
|
110
|
-
hostname: os.hostname(),
|
|
111
|
-
username: os.userInfo().username,
|
|
112
|
-
};
|
|
113
|
-
const systemString = JSON.stringify(osInfo);
|
|
114
|
-
return createHash('sha256').update(systemString).digest('hex');
|
|
50
|
+
// Display custom notification
|
|
51
|
+
notifier.notify({ message });
|
|
115
52
|
}
|
|
116
53
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
? 'http://localhost:4000'
|
|
121
|
-
: 'https://d1-studio.com';
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const response = await fetch(`${baseUrl}/api/validate-license`, {
|
|
125
|
-
method: 'POST',
|
|
126
|
-
headers: { 'Content-Type': 'application/json' },
|
|
127
|
-
body: JSON.stringify({ licenseKey, email, hardwareId }),
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const result = await response.json();
|
|
131
|
-
|
|
132
|
-
if (!response.ok) {
|
|
133
|
-
throw new LicenseError(
|
|
134
|
-
result.message || 'License validation failed',
|
|
135
|
-
result.type || 'VALIDATION_ERROR'
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return result;
|
|
140
|
-
} catch (error) {
|
|
141
|
-
if (error instanceof LicenseError) {
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
|
145
|
-
throw new LicenseError(
|
|
146
|
-
process.env.NODE_ENV === 'development'
|
|
147
|
-
? 'Unable to connect to local license validation service. Make sure the development server is running.'
|
|
148
|
-
: 'Unable to connect to license server. Please check your internet connection.',
|
|
149
|
-
'CONNECTION_ERROR'
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
throw new LicenseError(
|
|
153
|
-
'An unexpected error occurred during license validation',
|
|
154
|
-
'VALIDATION_ERROR'
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
54
|
+
// Compute the absolute path of this file and its directory
|
|
55
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
56
|
+
const __dirname = dirname(__filename);
|
|
158
57
|
|
|
159
58
|
async function startServer(port, projectDirectory) {
|
|
160
59
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
@@ -210,69 +109,6 @@ async function startStudio() {
|
|
|
210
109
|
try {
|
|
211
110
|
logger.info(chalk.green('Initializing D1 Studio...'));
|
|
212
111
|
|
|
213
|
-
const licenseManager = new StudioLicenseManager();
|
|
214
|
-
|
|
215
|
-
// Check existing license
|
|
216
|
-
const licenseCheck = await licenseManager.checkLicenseFile();
|
|
217
|
-
|
|
218
|
-
// Handle license validation
|
|
219
|
-
if (!licenseCheck || !licenseCheck.isValid) {
|
|
220
|
-
// Prompt for license information if needed
|
|
221
|
-
const { default: inquirer } = await import('inquirer');
|
|
222
|
-
logger.info(chalk.yellow('\nPlease enter your D1 Studio license information:'));
|
|
223
|
-
|
|
224
|
-
const answers = await inquirer.prompt([
|
|
225
|
-
{
|
|
226
|
-
type: 'input',
|
|
227
|
-
name: 'licenseKey',
|
|
228
|
-
message: 'License Key:',
|
|
229
|
-
validate: (input) => input.length > 0 || 'License key is required',
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
type: 'input',
|
|
233
|
-
name: 'email',
|
|
234
|
-
message: 'Email:',
|
|
235
|
-
validate: (input) => {
|
|
236
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
237
|
-
return emailRegex.test(input) || 'Please enter a valid email';
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
]);
|
|
241
|
-
|
|
242
|
-
const hardwareId = await generateHardwareId();
|
|
243
|
-
|
|
244
|
-
try {
|
|
245
|
-
const validation = await validateLicenseRemote(
|
|
246
|
-
answers.licenseKey,
|
|
247
|
-
answers.email,
|
|
248
|
-
hardwareId
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
// Save the complete validation response
|
|
252
|
-
await licenseManager.saveLicenseFile({
|
|
253
|
-
licenseKey: answers.licenseKey,
|
|
254
|
-
email: answers.email,
|
|
255
|
-
hardwareId,
|
|
256
|
-
end_date: validation.end_date,
|
|
257
|
-
subscription_status: validation.subscription_status,
|
|
258
|
-
type: validation.licenseType,
|
|
259
|
-
activatedAt: new Date().toISOString()
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
logger.info(chalk.green('\nLicense activated successfully!'));
|
|
263
|
-
} catch (error) {
|
|
264
|
-
logger.error(chalk.red(`\nLicense validation failed: ${error.message}`));
|
|
265
|
-
process.exit(1);
|
|
266
|
-
}
|
|
267
|
-
} else if (licenseCheck.isExpiringSoon) {
|
|
268
|
-
// Show warning about expiring subscription
|
|
269
|
-
const endDate = new Date(licenseCheck.licenseData.end_date).toLocaleDateString();
|
|
270
|
-
logger.warn(chalk.yellow(`
|
|
271
|
-
⚠️ Your subscription will expire on ${endDate}
|
|
272
|
-
Please renew your subscription at https://d1-studio.com to continue using D1 Studio.
|
|
273
|
-
`));
|
|
274
|
-
}
|
|
275
|
-
|
|
276
112
|
// Get wrangler configuration
|
|
277
113
|
try {
|
|
278
114
|
const userProjectPath = process.cwd();
|
|
@@ -1,31 +1,40 @@
|
|
|
1
1
|
// app/api/update/route.js
|
|
2
2
|
import { NextResponse } from 'next/server';
|
|
3
|
-
import updateNotifier from 'update-notifier';
|
|
4
3
|
import pkg from '../../../../package.json' assert { type: 'json' };
|
|
5
4
|
|
|
6
5
|
export async function GET() {
|
|
7
6
|
try {
|
|
8
|
-
//
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
// Get current version from package.json
|
|
8
|
+
const currentVersion = pkg.version;
|
|
9
|
+
|
|
10
|
+
// Fetch the latest version from npm registry
|
|
11
|
+
const response = await fetch('https://registry.npmjs.org/d1s/latest', {
|
|
12
|
+
headers: {
|
|
13
|
+
'Accept': 'application/json',
|
|
14
|
+
'User-Agent': 'd1-studio'
|
|
15
|
+
},
|
|
16
|
+
// Add cache control to prevent excessive requests
|
|
17
|
+
cache: 'no-cache'
|
|
12
18
|
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const { latest, current } = notifier.update;
|
|
17
|
-
|
|
18
|
-
// Only return update info if versions are different
|
|
19
|
-
if (latest !== current) {
|
|
20
|
-
return NextResponse.json({
|
|
21
|
-
update: {
|
|
22
|
-
latest,
|
|
23
|
-
current
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
}
|
|
19
|
+
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error('Failed to check for updates');
|
|
27
22
|
}
|
|
28
|
-
|
|
23
|
+
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
const latestVersion = data.version;
|
|
26
|
+
|
|
27
|
+
// Compare versions - only return update info if there's a newer version
|
|
28
|
+
if (latestVersion && currentVersion && latestVersion !== currentVersion) {
|
|
29
|
+
console.log(`Update available: ${currentVersion} → ${latestVersion}`);
|
|
30
|
+
return NextResponse.json({
|
|
31
|
+
update: {
|
|
32
|
+
latest: latestVersion,
|
|
33
|
+
current: currentVersion
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
// No update available
|
|
30
39
|
return NextResponse.json({ update: null });
|
|
31
40
|
} catch (error) {
|
package/src/app/page.jsx
CHANGED
|
@@ -5,7 +5,6 @@ import { useState } from 'react';
|
|
|
5
5
|
import { AppSidebar } from '../components/sidebar';
|
|
6
6
|
import { SQLEditor } from '../components/sql-editor';
|
|
7
7
|
import { TableView } from '../components/table-view';
|
|
8
|
-
import UpdateBanner from '@/components/update-banner';
|
|
9
8
|
|
|
10
9
|
export default function StudioPage() {
|
|
11
10
|
const [currentView, setCurrentView] = useState('sql-editor');
|
|
@@ -17,28 +16,19 @@ export default function StudioPage() {
|
|
|
17
16
|
};
|
|
18
17
|
|
|
19
18
|
return (
|
|
20
|
-
<div className="flex
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{/* Main content */}
|
|
35
|
-
<main className="ml-64 flex-1 h-[calc(100vh-var(--banner-height,0px))]">
|
|
36
|
-
{currentView === 'sql-editor' && <SQLEditor />}
|
|
37
|
-
{currentView === 'table-view' && selectedTable && (
|
|
38
|
-
<TableView tableName={selectedTable} />
|
|
39
|
-
)}
|
|
40
|
-
</main>
|
|
41
|
-
</div>
|
|
19
|
+
<div className="flex h-screen bg-background">
|
|
20
|
+
<AppSidebar
|
|
21
|
+
className="w-64 border-r flex-shrink-0"
|
|
22
|
+
currentView={currentView}
|
|
23
|
+
onNavigate={setCurrentView}
|
|
24
|
+
onTableSelect={handleTableSelect}
|
|
25
|
+
/>
|
|
26
|
+
<main className="flex-1 overflow-hidden">
|
|
27
|
+
{currentView === 'sql-editor' && <SQLEditor />}
|
|
28
|
+
{currentView === 'table-view' && selectedTable && (
|
|
29
|
+
<TableView tableName={selectedTable} />
|
|
30
|
+
)}
|
|
31
|
+
</main>
|
|
42
32
|
</div>
|
|
43
33
|
);
|
|
44
34
|
}
|
|
@@ -133,7 +133,8 @@ export function AppSidebar({
|
|
|
133
133
|
|
|
134
134
|
return (
|
|
135
135
|
<>
|
|
136
|
-
|
|
136
|
+
{/* The sidebar now has top position adjusted for the banner */}
|
|
137
|
+
<aside className={`${className} top-[var(--banner-height,0px)] left-0 h-[calc(100vh-var(--banner-height,0px))] bg-neutral-50 p-4`}>
|
|
137
138
|
<div className="flex flex-col h-full font-sans">
|
|
138
139
|
{/* Header */}
|
|
139
140
|
<div className="flex items-center gap-2 mb-4">
|
|
@@ -684,7 +684,7 @@ export function TableView({ tableName }) {
|
|
|
684
684
|
/>
|
|
685
685
|
|
|
686
686
|
<ResizableTable columns={schema} onColumnsResize={handleColumnResize}>
|
|
687
|
-
<div className="
|
|
687
|
+
<div className="flex-1 overflow-hidden bg-background font-mono">
|
|
688
688
|
<ScrollArea className="w-full h-screen">
|
|
689
689
|
<div className="relative w-max min-w-fit pb-4">
|
|
690
690
|
<Table>
|
|
@@ -2,61 +2,92 @@
|
|
|
2
2
|
'use client'
|
|
3
3
|
|
|
4
4
|
import { useEffect, useState } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { Info } from 'lucide-react';
|
|
6
6
|
import axios from 'axios';
|
|
7
7
|
|
|
8
8
|
export default function UpdateBanner() {
|
|
9
9
|
const [updateInfo, setUpdateInfo] = useState(null);
|
|
10
|
-
const [visible, setVisible] = useState(
|
|
10
|
+
const [visible, setVisible] = useState(false);
|
|
11
|
+
const [loading, setLoading] = useState(true);
|
|
11
12
|
|
|
12
13
|
useEffect(() => {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (data
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
14
|
+
const checkForUpdates = async () => {
|
|
15
|
+
try {
|
|
16
|
+
setLoading(true);
|
|
17
|
+
|
|
18
|
+
// Check if we've already dismissed this specific version update
|
|
19
|
+
const dismissedVersion = localStorage.getItem('dismissedVersion');
|
|
20
|
+
|
|
21
|
+
const { data } = await axios.get('/api/update');
|
|
22
|
+
|
|
23
|
+
if (data?.update) {
|
|
24
|
+
// Only show if the user hasn't dismissed this specific version
|
|
25
|
+
if (dismissedVersion !== data.update.latest) {
|
|
26
|
+
setUpdateInfo(data.update);
|
|
27
|
+
setVisible(true);
|
|
28
|
+
} else {
|
|
29
|
+
setVisible(false);
|
|
30
30
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
} else {
|
|
32
|
+
setVisible(false);
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error('Error checking for updates:', error);
|
|
36
|
+
setVisible(false);
|
|
37
|
+
} finally {
|
|
38
|
+
setLoading(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
// Check immediately and then daily
|
|
43
|
+
checkForUpdates();
|
|
44
|
+
const interval = setInterval(checkForUpdates, 24 * 60 * 60 * 1000); // Check daily
|
|
45
|
+
|
|
46
|
+
return () => clearInterval(interval);
|
|
36
47
|
}, []);
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
// If banner is dismissed or no update info, don't render and reset CSS variable
|
|
50
|
+
if (!updateInfo || !visible || loading) {
|
|
51
|
+
// Reset the banner height when hidden
|
|
52
|
+
if (typeof document !== 'undefined') {
|
|
53
|
+
document.documentElement.style.setProperty('--banner-height', '0px');
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Set the banner height CSS variable when visible
|
|
59
|
+
if (typeof document !== 'undefined') {
|
|
60
|
+
document.documentElement.style.setProperty('--banner-height', '37px'); // Adjust based on actual banner height
|
|
61
|
+
}
|
|
39
62
|
|
|
40
63
|
const handleClose = () => {
|
|
41
|
-
|
|
64
|
+
// Store the dismissed version to avoid showing the same update again
|
|
65
|
+
if (updateInfo?.latest) {
|
|
66
|
+
localStorage.setItem('dismissedVersion', updateInfo.latest);
|
|
67
|
+
}
|
|
42
68
|
setVisible(false);
|
|
43
69
|
};
|
|
44
70
|
|
|
45
71
|
return (
|
|
46
|
-
<div className="
|
|
47
|
-
<div className="flex items-center
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
72
|
+
<div className="fixed top-0 left-0 right-0 z-50 bg-yellow-200 border-b border-yellow-500 px-3 py-2 font-sans">
|
|
73
|
+
<div className="flex items-center justify-center relative">
|
|
74
|
+
{/* Info icon and text centered */}
|
|
75
|
+
<div className="flex items-center gap-2 mx-auto">
|
|
76
|
+
<Info className="h-4 w-4 text-neutral-900" />
|
|
77
|
+
<span className="text-sm font-medium">
|
|
78
|
+
Version {updateInfo.latest} is available — npm install -g d1s@latest
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* Close button positioned absolutely on the right */}
|
|
83
|
+
<button
|
|
84
|
+
onClick={handleClose}
|
|
85
|
+
className="absolute right-0 text-neutral-500 hover:text-neutral-700"
|
|
86
|
+
aria-label="Close update banner"
|
|
87
|
+
>
|
|
88
|
+
×
|
|
89
|
+
</button>
|
|
52
90
|
</div>
|
|
53
|
-
<button
|
|
54
|
-
onClick={handleClose}
|
|
55
|
-
className="text-neutral-500 hover:text-neutral-700"
|
|
56
|
-
aria-label="Close update banner"
|
|
57
|
-
>
|
|
58
|
-
×
|
|
59
|
-
</button>
|
|
60
91
|
</div>
|
|
61
92
|
);
|
|
62
93
|
}
|