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.
Files changed (61) hide show
  1. package/README.md +1 -1
  2. package/dist/.next/BUILD_ID +1 -1
  3. package/dist/.next/app-build-manifest.json +50 -50
  4. package/dist/.next/app-path-routes-manifest.json +5 -5
  5. package/dist/.next/build-manifest.json +5 -5
  6. package/dist/.next/prerender-manifest.json +7 -7
  7. package/dist/.next/react-loadable-manifest.json +1 -1
  8. package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  9. package/dist/.next/server/app/_not-found.html +1 -1
  10. package/dist/.next/server/app/_not-found.rsc +4 -4
  11. package/dist/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
  12. package/dist/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
  13. package/dist/.next/server/app/api/migrations/list/route_client-reference-manifest.js +1 -1
  14. package/dist/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
  15. package/dist/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
  16. package/dist/.next/server/app/api/pro/route_client-reference-manifest.js +1 -1
  17. package/dist/.next/server/app/api/pull-data/confirm/route_client-reference-manifest.js +1 -1
  18. package/dist/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
  19. package/dist/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
  20. package/dist/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
  21. package/dist/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
  22. package/dist/.next/server/app/api/query/route.js +8 -8
  23. package/dist/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
  24. package/dist/.next/server/app/api/schema/route.js +1 -1
  25. package/dist/.next/server/app/api/schema/route_client-reference-manifest.js +1 -1
  26. package/dist/.next/server/app/api/tables/[name]/import/route_client-reference-manifest.js +1 -1
  27. package/dist/.next/server/app/api/tables/[name]/route.js +5 -5
  28. package/dist/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
  29. package/dist/.next/server/app/api/tables/route.js +2 -2
  30. package/dist/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
  31. package/dist/.next/server/app/api/time-travel/preview/route_client-reference-manifest.js +1 -1
  32. package/dist/.next/server/app/api/time-travel/restore/route_client-reference-manifest.js +1 -1
  33. package/dist/.next/server/app/api/update/route.js +1 -1
  34. package/dist/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  35. package/dist/.next/server/app/index.html +2 -2
  36. package/dist/.next/server/app/index.rsc +5 -5
  37. package/dist/.next/server/app/page.js +9 -9
  38. package/dist/.next/server/app/page_client-reference-manifest.js +1 -1
  39. package/dist/.next/server/app-paths-manifest.json +5 -5
  40. package/dist/.next/server/chunks/839.js +1 -1
  41. package/dist/.next/server/middleware-build-manifest.js +1 -1
  42. package/dist/.next/server/middleware-react-loadable-manifest.js +1 -1
  43. package/dist/.next/server/pages/404.html +1 -1
  44. package/dist/.next/server/pages/500.html +1 -1
  45. package/dist/.next/static/chunks/823-77b9a2d152da81dd.js +1 -0
  46. package/dist/.next/static/chunks/{950.01ba9ad634bd1f43.js → 950.edd33691c78c1807.js} +1 -1
  47. package/dist/.next/static/chunks/app/page-4170252891367dde.js +1 -0
  48. package/dist/.next/static/chunks/{webpack-c7b9f95656aa8fe9.js → webpack-d5e8b5757f0fec83.js} +1 -1
  49. package/dist/.next/static/css/262ae258bbcfd289.css +3 -0
  50. package/dist/.next/trace +3 -3
  51. package/dist/app/api/query/route.js +35 -16
  52. package/dist/app/api/schema/route.js +16 -6
  53. package/dist/app/api/tables/[name]/route.js +23 -12
  54. package/dist/app/api/tables/route.js +21 -6
  55. package/dist/d1-studio.js +1 -0
  56. package/package.json +5 -1
  57. package/dist/.next/static/chunks/823-5f8f96173644e0cc.js +0 -1
  58. package/dist/.next/static/chunks/app/page-5f0b0b876d1c4c1c.js +0 -1
  59. package/dist/.next/static/css/b69f03bff92613ce.css +0 -3
  60. /package/dist/.next/static/{TDqrfll63o1FXL7rpt5E_ → kGRfd0CDzKPsrYHw9rLTQ}/_buildManifest.js +0 -0
  61. /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} --local --command="${query.replace(/"/g, '\\"')}" --json`,
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
- // Try direct SQLite access first (fast path)
27
- const localOutput = executeLocalSQL(projectDir, dbConfig, sql);
28
- if (localOutput !== null) {
29
- const parsed = JSON.parse(localOutput);
30
- return parsed[0]?.results || [];
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} --local --command="${sql.replace(/"/g, '\\"')}" --json`,
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
- // Try direct SQLite access first (fast), fall back to wrangler CLI
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
- const localOutput = executeLocalSQL(projectDir, dbConfig, query.trim());
133
- if (localOutput !== null) {
134
- result = JSON.parse(localOutput);
135
- } else {
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} --local --json --command="${query.trim().replace(/\n\s*/g, ' ')}"`,
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.10",
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))}}}]);