d1s 0.3.10 → 0.3.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/README.md +1 -1
- package/dist/.next/BUILD_ID +1 -1
- package/dist/.next/app-build-manifest.json +50 -50
- package/dist/.next/app-path-routes-manifest.json +5 -5
- package/dist/.next/build-manifest.json +5 -5
- package/dist/.next/prerender-manifest.json +7 -7
- package/dist/.next/react-loadable-manifest.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_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/migrations/list/route_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pro/route_client-reference-manifest.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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.js +1 -1
- 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 +8 -8
- package/dist/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/schema/route.js +1 -1
- package/dist/.next/server/app/api/schema/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/tables/[name]/import/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/tables/[name]/route.js +5 -5
- 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/time-travel/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/time-travel/restore/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/update/route.js +1 -1
- package/dist/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/index.html +2 -2
- package/dist/.next/server/app/index.rsc +5 -5
- package/dist/.next/server/app/page.js +9 -9
- 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/839.js +1 -1
- package/dist/.next/server/middleware-build-manifest.js +1 -1
- package/dist/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/.next/server/pages/404.html +1 -1
- package/dist/.next/server/pages/500.html +1 -1
- package/dist/.next/static/chunks/823-77b9a2d152da81dd.js +1 -0
- package/dist/.next/static/chunks/{950.01ba9ad634bd1f43.js → 950.edd33691c78c1807.js} +1 -1
- package/dist/.next/static/chunks/app/page-4170252891367dde.js +1 -0
- package/dist/.next/static/chunks/{webpack-c7b9f95656aa8fe9.js → webpack-d5e8b5757f0fec83.js} +1 -1
- package/dist/.next/static/css/262ae258bbcfd289.css +3 -0
- package/dist/.next/trace +3 -3
- package/dist/app/api/query/route.js +35 -16
- package/dist/app/api/schema/route.js +16 -6
- package/dist/app/api/tables/[name]/route.js +23 -12
- package/dist/app/api/tables/route.js +21 -6
- package/dist/d1-studio.js +1 -0
- package/package.json +5 -1
- package/dist/.next/static/chunks/823-5f8f96173644e0cc.js +0 -1
- package/dist/.next/static/chunks/app/page-5f0b0b876d1c4c1c.js +0 -1
- package/dist/.next/static/css/b69f03bff92613ce.css +0 -3
- /package/dist/.next/static/{TDqrfll63o1FXL7rpt5E_ → kGRfd0CDzKPsrYHw9rLTQ}/_buildManifest.js +0 -0
- /package/dist/.next/static/{TDqrfll63o1FXL7rpt5E_ → kGRfd0CDzKPsrYHw9rLTQ}/_ssgManifest.js +0 -0
|
@@ -8,9 +8,9 @@ import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
|
|
|
8
8
|
|
|
9
9
|
const execAsync = promisify(exec);
|
|
10
10
|
|
|
11
|
-
async function runPragma(databaseName, query, projectDir) {
|
|
11
|
+
async function runPragma(databaseName, query, projectDir, source = 'local') {
|
|
12
12
|
const { stdout } = await execAsync(
|
|
13
|
-
`wrangler d1 execute ${databaseName}
|
|
13
|
+
`wrangler d1 execute ${databaseName} --${source} --command="${query.replace(/"/g, '\\"')}" --json`,
|
|
14
14
|
{ cwd: projectDir }
|
|
15
15
|
);
|
|
16
16
|
const result = JSON.parse(stdout);
|
|
@@ -21,10 +21,19 @@ export async function GET(request) {
|
|
|
21
21
|
try {
|
|
22
22
|
const { searchParams } = new URL(request.url);
|
|
23
23
|
const dbParam = searchParams.get('db');
|
|
24
|
+
const sourceParam = searchParams.get('source');
|
|
25
|
+
const source = sourceParam === 'remote' ? 'remote' : 'local';
|
|
24
26
|
|
|
25
27
|
const projectDir = process.env.D1_PROJECT_DIR;
|
|
26
28
|
if (!projectDir) throw new Error('Project directory not configured');
|
|
27
29
|
|
|
30
|
+
if (source === 'remote' && process.env.D1_IS_PRO !== 'true') {
|
|
31
|
+
return NextResponse.json(
|
|
32
|
+
{ success: false, error: 'Pro license required. Run: d1s --license YOUR-KEY' },
|
|
33
|
+
{ status: 402 }
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
const wranglerResult = await findWranglerConfig(projectDir);
|
|
29
38
|
if (!wranglerResult?.config) throw new Error('No Wrangler configuration found');
|
|
30
39
|
const config = wranglerResult.config;
|
|
@@ -40,19 +49,20 @@ export async function GET(request) {
|
|
|
40
49
|
const tableRows = await runPragma(
|
|
41
50
|
databaseName,
|
|
42
51
|
`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_cf_%' ORDER BY name;`,
|
|
43
|
-
projectDir
|
|
52
|
+
projectDir,
|
|
53
|
+
source
|
|
44
54
|
);
|
|
45
55
|
const tableNames = tableRows.map(r => r.name);
|
|
46
56
|
|
|
47
57
|
// Run sequentially to avoid SQLITE_BUSY (database locked) errors
|
|
48
58
|
const tables = [];
|
|
49
59
|
for (const name of tableNames) {
|
|
50
|
-
const columns = await runPragma(databaseName, `PRAGMA table_info(${name});`, projectDir);
|
|
51
|
-
const foreignKeys = await runPragma(databaseName, `PRAGMA foreign_key_list(${name});`, projectDir);
|
|
60
|
+
const columns = await runPragma(databaseName, `PRAGMA table_info(${name});`, projectDir, source);
|
|
61
|
+
const foreignKeys = await runPragma(databaseName, `PRAGMA foreign_key_list(${name});`, projectDir, source);
|
|
52
62
|
tables.push({ name, columns, foreignKeys });
|
|
53
63
|
}
|
|
54
64
|
|
|
55
|
-
return NextResponse.json({ success: true, tables });
|
|
65
|
+
return NextResponse.json({ success: true, tables, source });
|
|
56
66
|
} catch (error) {
|
|
57
67
|
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
|
|
58
68
|
}
|
|
@@ -22,12 +22,14 @@ let isProcessing = false;
|
|
|
22
22
|
/**
|
|
23
23
|
* Executes a D1 SQL query, preferring direct SQLite access over the wrangler CLI.
|
|
24
24
|
*/
|
|
25
|
-
async function executeD1Query(sql, projectDir, dbConfig) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
async function executeD1Query(sql, projectDir, dbConfig, source = 'local') {
|
|
26
|
+
if (source === 'local') {
|
|
27
|
+
// Try direct SQLite access first (fast path)
|
|
28
|
+
const localOutput = executeLocalSQL(projectDir, dbConfig, sql);
|
|
29
|
+
if (localOutput !== null) {
|
|
30
|
+
const parsed = JSON.parse(localOutput);
|
|
31
|
+
return parsed[0]?.results || [];
|
|
32
|
+
}
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
// Fall back to wrangler CLI
|
|
@@ -38,7 +40,7 @@ async function executeD1Query(sql, projectDir, dbConfig) {
|
|
|
38
40
|
}
|
|
39
41
|
isProcessing = true;
|
|
40
42
|
const { stdout } = await execAsync(
|
|
41
|
-
`wrangler d1 execute ${databaseName}
|
|
43
|
+
`wrangler d1 execute ${databaseName} --${source} --command="${sql.replace(/"/g, '\\"')}" --json`,
|
|
42
44
|
{ cwd: projectDir }
|
|
43
45
|
);
|
|
44
46
|
const result = JSON.parse(stdout);
|
|
@@ -89,12 +91,21 @@ export async function GET(request, context) {
|
|
|
89
91
|
const offset = (page - 1) * limit;
|
|
90
92
|
const isExport = searchParams.get('export') === 'true';
|
|
91
93
|
const dbParam = searchParams.get('db');
|
|
94
|
+
const sourceParam = searchParams.get('source');
|
|
95
|
+
const source = sourceParam === 'remote' ? 'remote' : 'local';
|
|
92
96
|
|
|
93
97
|
const projectDir = process.env.D1_PROJECT_DIR;
|
|
94
98
|
if (!projectDir) {
|
|
95
99
|
throw new Error('Project directory not configured');
|
|
96
100
|
}
|
|
97
101
|
|
|
102
|
+
if (source === 'remote' && process.env.D1_IS_PRO !== 'true') {
|
|
103
|
+
return NextResponse.json(
|
|
104
|
+
{ success: false, error: { message: 'Pro license required. Run: d1s --license YOUR-KEY' } },
|
|
105
|
+
{ status: 402 }
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
98
109
|
// Use the centralized helper to find Wrangler configuration
|
|
99
110
|
const wranglerResult = await findWranglerConfig(projectDir);
|
|
100
111
|
if (!wranglerResult || !wranglerResult.config) {
|
|
@@ -116,11 +127,11 @@ export async function GET(request, context) {
|
|
|
116
127
|
|
|
117
128
|
// Get table schema using PRAGMA query (PRAGMA is safe from injection when table name is sanitized)
|
|
118
129
|
const schemaQuery = `PRAGMA table_info(${sanitizedTableName});`;
|
|
119
|
-
const schema = await executeD1Query(schemaQuery, projectDir, dbConfig);
|
|
130
|
+
const schema = await executeD1Query(schemaQuery, projectDir, dbConfig, source);
|
|
120
131
|
|
|
121
132
|
// Get the CREATE TABLE statement from sqlite_master
|
|
122
133
|
const createTableQuerySafe = `SELECT sql FROM sqlite_master WHERE type='table' AND name='${name.replace(/'/g, "''")}';`;
|
|
123
|
-
const tableDefinition = await executeD1Query(createTableQuerySafe, projectDir, dbConfig);
|
|
134
|
+
const tableDefinition = await executeD1Query(createTableQuerySafe, projectDir, dbConfig, source);
|
|
124
135
|
const createTableSQL = tableDefinition[0]?.sql || '';
|
|
125
136
|
|
|
126
137
|
// Extract CHECK constraints from the CREATE TABLE statement
|
|
@@ -134,7 +145,7 @@ export async function GET(request, context) {
|
|
|
134
145
|
|
|
135
146
|
// Get total row count for the table (using sanitized table name)
|
|
136
147
|
const countQuery = `SELECT COUNT(*) as total FROM ${sanitizedTableName};`;
|
|
137
|
-
const countResult = await executeD1Query(countQuery, projectDir, dbConfig);
|
|
148
|
+
const countResult = await executeD1Query(countQuery, projectDir, dbConfig, source);
|
|
138
149
|
const totalRows = countResult[0]?.total || 0;
|
|
139
150
|
|
|
140
151
|
// Fetch paginated data using the primary key column for ordering
|
|
@@ -152,7 +163,7 @@ export async function GET(request, context) {
|
|
|
152
163
|
ORDER BY ${sanitizedPrimaryKey} DESC
|
|
153
164
|
LIMIT ${safeLimitNum} OFFSET ${safeOffsetNum};
|
|
154
165
|
`;
|
|
155
|
-
const data = await executeD1Query(dataQuery, projectDir, dbConfig);
|
|
166
|
+
const data = await executeD1Query(dataQuery, projectDir, dbConfig, source);
|
|
156
167
|
|
|
157
168
|
if (isExport) trackEvent('export');
|
|
158
169
|
return NextResponse.json({
|
|
@@ -160,6 +171,7 @@ export async function GET(request, context) {
|
|
|
160
171
|
schema: enhancedSchema,
|
|
161
172
|
data,
|
|
162
173
|
meta: {
|
|
174
|
+
source,
|
|
163
175
|
tableName: name,
|
|
164
176
|
total: totalRows,
|
|
165
177
|
page,
|
|
@@ -194,4 +206,3 @@ export async function OPTIONS() {
|
|
|
194
206
|
},
|
|
195
207
|
});
|
|
196
208
|
}
|
|
197
|
-
|
|
@@ -82,6 +82,8 @@ export async function GET(request) {
|
|
|
82
82
|
try {
|
|
83
83
|
const { searchParams } = new URL(request.url);
|
|
84
84
|
const dbParam = searchParams.get('db');
|
|
85
|
+
const sourceParam = searchParams.get('source');
|
|
86
|
+
const source = sourceParam === 'remote' ? 'remote' : 'local';
|
|
85
87
|
|
|
86
88
|
const projectDir = process.env.D1_PROJECT_DIR;
|
|
87
89
|
|
|
@@ -93,6 +95,13 @@ export async function GET(request) {
|
|
|
93
95
|
);
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
if (source === 'remote' && process.env.D1_IS_PRO !== 'true') {
|
|
99
|
+
return NextResponse.json(
|
|
100
|
+
{ error: 'Pro license required. Run: d1s --license YOUR-KEY' },
|
|
101
|
+
{ status: 402 }
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
96
105
|
// Use the centralized helper to get Wrangler configuration
|
|
97
106
|
const wranglerResult = await findWranglerConfig(projectDir);
|
|
98
107
|
if (!wranglerResult || !wranglerResult.config) {
|
|
@@ -127,16 +136,21 @@ export async function GET(request) {
|
|
|
127
136
|
ORDER BY name;
|
|
128
137
|
`;
|
|
129
138
|
|
|
130
|
-
//
|
|
139
|
+
// For local mode, try direct SQLite access first (fast), then wrangler --local.
|
|
140
|
+
// For remote mode, always use wrangler --remote.
|
|
131
141
|
let result;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
if (source === 'local') {
|
|
143
|
+
const localOutput = executeLocalSQL(projectDir, dbConfig, query.trim());
|
|
144
|
+
if (localOutput !== null) {
|
|
145
|
+
result = JSON.parse(localOutput);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!result) {
|
|
136
150
|
const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
137
151
|
const { stdout } = await retryWithTimeout(async () => {
|
|
138
152
|
return execAsync(
|
|
139
|
-
`${npxCmd} wrangler d1 execute ${databaseName}
|
|
153
|
+
`${npxCmd} wrangler d1 execute ${databaseName} --${source} --json --command="${query.trim().replace(/\n\s*/g, ' ')}"`,
|
|
140
154
|
{ cwd: projectDir, timeout: 10000 }
|
|
141
155
|
);
|
|
142
156
|
});
|
|
@@ -148,6 +162,7 @@ export async function GET(request) {
|
|
|
148
162
|
return NextResponse.json({
|
|
149
163
|
success: true,
|
|
150
164
|
tables,
|
|
165
|
+
source,
|
|
151
166
|
debug: {
|
|
152
167
|
projectDir,
|
|
153
168
|
tableCount: tables.length
|
package/dist/d1-studio.js
CHANGED
|
@@ -92,6 +92,7 @@ if (notifier.update) {
|
|
|
92
92
|
const versionMessages = {
|
|
93
93
|
'0.2.11': 'Fixed table toolbar and table view horizontal/vertical scroll issues',
|
|
94
94
|
'0.3.0': 'Per-db migrations_dir, seed exclusion from push, sidebar UX improvements',
|
|
95
|
+
'0.3.11': 'Global Local/Remote source mode, remote table/schema browsing, and safer read-only remote editing UX',
|
|
95
96
|
};
|
|
96
97
|
|
|
97
98
|
// Create custom message
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "d1s",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The first native studio for Cloudflare D1. Zero setup. No extra dependencies.",
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"test": "node scripts/test.js",
|
|
21
21
|
"test:api": "node tests/run-tests.mjs",
|
|
22
22
|
"test:cli": "d1-studio --version",
|
|
23
|
+
"link:test:setup": "bash scripts/link-test.sh setup",
|
|
24
|
+
"link:test:run": "bash scripts/link-test.sh run",
|
|
25
|
+
"link:test:doctor": "bash scripts/link-test.sh doctor",
|
|
26
|
+
"link:test:clean": "bash scripts/link-test.sh clean",
|
|
23
27
|
"test:all": "npm run test && npm run test:cli",
|
|
24
28
|
"build:all": "npm run build:cli",
|
|
25
29
|
"prepublishOnly": "npm run build:all"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[823],{1374:(e,t,a)=>{a.d(t,{dj:()=>d});var r=a(2115);let o=0,c=new Map,s=e=>{if(c.has(e))return;let t=setTimeout(()=>{c.delete(e),i({type:"REMOVE_TOAST",toastId:e})},1e6);c.set(e,t)},n=[],l={toasts:[]};function i(e){l=((e,t)=>{switch(t.type){case"ADD_TOAST":return{...e,toasts:[t.toast,...e.toasts].slice(0,1)};case"UPDATE_TOAST":return{...e,toasts:e.toasts.map(e=>e.id===t.toast.id?{...e,...t.toast}:e)};case"DISMISS_TOAST":{let{toastId:a}=t;return a?s(a):e.toasts.forEach(e=>{s(e.id)}),{...e,toasts:e.toasts.map(e=>e.id===a||void 0===a?{...e,open:!1}:e)}}case"REMOVE_TOAST":if(void 0===t.toastId)return{...e,toasts:[]};return{...e,toasts:e.toasts.filter(e=>e.id!==t.toastId)}}})(l,e),n.forEach(e=>{e(l)})}function u(e){let{...t}=e,a=(o=(o+1)%Number.MAX_SAFE_INTEGER).toString(),r=()=>i({type:"DISMISS_TOAST",toastId:a});return i({type:"ADD_TOAST",toast:{...t,id:a,open:!0,onOpenChange:e=>{e||r()}}}),{id:a,dismiss:r,update:e=>i({type:"UPDATE_TOAST",toast:{...e,id:a}})}}function d(){let[e,t]=r.useState(l);return r.useEffect(()=>(n.push(t),()=>{let e=n.indexOf(t);e>-1&&n.splice(e,1)}),[e]),{...e,toast:u,dismiss:e=>i({type:"DISMISS_TOAST",toastId:e})}}},3190:(e,t,a)=>{a.d(t,{DatabaseProvider:()=>u,t:()=>d});var r=a(5155),o=a(2115);let c=new Map;function s(e,t){let a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e3,r=c.get(e);if(r&&Date.now()-r.timestamp<a)return r.promise;let o=t();return c.set(e,{promise:o,timestamp:Date.now()}),o.finally(()=>{setTimeout(()=>{c.delete(e)},a)}),o}let n=(0,o.createContext)(void 0);class l{get(e){let t=this.cache.get(e);return t||null}set(e,t,a,r){this.cache.set(e,{data:t,schema:a,meta:r,timestamp:Date.now()})}update(e,t,a,r){let o=this.cache.get(e);o&&this.cache.set(e,{data:t||o.data,schema:a||o.schema,meta:r||o.meta,timestamp:Date.now()})}invalidate(e){this.cache.delete(e)}clear(){this.cache.clear()}constructor(){this.cache=new Map}}class i{async add(e){return new Promise((t,a)=>{this.queue.push({operation:e,resolve:t,reject:a}),this.process()})}async process(){if(this.processing||0===this.queue.length)return;this.processing=!0;let{operation:e,resolve:t,reject:a}=this.queue.shift();try{let a=await e();t(a)}catch(e){a(e)}finally{this.processing=!1,this.process()}}constructor(){this.queue=[],this.processing=!1}}function u(e){let{children:t}=e,[a,c]=(0,o.useState)([]),[u,d]=(0,o.useState)(!0),[h,f]=(0,o.useState)(null),[g,p]=(0,o.useState)([]),[m,w]=(0,o.useState)(null);(0,o.useRef)(new i);let[y,b]=(0,o.useState)(null),S=(0,o.useRef)(new l).current,E=(0,o.useRef)(null),[v,T]=(0,o.useState)(null),C=(0,o.useCallback)(function(e,t){let a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;T({type:e,message:t,detail:a,ts:new Date})},[]),[k,O]=(0,o.useState)(()=>localStorage.getItem("d1_last_sync")||null),_=(0,o.useCallback)(e=>{let t=e instanceof Date?e.toISOString():new Date().toISOString();localStorage.setItem("d1_last_sync",t),O(t)},[]),I=(0,o.useCallback)(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:50,r="table-".concat(e,"-").concat(t,"-").concat(a);return S.get(r)},[]),P=(0,o.useCallback)(e=>{S.cache.forEach((t,a)=>{a.startsWith("table-".concat(e,"-"))&&S.invalidate(a)})},[]),D=(0,o.useCallback)(function(e,t,a,r){let o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:1,c=arguments.length>5&&void 0!==arguments[5]?arguments[5]:50,s="table-".concat(e,"-").concat(o,"-").concat(c);S.update(s,t,a,r)},[]),A=(0,o.useCallback)(async e=>{let t=/^\s*(SELECT|PRAGMA|EXPLAIN)/i.test(e),a="query-".concat(e),r=async()=>{C("loading","Executing query…");try{var t,a;let r=null==(t=E.current)?void 0:t.database_name,o=await fetch("/api/query",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,...r&&{db:r}})});if(!o.ok){let e="Failed to execute query";try{let t=await o.json();e=(null==t||null==(a=t.error)?void 0:a.message)||e}catch(e){}throw Error(e)}let c=await o.json();if(!c.success)throw Error(c.error||"Query execution failed");return C("success","Query executed"),c}catch(e){throw C("error",e.message||"Query failed"),e||Error("An unknown error occurred")}};return t?s(a,r,1e3):r()},[C]),j=(0,o.useCallback)(async e=>{try{let t=await fetch("/api/tables/".concat(e)),a=await t.json();if(!t.ok||!a.success)throw Error(a.error||"Failed to fetch table schema");return a.schema||[]}catch(e){throw console.error("Schema fetch error:",e),e}},[]),F=(0,o.useCallback)(async function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},{forceFetch:a=!1,page:r=1,limit:o=50}=t,c="table-".concat(e,"-").concat(r,"-").concat(o);if(!a){let e=S.get(c);if(e)return e}return s("table-".concat(e,"-").concat(r,"-").concat(o),async()=>{C("loading","Loading ".concat(e,"…"));try{var t,a;let s=null==(t=E.current)?void 0:t.database_name,n=s?"&db=".concat(encodeURIComponent(s)):"",l=await fetch("/api/tables/".concat(e,"?page=").concat(r,"&limit=").concat(o).concat(n),{headers:{"Cache-Control":"no-cache",Pragma:"no-cache"}});if(!l.ok)throw Error("Failed to fetch table data");let i=await l.json();if(!i.success)throw Error(i.error||"Failed to fetch table data");S.set(c,i.data,i.schema,i.meta);let u=(null==(a=i.data)?void 0:a.length)||0;return C("success","Loaded ".concat(u," row").concat(1!==u?"s":""," \xb7 ").concat(e)),i}catch(t){throw console.error("Error fetching table data:",t),C("error",t.message,"Failed loading ".concat(e)),t}},1e3)},[C]),M=(0,o.useCallback)(async e=>{C("loading","Refreshing ".concat(e,"…"));try{return S.cache.forEach((t,a)=>{a.startsWith("table-".concat(e,"-"))&&S.invalidate(a)}),await F(e,{forceFetch:!0})}catch(t){throw console.error("Error updating table data:",t),C("error",t.message,"Failed refreshing ".concat(e)),t}},[F,C]),N=(0,o.useCallback)(async function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return b(e),await F(e,t)},[F]),R=(0,o.useCallback)(async()=>{var e;let t=(null==(e=E.current)?void 0:e.database_name)||"default";return s("refresh-tables-".concat(t),async()=>{C("loading","Loading tables…"),d(!0),f(null);try{var e;let t=null==(e=E.current)?void 0:e.database_name,a=t?"?db=".concat(encodeURIComponent(t)):"",r=await fetch("/api/tables".concat(a));if(!r.ok)throw Error("Failed to fetch tables");let o=await r.json();if(o.error)throw Error(o.error);let s=o.tables||[];return c(s),C("success","".concat(s.length," table").concat(1!==s.length?"s":""," loaded")),s}catch(e){throw f(e.message),C("error",e.message,"Failed loading tables"),e}finally{d(!1)}},2e3)},[C]),q=(0,o.useCallback)(async e=>{C("loading","Creating table…");try{await A(e),S.clear(),await R(),C("success","Table created")}catch(e){throw C("error",e.message,"Create table failed"),Error("Failed to create table: ".concat(e.message))}},[A,R,C]),x=(0,o.useCallback)(async e=>{C("loading",'Dropping "'.concat(e,'"…'));try{await A("DROP TABLE ".concat(e,";")),S.invalidate(e),await R(),C("success",'Table "'.concat(e,'" dropped'))}catch(t){throw C("error",t.message,'Drop "'.concat(e,'" failed')),Error("Failed to drop table: ".concat(t.message))}},[A,R,C]),L=(0,o.useCallback)(async e=>{C("loading","Syncing migrations…");try{let a=await fetch("/api/migrations/sync",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok){var t;let e=await a.json();throw Error((null==(t=e.error)?void 0:t.message)||"Failed to sync migrations")}let r=await a.json();return C("success","Migrations synced"),r}catch(e){throw console.error("Error syncing migrations:",e),C("error",e.message,"Migration sync failed"),e}},[C]),J=(0,o.useCallback)(async()=>{C("loading","Pulling from remote…");try{var e,t,a,r;let o=localStorage.getItem("cf_credentials");if(!o)throw Error("No Cloudflare credentials found. Please connect to Cloudflare first.");let c=JSON.parse(o);if(!c.accountId||!c.apiToken)throw Error("Invalid credentials stored. Please reconnect to Cloudflare.");let s=await fetch("/api/pull-remote/confirm",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({accountId:c.accountId,apiToken:c.apiToken})}),n=await s.json();if(!s.ok||!n.success)throw Error((null==(a=n.error)?void 0:a.message)||"Failed to pull from remote");S.clear(),await R();let l=null!=(r=null==(t=n.details)||null==(e=t.appliedMigrations)?void 0:e.length)?r:0;return C("success","Pull complete — ".concat(l," migration").concat(1!==l?"s":""," applied")),n}catch(e){throw console.error("Error pulling from remote:",e),C("error",e.message,"Pull from remote failed"),e}},[R,C]),Q=(0,o.useCallback)(async e=>{C("loading","Pushing to remote…");try{var t,a;let r=await fetch("/api/push-remote/confirm",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),o=await r.json();if(!r.ok)throw Error((null==(t=o.error)?void 0:t.message)||"Failed to push to remote");let c=null!=(a=o.applied)?a:0;return C("success","Push complete — ".concat(c," migration").concat(1!==c?"s":""," applied")),o}catch(e){throw console.error("Error pushing to remote:",e),C("error",e.message,"Push to remote failed"),e}},[C]),U=(0,o.useCallback)(async e=>{let t=g.find(t=>t.database_name===e);t&&(E.current=t,w(t),S.clear(),c([]),b(null),await R())},[g,R]);(0,o.useEffect)(()=>((async()=>{try{let e=await fetch("/api/databases");if(e.ok){let t=(await e.json()).databases||[];p(t),t.length>0&&!E.current&&(E.current=t[0],w(t[0]))}}catch(e){}R()})(),()=>{S.clear()}),[R]);let G=(0,o.useMemo)(()=>({tables:a,databases:g,currentDatabase:m,switchDatabase:U,isLoading:u,error:h,activeTable:y,lastStatus:v,setOperationStatus:C,lastSyncTime:k,setLastSyncTime:_,refreshTables:R,executeQuery:A,fetchTableSchema:j,fetchTableData:F,updateTableData:M,createTable:q,dropTable:x,syncMigrations:L,pullFromRemote:J,pushToRemote:Q,getCachedTable:I,updateTableCache:D,invalidateTable:P,switchTable:N}),[a,g,m,U,u,h,y,v,C,k,_,R,A,j,F,M,q,x,L,J,Q,I,D,P,N]);return(0,r.jsx)(n.Provider,{value:G,children:t})}function d(){let e=(0,o.useContext)(n);if(!e)throw Error("useDatabase must be used within a DatabaseProvider");return e}},3537:(e,t,a)=>{a.d(t,{cn:()=>c});var r=a(2821),o=a(5889);function c(){for(var e=arguments.length,t=Array(e),a=0;a<e;a++)t[a]=arguments[a];return(0,o.QP)((0,r.$)(t))}}}]);
|