@edge-base/server 0.2.0 → 0.2.2

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 (87) hide show
  1. package/admin-build/_app/immutable/assets/19.4Si2ZFC_.css +1 -0
  2. package/admin-build/_app/immutable/assets/{3.Dg81Pgmd.css → 3.BtHYobTg.css} +1 -1
  3. package/admin-build/_app/immutable/assets/SqlEditor.Bbp1RIk0.css +1 -0
  4. package/admin-build/_app/immutable/assets/TableSqlTab.yeNZfhgG.css +1 -0
  5. package/admin-build/_app/immutable/chunks/4vlsb8ej.js +1 -0
  6. package/admin-build/_app/immutable/chunks/{CfPHB4r5.js → 5PDcRlfX.js} +1 -1
  7. package/admin-build/_app/immutable/chunks/{BSfSfeDG.js → B8DT4fss.js} +1 -1
  8. package/admin-build/_app/immutable/chunks/{DP9kmlCd.js → BEYYl662.js} +1 -1
  9. package/admin-build/_app/immutable/chunks/{B-WlnirM.js → BKXmgPq4.js} +1 -1
  10. package/admin-build/_app/immutable/chunks/{cqSkc6KP.js → BWyDPAjM.js} +1 -1
  11. package/admin-build/_app/immutable/chunks/{mD4EETH_.js → BaCHY17I.js} +1 -1
  12. package/admin-build/_app/immutable/chunks/C-DsDCNG.js +128 -0
  13. package/admin-build/_app/immutable/chunks/{2nyN5wuZ.js → C85dMlzL.js} +1 -1
  14. package/admin-build/_app/immutable/chunks/{DgxOZ3uv.js → CPdXvRUb.js} +1 -1
  15. package/admin-build/_app/immutable/chunks/{DpuSetmN.js → CTngeX8H.js} +1 -1
  16. package/admin-build/_app/immutable/chunks/{BKLsgaNT.js → DzXaj-Ja.js} +1 -1
  17. package/admin-build/_app/immutable/chunks/{D43CH5ty.js → c5iKSdWY.js} +1 -1
  18. package/admin-build/_app/immutable/chunks/{B14gOIqE.js → g3ZZdY-r.js} +1 -1
  19. package/admin-build/_app/immutable/chunks/{CN6aakgF.js → kiJ6KthZ.js} +1 -1
  20. package/admin-build/_app/immutable/chunks/lSpxLU5p.js +2 -0
  21. package/admin-build/_app/immutable/chunks/{uboHVq-x.js → qiZXAKh-.js} +1 -1
  22. package/admin-build/_app/immutable/entry/{app.Dc071f6C.js → app.BZxfavhF.js} +2 -2
  23. package/admin-build/_app/immutable/entry/start.Mr9mmopc.js +1 -0
  24. package/admin-build/_app/immutable/nodes/0.DlsaydXO.js +1 -0
  25. package/admin-build/_app/immutable/nodes/{1.rMaczUKT.js → 1.D2NWN5eG.js} +1 -1
  26. package/admin-build/_app/immutable/nodes/{10.DIOlO4hv.js → 10.EMDaN3nw.js} +1 -1
  27. package/admin-build/_app/immutable/nodes/{11.WxD9E0Eq.js → 11.BasqQ_o9.js} +1 -1
  28. package/admin-build/_app/immutable/nodes/{12.CNcefK3l.js → 12.DO31Ljs7.js} +1 -1
  29. package/admin-build/_app/immutable/nodes/{13.aAWsqDdR.js → 13.DhyAy-GZ.js} +1 -1
  30. package/admin-build/_app/immutable/nodes/{14.C9hdr3EN.js → 14.CLecGWc4.js} +1 -1
  31. package/admin-build/_app/immutable/nodes/{15.43r5uVx5.js → 15.B9kp3W4e.js} +1 -1
  32. package/admin-build/_app/immutable/nodes/{16.D519948J.js → 16.Pu_8T3RI.js} +1 -1
  33. package/admin-build/_app/immutable/nodes/{17.ks4I4yoH.js → 17.DX4z43t6.js} +1 -1
  34. package/admin-build/_app/immutable/nodes/{18.ZuNm22dY.js → 18.BKsSaxrr.js} +1 -1
  35. package/admin-build/_app/immutable/nodes/19.DXNF1htN.js +2 -0
  36. package/admin-build/_app/immutable/nodes/{20.C9ASlwCn.js → 20.VRVb0wee.js} +1 -1
  37. package/admin-build/_app/immutable/nodes/21.Ck3_0D2f.js +1 -0
  38. package/admin-build/_app/immutable/nodes/{22.6k8cg0Pr.js → 22.DqZf4CtH.js} +1 -1
  39. package/admin-build/_app/immutable/nodes/{23.B9hcFTU-.js → 23.DtyxMiQG.js} +1 -1
  40. package/admin-build/_app/immutable/nodes/{24.OsQM9QtS.js → 24.CloWNmTd.js} +1 -1
  41. package/admin-build/_app/immutable/nodes/{25.ClwkdaPp.js → 25.CnZWMq7_.js} +1 -1
  42. package/admin-build/_app/immutable/nodes/26.DrV7XOmf.js +1 -0
  43. package/admin-build/_app/immutable/nodes/{27.J1QASB3b.js → 27.DV8L32OF.js} +1 -1
  44. package/admin-build/_app/immutable/nodes/{28.BKP1tVcZ.js → 28.Stil2D4u.js} +1 -1
  45. package/admin-build/_app/immutable/nodes/{29.mqIe62On.js → 29.Zsm1e5Dc.js} +1 -1
  46. package/admin-build/_app/immutable/nodes/3.CKoj2vNz.js +2 -0
  47. package/admin-build/_app/immutable/nodes/{30.BRk-4B3j.js → 30.Ni0k5bER.js} +1 -1
  48. package/admin-build/_app/immutable/nodes/{31.BBqGNVXN.js → 31.mnqj9EbV.js} +1 -1
  49. package/admin-build/_app/immutable/nodes/{4.Bi91lv2V.js → 4.B_-z9AzT.js} +1 -1
  50. package/admin-build/_app/immutable/nodes/{5.BumjsbNK.js → 5.yiZ72j4k.js} +1 -1
  51. package/admin-build/_app/immutable/nodes/{6.CMTP_7xN.js → 6.BqykybBG.js} +1 -1
  52. package/admin-build/_app/immutable/nodes/{7.4T4wo7Kg.js → 7.BDAHlhsF.js} +1 -1
  53. package/admin-build/_app/immutable/nodes/{8.MUZQPNsN.js → 8.D8Xvy0lH.js} +1 -1
  54. package/admin-build/_app/immutable/nodes/{9.3SV00WXe.js → 9.Dddmd7_F.js} +1 -1
  55. package/admin-build/_app/version.json +1 -1
  56. package/admin-build/index.html +7 -7
  57. package/package.json +3 -2
  58. package/src/__tests__/functions-context.test.ts +5 -5
  59. package/src/__tests__/meta-export-coverage.test.ts +1 -0
  60. package/src/__tests__/pagination.test.ts +12 -8
  61. package/src/__tests__/postgres-dialect.test.ts +2 -2
  62. package/src/__tests__/query.test.ts +7 -7
  63. package/src/durable-objects/database-do.ts +3 -3
  64. package/src/durable-objects/logs-do.ts +2 -2
  65. package/src/lib/auth-d1-service.ts +1 -1
  66. package/src/lib/auth-d1.ts +10 -0
  67. package/src/lib/d1-handler.ts +23 -4
  68. package/src/lib/functions.ts +204 -397
  69. package/src/lib/internal-transport.ts +316 -0
  70. package/src/lib/pagination.ts +3 -3
  71. package/src/lib/plugin-migrations.ts +2 -2
  72. package/src/lib/postgres-handler.ts +2 -2
  73. package/src/lib/query-engine.ts +2 -2
  74. package/src/middleware/rate-limit.ts +11 -11
  75. package/src/routes/admin.ts +9 -3
  76. package/src/routes/auth.ts +13 -12
  77. package/src/routes/storage.ts +6 -12
  78. package/src/types.ts +2 -0
  79. package/admin-build/_app/immutable/assets/TableSqlTab.BHquaMBM.css +0 -1
  80. package/admin-build/_app/immutable/chunks/CkdaVlhQ.js +0 -2
  81. package/admin-build/_app/immutable/chunks/D8Nrx_IG.js +0 -128
  82. package/admin-build/_app/immutable/entry/start.Bhlxoqtt.js +0 -1
  83. package/admin-build/_app/immutable/nodes/0.CCfcYVV2.js +0 -1
  84. package/admin-build/_app/immutable/nodes/19.D519948J.js +0 -1
  85. package/admin-build/_app/immutable/nodes/21.BhSD2EfX.js +0 -1
  86. package/admin-build/_app/immutable/nodes/26._-65WG0q.js +0 -1
  87. package/admin-build/_app/immutable/nodes/3.WkDZWDQC.js +0 -2
@@ -0,0 +1,316 @@
1
+ /**
2
+ * Internal HttpTransport implementation for server-side function context.
3
+ *
4
+ * Replaces the old TableProxy by implementing the same HttpTransport interface
5
+ * that @edge-base/core's DefaultDbApi expects. This lets us use the real
6
+ * TableRef/DbRef classes from the core SDK while routing requests directly
7
+ * to D1, PostgreSQL, or DurableObject handlers — no HTTP round-trip.
8
+ */
9
+ import type { HttpTransport } from '@edge-base/core';
10
+ import type { EdgeBaseConfig } from '@edge-base/shared';
11
+ import { getDbDoName, callDO, shouldRouteToD1 } from './do-router.js';
12
+ import { handleD1Request } from './d1-handler.js';
13
+ import { handlePgRequest } from './postgres-handler.js';
14
+ import { buildInternalHandlerContext } from './internal-request.js';
15
+ import type { Env } from '../types.js';
16
+
17
+ export interface InternalTransportOptions {
18
+ databaseNamespace: DurableObjectNamespace;
19
+ config: EdgeBaseConfig;
20
+ workerUrl?: string;
21
+ serviceKey?: string;
22
+ env?: Env;
23
+ executionCtx?: ExecutionContext;
24
+ preferDirectDo?: boolean;
25
+ /**
26
+ * When set, the transport knows this DbRef targets a specific
27
+ * namespace + optional instanceId. This avoids ambiguous path parsing
28
+ * when instanceId happens to be "tables".
29
+ */
30
+ dbContext?: { namespace: string; instanceId?: string };
31
+ }
32
+
33
+ /**
34
+ * Parse a DefaultDbApi path into routing components.
35
+ *
36
+ * Paths follow two patterns:
37
+ * /api/db/{namespace}/tables/{table}[/rest] → static DB
38
+ * /api/db/{namespace}/{instanceId}/tables/{table}[/rest] → dynamic DB
39
+ *
40
+ * Returns namespace, optional instanceId, tableName, and directPath
41
+ * (everything from /tables/... onward, which D1/PG handlers expect).
42
+ */
43
+ /**
44
+ * Parse a DefaultDbApi path, optionally guided by known dbContext.
45
+ *
46
+ * When dbContext is provided (recommended), we know whether this is a
47
+ * static or dynamic DB, so we can unambiguously find the 'tables' keyword
48
+ * even when instanceId === 'tables'.
49
+ *
50
+ * Without dbContext, falls back to heuristic: first 'tables' at index 1
51
+ * means static, at index 2 means dynamic.
52
+ */
53
+ function parsePath(
54
+ path: string,
55
+ dbContext?: { namespace: string; instanceId?: string },
56
+ ): {
57
+ namespace: string;
58
+ instanceId?: string;
59
+ tableName: string;
60
+ directPath: string;
61
+ } {
62
+ // Strip leading /api/db/
63
+ const stripped = path.replace(/^\/api\/db\//, '');
64
+ const segments = stripped.split('/');
65
+
66
+ let tablesIdx: number;
67
+ if (dbContext) {
68
+ // We know the shape: static has 'tables' at index 1, dynamic at index 2
69
+ tablesIdx = dbContext.instanceId ? 2 : 1;
70
+ } else {
71
+ // Heuristic fallback: find first 'tables' keyword
72
+ tablesIdx = segments.indexOf('tables', 1);
73
+ }
74
+ if (tablesIdx < 0 || segments[tablesIdx] !== 'tables') {
75
+ throw new Error(`Invalid DB path: missing 'tables' segment in ${path}`);
76
+ }
77
+
78
+ const namespace = segments[0];
79
+ const instanceId = tablesIdx === 2 ? segments[1] : undefined;
80
+ const rawTableName = segments[tablesIdx + 1];
81
+ // Decode URL-encoded table names (e.g. 'plugin-a%2Fevents' → 'plugin-a/events')
82
+ // Use decoded name consistently in both tableName and directPath so that
83
+ // handler suffix parsing (doPath.replace(`/tables/${tableName}`, '')) matches.
84
+ const tableName = decodeURIComponent(rawTableName);
85
+ const rest = segments.slice(tablesIdx + 2);
86
+ const directPath = `/tables/${tableName}${rest.length ? '/' + rest.join('/') : ''}`;
87
+
88
+ return { namespace, instanceId, tableName, directPath };
89
+ }
90
+
91
+ export class InternalHttpTransport implements HttpTransport {
92
+ private readonly databaseNamespace: DurableObjectNamespace;
93
+ private readonly config: EdgeBaseConfig;
94
+ private readonly workerUrl?: string;
95
+ private readonly serviceKey?: string;
96
+ private readonly env?: Env;
97
+ private readonly executionCtx?: ExecutionContext;
98
+ private readonly preferDirectDo: boolean;
99
+ private readonly dbContext?: { namespace: string; instanceId?: string };
100
+
101
+ constructor(options: InternalTransportOptions) {
102
+ this.databaseNamespace = options.databaseNamespace;
103
+ this.config = options.config;
104
+ this.workerUrl = options.workerUrl;
105
+ this.serviceKey = options.serviceKey;
106
+ this.env = options.env;
107
+ this.executionCtx = options.executionCtx;
108
+ this.preferDirectDo = options.preferDirectDo ?? false;
109
+ this.dbContext = options.dbContext;
110
+ }
111
+
112
+ async request<T>(
113
+ method: string,
114
+ path: string,
115
+ options?: { query?: Record<string, string>; body?: unknown },
116
+ ): Promise<T> {
117
+ const { namespace, instanceId, tableName, directPath } = parsePath(path, this.dbContext);
118
+ const doName = getDbDoName(namespace, instanceId);
119
+
120
+ // Build internal headers
121
+ const headers: Record<string, string> = {
122
+ 'Content-Type': 'application/json',
123
+ 'X-DO-Name': doName,
124
+ 'X-EdgeBase-Internal': 'true',
125
+ };
126
+ if (this.serviceKey) {
127
+ headers['X-EdgeBase-Service-Key'] = this.serviceKey;
128
+ }
129
+
130
+ // Convert query Record to URLSearchParams
131
+ const query = new URLSearchParams();
132
+ if (options?.query) {
133
+ for (const [k, v] of Object.entries(options.query)) {
134
+ if (v !== undefined && v !== '') query.set(k, v);
135
+ }
136
+ }
137
+
138
+ const body = options?.body as Record<string, unknown> | undefined;
139
+
140
+ // Route to the appropriate handler
141
+ const res = await this.routeRequest(
142
+ method as 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT',
143
+ namespace,
144
+ instanceId,
145
+ tableName,
146
+ directPath,
147
+ doName,
148
+ headers,
149
+ query,
150
+ body,
151
+ );
152
+
153
+ if (!res.ok) {
154
+ const err = (await res.json().catch(() => ({}))) as Record<string, unknown>;
155
+ throw new Error(String(err.message || `Internal request failed: ${res.status}`));
156
+ }
157
+
158
+ return (await res.json()) as T;
159
+ }
160
+
161
+ async head(_path: string): Promise<boolean> {
162
+ // HEAD is only used by StorageClient.checkFileExists — not relevant for DB ops
163
+ return false;
164
+ }
165
+
166
+ private async routeRequest(
167
+ method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT',
168
+ namespace: string,
169
+ instanceId: string | undefined,
170
+ tableName: string,
171
+ directPath: string,
172
+ doName: string,
173
+ headers: Record<string, string>,
174
+ query: URLSearchParams,
175
+ body?: Record<string, unknown>,
176
+ ): Promise<Response> {
177
+ const queryString = Array.from(query.keys()).length > 0 ? `?${query.toString()}` : '';
178
+ const directPathWithQuery = `${directPath}${queryString}`;
179
+ const provider = this.config.databases?.[namespace]?.provider;
180
+ const httpMethod = method === 'PUT' ? 'PATCH' : method; // normalize PUT → PATCH
181
+
182
+ // 1. D1 route
183
+ if (!this.preferDirectDo && shouldRouteToD1(namespace, this.config) && this.env) {
184
+ return this.requestViaD1Handler(httpMethod, namespace, instanceId, tableName, directPath, headers, query, body);
185
+ }
186
+
187
+ // 2. PostgreSQL route
188
+ if ((provider === 'neon' || provider === 'postgres') && this.env) {
189
+ return this.requestViaPgHandler(httpMethod, namespace, instanceId, tableName, directPath, headers, query, body);
190
+ }
191
+
192
+ // 3. Direct DO route
193
+ if (this.env) {
194
+ return this.requestViaDirectDo(httpMethod, doName, directPathWithQuery, headers, body, !!instanceId);
195
+ }
196
+
197
+ // 4. Worker HTTP fallback
198
+ if (this.workerUrl) {
199
+ const apiPath = instanceId
200
+ ? `/api/db/${namespace}/${instanceId}${directPathWithQuery}`
201
+ : `/api/db/${namespace}${directPathWithQuery}`;
202
+ return this.requestViaWorker(httpMethod, apiPath, headers, body);
203
+ }
204
+
205
+ // 5. Fallback: direct DO
206
+ return this.requestViaDirectDo(httpMethod, doName, directPathWithQuery, headers, body, !!instanceId);
207
+ }
208
+
209
+ private async requestViaWorker(
210
+ method: string,
211
+ path: string,
212
+ headers: Record<string, string>,
213
+ body?: Record<string, unknown>,
214
+ ): Promise<Response> {
215
+ return fetch(`${this.workerUrl}${path}`, {
216
+ method,
217
+ headers,
218
+ body: body === undefined || method === 'GET' || method === 'DELETE'
219
+ ? undefined
220
+ : JSON.stringify(body),
221
+ });
222
+ }
223
+
224
+ private async requestViaD1Handler(
225
+ method: string,
226
+ namespace: string,
227
+ instanceId: string | undefined,
228
+ tableName: string,
229
+ directPath: string,
230
+ headers: Record<string, string>,
231
+ query: URLSearchParams,
232
+ body?: Record<string, unknown>,
233
+ ): Promise<Response> {
234
+ if (!this.env) throw new Error('D1 table proxy requires env.');
235
+
236
+ const queryString = Array.from(query.keys()).length > 0 ? `?${query.toString()}` : '';
237
+ const url = `http://internal/api/db/${namespace}${instanceId ? `/${instanceId}` : ''}${directPath}${queryString}`;
238
+
239
+ const request = new Request(url, {
240
+ method,
241
+ headers,
242
+ body: body === undefined || method === 'GET' || method === 'DELETE'
243
+ ? undefined
244
+ : JSON.stringify(body),
245
+ });
246
+
247
+ return handleD1Request(
248
+ buildInternalHandlerContext({ env: this.env, request, body, executionCtx: this.executionCtx }),
249
+ namespace,
250
+ tableName,
251
+ directPath,
252
+ );
253
+ }
254
+
255
+ private async requestViaPgHandler(
256
+ method: string,
257
+ namespace: string,
258
+ instanceId: string | undefined,
259
+ tableName: string,
260
+ directPath: string,
261
+ headers: Record<string, string>,
262
+ query: URLSearchParams,
263
+ body?: Record<string, unknown>,
264
+ ): Promise<Response> {
265
+ if (!this.env) throw new Error('PostgreSQL table proxy requires env.');
266
+
267
+ const queryString = Array.from(query.keys()).length > 0 ? `?${query.toString()}` : '';
268
+ const url = `http://internal/api/db/${namespace}${instanceId ? `/${instanceId}` : ''}${directPath}${queryString}`;
269
+
270
+ const request = new Request(url, {
271
+ method,
272
+ headers,
273
+ body: body === undefined || method === 'GET' || method === 'DELETE'
274
+ ? undefined
275
+ : JSON.stringify(body),
276
+ });
277
+
278
+ return handlePgRequest(
279
+ buildInternalHandlerContext({ env: this.env, request, body, executionCtx: this.executionCtx }),
280
+ namespace,
281
+ tableName,
282
+ directPath,
283
+ );
284
+ }
285
+
286
+ private async requestViaDirectDo(
287
+ method: string,
288
+ doName: string,
289
+ directPathWithQuery: string,
290
+ headers: Record<string, string>,
291
+ body?: Record<string, unknown>,
292
+ isDynamic?: boolean,
293
+ ): Promise<Response> {
294
+ const res = await callDO(this.databaseNamespace, doName, directPathWithQuery, {
295
+ method,
296
+ body,
297
+ headers,
298
+ });
299
+
300
+ // Handle dynamic DO creation
301
+ if (isDynamic && res.status === 201) {
302
+ const createPayload = await res.clone().json().catch(() => null) as
303
+ | { needsCreate?: boolean }
304
+ | null;
305
+ if (createPayload?.needsCreate) {
306
+ return callDO(this.databaseNamespace, doName, directPathWithQuery, {
307
+ method,
308
+ body,
309
+ headers: { ...headers, 'X-DO-Create-Authorized': '1' },
310
+ });
311
+ }
312
+ }
313
+
314
+ return res;
315
+ }
316
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Pagination parameter validation.
3
3
  *
4
- * Clamps limit to 1..100 (default 20) and offset to ≥0 (default 0).
4
+ * Clamps limit to 1..1000 (default 100) and offset to ≥0 (default 0).
5
5
  * Handles NaN, negative, and absurdly large values safely.
6
6
  */
7
7
 
@@ -9,8 +9,8 @@ export function parsePagination(
9
9
  limitParam: string | undefined,
10
10
  offsetParam: string | undefined,
11
11
  ): { limit: number; offset: number } {
12
- const rawLimit = parseInt(limitParam || '20', 10);
13
- const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 100) : 20;
12
+ const rawLimit = parseInt(limitParam || '100', 10);
13
+ const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 1000) : 100;
14
14
 
15
15
  const rawOffset = parseInt(offsetParam || '0', 10);
16
16
  const offset = Number.isFinite(rawOffset) && rawOffset >= 0 ? rawOffset : 0;
@@ -350,7 +350,7 @@ function buildMigrationAdminContext(
350
350
  return doAdminDb(namespace, id);
351
351
  },
352
352
 
353
- async sql(
353
+ async sqlWithDirectD1Access(
354
354
  namespace: string,
355
355
  id: string | undefined,
356
356
  query: string,
@@ -359,7 +359,7 @@ function buildMigrationAdminContext(
359
359
  const dbBlock = config.databases?.[namespace];
360
360
  const isDynamicNamespace = !!(dbBlock?.instance || dbBlock?.access?.canCreate || dbBlock?.access?.access);
361
361
  if (isDynamicNamespace && !id) {
362
- throw new Error(`admin.sql() requires an id for dynamic namespace '${namespace}'.`);
362
+ throw new Error(`admin.sqlWithDirectD1Access() requires an id for dynamic namespace '${namespace}'.`);
363
363
  }
364
364
 
365
365
  const pgConnStr = resolvePgConnString(namespace);
@@ -365,7 +365,7 @@ async function handleList(
365
365
  total = Number(countResult.rows[0]?.total ?? 0);
366
366
  }
367
367
 
368
- const perPage = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ?? 20;
368
+ const perPage = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ?? 100;
369
369
  const page = queryOpts.pagination?.page ?? 1;
370
370
  const hasMore = queryOpts.pagination?.after || queryOpts.pagination?.before
371
371
  ? items.length >= perPage
@@ -428,7 +428,7 @@ async function handleSearch(
428
428
  const ftsFields = tableConfig.fts?.length
429
429
  ? tableConfig.fts
430
430
  : getTextFields(tableConfig);
431
- const limit = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ?? 20;
431
+ const limit = queryOpts.pagination?.limit ?? queryOpts.pagination?.perPage ?? 100;
432
432
  const offset = queryOpts.pagination?.offset ?? ((queryOpts.pagination?.page ?? 1) - 1) * limit;
433
433
  const searchQuery = buildSearchQuery(tableName, searchTerm, {
434
434
  pagination: queryOpts.pagination,
@@ -657,10 +657,10 @@ function buildLimitClause(
657
657
  const _bt = bt ?? new BindTracker('sqlite');
658
658
 
659
659
  if (!pagination) {
660
- return { limitClause: `LIMIT ${_bt.next()}`, limitParams: [20] }; // Default limit
660
+ return { limitClause: `LIMIT ${_bt.next()}`, limitParams: [100] }; // Default limit
661
661
  }
662
662
 
663
- const limit = pagination.limit ?? pagination.perPage ?? 20;
663
+ const limit = Math.min(pagination.limit ?? pagination.perPage ?? 100, 1000);
664
664
 
665
665
  // Cursor-based: no offset
666
666
  if (pagination.after || pagination.before) {
@@ -50,17 +50,17 @@ export const RATE_LIMIT_DEFAULTS: Record<string, { requests: number; windowSec:
50
50
  events: { requests: 100, windowSec: 60 },
51
51
  };
52
52
 
53
- // Dev mode defaults: 10x higher to accommodate React strict mode double-rendering,
54
- // hot-reload page refreshes, and onSnapshot polling during development.
53
+ // Dev mode defaults: significantly higher to accommodate React strict mode double-rendering,
54
+ // hot-reload page refreshes, onSnapshot polling, and multi-client testing during development.
55
55
  export const RATE_LIMIT_DEV_DEFAULTS: Record<string, { requests: number; windowSec: number }> = {
56
56
  global: { requests: 10_000_000, windowSec: 60 },
57
- db: { requests: 1000, windowSec: 60 },
58
- storage: { requests: 500, windowSec: 60 },
59
- functions: { requests: 500, windowSec: 60 },
60
- auth: { requests: 300, windowSec: 60 },
57
+ db: { requests: 5000, windowSec: 60 },
58
+ storage: { requests: 1000, windowSec: 60 },
59
+ functions: { requests: 1000, windowSec: 60 },
60
+ auth: { requests: 500, windowSec: 60 },
61
61
  authSignin: { requests: 100, windowSec: 60 },
62
62
  authSignup: { requests: 100, windowSec: 60 },
63
- events: { requests: 1000, windowSec: 60 },
63
+ events: { requests: 5000, windowSec: 60 },
64
64
  };
65
65
 
66
66
  // ─── Window parser ───
@@ -256,7 +256,7 @@ export const rateLimitMiddleware: MiddlewareHandler<HonoEnv> = async (c, next) =
256
256
  if (!counter.check(counterKey, requests, windowSec)) {
257
257
  c.header('Retry-After', String(counter.getRetryAfter(counterKey)));
258
258
  return c.json(
259
- { code: 429, message: 'Too many requests. Please try again later.' },
259
+ { code: 429, message: 'Too many requests. Please try again later.', group },
260
260
  429,
261
261
  );
262
262
  }
@@ -268,7 +268,7 @@ export const rateLimitMiddleware: MiddlewareHandler<HonoEnv> = async (c, next) =
268
268
  if (!success) {
269
269
  c.header('Retry-After', '60');
270
270
  return c.json(
271
- { code: 429, message: 'Too many requests. Please try again later.' },
271
+ { code: 429, message: 'Too many requests. Please try again later.', group },
272
272
  429,
273
273
  );
274
274
  }
@@ -282,7 +282,7 @@ export const rateLimitMiddleware: MiddlewareHandler<HonoEnv> = async (c, next) =
282
282
  if (!counter.check(globalKey, globalLimit.requests, globalLimit.windowSec)) {
283
283
  c.header('Retry-After', String(counter.getRetryAfter(globalKey)));
284
284
  return c.json(
285
- { code: 429, message: 'Too many requests. Please try again later.' },
285
+ { code: 429, message: 'Too many requests. Please try again later.', group: 'global' },
286
286
  429,
287
287
  );
288
288
  }
@@ -294,7 +294,7 @@ export const rateLimitMiddleware: MiddlewareHandler<HonoEnv> = async (c, next) =
294
294
  if (!success) {
295
295
  c.header('Retry-After', '60');
296
296
  return c.json(
297
- { code: 429, message: 'Too many requests. Please try again later.' },
297
+ { code: 429, message: 'Too many requests. Please try again later.', group: 'global' },
298
298
  429,
299
299
  );
300
300
  }
@@ -119,10 +119,16 @@ function quoteSqlIdentifier(identifier: string): string {
119
119
  return `"${identifier}"`;
120
120
  }
121
121
 
122
+ // Browser bootstrap is opt-in and only enabled by the local dev workflow.
123
+ function isPublicAdminSetupEnabled(env: Env): boolean {
124
+ return env.EDGEBASE_ALLOW_PUBLIC_ADMIN_SETUP === '1'
125
+ || env.EDGEBASE_ALLOW_PUBLIC_ADMIN_SETUP === 'true';
126
+ }
127
+
122
128
  function isPublicAdminSetupAllowed(env: Env): boolean {
123
129
  try {
124
130
  const config = parseConfig(env);
125
- return config.release !== true;
131
+ return config.release !== true && isPublicAdminSetupEnabled(env);
126
132
  } catch {
127
133
  return false;
128
134
  }
@@ -240,8 +246,8 @@ function getLogStatusCode(log: Record<string, unknown>): number {
240
246
  function matchesLogLevel(status: number, level: string): boolean {
241
247
  const normalized = level.toLowerCase();
242
248
  if (normalized === 'error') return status >= 500;
243
- if (normalized === 'warn') return status >= 300 && status < 500;
244
- if (normalized === 'info') return status >= 200 && status < 300;
249
+ if (normalized === 'warn') return (status >= 300 && status < 500 && status !== 304);
250
+ if (normalized === 'info') return (status >= 200 && status < 300) || status === 304;
245
251
  return true;
246
252
  }
247
253
 
@@ -54,6 +54,7 @@ import {
54
54
  buildFunctionPushProxy,
55
55
  buildAdminAuthContext,
56
56
  buildAdminDbProxy,
57
+ executeSqlWithDirectD1Access,
57
58
  getWorkerUrl,
58
59
  } from '../lib/functions.js';
59
60
  import * as authService from '../lib/auth-d1-service.js';
@@ -509,6 +510,13 @@ async function createSessionAndTokens(
509
510
  metadata,
510
511
  });
511
512
 
513
+ // Update lastSignedInAt on the user record
514
+ try {
515
+ await db.run('UPDATE _users SET lastSignedInAt = ? WHERE id = ?', [now, userId]);
516
+ } catch {
517
+ // Non-critical: don't block sign-in if this column doesn't exist yet
518
+ }
519
+
512
520
  return { accessToken, refreshToken, sessionId };
513
521
  }
514
522
 
@@ -652,18 +660,11 @@ export async function executeAuthHook(
652
660
  db: adminDb,
653
661
  table: (name: string) => adminDb('shared').table(name),
654
662
  auth: authAdmin,
655
- async sql(namespace: string, id: string | undefined, query: string, params?: unknown[]) {
656
- if (options.workerUrl && serviceKey) {
657
- const res = await fetch(`${options.workerUrl}/api/sql`, {
658
- method: 'POST',
659
- headers: { 'Content-Type': 'application/json', 'X-EdgeBase-Service-Key': serviceKey },
660
- body: JSON.stringify({ namespace, id, sql: query, params: params ?? [] }),
661
- });
662
- if (!res.ok) throw new Error(`admin.sql() failed: ${res.status}`);
663
- return res.json();
664
- }
665
- throw new Error('admin.sql() requires workerUrl in auth hook context.');
666
- },
663
+ sqlWithDirectD1Access: (namespace: string, id: string | undefined, query: string, params?: unknown[]) =>
664
+ executeSqlWithDirectD1Access(
665
+ { env, config, databaseNamespace: env.DATABASE, workerUrl: options.workerUrl, serviceKey },
666
+ namespace, id, query, params,
667
+ ),
667
668
  async broadcast(channel: string, event: string, payload?: Record<string, unknown>) {
668
669
  if (options.workerUrl && serviceKey) {
669
670
  await fetch(`${options.workerUrl}/api/db/broadcast`, {
@@ -44,6 +44,7 @@ import {
44
44
  buildFunctionPushProxy,
45
45
  buildAdminAuthContext,
46
46
  buildAdminDbProxy,
47
+ executeSqlWithDirectD1Access,
47
48
  getWorkerUrl,
48
49
  } from '../lib/functions.js';
49
50
 
@@ -165,18 +166,11 @@ function buildStorageHookAdminContext(
165
166
  db: adminDb,
166
167
  table: (name: string) => adminDb('shared').table(name),
167
168
  auth: buildAdminAuthContext({ d1Database: env.AUTH_DB, serviceKey, workerUrl }),
168
- async sql(namespace: string, id: string | undefined, query: string, params?: unknown[]) {
169
- if (workerUrl && serviceKey) {
170
- const res = await fetch(`${workerUrl}/api/sql`, {
171
- method: 'POST',
172
- headers: { 'Content-Type': 'application/json', 'X-EdgeBase-Service-Key': serviceKey },
173
- body: JSON.stringify({ namespace, id, sql: query, params: params ?? [] }),
174
- });
175
- if (!res.ok) throw new Error(`admin.sql() failed: ${res.status}`);
176
- return res.json();
177
- }
178
- throw new Error('admin.sql() requires workerUrl in storage hook context.');
179
- },
169
+ sqlWithDirectD1Access: (namespace: string, id: string | undefined, query: string, params?: unknown[]) =>
170
+ executeSqlWithDirectD1Access(
171
+ { env, config, databaseNamespace: env.DATABASE, workerUrl, serviceKey },
172
+ namespace, id, query, params,
173
+ ),
180
174
  async broadcast(channel: string, event: string, payload?: Record<string, unknown>) {
181
175
  if (workerUrl && serviceKey) {
182
176
  await fetch(`${workerUrl}/api/db/broadcast`, {
package/src/types.ts CHANGED
@@ -100,6 +100,8 @@ export interface Env {
100
100
  EDGEBASE_USE_TEST_CONFIG?: string;
101
101
 
102
102
  // ─── Dev Mode ───
103
+ /** Enables browser-based first-admin setup for the local dev server. */
104
+ EDGEBASE_ALLOW_PUBLIC_ADMIN_SETUP?: string;
103
105
  /** Schema Editor sidecar port — set by CLI dev command via --var */
104
106
  EDGEBASE_DEV_SIDECAR_PORT?: string;
105
107
  }
@@ -1 +0,0 @@
1
- .sql-editor-wrap.svelte-392xt8{border:1px solid var(--color-border);border-radius:var(--radius-md);overflow:hidden}.sql-editor-wrap.svelte-392xt8 .cm-editor{min-height:120px}.sql-editor-wrap.svelte-392xt8 .cm-editor.cm-focused{border-color:var(--color-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 20%,transparent)}.table-sql.svelte-1syrthj{display:flex;flex-direction:column;gap:var(--space-4)}.table-sql__toolbar.svelte-1syrthj{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap}.table-sql__target.svelte-1syrthj{display:inline-flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-secondary);font-size:12px}.table-sql__target-label.svelte-1syrthj{color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.04em}.table-sql__target.svelte-1syrthj code:where(.svelte-1syrthj){font-family:var(--font-mono);color:var(--color-text)}.table-sql__shortcut.svelte-1syrthj{font-size:12px;color:var(--color-text-tertiary);margin-left:auto}.table-sql__result-tabs.svelte-1syrthj{display:flex;flex-wrap:wrap;gap:var(--space-2)}.table-sql__result-tab.svelte-1syrthj{display:inline-flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-secondary);color:var(--color-text-secondary);cursor:pointer}.table-sql__result-tab--active.svelte-1syrthj{border-color:var(--color-primary);color:var(--color-primary)}.table-sql__result-open.svelte-1syrthj{border:none;background:transparent;color:inherit;cursor:pointer;padding:0;font:inherit}.table-sql__result-close.svelte-1syrthj{border:none;background:transparent;color:inherit;cursor:pointer;padding:0;line-height:1}.table-sql__result-panel.svelte-1syrthj{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-4);background:var(--color-bg)}.table-sql__result-meta.svelte-1syrthj{margin-bottom:var(--space-3);font-size:12px;color:var(--color-text-secondary)}.table-sql__result-error.svelte-1syrthj{color:var(--color-danger, #ef4444)}.table-sql__error-block.svelte-1syrthj,.table-sql__empty.svelte-1syrthj{margin:0;padding:var(--space-4);border-radius:var(--radius-md);background:var(--color-bg-secondary);color:var(--color-text-secondary);font-size:13px}
@@ -1,2 +0,0 @@
1
- import"./CWj6FrbW.js";import{p as je,w as le,a as Be,b as k,g as o,d as M,M as Ke,t as E,u as D,s as w,c as g,r as h,ae as ke,f as Ge}from"./BdTBlfLy.js";import{s as B,d as Ye,a as pe}from"./DtZk82gG.js";import{a as C,f as P,t as He}from"./DEELgv7K.js";import{i as W}from"./Y22E1hJM.js";import{a as Je,t as $e,e as et}from"./B14gOIqE.js";import{s as tt,a as at}from"./CoI6jjbg.js";import{a as rt,s as st}from"./Cp8V0Xy2.js";import{B as ot}from"./C72lTcG0.js";import{D as nt}from"./D8Nrx_IG.js";import{o as lt,a as it}from"./Bn2NtlTj.js";import{b as ct}from"./CGgVJi7f.js";import{p as xe}from"./F9_4wRrd.js";import{L as dt,a as ft,i as mt,c as ut,b as ht,d as vt,f as Ot,s as gt,t as m,E as bt,e as kt,g as pt,h as se,j as xt,k as yt,l as Qt,m as _t,n as Ct,p as St,o as wt,q as Pt,r as Tt}from"./gtu8uwJD.js";import{s as ye}from"./BKLsgaNT.js";const qt=36,Qe=1,Ut=2,V=3,oe=4,jt=5,Bt=6,Et=7,It=8,Dt=9,Lt=10,Rt=11,Xt=12,Nt=13,Zt=14,zt=15,Vt=16,Mt=17,_e=18,At=19,Ee=20,Ie=21,Ce=22,Ft=23,Wt=24;function ie(t){return t>=65&&t<=90||t>=97&&t<=122||t>=48&&t<=57}function Kt(t){return t>=48&&t<=57||t>=97&&t<=102||t>=65&&t<=70}function R(t,e,r){for(let a=!1;;){if(t.next<0)return;if(t.next==e&&!a){t.advance();return}a=r&&!a&&t.next==92,t.advance()}}function Gt(t,e){e:for(;;){if(t.next<0)return;if(t.next==36){t.advance();for(let r=0;r<e.length;r++){if(t.next!=e.charCodeAt(r))continue e;t.advance()}if(t.next==36){t.advance();return}}else t.advance()}}function Yt(t,e){let r="[{<(".indexOf(String.fromCharCode(e)),a=r<0?e:"]}>)".charCodeAt(r);for(;;){if(t.next<0)return;if(t.next==a&&t.peek(1)==39){t.advance(2);return}t.advance()}}function ce(t,e){for(;!(t.next!=95&&!ie(t.next));)e!=null&&(e+=String.fromCharCode(t.next)),t.advance();return e}function Ht(t){if(t.next==39||t.next==34||t.next==96){let e=t.next;t.advance(),R(t,e,!1)}else ce(t)}function Se(t,e){for(;t.next==48||t.next==49;)t.advance();e&&t.next==e&&t.advance()}function we(t,e){for(;;){if(t.next==46){if(e)break;e=!0}else if(t.next<48||t.next>57)break;t.advance()}if(t.next==69||t.next==101)for(t.advance(),(t.next==43||t.next==45)&&t.advance();t.next>=48&&t.next<=57;)t.advance()}function Pe(t){for(;!(t.next<0||t.next==10);)t.advance()}function L(t,e){for(let r=0;r<e.length;r++)if(e.charCodeAt(r)==t)return!0;return!1}const ne=` \r
2
- `;function De(t,e,r){let a=Object.create(null);a.true=a.false=jt,a.null=a.unknown=Bt;for(let s of t.split(" "))s&&(a[s]=Ee);for(let s of e.split(" "))s&&(a[s]=Ie);for(let s of(r||"").split(" "))s&&(a[s]=Wt);return a}const Le="array binary bit boolean char character clob date decimal double float int integer interval large national nchar nclob numeric object precision real smallint time timestamp varchar varying ",Re="absolute action add after all allocate alter and any are as asc assertion at authorization before begin between both breadth by call cascade cascaded case cast catalog check close collate collation column commit condition connect connection constraint constraints constructor continue corresponding count create cross cube current current_date current_default_transform_group current_transform_group_for_type current_path current_role current_time current_timestamp current_user cursor cycle data day deallocate declare default deferrable deferred delete depth deref desc describe descriptor deterministic diagnostics disconnect distinct do domain drop dynamic each else elseif end end-exec equals escape except exception exec execute exists exit external fetch first for foreign found from free full function general get global go goto grant group grouping handle having hold hour identity if immediate in indicator initially inner inout input insert intersect into is isolation join key language last lateral leading leave left level like limit local localtime localtimestamp locator loop map match method minute modifies module month names natural nesting new next no none not of old on only open option or order ordinality out outer output overlaps pad parameter partial path prepare preserve primary prior privileges procedure public read reads recursive redo ref references referencing relative release repeat resignal restrict result return returns revoke right role rollback rollup routine row rows savepoint schema scroll search second section select session session_user set sets signal similar size some space specific specifictype sql sqlexception sqlstate sqlwarning start state static system_user table temporary then timezone_hour timezone_minute to trailing transaction translation treat trigger under undo union unique unnest until update usage user using value values view when whenever where while with without work write year zone ",de={backslashEscapes:!1,hashComments:!1,spaceAfterDashes:!1,slashComments:!1,doubleQuotedStrings:!1,doubleDollarQuotedStrings:!1,unquotedBitLiterals:!1,treatBitsAsBytes:!1,charSetCasts:!1,plsqlQuotingMechanism:!1,operatorChars:"*+-%<>!=&|~^/",specialVar:"?",identifierQuotes:'"',caseInsensitiveIdentifiers:!1,words:De(Re,Le)};function Jt(t,e,r,a){let s={};for(let n in de)s[n]=(t.hasOwnProperty(n)?t:de)[n];return e&&(s.words=De(e,r||"",a)),s}function Xe(t){return new bt(e=>{var r;let{next:a}=e;if(e.advance(),L(a,ne)){for(;L(e.next,ne);)e.advance();e.acceptToken(qt)}else if(a==36&&t.doubleDollarQuotedStrings){let s=ce(e,"");e.next==36&&(e.advance(),Gt(e,s),e.acceptToken(V))}else if(a==39||a==34&&t.doubleQuotedStrings)R(e,a,t.backslashEscapes),e.acceptToken(V);else if(a==35&&t.hashComments||a==47&&e.next==47&&t.slashComments)Pe(e),e.acceptToken(Qe);else if(a==45&&e.next==45&&(!t.spaceAfterDashes||e.peek(1)==32))Pe(e),e.acceptToken(Qe);else if(a==47&&e.next==42){e.advance();for(let s=1;;){let n=e.next;if(e.next<0)break;if(e.advance(),n==42&&e.next==47){if(s--,e.advance(),!s)break}else n==47&&e.next==42&&(s++,e.advance())}e.acceptToken(Ut)}else if((a==101||a==69)&&e.next==39)e.advance(),R(e,39,!0),e.acceptToken(V);else if((a==110||a==78)&&e.next==39&&t.charSetCasts)e.advance(),R(e,39,t.backslashEscapes),e.acceptToken(V);else if(a==95&&t.charSetCasts)for(let s=0;;s++){if(e.next==39&&s>1){e.advance(),R(e,39,t.backslashEscapes),e.acceptToken(V);break}if(!ie(e.next))break;e.advance()}else if(t.plsqlQuotingMechanism&&(a==113||a==81)&&e.next==39&&e.peek(1)>0&&!L(e.peek(1),ne)){let s=e.peek(1);e.advance(2),Yt(e,s),e.acceptToken(V)}else if(L(a,t.identifierQuotes)){const s=a==91?93:a;R(e,s,!1),e.acceptToken(At)}else if(a==40)e.acceptToken(Et);else if(a==41)e.acceptToken(It);else if(a==123)e.acceptToken(Dt);else if(a==125)e.acceptToken(Lt);else if(a==91)e.acceptToken(Rt);else if(a==93)e.acceptToken(Xt);else if(a==59)e.acceptToken(Nt);else if(t.unquotedBitLiterals&&a==48&&e.next==98)e.advance(),Se(e),e.acceptToken(Ce);else if((a==98||a==66)&&(e.next==39||e.next==34)){const s=e.next;e.advance(),t.treatBitsAsBytes?(R(e,s,t.backslashEscapes),e.acceptToken(Ft)):(Se(e,s),e.acceptToken(Ce))}else if(a==48&&(e.next==120||e.next==88)||(a==120||a==88)&&e.next==39){let s=e.next==39;for(e.advance();Kt(e.next);)e.advance();s&&e.next==39&&e.advance(),e.acceptToken(oe)}else if(a==46&&e.next>=48&&e.next<=57)we(e,!0),e.acceptToken(oe);else if(a==46)e.acceptToken(Zt);else if(a>=48&&a<=57)we(e,!1),e.acceptToken(oe);else if(L(a,t.operatorChars)){for(;L(e.next,t.operatorChars);)e.advance();e.acceptToken(zt)}else if(L(a,t.specialVar))e.next==a&&e.advance(),Ht(e),e.acceptToken(Mt);else if(a==58||a==44)e.acceptToken(Vt);else if(ie(a)){let s=ce(e,String.fromCharCode(a));e.acceptToken(e.next==46||e.peek(-s.length-1)==46?_e:(r=t.words[s.toLowerCase()])!==null&&r!==void 0?r:_e)}})}const Ne=Xe(de),$t=kt.deserialize({version:14,states:"%vQ]QQOOO#wQRO'#DSO$OQQO'#CwO%eQQO'#CxO%lQQO'#CyO%sQQO'#CzOOQQ'#DS'#DSOOQQ'#C}'#C}O'UQRO'#C{OOQQ'#Cv'#CvOOQQ'#C|'#C|Q]QQOOQOQQOOO'`QQO'#DOO(xQRO,59cO)PQQO,59cO)UQQO'#DSOOQQ,59d,59dO)cQQO,59dOOQQ,59e,59eO)jQQO,59eOOQQ,59f,59fO)qQQO,59fOOQQ-E6{-E6{OOQQ,59b,59bOOQQ-E6z-E6zOOQQ,59j,59jOOQQ-E6|-E6|O+VQRO1G.}O+^QQO,59cOOQQ1G/O1G/OOOQQ1G/P1G/POOQQ1G/Q1G/QP+kQQO'#C}O+rQQO1G.}O)PQQO,59cO,PQQO'#Cw",stateData:",[~OtOSPOSQOS~ORUOSUOTUOUUOVROXSOZTO]XO^QO_UO`UOaPObPOcPOdUOeUOfUOgUOhUO~O^]ORvXSvXTvXUvXVvXXvXZvX]vX_vX`vXavXbvXcvXdvXevXfvXgvXhvX~OsvX~P!jOa_Ob_Oc_O~ORUOSUOTUOUUOVROXSOZTO^tO_UO`UOa`Ob`Oc`OdUOeUOfUOgUOhUO~OWaO~P$ZOYcO~P$ZO[eO~P$ZORUOSUOTUOUUOVROXSOZTO^QO_UO`UOaPObPOcPOdUOeUOfUOgUOhUO~O]hOsoX~P%zOajObjOcjO~O^]ORkaSkaTkaUkaVkaXkaZka]ka_ka`kaakabkackadkaekafkagkahka~Oska~P'kO^]O~OWvXYvX[vX~P!jOWnO~P$ZOYoO~P$ZO[pO~P$ZO^]ORkiSkiTkiUkiVkiXkiZki]ki_ki`kiakibkickidkiekifkigkihki~Oski~P)xOWkaYka[ka~P'kO]hO~P$ZOWkiYki[ki~P)xOasObsOcsO~O",goto:"#hwPPPPPPPPPPPPPPPPPPPPPPPPPPx||||!Y!^!d!xPPP#[TYOZeUORSTWZbdfqT[OZQZORiZSWOZQbRQdSQfTZgWbdfqQ^PWk^lmrQl_Qm`RrseVORSTWZbdfq",nodeNames:"⚠ LineComment BlockComment String Number Bool Null ( ) { } [ ] ; . Operator Punctuation SpecialVar Identifier QuotedIdentifier Keyword Type Bits Bytes Builtin Script Statement CompositeIdentifier Parens Braces Brackets Statement",maxTerm:38,nodeProps:[["isolate",-4,1,2,3,19,""]],skippedNodes:[0,1,2],repeatNodeCount:3,tokenData:"RORO",tokenizers:[0,Ne],topRules:{Script:[0,25]},tokenPrec:0});function fe(t){let e=t.cursor().moveTo(t.from,-1);for(;/Comment/.test(e.name);)e.moveTo(e.from,-1);return e.node}function K(t,e){let r=t.sliceString(e.from,e.to),a=/^([`'"\[])(.*)([`'"\]])$/.exec(r);return a?a[2]:r}function ee(t){return t&&(t.name=="Identifier"||t.name=="QuotedIdentifier")}function ea(t,e){if(e.name=="CompositeIdentifier"){let r=[];for(let a=e.firstChild;a;a=a.nextSibling)ee(a)&&r.push(K(t,a));return r}return[K(t,e)]}function Te(t,e){for(let r=[];;){if(!e||e.name!=".")return r;let a=fe(e);if(!ee(a))return r;r.unshift(K(t,a)),e=fe(a)}}function ta(t,e){let r=pt(t).resolveInner(e,-1),a=ra(t.doc,r);return r.name=="Identifier"||r.name=="QuotedIdentifier"||r.name=="Keyword"?{from:r.from,quoted:r.name=="QuotedIdentifier"?t.doc.sliceString(r.from,r.from+1):null,parents:Te(t.doc,fe(r)),aliases:a}:r.name=="."?{from:e,quoted:null,parents:Te(t.doc,r),aliases:a}:{from:e,quoted:null,parents:[],empty:!0,aliases:a}}const aa=new Set("where group having order union intersect except all distinct limit offset fetch for".split(" "));function ra(t,e){let r;for(let s=e;!r;s=s.parent){if(!s)return null;s.name=="Statement"&&(r=s)}let a=null;for(let s=r.firstChild,n=!1,f=null;s;s=s.nextSibling){let l=s.name=="Keyword"?t.sliceString(s.from,s.to).toLowerCase():null,i=null;if(!n)n=l=="from";else if(l=="as"&&f&&ee(s.nextSibling))i=K(t,s.nextSibling);else{if(l&&aa.has(l))break;f&&ee(s)&&(i=K(t,s))}i&&(a||(a=Object.create(null)),a[i]=ea(t,f)),f=/Identifier$/.test(s.name)?s:null}return a}function sa(t,e,r){return r.map(a=>({...a,label:a.label[0]==t?a.label:t+a.label+e,apply:void 0}))}const oa=/^\w*$/,na=/^[`'"\[]?\w*[`'"\]]?$/;function qe(t){return t.self&&typeof t.self.label=="string"}class me{constructor(e,r){this.idQuote=e,this.idCaseInsensitive=r,this.list=[],this.children=void 0}child(e){let r=this.children||(this.children=Object.create(null)),a=r[e];return a||(e&&!this.list.some(s=>s.label==e)&&this.list.push(Ue(e,"type",this.idQuote,this.idCaseInsensitive)),r[e]=new me(this.idQuote,this.idCaseInsensitive))}maybeChild(e){return this.children?this.children[e]:null}addCompletion(e){let r=this.list.findIndex(a=>a.label==e.label);r>-1?this.list[r]=e:this.list.push(e)}addCompletions(e){for(let r of e)this.addCompletion(typeof r=="string"?Ue(r,"property",this.idQuote,this.idCaseInsensitive):r)}addNamespace(e){Array.isArray(e)?this.addCompletions(e):qe(e)?this.addNamespace(e.children):this.addNamespaceObject(e)}addNamespaceObject(e){for(let r of Object.keys(e)){let a=e[r],s=null,n=r.replace(/\\?\./g,l=>l=="."?"\0":l).split("\0"),f=this;qe(a)&&(s=a.self,a=a.children);for(let l=0;l<n.length;l++)s&&l==n.length-1&&f.addCompletion(s),f=f.child(n[l].replace(/\\\./g,"."));f.addNamespace(a)}}}function Ue(t,e,r,a){return new RegExp("^[a-z_][a-z_\\d]*$",a?"i":"").test(t)?{label:t,type:e}:{label:t,type:e,apply:r+t+Ze(r)}}function Ze(t){return t==="["?"]":t}function la(t,e,r,a,s,n){var f;let l=((f=n==null?void 0:n.spec.identifierQuotes)===null||f===void 0?void 0:f[0])||'"',i=new me(l,!!(n!=null&&n.spec.caseInsensitiveIdentifiers)),v=s?i.child(s):null;return i.addNamespace(t),e&&(v||i).addCompletions(e),r&&i.addCompletions(r),v&&i.addCompletions(v.list),a&&i.addCompletions((v||i).child(a).list),p=>{let{parents:O,from:y,quoted:T,empty:te,aliases:X}=ta(p.state,p.pos);if(te&&!p.explicit)return null;X&&O.length==1&&(O=X[O[0]]||O);let x=i;for(let S of O){for(;!x.children||!x.children[S];)if(x==i&&v)x=v;else if(x==v&&a)x=x.child(a);else return null;let q=x.maybeChild(S);if(!q)return null;x=q}let N=x.list;if(x==i&&X&&(N=N.concat(Object.keys(X).map(S=>({label:S,type:"constant"})))),T){let S=T[0],q=Ze(S),A=p.state.sliceDoc(p.pos,p.pos+1)==q;return{from:y,to:A?p.pos+1:void 0,options:sa(S,q,N),validFor:na}}else return{from:y,options:N,validFor:oa}}}function ia(t){return t==Ie?"type":t==Ee?"keyword":"variable"}function ca(t,e,r){let a=Object.keys(t).map(s=>r(e?s.toUpperCase():s,ia(t[s])));return mt(["QuotedIdentifier","String","LineComment","BlockComment","."],ut(a))}let da=$t.configure({props:[ht.add({Statement:vt()}),Ot.add({Statement(t,e){return{from:Math.min(t.from+100,e.doc.lineAt(t.from).to),to:t.to}},BlockComment(t){return{from:t.from+2,to:t.to-2}}}),gt({Keyword:m.keyword,Type:m.typeName,Builtin:m.standard(m.name),Bits:m.number,Bytes:m.string,Bool:m.bool,Null:m.null,Number:m.number,String:m.string,Identifier:m.name,QuotedIdentifier:m.special(m.string),SpecialVar:m.special(m.name),LineComment:m.lineComment,BlockComment:m.blockComment,Operator:m.operator,"Semi Punctuation":m.punctuation,"( )":m.paren,"{ }":m.brace,"[ ]":m.squareBracket})]});class G{constructor(e,r,a){this.dialect=e,this.language=r,this.spec=a}get extension(){return this.language.extension}configureLanguage(e,r){return new G(this.dialect,this.language.configure(e,r),this.spec)}static define(e){let r=Jt(e,e.keywords,e.types,e.builtin),a=ft.define({name:"sql",parser:da.configure({tokenizers:[{from:Ne,to:Xe(r)}]}),languageData:{commentTokens:{line:"--",block:{open:"/*",close:"*/"}},closeBrackets:{brackets:["(","[","{","'",'"',"`"]}}});return new G(r,a,e)}}function fa(t,e){return{label:t,type:e,boost:-1}}function ma(t,e=!1,r){return ca(t.dialect.words,e,r||fa)}function ua(t){return t.schema?la(t.schema,t.tables,t.schemas,t.defaultTable,t.defaultSchema,t.dialect||ue):()=>null}function ha(t){return t.schema?(t.dialect||ue).language.data.of({autocomplete:ua(t)}):[]}function va(t={}){let e=t.dialect||ue;return new dt(e.language,[ha(t),e.language.data.of({autocomplete:ma(e,t.upperCaseKeywords,t.keywordCompletion)})])}const ue=G.define({}),Oa=G.define({keywords:Re+"abort analyze attach autoincrement conflict database detach exclusive fail glob ignore index indexed instead isnull notnull offset plan pragma query raise regexp reindex rename replace temp vacuum virtual",types:Le+"bool blob long longblob longtext medium mediumblob mediumint mediumtext tinyblob tinyint tinytext text bigint int2 int8 unsigned signed real",builtin:"auth backup bail changes clone databases dbinfo dump echo eqp explain fullschema headers help import imposter indexes iotrace lint load log mode nullvalue once print prompt quit restore save scanstats separator shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width",operatorChars:"*+-%<>!=&|/~",identifierQuotes:'`"',specialVar:"@:?$"});var ga=P('<div class="sql-editor-wrap svelte-392xt8"></div>');function ba(t,e){je(e,!0);let r=xe(e,"placeholder",3,""),a=xe(e,"schema",19,()=>({})),s,n,f=M(!1);function l(){return document.documentElement.dataset.theme==="dark"||document.documentElement.dataset.theme!=="light"&&window.matchMedia("(prefers-color-scheme: dark)").matches}function i(){const O=[va({dialect:Oa,schema:a()}),yt(),Qt.of([..._t,Ct,{key:"Mod-Enter",run:()=>{var y;return(y=e.onExecute)==null||y.call(e),!0}}]),se.updateListener.of(y=>{if(y.docChanged){const T=y.state.doc.toString();e.onchange(T)}}),se.theme({"&":{fontSize:"13px",maxHeight:"300px"},".cm-scroller":{overflow:"auto",fontFamily:"var(--font-mono)"},".cm-content":{minHeight:"100px",padding:"8px 0"},"&.cm-focused":{outline:"none"}})];return r()&&O.push(St(r())),o(f)?O.push(wt):O.push(Pt(Tt)),O}function v(){n&&n.destroy(),n=new se({state:xt.create({doc:e.value,extensions:i()}),parent:s})}lt(()=>{k(f,l(),!0),v();const O=new MutationObserver(()=>{const y=l();if(y!==o(f)){k(f,y,!0);const T=(n==null?void 0:n.state.doc.toString())??e.value;v(),T!==e.value&&(n==null||n.dispatch({changes:{from:0,to:n.state.doc.length,insert:T}}))}});return O.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),()=>{O.disconnect(),n==null||n.destroy()}}),it(()=>{n==null||n.destroy()}),le(()=>{n&&e.value!==n.state.doc.toString()&&n.dispatch({changes:{from:0,to:n.state.doc.length,insert:e.value}})});var p=ga();ct(p,O=>s=O,()=>s),C(t,p),Be()}var ka=P('<div class="table-sql__target svelte-1syrthj"><span class="table-sql__target-label svelte-1syrthj"> </span> <code class="svelte-1syrthj"> </code></div>'),pa=P('<div><button type="button" class="table-sql__result-open svelte-1syrthj"><span> </span></button> <button type="button" class="table-sql__result-close svelte-1syrthj">&times;</button></div>'),xa=P('<span class="table-sql__result-error svelte-1syrthj"> </span>'),ya=P("<span> </span>"),Qa=P('<pre class="table-sql__error-block svelte-1syrthj"> </pre>'),_a=P('<div class="table-sql__empty svelte-1syrthj">Query returned no rows.</div>'),Ca=P('<div class="table-sql__result-panel svelte-1syrthj"><div class="table-sql__result-meta svelte-1syrthj"><!></div> <!></div>'),Sa=P('<div class="table-sql__result-tabs svelte-1syrthj"></div> <!>',1),wa=P('<div class="table-sql__empty svelte-1syrthj">Run a query against <code> </code> to inspect rows, joins, or aggregates for this target.</div>'),Pa=P('<div class="table-sql svelte-1syrthj"><div class="table-sql__toolbar svelte-1syrthj"><div class="table-sql__target svelte-1syrthj"><span class="table-sql__target-label svelte-1syrthj">Database</span> <code class="svelte-1syrthj"> </code></div> <!> <!> <span class="table-sql__shortcut svelte-1syrthj"></span></div> <!> <!></div>');function Ma(t,e){var be;je(e,!0);const r=()=>rt(ye,"$schemaStore",a),[a,s]=st();let n=M(""),f=M(!1),l=M(Ke([])),i=M(0),v=M(0),p=D(()=>r().schema[e.tableName]),O=D(()=>(()=>{const c={},d=r().schema;for(const[u,_]of Object.entries(d)){const I=_.fields;c[u]=I?Object.keys(I):[]}return c})()),y=D(()=>{var c;return(c=o(p))!=null&&c.dynamic?"Per-tenant DB":"Single DB"}),T=D(()=>{var c,d,u;return((u=(d=(c=o(p))==null?void 0:c.instanceDiscovery)==null?void 0:d.targetLabel)==null?void 0:u.trim())||"Target"}),te=D(()=>e.instanceId||"Not selected");function X(c){return`SELECT * FROM "${c}" LIMIT 100;`}le(()=>{k(n,X(e.tableName),!0)}),le(()=>{Object.keys(r().schema).length===0&&ye.loadSchema({silent:!0})});async function x(){if(o(n).trim()){k(f,!0);try{const c=await Je.fetch("data/sql",{method:"POST",body:{namespace:e.namespace,id:e.instanceId||void 0,sql:o(n).trim()}}),d=(c.columns??[]).map(u=>({key:u,label:u,type:"text",editable:!1}));k(v,o(v)+1),k(l,[...o(l),{id:o(v),sql:o(n).trim(),columns:d,rows:c.rows??[],rowCount:c.rowCount??0,time:c.time??0}],!0),k(i,o(l).length-1)}catch(c){const d=c instanceof Error?c.message:"Query failed";$e(d),k(v,o(v)+1),k(l,[...o(l),{id:o(v),sql:o(n).trim(),columns:[],rows:[],rowCount:0,time:0,error:d}],!0),k(i,o(l).length-1)}finally{k(f,!1)}}}function N(c){const d=o(l).filter((u,_)=>_!==c);o(i)>c?k(i,o(i)-1):o(i)===c&&k(i,Math.min(c,Math.max(0,d.length-1)),!0),k(l,d,!0)}var S=Pa(),q=g(S),A=g(q),he=w(g(A),2),ze=g(he,!0);h(he),h(A);var ve=w(A,2);{var Ve=c=>{var d=ka(),u=g(d),_=g(u,!0);h(u);var I=w(u,2),U=g(I,!0);h(I),h(d),E(()=>{B(_,o(T)),B(U,o(te))}),C(c,d)};W(ve,c=>{var d;(d=o(p))!=null&&d.dynamic&&c(Ve)})}var Oe=w(ve,2);{let c=D(()=>o(f)||!o(n).trim());ot(Oe,{variant:"primary",size:"sm",onclick:x,get loading(){return o(f)},get disabled(){return o(c)},children:(d,u)=>{ke();var _=He();E(()=>B(_,o(f)?"Executing...":"Execute")),C(d,_)},$$slots:{default:!0}})}var Me=w(Oe,2);Me.textContent=`${(be=navigator.platform)!=null&&be.includes("Mac")?"⌘":"Ctrl"}+Enter`,h(q);var ge=w(q,2);{let c=D(()=>`SELECT * FROM "${e.tableName}" LIMIT 100;`);ba(ge,{get value(){return o(n)},onchange:d=>k(n,d,!0),onExecute:x,get placeholder(){return o(c)},get schema(){return o(O)}})}var Ae=w(ge,2);{var Fe=c=>{var d=Sa(),u=Ge(d);et(u,23,()=>o(l),U=>U.id,(U,Y,j)=>{var Z=pa();let H;var z=g(Z),J=g(z),ae=g(J);h(J),h(z);var $=w(z,2);h(Z),E(()=>{H=tt(Z,1,"table-sql__result-tab svelte-1syrthj",null,H,{"table-sql__result-tab--active":o(j)===o(i)}),B(ae,`Result ${o(j)+1}`),at($,"aria-label",`Close result ${o(j)+1}`)}),pe("click",z,()=>k(i,o(j),!0)),pe("click",$,re=>{re.stopPropagation(),N(o(j))}),C(U,Z)}),h(u);var _=w(u,2);{var I=U=>{var Y=Ca(),j=g(Y),Z=g(j);{var H=b=>{var Q=xa(),F=g(Q,!0);h(Q),E(()=>B(F,o(l)[o(i)].error)),C(b,Q)},z=b=>{var Q=ya(),F=g(Q);h(Q),E(()=>B(F,`${o(l)[o(i)].rowCount??""} row${o(l)[o(i)].rowCount===1?"":"s"} · ${o(l)[o(i)].time??""}ms`)),C(b,Q)};W(Z,b=>{o(l)[o(i)].error?b(H):b(z,!1)})}h(j);var J=w(j,2);{var ae=b=>{var Q=Qa(),F=g(Q,!0);h(Q),E(()=>B(F,o(l)[o(i)].sql)),C(b,Q)},$=b=>{var Q=_a();C(b,Q)},re=b=>{nt(b,{get columns(){return o(l)[o(i)].columns},get rows(){return o(l)[o(i)].rows},readonly:!0})};W(J,b=>{o(l)[o(i)].error?b(ae):o(l)[o(i)].rows.length===0?b($,1):b(re,!1)})}h(Y),C(U,Y)};W(_,U=>{o(l)[o(i)]&&U(I)})}C(c,d)},We=c=>{var d=wa(),u=w(g(d)),_=g(u,!0);h(u),ke(),h(d),E(()=>B(_,e.tableName)),C(c,d)};W(Ae,c=>{o(l).length>0?c(Fe):c(We,!1)})}h(S),E(()=>B(ze,o(y))),C(t,S),Be(),s()}Ye(["click"]);export{Ma as default};