@edge-base/server 0.2.1 → 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 (63) hide show
  1. package/admin-build/_app/immutable/chunks/{BY07qVPA.js → 4vlsb8ej.js} +1 -1
  2. package/admin-build/_app/immutable/chunks/{D755Tqat.js → 5PDcRlfX.js} +1 -1
  3. package/admin-build/_app/immutable/chunks/{BFs_qStz.js → B8DT4fss.js} +1 -1
  4. package/admin-build/_app/immutable/chunks/{DnLqc9L1.js → BEYYl662.js} +1 -1
  5. package/admin-build/_app/immutable/chunks/{Dqk2TGNU.js → BKXmgPq4.js} +1 -1
  6. package/admin-build/_app/immutable/chunks/{DjOEv9M9.js → BWyDPAjM.js} +1 -1
  7. package/admin-build/_app/immutable/chunks/{BcIUK2sk.js → BaCHY17I.js} +1 -1
  8. package/admin-build/_app/immutable/chunks/{B0QyxC2M.js → C-DsDCNG.js} +3 -3
  9. package/admin-build/_app/immutable/chunks/{k0CIJkw4.js → C85dMlzL.js} +1 -1
  10. package/admin-build/_app/immutable/chunks/{BsFiK_FJ.js → CPdXvRUb.js} +1 -1
  11. package/admin-build/_app/immutable/chunks/{BCKr7yKd.js → CTngeX8H.js} +1 -1
  12. package/admin-build/_app/immutable/chunks/{D-x55wdW.js → DzXaj-Ja.js} +1 -1
  13. package/admin-build/_app/immutable/chunks/{BTJcQFEp.js → c5iKSdWY.js} +1 -1
  14. package/admin-build/_app/immutable/chunks/{CqUxCvs_.js → g3ZZdY-r.js} +1 -1
  15. package/admin-build/_app/immutable/chunks/{CSGrwS7E.js → kiJ6KthZ.js} +1 -1
  16. package/admin-build/_app/immutable/chunks/{m9QZTyVV.js → qiZXAKh-.js} +1 -1
  17. package/admin-build/_app/immutable/entry/{app.BTsq3_xq.js → app.BZxfavhF.js} +2 -2
  18. package/admin-build/_app/immutable/entry/start.Mr9mmopc.js +1 -0
  19. package/admin-build/_app/immutable/nodes/{0.BZ00WDYH.js → 0.DlsaydXO.js} +1 -1
  20. package/admin-build/_app/immutable/nodes/{1.RzSJ3yyr.js → 1.D2NWN5eG.js} +1 -1
  21. package/admin-build/_app/immutable/nodes/{10.D-rsiquF.js → 10.EMDaN3nw.js} +1 -1
  22. package/admin-build/_app/immutable/nodes/{11.l7-bgtFD.js → 11.BasqQ_o9.js} +1 -1
  23. package/admin-build/_app/immutable/nodes/{12.Dkq0H7B5.js → 12.DO31Ljs7.js} +1 -1
  24. package/admin-build/_app/immutable/nodes/{13.DtK_4oRz.js → 13.DhyAy-GZ.js} +1 -1
  25. package/admin-build/_app/immutable/nodes/{14.BKo7-AMx.js → 14.CLecGWc4.js} +1 -1
  26. package/admin-build/_app/immutable/nodes/{15.CQAj_6lq.js → 15.B9kp3W4e.js} +1 -1
  27. package/admin-build/_app/immutable/nodes/{16.XVIG-Ffr.js → 16.Pu_8T3RI.js} +1 -1
  28. package/admin-build/_app/immutable/nodes/{17.g6raZLCM.js → 17.DX4z43t6.js} +1 -1
  29. package/admin-build/_app/immutable/nodes/{18.IQz6a3T6.js → 18.BKsSaxrr.js} +1 -1
  30. package/admin-build/_app/immutable/nodes/{19.CAAZ8i8h.js → 19.DXNF1htN.js} +1 -1
  31. package/admin-build/_app/immutable/nodes/{20.BPcX3KPj.js → 20.VRVb0wee.js} +1 -1
  32. package/admin-build/_app/immutable/nodes/21.Ck3_0D2f.js +1 -0
  33. package/admin-build/_app/immutable/nodes/{22.Br5AG_5Z.js → 22.DqZf4CtH.js} +1 -1
  34. package/admin-build/_app/immutable/nodes/{23.KjbrdXoE.js → 23.DtyxMiQG.js} +1 -1
  35. package/admin-build/_app/immutable/nodes/{24.C3n2-hgw.js → 24.CloWNmTd.js} +1 -1
  36. package/admin-build/_app/immutable/nodes/{25.SFDSBzHd.js → 25.CnZWMq7_.js} +1 -1
  37. package/admin-build/_app/immutable/nodes/{26.D95vui6E.js → 26.DrV7XOmf.js} +1 -1
  38. package/admin-build/_app/immutable/nodes/{27.FgLgdjwB.js → 27.DV8L32OF.js} +1 -1
  39. package/admin-build/_app/immutable/nodes/{28.B9sYYm1F.js → 28.Stil2D4u.js} +1 -1
  40. package/admin-build/_app/immutable/nodes/{29.DyqZ_wbN.js → 29.Zsm1e5Dc.js} +1 -1
  41. package/admin-build/_app/immutable/nodes/{3.Bzo2yVIO.js → 3.CKoj2vNz.js} +1 -1
  42. package/admin-build/_app/immutable/nodes/{30.c1CiNwiS.js → 30.Ni0k5bER.js} +1 -1
  43. package/admin-build/_app/immutable/nodes/{31.CXty66Vh.js → 31.mnqj9EbV.js} +1 -1
  44. package/admin-build/_app/immutable/nodes/{4.BgQaXZ27.js → 4.B_-z9AzT.js} +1 -1
  45. package/admin-build/_app/immutable/nodes/{5.BuJrHvxH.js → 5.yiZ72j4k.js} +1 -1
  46. package/admin-build/_app/immutable/nodes/{6.CkBBC94k.js → 6.BqykybBG.js} +1 -1
  47. package/admin-build/_app/immutable/nodes/{7.D2YBvNFM.js → 7.BDAHlhsF.js} +1 -1
  48. package/admin-build/_app/immutable/nodes/{8.D8qQWo_z.js → 8.D8Xvy0lH.js} +1 -1
  49. package/admin-build/_app/immutable/nodes/{9.BLDLX5hV.js → 9.Dddmd7_F.js} +1 -1
  50. package/admin-build/_app/version.json +1 -1
  51. package/admin-build/index.html +7 -7
  52. package/package.json +3 -2
  53. package/src/__tests__/functions-context.test.ts +5 -5
  54. package/src/__tests__/meta-export-coverage.test.ts +1 -0
  55. package/src/lib/functions.ts +204 -397
  56. package/src/lib/internal-transport.ts +316 -0
  57. package/src/lib/plugin-migrations.ts +2 -2
  58. package/src/routes/admin.ts +7 -1
  59. package/src/routes/auth.ts +6 -12
  60. package/src/routes/storage.ts +6 -12
  61. package/src/types.ts +2 -0
  62. package/admin-build/_app/immutable/entry/start.zXCirpgY.js +0 -1
  63. package/admin-build/_app/immutable/nodes/21.DoPabrY_.js +0 -1
@@ -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
+ }
@@ -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);
@@ -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
  }
@@ -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';
@@ -659,18 +660,11 @@ export async function executeAuthHook(
659
660
  db: adminDb,
660
661
  table: (name: string) => adminDb('shared').table(name),
661
662
  auth: authAdmin,
662
- async sql(namespace: string, id: string | undefined, query: string, params?: unknown[]) {
663
- if (options.workerUrl && serviceKey) {
664
- const res = await fetch(`${options.workerUrl}/api/sql`, {
665
- method: 'POST',
666
- headers: { 'Content-Type': 'application/json', 'X-EdgeBase-Service-Key': serviceKey },
667
- body: JSON.stringify({ namespace, id, sql: query, params: params ?? [] }),
668
- });
669
- if (!res.ok) throw new Error(`admin.sql() failed: ${res.status}`);
670
- return res.json();
671
- }
672
- throw new Error('admin.sql() requires workerUrl in auth hook context.');
673
- },
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
+ ),
674
668
  async broadcast(channel: string, event: string, payload?: Record<string, unknown>) {
675
669
  if (options.workerUrl && serviceKey) {
676
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
- import{a as r}from"../chunks/m9QZTyVV.js";import{w as t}from"../chunks/D755Tqat.js";export{t as load_css,r as start};
@@ -1 +0,0 @@
1
- import{_ as m}from"../chunks/B0QyxC2M.js";export{m as component};