@ezcoder.dev/sdk 1.0.0 → 1.2.0

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 (53) hide show
  1. package/dist/DatabaseProvider-DalP-KHC.d.ts +167 -0
  2. package/dist/analytics/index.d.ts +22 -1
  3. package/dist/analytics/index.js +79 -4
  4. package/dist/analytics/index.js.map +1 -1
  5. package/dist/animation/index.js.map +1 -1
  6. package/dist/auth/index.d.ts +2 -1
  7. package/dist/auth/index.js +25 -6
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/chunk-CQKYANAW.js +44 -0
  10. package/dist/chunk-CQKYANAW.js.map +1 -0
  11. package/dist/{chunk-5XIZHBKE.js → chunk-HJ2EIZ4S.js} +23 -6
  12. package/dist/chunk-HJ2EIZ4S.js.map +1 -0
  13. package/dist/{chunk-G7XDUN3Z.js → chunk-I2YGB7Z6.js} +24 -44
  14. package/dist/chunk-I2YGB7Z6.js.map +1 -0
  15. package/dist/chunk-LIUE7M7K.js +72 -0
  16. package/dist/chunk-LIUE7M7K.js.map +1 -0
  17. package/dist/{chunk-YNDCD53D.js → chunk-QHB7LGCA.js} +123 -16
  18. package/dist/chunk-QHB7LGCA.js.map +1 -0
  19. package/dist/chunk-R4MDAYFO.js +642 -0
  20. package/dist/chunk-R4MDAYFO.js.map +1 -0
  21. package/dist/cms/index.js +4 -2
  22. package/dist/cms/index.js.map +1 -1
  23. package/dist/cron/index.d.ts +32 -0
  24. package/dist/cron/index.js +63 -0
  25. package/dist/cron/index.js.map +1 -0
  26. package/dist/database/index.d.ts +37 -0
  27. package/dist/database/index.js +32 -0
  28. package/dist/database/index.js.map +1 -0
  29. package/dist/email/index.d.ts +29 -0
  30. package/dist/email/index.js +60 -0
  31. package/dist/email/index.js.map +1 -0
  32. package/dist/errors/index.js.map +1 -1
  33. package/dist/index.d.ts +176 -3
  34. package/dist/index.js +270 -6
  35. package/dist/index.js.map +1 -1
  36. package/dist/notifications/index.d.ts +35 -1
  37. package/dist/notifications/index.js +61 -4
  38. package/dist/notifications/index.js.map +1 -1
  39. package/dist/payments/index.d.ts +11 -3
  40. package/dist/payments/index.js +101 -7
  41. package/dist/payments/index.js.map +1 -1
  42. package/dist/roles/index.js +6 -4
  43. package/dist/roles/index.js.map +1 -1
  44. package/dist/seo/index.js.map +1 -1
  45. package/dist/server/index.js.map +1 -1
  46. package/dist/storage/index.d.ts +1 -1
  47. package/dist/storage/index.js +18 -9
  48. package/dist/storage/index.js.map +1 -1
  49. package/dist/{types-DtY5lp3P.d.ts → types-1uP3V_pe.d.ts} +5 -0
  50. package/package.json +120 -105
  51. package/dist/chunk-5XIZHBKE.js.map +0 -1
  52. package/dist/chunk-G7XDUN3Z.js.map +0 -1
  53. package/dist/chunk-YNDCD53D.js.map +0 -1
@@ -0,0 +1,642 @@
1
+ import {
2
+ ezcoder
3
+ } from "./chunk-HJ2EIZ4S.js";
4
+ import {
5
+ isSupabaseConfigured,
6
+ supabase
7
+ } from "./chunk-I2YGB7Z6.js";
8
+ import {
9
+ env,
10
+ features
11
+ } from "./chunk-LIUE7M7K.js";
12
+
13
+ // src/database/DatabaseProvider.tsx
14
+ import { createContext, useContext, useMemo } from "react";
15
+
16
+ // src/database/errors.ts
17
+ var DatabaseError = class extends Error {
18
+ constructor(message, code = "DATABASE_ERROR", statusCode = 500) {
19
+ super(message);
20
+ this.name = "DatabaseError";
21
+ this.code = code;
22
+ this.statusCode = statusCode;
23
+ }
24
+ };
25
+ var QueryError = class extends DatabaseError {
26
+ constructor(message, sql) {
27
+ super(message, "QUERY_ERROR", 400);
28
+ this.name = "QueryError";
29
+ this.sql = sql;
30
+ }
31
+ };
32
+ var ValidationError = class extends DatabaseError {
33
+ constructor(message) {
34
+ super(message, "VALIDATION_ERROR", 422);
35
+ this.name = "ValidationError";
36
+ }
37
+ };
38
+ var ConnectionError = class extends DatabaseError {
39
+ constructor(message) {
40
+ super(message, "CONNECTION_ERROR", 503);
41
+ this.name = "ConnectionError";
42
+ }
43
+ };
44
+ var NotFoundError = class extends DatabaseError {
45
+ constructor(message = "Record not found") {
46
+ super(message, "NOT_FOUND", 404);
47
+ this.name = "NotFoundError";
48
+ }
49
+ };
50
+ function mapRpcError(rpcResult) {
51
+ if (rpcResult.success) return null;
52
+ const msg = rpcResult.error || "Unknown database error";
53
+ if (msg.includes("No database schema exists")) return new NotFoundError(msg);
54
+ if (msg.includes("Authentication required")) return new ConnectionError(msg);
55
+ if (msg.includes("blocked") || msg.includes("forbidden")) return new ValidationError(msg);
56
+ return new QueryError(msg);
57
+ }
58
+
59
+ // src/database/QueryBuilder.ts
60
+ function escapeSqlValue(value) {
61
+ if (value === null) return "NULL";
62
+ if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
63
+ if (typeof value === "number") {
64
+ if (!Number.isFinite(value)) throw new ValidationError("Non-finite numbers are not allowed");
65
+ return String(value);
66
+ }
67
+ return `'${String(value).replace(/'/g, "''")}'`;
68
+ }
69
+ function escapeIdentifier(name) {
70
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
71
+ throw new ValidationError(`Invalid identifier: ${name}`);
72
+ }
73
+ return `"${name}"`;
74
+ }
75
+ var QueryBuilder = class {
76
+ constructor(supabase2, projectId, table, serverProxy) {
77
+ this.operation = "select";
78
+ this.selectColumns = "*";
79
+ this.filters = [];
80
+ this.orderClauses = [];
81
+ this.limitValue = null;
82
+ this.offsetValue = null;
83
+ this.insertData = null;
84
+ this.updateData = null;
85
+ this.upsertConflict = null;
86
+ this.singleMode = false;
87
+ this.maybeSingleMode = false;
88
+ this.supabase = supabase2;
89
+ this.projectId = projectId;
90
+ this.table = table;
91
+ this.serverProxy = serverProxy;
92
+ }
93
+ select(columns = "*") {
94
+ this.operation = "select";
95
+ this.selectColumns = columns;
96
+ return this;
97
+ }
98
+ // --- Filters ---
99
+ eq(column, value) {
100
+ this.filters.push({ column, op: "=", value });
101
+ return this;
102
+ }
103
+ neq(column, value) {
104
+ this.filters.push({ column, op: "!=", value });
105
+ return this;
106
+ }
107
+ gt(column, value) {
108
+ this.filters.push({ column, op: ">", value });
109
+ return this;
110
+ }
111
+ lt(column, value) {
112
+ this.filters.push({ column, op: "<", value });
113
+ return this;
114
+ }
115
+ gte(column, value) {
116
+ this.filters.push({ column, op: ">=", value });
117
+ return this;
118
+ }
119
+ lte(column, value) {
120
+ this.filters.push({ column, op: "<=", value });
121
+ return this;
122
+ }
123
+ like(column, pattern) {
124
+ this.filters.push({ column, op: "LIKE", value: pattern });
125
+ return this;
126
+ }
127
+ ilike(column, pattern) {
128
+ this.filters.push({ column, op: "ILIKE", value: pattern });
129
+ return this;
130
+ }
131
+ in(column, values) {
132
+ this.filters.push({ column, op: "IN", value: values });
133
+ return this;
134
+ }
135
+ is(column, value) {
136
+ this.filters.push({ column, op: "IS", value });
137
+ return this;
138
+ }
139
+ // --- Ordering & Pagination ---
140
+ order(column, options = {}) {
141
+ const dir = options.ascending === false ? "DESC" : "ASC";
142
+ const nulls = options.nullsFirst ? "NULLS FIRST" : "NULLS LAST";
143
+ this.orderClauses.push(`${escapeIdentifier(column)} ${dir} ${nulls}`);
144
+ return this;
145
+ }
146
+ limit(count) {
147
+ this.limitValue = count;
148
+ return this;
149
+ }
150
+ offset(count) {
151
+ this.offsetValue = count;
152
+ return this;
153
+ }
154
+ // --- Mutations ---
155
+ insert(records) {
156
+ this.operation = "insert";
157
+ this.insertData = Array.isArray(records) ? records : [records];
158
+ return this;
159
+ }
160
+ update(values) {
161
+ this.operation = "update";
162
+ this.updateData = values;
163
+ return this;
164
+ }
165
+ delete() {
166
+ this.operation = "delete";
167
+ return this;
168
+ }
169
+ upsert(records, options) {
170
+ this.operation = "upsert";
171
+ this.insertData = Array.isArray(records) ? records : [records];
172
+ this.upsertConflict = options?.onConflict || null;
173
+ return this;
174
+ }
175
+ // --- Result Helpers ---
176
+ count() {
177
+ this.operation = "count";
178
+ return this;
179
+ }
180
+ single() {
181
+ this.singleMode = true;
182
+ this.limitValue = 1;
183
+ return this;
184
+ }
185
+ maybeSingle() {
186
+ this.maybeSingleMode = true;
187
+ this.limitValue = 1;
188
+ return this;
189
+ }
190
+ // --- SQL Building ---
191
+ buildWhereClause() {
192
+ if (this.filters.length === 0) return "";
193
+ const conditions = this.filters.map((f) => {
194
+ const col = escapeIdentifier(f.column);
195
+ if (f.op === "IS") {
196
+ const val = f.value === null ? "NULL" : f.value ? "TRUE" : "FALSE";
197
+ return `${col} IS ${val}`;
198
+ }
199
+ if (f.op === "IN") {
200
+ const vals = f.value.map(escapeSqlValue).join(", ");
201
+ return `${col} IN (${vals})`;
202
+ }
203
+ return `${col} ${f.op} ${escapeSqlValue(f.value)}`;
204
+ });
205
+ return ` WHERE ${conditions.join(" AND ")}`;
206
+ }
207
+ buildSelectSql() {
208
+ let sql = `SELECT ${this.selectColumns} FROM "${this.table}"`;
209
+ sql += this.buildWhereClause();
210
+ if (this.orderClauses.length > 0) sql += ` ORDER BY ${this.orderClauses.join(", ")}`;
211
+ if (this.limitValue !== null) sql += ` LIMIT ${this.limitValue}`;
212
+ if (this.offsetValue !== null && this.offsetValue > 0) sql += ` OFFSET ${this.offsetValue}`;
213
+ return sql;
214
+ }
215
+ buildCountSql() {
216
+ let sql = `SELECT COUNT(*) as count FROM "${this.table}"`;
217
+ sql += this.buildWhereClause();
218
+ return sql;
219
+ }
220
+ buildInsertSql() {
221
+ if (!this.insertData || this.insertData.length === 0) {
222
+ throw new ValidationError("No data provided for insert");
223
+ }
224
+ const columns = Object.keys(this.insertData[0]);
225
+ const colList = columns.map(escapeIdentifier).join(", ");
226
+ const rows = this.insertData.map(
227
+ (row) => `(${columns.map((c) => escapeSqlValue(row[c] ?? null)).join(", ")})`
228
+ );
229
+ return `INSERT INTO "${this.table}" (${colList}) VALUES ${rows.join(", ")}`;
230
+ }
231
+ buildUpsertSql() {
232
+ let sql = this.buildInsertSql();
233
+ const conflict = this.upsertConflict || "id";
234
+ const columns = Object.keys(this.insertData[0]);
235
+ const updateCols = columns.filter((c) => c !== conflict).map((c) => `${escapeIdentifier(c)} = EXCLUDED.${escapeIdentifier(c)}`).join(", ");
236
+ sql += ` ON CONFLICT (${escapeIdentifier(conflict)}) DO UPDATE SET ${updateCols}`;
237
+ return sql;
238
+ }
239
+ buildUpdateSql() {
240
+ if (!this.updateData || Object.keys(this.updateData).length === 0) {
241
+ throw new ValidationError("No data provided for update");
242
+ }
243
+ if (this.filters.length === 0) {
244
+ throw new ValidationError("UPDATE without filters is not allowed \u2014 add .eq() or other filters");
245
+ }
246
+ const setClauses = Object.entries(this.updateData).map(([col, val]) => `${escapeIdentifier(col)} = ${escapeSqlValue(val)}`).join(", ");
247
+ return `UPDATE "${this.table}" SET ${setClauses}${this.buildWhereClause()}`;
248
+ }
249
+ buildDeleteSql() {
250
+ if (this.filters.length === 0) {
251
+ throw new ValidationError("DELETE without filters is not allowed \u2014 add .eq() or other filters");
252
+ }
253
+ return `DELETE FROM "${this.table}"${this.buildWhereClause()}`;
254
+ }
255
+ // --- Execution ---
256
+ async executeQuery() {
257
+ const isRead = this.operation === "select" || this.operation === "count";
258
+ let sql;
259
+ switch (this.operation) {
260
+ case "select":
261
+ sql = this.buildSelectSql();
262
+ break;
263
+ case "count":
264
+ sql = this.buildCountSql();
265
+ break;
266
+ case "insert":
267
+ sql = this.buildInsertSql();
268
+ break;
269
+ case "upsert":
270
+ sql = this.buildUpsertSql();
271
+ break;
272
+ case "update":
273
+ sql = this.buildUpdateSql();
274
+ break;
275
+ case "delete":
276
+ sql = this.buildDeleteSql();
277
+ break;
278
+ default:
279
+ throw new QueryError(`Unknown operation: ${this.operation}`);
280
+ }
281
+ if (this.serverProxy) {
282
+ const fn = isRead ? this.serverProxy.serverQuery : this.serverProxy.serverExec;
283
+ const data2 = await fn(sql);
284
+ if (!data2.success) {
285
+ const dbError = mapRpcError(data2);
286
+ if (dbError) throw dbError;
287
+ throw new QueryError(data2.error || "Query failed", sql);
288
+ }
289
+ return {
290
+ success: true,
291
+ data: isRead ? data2.data || [] : [],
292
+ rowCount: data2.rowCount ?? (isRead ? data2.data?.length || 0 : 0),
293
+ schema: data2.schema
294
+ };
295
+ }
296
+ const rpcName = isRead ? "sdk_query_project" : "sdk_exec_project";
297
+ const { data, error } = await this.supabase.rpc(rpcName, {
298
+ p_project_id: this.projectId,
299
+ p_sql: sql
300
+ });
301
+ if (error) {
302
+ throw new QueryError(error.message, sql);
303
+ }
304
+ if (!data.success) {
305
+ const dbError = mapRpcError(data);
306
+ if (dbError) throw dbError;
307
+ throw new QueryError(data.error || "Query failed", sql);
308
+ }
309
+ if (isRead) {
310
+ return {
311
+ success: true,
312
+ data: data.data || [],
313
+ rowCount: data.rowCount ?? (data.data?.length || 0),
314
+ schema: data.schema
315
+ };
316
+ }
317
+ return {
318
+ success: true,
319
+ data: [],
320
+ rowCount: data.rowCount ?? 0,
321
+ schema: data.schema
322
+ };
323
+ }
324
+ then(onfulfilled, onrejected) {
325
+ const promise = this.executeQuery().then((result) => {
326
+ if (this.singleMode) {
327
+ if (result.data.length === 0) {
328
+ throw new QueryError("Expected exactly one row, got 0");
329
+ }
330
+ return { success: true, data: result.data[0], error: void 0 };
331
+ }
332
+ if (this.maybeSingleMode) {
333
+ return {
334
+ success: true,
335
+ data: result.data.length > 0 ? result.data[0] : null,
336
+ error: void 0
337
+ };
338
+ }
339
+ return result;
340
+ });
341
+ return promise.then(onfulfilled, onrejected);
342
+ }
343
+ };
344
+
345
+ // src/database/DatabaseClient.ts
346
+ function trackDbEvent(event, props) {
347
+ if (!features.analytics) return;
348
+ try {
349
+ ezcoder.analytics.track(event, props);
350
+ } catch {
351
+ }
352
+ }
353
+ var MAX_RETRIES = 3;
354
+ var SERVER_TIMEOUT_MS = 5e3;
355
+ var DatabaseClient = class _DatabaseClient {
356
+ constructor(supabase2, projectId) {
357
+ this.serverMode = false;
358
+ this.supabase = supabase2;
359
+ this.projectId = projectId;
360
+ }
361
+ static createServerClient(projectId, apiKey, platformUrl) {
362
+ const client = new _DatabaseClient(null, projectId);
363
+ client.apiKey = apiKey;
364
+ client.platformUrl = platformUrl || typeof process !== "undefined" && process.env?.EZCODER_API_URL || "https://ezcoder.app";
365
+ client.serverMode = true;
366
+ return client;
367
+ }
368
+ from(table) {
369
+ if (this.serverMode) {
370
+ return new QueryBuilder(null, this.projectId, table, {
371
+ serverQuery: (sql) => this._serverRequest("/query", sql),
372
+ serverExec: (sql) => this._serverRequest("/execute", sql)
373
+ });
374
+ }
375
+ return new QueryBuilder(this.supabase, this.projectId, table);
376
+ }
377
+ async sql(query) {
378
+ const trimmed = query.trim().toLowerCase();
379
+ if (!trimmed.startsWith("select")) {
380
+ throw new QueryError("sql() is for read-only queries. Use execute() for mutations.");
381
+ }
382
+ if (this.serverMode) {
383
+ return this._serverQuery(query);
384
+ }
385
+ const start = Date.now();
386
+ const { data, error } = await this.supabase.rpc("sdk_query_project", {
387
+ p_project_id: this.projectId,
388
+ p_sql: query
389
+ });
390
+ const latencyMs = Date.now() - start;
391
+ if (error) {
392
+ trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: false, error: error.message });
393
+ throw new QueryError(error.message, query);
394
+ }
395
+ if (!data.success) {
396
+ trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: false, error: data.error });
397
+ throw new QueryError(data.error || "Query failed", query);
398
+ }
399
+ trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: true, rowCount: data.rowCount ?? 0 });
400
+ return {
401
+ success: true,
402
+ data: data.data || [],
403
+ rowCount: data.rowCount ?? (data.data?.length || 0),
404
+ schema: data.schema
405
+ };
406
+ }
407
+ async execute(sql) {
408
+ if (this.serverMode) {
409
+ return this._serverExecute(sql);
410
+ }
411
+ const start = Date.now();
412
+ const { data, error } = await this.supabase.rpc("sdk_exec_project", {
413
+ p_project_id: this.projectId,
414
+ p_sql: sql
415
+ });
416
+ const latencyMs = Date.now() - start;
417
+ if (error) {
418
+ trackDbEvent("db_mutation", { projectId: this.projectId, latencyMs, success: false, error: error.message });
419
+ throw new QueryError(error.message, sql);
420
+ }
421
+ if (!data.success) {
422
+ trackDbEvent("db_mutation", { projectId: this.projectId, latencyMs, success: false, error: data.error });
423
+ throw new QueryError(data.error || "Execution failed", sql);
424
+ }
425
+ trackDbEvent("db_mutation", { projectId: this.projectId, latencyMs, success: true, rowCount: data.rowCount ?? 0 });
426
+ return {
427
+ success: true,
428
+ rowCount: data.rowCount ?? 0,
429
+ schema: data.schema,
430
+ executed_sql: data.executed_sql
431
+ };
432
+ }
433
+ async getSchema() {
434
+ if (this.serverMode) {
435
+ return this._serverGetSchema();
436
+ }
437
+ const { data, error } = await this.supabase.rpc("sdk_get_schema_info", {
438
+ p_project_id: this.projectId
439
+ });
440
+ if (error) throw new ConnectionError(error.message);
441
+ return {
442
+ exists: data.exists ?? false,
443
+ schema: data.schema,
444
+ tables: data.tables || [],
445
+ tablesCount: data.tablesCount ?? 0,
446
+ createdAt: data.createdAt,
447
+ lastAccessedAt: data.lastAccessedAt
448
+ };
449
+ }
450
+ async _serverRequest(endpoint, sql) {
451
+ const url = `${this.platformUrl}/api/platform/db/${this.projectId}${endpoint}`;
452
+ let lastError;
453
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
454
+ try {
455
+ const res = await fetch(url, {
456
+ method: "POST",
457
+ headers: {
458
+ "Authorization": `Bearer ${this.apiKey}`,
459
+ "Content-Type": "application/json"
460
+ },
461
+ body: JSON.stringify({ sql }),
462
+ signal: AbortSignal.timeout(SERVER_TIMEOUT_MS)
463
+ });
464
+ if (res.status === 429) {
465
+ const retryAfter = parseInt(res.headers.get("retry-after") || "60", 10);
466
+ await new Promise((r) => setTimeout(r, retryAfter * 1e3));
467
+ continue;
468
+ }
469
+ if (res.status === 503) {
470
+ await new Promise((r) => setTimeout(r, (attempt + 1) * 2e3));
471
+ continue;
472
+ }
473
+ return await res.json();
474
+ } catch (e) {
475
+ lastError = e instanceof Error ? e : new Error(String(e));
476
+ }
477
+ }
478
+ return { success: false, error: lastError?.message || "Request failed after retries" };
479
+ }
480
+ async _serverQuery(query) {
481
+ const start = Date.now();
482
+ const result = await this._serverRequest("/query", query);
483
+ const latencyMs = Date.now() - start;
484
+ if (!result.success) {
485
+ trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: false, error: result.error, serverMode: true });
486
+ throw new QueryError(result.error || "Query failed", query);
487
+ }
488
+ trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: true, rowCount: result.rowCount ?? 0, serverMode: true });
489
+ return {
490
+ success: true,
491
+ data: result.data || [],
492
+ rowCount: result.rowCount ?? (result.data?.length || 0),
493
+ schema: result.schema
494
+ };
495
+ }
496
+ async _serverExecute(sql) {
497
+ const start = Date.now();
498
+ const result = await this._serverRequest("/execute", sql);
499
+ const latencyMs = Date.now() - start;
500
+ if (!result.success) {
501
+ trackDbEvent("db_mutation", { projectId: this.projectId, latencyMs, success: false, error: result.error, serverMode: true });
502
+ throw new QueryError(result.error || "Execution failed", sql);
503
+ }
504
+ trackDbEvent("db_mutation", { projectId: this.projectId, latencyMs, success: true, rowCount: result.rowCount ?? 0, serverMode: true });
505
+ return {
506
+ success: true,
507
+ rowCount: result.rowCount ?? 0,
508
+ schema: result.schema,
509
+ executed_sql: result.executed_sql
510
+ };
511
+ }
512
+ async _serverGetSchema() {
513
+ const url = `${this.platformUrl}/api/platform/db/${this.projectId}/schema`;
514
+ let lastError;
515
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
516
+ try {
517
+ const res = await fetch(url, {
518
+ method: "GET",
519
+ headers: { "Authorization": `Bearer ${this.apiKey}` },
520
+ signal: AbortSignal.timeout(SERVER_TIMEOUT_MS)
521
+ });
522
+ if (res.status === 503) {
523
+ await new Promise((r) => setTimeout(r, (attempt + 1) * 2e3));
524
+ continue;
525
+ }
526
+ const data = await res.json();
527
+ if (!data.success) throw new ConnectionError(data.error || "Schema fetch failed");
528
+ return {
529
+ exists: data.exists ?? false,
530
+ schema: data.schema,
531
+ tables: data.tables || [],
532
+ tablesCount: data.tablesCount ?? 0,
533
+ createdAt: data.createdAt,
534
+ lastAccessedAt: data.lastAccessedAt
535
+ };
536
+ } catch (e) {
537
+ lastError = e instanceof Error ? e : new Error(String(e));
538
+ }
539
+ }
540
+ throw new ConnectionError(lastError?.message || "Schema request failed after retries");
541
+ }
542
+ };
543
+
544
+ // src/database/DatabaseProvider.tsx
545
+ import { jsx } from "react/jsx-runtime";
546
+ var DatabaseContext = createContext({
547
+ db: null,
548
+ isConfigured: false
549
+ });
550
+ function DatabaseProvider({ children, projectId }) {
551
+ const resolvedProjectId = projectId || env.EZC_PROJECT_ID;
552
+ const configured = isSupabaseConfigured && Boolean(resolvedProjectId);
553
+ const db = useMemo(
554
+ () => configured ? new DatabaseClient(supabase, resolvedProjectId) : null,
555
+ [configured, resolvedProjectId]
556
+ );
557
+ const value = useMemo(() => ({ db, isConfigured: configured }), [db, configured]);
558
+ return /* @__PURE__ */ jsx(DatabaseContext.Provider, { value, children });
559
+ }
560
+ function useDatabase() {
561
+ const { db } = useContext(DatabaseContext);
562
+ if (!db) {
563
+ throw new Error(
564
+ "useDatabase() must be used within <DatabaseProvider>. Ensure EZC_PROJECT_ID and Supabase credentials are configured."
565
+ );
566
+ }
567
+ return db;
568
+ }
569
+ function useDatabaseOptional() {
570
+ const { db } = useContext(DatabaseContext);
571
+ return db;
572
+ }
573
+ function useIsDatabaseConfigured() {
574
+ const { isConfigured } = useContext(DatabaseContext);
575
+ return isConfigured;
576
+ }
577
+
578
+ // src/database/useRealtime.ts
579
+ import { useEffect, useState, useRef } from "react";
580
+ function useRealtime(table, options = {}) {
581
+ const [data, setData] = useState([]);
582
+ const [status, setStatus] = useState("connecting");
583
+ const optionsRef = useRef(options);
584
+ optionsRef.current = options;
585
+ const projectId = env.EZC_PROJECT_ID;
586
+ useEffect(() => {
587
+ if (!isSupabaseConfigured || !projectId) {
588
+ setStatus("disabled");
589
+ return;
590
+ }
591
+ const schemaName = `proj_${projectId.replace(/-/g, "_")}`;
592
+ const channelName = `${schemaName}_${table}_${optionsRef.current.event || "all"}`;
593
+ const channel = supabase.channel(channelName).on(
594
+ "postgres_changes",
595
+ {
596
+ event: optionsRef.current.event || "*",
597
+ schema: schemaName,
598
+ table,
599
+ filter: optionsRef.current.filter
600
+ },
601
+ (payload) => {
602
+ if (payload.eventType === "INSERT") {
603
+ setData((prev) => [...prev, payload.new]);
604
+ } else if (payload.eventType === "UPDATE") {
605
+ setData(
606
+ (prev) => prev.map(
607
+ (item) => item.id === payload.new.id ? payload.new : item
608
+ )
609
+ );
610
+ } else if (payload.eventType === "DELETE") {
611
+ setData(
612
+ (prev) => prev.filter(
613
+ (item) => item.id !== payload.old.id
614
+ )
615
+ );
616
+ }
617
+ }
618
+ ).subscribe((subStatus) => {
619
+ setStatus(subStatus === "SUBSCRIBED" ? "connected" : "connecting");
620
+ });
621
+ return () => {
622
+ supabase.removeChannel(channel);
623
+ };
624
+ }, [projectId, table]);
625
+ return { data, status, setData };
626
+ }
627
+
628
+ export {
629
+ DatabaseError,
630
+ QueryError,
631
+ ValidationError,
632
+ ConnectionError,
633
+ NotFoundError,
634
+ QueryBuilder,
635
+ DatabaseClient,
636
+ DatabaseProvider,
637
+ useDatabase,
638
+ useDatabaseOptional,
639
+ useIsDatabaseConfigured,
640
+ useRealtime
641
+ };
642
+ //# sourceMappingURL=chunk-R4MDAYFO.js.map