@vaiftechnologies/vaif-client 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,2428 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ RealtimeChannel: () => RealtimeChannel,
24
+ VaifAI: () => VaifAI,
25
+ VaifAuth: () => VaifAuth,
26
+ VaifClient: () => VaifClient,
27
+ VaifDatabase: () => VaifDatabase,
28
+ VaifFunctions: () => VaifFunctions,
29
+ VaifRealtime: () => VaifRealtime,
30
+ VaifStorage: () => VaifStorage,
31
+ VaifTypeGen: () => VaifTypeGen,
32
+ createClient: () => createClient,
33
+ createTypeGen: () => createTypeGen
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/lib/database.ts
38
+ var VaifDatabase = class {
39
+ constructor(client) {
40
+ this.client = client;
41
+ }
42
+ /**
43
+ * Start a query on a table
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const users = await vaif.db.from('users').select('*').execute();
48
+ * ```
49
+ */
50
+ from(table) {
51
+ return new QueryBuilder(this.client, table);
52
+ }
53
+ /**
54
+ * Execute raw SQL query (admin only)
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const result = await vaif.db.raw('SELECT * FROM users WHERE id = $1', [userId]);
59
+ * ```
60
+ */
61
+ async raw(sql, params) {
62
+ const response = await this.client.request("/db/query", {
63
+ method: "POST",
64
+ body: { sql, params }
65
+ });
66
+ if (response.error) {
67
+ return {
68
+ data: [],
69
+ error: {
70
+ message: response.error.message,
71
+ code: response.error.code ?? "QUERY_ERROR"
72
+ },
73
+ status: response.status
74
+ };
75
+ }
76
+ return {
77
+ data: response.data?.rows ?? [],
78
+ error: null,
79
+ status: response.status
80
+ };
81
+ }
82
+ /**
83
+ * Execute a function (stored procedure)
84
+ */
85
+ async rpc(functionName, params) {
86
+ const response = await this.client.request(`/db/rpc/${functionName}`, {
87
+ method: "POST",
88
+ body: params ?? {}
89
+ });
90
+ return {
91
+ data: response.data,
92
+ error: response.error ? {
93
+ message: response.error.message,
94
+ code: response.error.code ?? "RPC_ERROR"
95
+ } : null,
96
+ status: response.status
97
+ };
98
+ }
99
+ };
100
+ var QueryBuilder = class {
101
+ constructor(client, table) {
102
+ this.queryType = "select";
103
+ this.selectColumns = "*";
104
+ this.insertData = null;
105
+ this.updateData = null;
106
+ this.filters = [];
107
+ this.orderSpecs = [];
108
+ this.limitCount = null;
109
+ this.offsetCount = null;
110
+ this.returning = null;
111
+ this.countOption = null;
112
+ this.negateNext = false;
113
+ this.onConflictColumns = null;
114
+ this.onConflictAction = null;
115
+ this.onConflictUpdate = null;
116
+ this.client = client;
117
+ this.table = table;
118
+ }
119
+ /**
120
+ * Select columns
121
+ */
122
+ select(columns = "*") {
123
+ this.queryType = "select";
124
+ this.selectColumns = Array.isArray(columns) ? columns.join(", ") : columns;
125
+ return this;
126
+ }
127
+ /**
128
+ * Insert data
129
+ */
130
+ insert(data) {
131
+ this.queryType = "insert";
132
+ this.insertData = data;
133
+ return this;
134
+ }
135
+ /**
136
+ * Update data
137
+ */
138
+ update(data) {
139
+ this.queryType = "update";
140
+ this.updateData = data;
141
+ return this;
142
+ }
143
+ /**
144
+ * Delete rows
145
+ */
146
+ delete() {
147
+ this.queryType = "delete";
148
+ return this;
149
+ }
150
+ /**
151
+ * Handle insert conflicts (upsert)
152
+ */
153
+ onConflict(columns) {
154
+ this.onConflictColumns = Array.isArray(columns) ? columns : [columns];
155
+ return this;
156
+ }
157
+ /**
158
+ * Do nothing on conflict
159
+ */
160
+ doNothing() {
161
+ this.onConflictAction = "nothing";
162
+ return this;
163
+ }
164
+ /**
165
+ * Update on conflict
166
+ */
167
+ doUpdate(data) {
168
+ this.onConflictAction = "update";
169
+ this.onConflictUpdate = data;
170
+ return this;
171
+ }
172
+ // Filter methods
173
+ eq(column, value) {
174
+ this.addFilter(column, this.negateNext ? "neq" : "eq", value);
175
+ this.negateNext = false;
176
+ return this;
177
+ }
178
+ neq(column, value) {
179
+ this.addFilter(column, this.negateNext ? "eq" : "neq", value);
180
+ this.negateNext = false;
181
+ return this;
182
+ }
183
+ gt(column, value) {
184
+ this.addFilter(column, this.negateNext ? "lte" : "gt", value);
185
+ this.negateNext = false;
186
+ return this;
187
+ }
188
+ gte(column, value) {
189
+ this.addFilter(column, this.negateNext ? "lt" : "gte", value);
190
+ this.negateNext = false;
191
+ return this;
192
+ }
193
+ lt(column, value) {
194
+ this.addFilter(column, this.negateNext ? "gte" : "lt", value);
195
+ this.negateNext = false;
196
+ return this;
197
+ }
198
+ lte(column, value) {
199
+ this.addFilter(column, this.negateNext ? "gt" : "lte", value);
200
+ this.negateNext = false;
201
+ return this;
202
+ }
203
+ like(column, pattern) {
204
+ this.addFilter(column, this.negateNext ? "nlike" : "like", pattern);
205
+ this.negateNext = false;
206
+ return this;
207
+ }
208
+ ilike(column, pattern) {
209
+ this.addFilter(column, this.negateNext ? "nilike" : "ilike", pattern);
210
+ this.negateNext = false;
211
+ return this;
212
+ }
213
+ is(column, value) {
214
+ this.addFilter(column, this.negateNext ? "isnot" : "is", value);
215
+ this.negateNext = false;
216
+ return this;
217
+ }
218
+ in(column, values) {
219
+ this.addFilter(column, this.negateNext ? "nin" : "in", values);
220
+ this.negateNext = false;
221
+ return this;
222
+ }
223
+ contains(column, value) {
224
+ this.addFilter(column, "cs", value);
225
+ this.negateNext = false;
226
+ return this;
227
+ }
228
+ containedBy(column, value) {
229
+ this.addFilter(column, "cd", value);
230
+ this.negateNext = false;
231
+ return this;
232
+ }
233
+ overlaps(column, value) {
234
+ this.addFilter(column, "ov", value);
235
+ this.negateNext = false;
236
+ return this;
237
+ }
238
+ textSearch(column, query, options) {
239
+ this.addFilter(column, "fts", query, options?.config);
240
+ this.negateNext = false;
241
+ return this;
242
+ }
243
+ /**
244
+ * Negate the next filter
245
+ */
246
+ get not() {
247
+ this.negateNext = true;
248
+ return this;
249
+ }
250
+ /**
251
+ * Combine filters with OR
252
+ */
253
+ or(filters) {
254
+ this.filters.push({ type: "or", value: filters });
255
+ return this;
256
+ }
257
+ /**
258
+ * Order results
259
+ */
260
+ order(column, options = {}) {
261
+ this.orderSpecs.push({
262
+ column,
263
+ ascending: options.ascending ?? true,
264
+ nullsFirst: options.nullsFirst
265
+ });
266
+ return this;
267
+ }
268
+ /**
269
+ * Limit results
270
+ */
271
+ limit(count) {
272
+ this.limitCount = count;
273
+ return this;
274
+ }
275
+ /**
276
+ * Offset results
277
+ */
278
+ offset(count) {
279
+ this.offsetCount = count;
280
+ return this;
281
+ }
282
+ /**
283
+ * Range of results
284
+ */
285
+ range(from, to) {
286
+ this.offsetCount = from;
287
+ this.limitCount = to - from + 1;
288
+ return this;
289
+ }
290
+ /**
291
+ * Include count
292
+ */
293
+ count(type = "exact") {
294
+ this.countOption = type;
295
+ return this;
296
+ }
297
+ /**
298
+ * Return single result
299
+ */
300
+ async single() {
301
+ this.limitCount = 1;
302
+ const result = await this.execute();
303
+ if (result.error) {
304
+ return {
305
+ data: null,
306
+ error: result.error,
307
+ status: result.status
308
+ };
309
+ }
310
+ if (!result.data || result.data.length === 0) {
311
+ return {
312
+ data: null,
313
+ error: {
314
+ message: "No rows returned",
315
+ code: "PGRST116"
316
+ },
317
+ status: 406
318
+ };
319
+ }
320
+ return {
321
+ data: result.data[0],
322
+ error: null,
323
+ status: result.status
324
+ };
325
+ }
326
+ /**
327
+ * Return first result or null
328
+ */
329
+ async maybeSingle() {
330
+ this.limitCount = 1;
331
+ const result = await this.execute();
332
+ if (result.error) {
333
+ return {
334
+ data: null,
335
+ error: result.error,
336
+ status: result.status
337
+ };
338
+ }
339
+ return {
340
+ data: result.data?.[0] ?? null,
341
+ error: null,
342
+ status: result.status
343
+ };
344
+ }
345
+ /**
346
+ * Execute the query
347
+ */
348
+ async execute() {
349
+ const body = this.buildQuery();
350
+ const response = await this.client.request(
351
+ `/db/${this.table}`,
352
+ {
353
+ method: this.getHttpMethod(),
354
+ body
355
+ }
356
+ );
357
+ if (response.error) {
358
+ return {
359
+ data: [],
360
+ error: {
361
+ message: response.error.message,
362
+ code: response.error.code ?? "QUERY_ERROR",
363
+ details: response.error.details?.toString()
364
+ },
365
+ status: response.status
366
+ };
367
+ }
368
+ return {
369
+ data: response.data?.data ?? [],
370
+ error: null,
371
+ status: response.status,
372
+ count: response.data?.count
373
+ };
374
+ }
375
+ // Private helpers
376
+ addFilter(column, operator, value, extra) {
377
+ this.filters.push({ type: "filter", column, operator, value, extra });
378
+ }
379
+ getHttpMethod() {
380
+ switch (this.queryType) {
381
+ case "insert":
382
+ return "POST";
383
+ case "update":
384
+ return "PATCH";
385
+ case "delete":
386
+ return "DELETE";
387
+ default:
388
+ return "GET";
389
+ }
390
+ }
391
+ buildQuery() {
392
+ const query = {
393
+ type: this.queryType
394
+ };
395
+ if (this.queryType === "select") {
396
+ query.select = this.selectColumns;
397
+ }
398
+ if (this.queryType === "insert" && this.insertData) {
399
+ query.data = this.insertData;
400
+ if (this.onConflictColumns) {
401
+ query.onConflict = {
402
+ columns: this.onConflictColumns,
403
+ action: this.onConflictAction,
404
+ update: this.onConflictUpdate
405
+ };
406
+ }
407
+ }
408
+ if (this.queryType === "update" && this.updateData) {
409
+ query.data = this.updateData;
410
+ }
411
+ if (this.filters.length > 0) {
412
+ query.filters = this.filters;
413
+ }
414
+ if (this.orderSpecs.length > 0) {
415
+ query.order = this.orderSpecs;
416
+ }
417
+ if (this.limitCount !== null) {
418
+ query.limit = this.limitCount;
419
+ }
420
+ if (this.offsetCount !== null) {
421
+ query.offset = this.offsetCount;
422
+ }
423
+ if (this.countOption) {
424
+ query.count = this.countOption;
425
+ }
426
+ if (this.returning) {
427
+ query.returning = this.returning;
428
+ }
429
+ return query;
430
+ }
431
+ };
432
+
433
+ // src/lib/realtime.ts
434
+ var VaifRealtime = class {
435
+ constructor(client) {
436
+ this.socket = null;
437
+ this.channels = /* @__PURE__ */ new Map();
438
+ this.reconnectAttempts = 0;
439
+ this.maxReconnectAttempts = 10;
440
+ this.reconnectInterval = 1e3;
441
+ this.heartbeatInterval = null;
442
+ this.client = client;
443
+ }
444
+ /**
445
+ * Connect to realtime server
446
+ */
447
+ connect(options = {}) {
448
+ if (this.socket?.readyState === WebSocket.OPEN) {
449
+ return;
450
+ }
451
+ const config = this.client.getConfig();
452
+ const url = new URL(config.realtimeUrl);
453
+ url.searchParams.set("apikey", config.apiKey);
454
+ url.searchParams.set("project", config.projectId);
455
+ const token = this.client.getAccessToken();
456
+ if (token) {
457
+ url.searchParams.set("token", token);
458
+ }
459
+ this.socket = new WebSocket(url.toString());
460
+ this.socket.onopen = () => {
461
+ this.client.debug("Realtime connected");
462
+ this.reconnectAttempts = 0;
463
+ if (options.heartbeat !== false) {
464
+ this.startHeartbeat(options.heartbeatInterval ?? 3e4);
465
+ }
466
+ this.channels.forEach((channel) => {
467
+ channel.resubscribe();
468
+ });
469
+ };
470
+ this.socket.onmessage = (event) => {
471
+ try {
472
+ const message = JSON.parse(event.data);
473
+ this.handleMessage(message);
474
+ } catch {
475
+ this.client.debug("Failed to parse realtime message");
476
+ }
477
+ };
478
+ this.socket.onclose = () => {
479
+ this.client.debug("Realtime disconnected");
480
+ this.stopHeartbeat();
481
+ if (options.autoReconnect !== false && this.reconnectAttempts < (options.maxReconnectAttempts ?? this.maxReconnectAttempts)) {
482
+ this.reconnectAttempts++;
483
+ const delay = Math.min(
484
+ (options.reconnectInterval ?? this.reconnectInterval) * this.reconnectAttempts,
485
+ 3e4
486
+ );
487
+ setTimeout(() => this.connect(options), delay);
488
+ }
489
+ };
490
+ this.socket.onerror = (error) => {
491
+ this.client.debug("Realtime error", error);
492
+ };
493
+ }
494
+ /**
495
+ * Disconnect from realtime server
496
+ */
497
+ disconnect() {
498
+ this.stopHeartbeat();
499
+ this.channels.forEach((channel) => channel.unsubscribe());
500
+ this.channels.clear();
501
+ this.socket?.close();
502
+ this.socket = null;
503
+ }
504
+ /**
505
+ * Create or get a channel
506
+ */
507
+ channel(name) {
508
+ let channel = this.channels.get(name);
509
+ if (!channel) {
510
+ channel = new RealtimeChannel(this, name);
511
+ this.channels.set(name, channel);
512
+ }
513
+ return channel;
514
+ }
515
+ /**
516
+ * Remove a channel
517
+ */
518
+ removeChannel(name) {
519
+ const channel = this.channels.get(name);
520
+ if (channel) {
521
+ channel.unsubscribe();
522
+ this.channels.delete(name);
523
+ }
524
+ }
525
+ /**
526
+ * Send message to server
527
+ */
528
+ send(message) {
529
+ if (this.socket?.readyState === WebSocket.OPEN) {
530
+ this.socket.send(JSON.stringify(message));
531
+ }
532
+ }
533
+ /**
534
+ * Get WebSocket instance
535
+ */
536
+ getSocket() {
537
+ return this.socket;
538
+ }
539
+ handleMessage(message) {
540
+ const channel = this.channels.get(message.topic);
541
+ if (channel) {
542
+ channel.handleMessage(message);
543
+ }
544
+ }
545
+ startHeartbeat(interval) {
546
+ this.heartbeatInterval = setInterval(() => {
547
+ this.send({ topic: "phoenix", event: "heartbeat", payload: {} });
548
+ }, interval);
549
+ }
550
+ stopHeartbeat() {
551
+ if (this.heartbeatInterval) {
552
+ clearInterval(this.heartbeatInterval);
553
+ this.heartbeatInterval = null;
554
+ }
555
+ }
556
+ };
557
+ var RealtimeChannel = class {
558
+ constructor(realtime, name) {
559
+ this.subscriptions = /* @__PURE__ */ new Map();
560
+ this.status = "CLOSED";
561
+ this.presenceState = /* @__PURE__ */ new Map();
562
+ this.realtime = realtime;
563
+ this.name = name;
564
+ }
565
+ on(event, filter, callback) {
566
+ const key = `${event}:${JSON.stringify(filter)}`;
567
+ const callbacks = this.subscriptions.get(key) ?? [];
568
+ callbacks.push(callback);
569
+ this.subscriptions.set(key, callbacks);
570
+ return this;
571
+ }
572
+ /**
573
+ * Subscribe to channel
574
+ */
575
+ subscribe(callback) {
576
+ this.realtime.send({
577
+ topic: this.name,
578
+ event: "phx_join",
579
+ payload: {
580
+ subscriptions: Array.from(this.subscriptions.keys())
581
+ },
582
+ ref: `${this.name}-join`
583
+ });
584
+ this.status = "SUBSCRIBED";
585
+ callback?.(this.status);
586
+ return this;
587
+ }
588
+ /**
589
+ * Unsubscribe from channel
590
+ */
591
+ unsubscribe() {
592
+ this.realtime.send({
593
+ topic: this.name,
594
+ event: "phx_leave",
595
+ payload: {},
596
+ ref: `${this.name}-leave`
597
+ });
598
+ this.status = "CLOSED";
599
+ this.subscriptions.clear();
600
+ }
601
+ /**
602
+ * Broadcast message to channel
603
+ */
604
+ broadcast(event, payload) {
605
+ this.realtime.send({
606
+ topic: this.name,
607
+ event: "broadcast",
608
+ payload: { event, payload }
609
+ });
610
+ }
611
+ /**
612
+ * Track presence
613
+ */
614
+ track(payload) {
615
+ this.realtime.send({
616
+ topic: this.name,
617
+ event: "presence",
618
+ payload: { type: "track", payload }
619
+ });
620
+ }
621
+ /**
622
+ * Untrack presence
623
+ */
624
+ untrack() {
625
+ this.realtime.send({
626
+ topic: this.name,
627
+ event: "presence",
628
+ payload: { type: "untrack" }
629
+ });
630
+ }
631
+ /**
632
+ * Get current presence state
633
+ */
634
+ getPresenceState() {
635
+ return this.presenceState;
636
+ }
637
+ /**
638
+ * Handle incoming message
639
+ */
640
+ handleMessage(message) {
641
+ this.subscriptions.forEach((callbacks, key) => {
642
+ const [event] = key.split(":");
643
+ if (message.event === event || message.event === "broadcast") {
644
+ callbacks.forEach((callback) => callback(message.payload));
645
+ }
646
+ });
647
+ }
648
+ /**
649
+ * Resubscribe after reconnect
650
+ */
651
+ resubscribe() {
652
+ if (this.subscriptions.size > 0) {
653
+ this.subscribe();
654
+ }
655
+ }
656
+ };
657
+
658
+ // src/lib/storage.ts
659
+ var VaifStorage = class {
660
+ constructor(client) {
661
+ this.client = client;
662
+ }
663
+ /**
664
+ * Get a storage bucket
665
+ */
666
+ from(bucket) {
667
+ return new StorageBucket(this.client, bucket);
668
+ }
669
+ /**
670
+ * List all buckets
671
+ */
672
+ async listBuckets() {
673
+ const response = await this.client.request("/storage/buckets");
674
+ return {
675
+ data: response.data,
676
+ error: response.error ? {
677
+ message: response.error.message,
678
+ statusCode: String(response.status),
679
+ error: response.error.code ?? "STORAGE_ERROR"
680
+ } : null
681
+ };
682
+ }
683
+ /**
684
+ * Create a bucket
685
+ */
686
+ async createBucket(name, options = {}) {
687
+ const response = await this.client.request("/storage/buckets", {
688
+ method: "POST",
689
+ body: { name, ...options }
690
+ });
691
+ return {
692
+ data: response.data,
693
+ error: response.error ? {
694
+ message: response.error.message,
695
+ statusCode: String(response.status),
696
+ error: response.error.code ?? "STORAGE_ERROR"
697
+ } : null
698
+ };
699
+ }
700
+ /**
701
+ * Delete a bucket
702
+ */
703
+ async deleteBucket(name) {
704
+ const response = await this.client.request(`/storage/buckets/${name}`, {
705
+ method: "DELETE"
706
+ });
707
+ return {
708
+ error: response.error ? {
709
+ message: response.error.message,
710
+ statusCode: String(response.status),
711
+ error: response.error.code ?? "STORAGE_ERROR"
712
+ } : null
713
+ };
714
+ }
715
+ };
716
+ var StorageBucket = class {
717
+ constructor(client, bucket) {
718
+ this.client = client;
719
+ this.bucket = bucket;
720
+ }
721
+ /**
722
+ * Upload a file
723
+ */
724
+ async upload(path, file, options = {}) {
725
+ const formData = new FormData();
726
+ if (file instanceof File) {
727
+ formData.append("file", file);
728
+ } else if (file instanceof Blob) {
729
+ formData.append("file", file, path.split("/").pop() ?? "file");
730
+ } else if (file instanceof ArrayBuffer) {
731
+ formData.append("file", new Blob([file]), path.split("/").pop() ?? "file");
732
+ } else if (typeof file === "string") {
733
+ const blob = await fetch(file).then((r) => r.blob());
734
+ formData.append("file", blob, path.split("/").pop() ?? "file");
735
+ }
736
+ if (options.contentType) {
737
+ formData.append("contentType", options.contentType);
738
+ }
739
+ if (options.cacheControl) {
740
+ formData.append("cacheControl", options.cacheControl);
741
+ }
742
+ if (options.upsert) {
743
+ formData.append("upsert", "true");
744
+ }
745
+ if (options.metadata) {
746
+ formData.append("metadata", JSON.stringify(options.metadata));
747
+ }
748
+ const response = await this.client.request(
749
+ `/storage/${this.bucket}/${path}`,
750
+ {
751
+ method: "POST",
752
+ headers: {
753
+ // Don't set Content-Type, let browser set it with boundary
754
+ },
755
+ body: formData
756
+ }
757
+ );
758
+ return {
759
+ data: response.data,
760
+ error: response.error ? {
761
+ message: response.error.message,
762
+ statusCode: String(response.status),
763
+ error: response.error.code ?? "UPLOAD_ERROR"
764
+ } : null
765
+ };
766
+ }
767
+ /**
768
+ * Download a file
769
+ */
770
+ async download(path, options = {}) {
771
+ const params = {};
772
+ if (options.transform) {
773
+ if (options.transform.width) params.width = String(options.transform.width);
774
+ if (options.transform.height) params.height = String(options.transform.height);
775
+ if (options.transform.resize) params.resize = options.transform.resize;
776
+ if (options.transform.format) params.format = options.transform.format;
777
+ if (options.transform.quality) params.quality = String(options.transform.quality);
778
+ }
779
+ const url = new URL(`/storage/${this.bucket}/${path}`, this.client.getConfig().apiUrl);
780
+ Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
781
+ try {
782
+ const response = await fetch(url.toString(), {
783
+ headers: {
784
+ apikey: this.client.getConfig().apiKey,
785
+ Authorization: `Bearer ${this.client.getAccessToken() ?? this.client.getConfig().apiKey}`
786
+ }
787
+ });
788
+ if (!response.ok) {
789
+ return {
790
+ data: null,
791
+ error: {
792
+ message: `Download failed: ${response.statusText}`,
793
+ statusCode: String(response.status),
794
+ error: "DOWNLOAD_ERROR"
795
+ }
796
+ };
797
+ }
798
+ return { data: await response.blob(), error: null };
799
+ } catch (err) {
800
+ return {
801
+ data: null,
802
+ error: {
803
+ message: err instanceof Error ? err.message : "Download failed",
804
+ statusCode: "0",
805
+ error: "NETWORK_ERROR"
806
+ }
807
+ };
808
+ }
809
+ }
810
+ /**
811
+ * Get public URL for a file
812
+ */
813
+ getPublicUrl(path) {
814
+ const config = this.client.getConfig();
815
+ return {
816
+ data: {
817
+ publicUrl: `${config.apiUrl}/storage/${this.bucket}/${path}`
818
+ }
819
+ };
820
+ }
821
+ /**
822
+ * Create signed URL
823
+ */
824
+ async createSignedUrl(path, options) {
825
+ const response = await this.client.request(
826
+ `/storage/${this.bucket}/${path}/sign`,
827
+ {
828
+ method: "POST",
829
+ body: {
830
+ expiresIn: options.expiresIn,
831
+ transform: options.transform,
832
+ download: options.download
833
+ }
834
+ }
835
+ );
836
+ return {
837
+ data: response.data,
838
+ error: response.error ? {
839
+ message: response.error.message,
840
+ statusCode: String(response.status),
841
+ error: response.error.code ?? "SIGN_ERROR"
842
+ } : null
843
+ };
844
+ }
845
+ /**
846
+ * List files in a path
847
+ */
848
+ async list(path = "", options = {}) {
849
+ const response = await this.client.request(
850
+ `/storage/${this.bucket}/list`,
851
+ {
852
+ method: "POST",
853
+ body: {
854
+ prefix: path,
855
+ limit: options.limit,
856
+ offset: options.offset,
857
+ search: options.search,
858
+ sortBy: options.sortBy
859
+ }
860
+ }
861
+ );
862
+ return {
863
+ data: response.data,
864
+ error: response.error ? {
865
+ message: response.error.message,
866
+ statusCode: String(response.status),
867
+ error: response.error.code ?? "LIST_ERROR"
868
+ } : null
869
+ };
870
+ }
871
+ /**
872
+ * Move/rename a file
873
+ */
874
+ async move(fromPath, toPath) {
875
+ const response = await this.client.request(`/storage/${this.bucket}/move`, {
876
+ method: "POST",
877
+ body: { from: fromPath, to: toPath }
878
+ });
879
+ return {
880
+ error: response.error ? {
881
+ message: response.error.message,
882
+ statusCode: String(response.status),
883
+ error: response.error.code ?? "MOVE_ERROR"
884
+ } : null
885
+ };
886
+ }
887
+ /**
888
+ * Copy a file
889
+ */
890
+ async copy(fromPath, toPath) {
891
+ const response = await this.client.request(`/storage/${this.bucket}/copy`, {
892
+ method: "POST",
893
+ body: { from: fromPath, to: toPath }
894
+ });
895
+ return {
896
+ error: response.error ? {
897
+ message: response.error.message,
898
+ statusCode: String(response.status),
899
+ error: response.error.code ?? "COPY_ERROR"
900
+ } : null
901
+ };
902
+ }
903
+ /**
904
+ * Delete a file
905
+ */
906
+ async remove(paths) {
907
+ const pathArray = Array.isArray(paths) ? paths : [paths];
908
+ const response = await this.client.request(`/storage/${this.bucket}/delete`, {
909
+ method: "DELETE",
910
+ body: { paths: pathArray }
911
+ });
912
+ return {
913
+ error: response.error ? {
914
+ message: response.error.message,
915
+ statusCode: String(response.status),
916
+ error: response.error.code ?? "DELETE_ERROR"
917
+ } : null
918
+ };
919
+ }
920
+ };
921
+
922
+ // src/lib/functions.ts
923
+ var VaifFunctions = class {
924
+ constructor(client) {
925
+ this.client = client;
926
+ }
927
+ /**
928
+ * Invoke an edge function
929
+ *
930
+ * @example
931
+ * ```typescript
932
+ * const { data, error } = await vaif.functions.invoke('send-email', {
933
+ * body: { to: 'user@example.com', subject: 'Hello' }
934
+ * });
935
+ * ```
936
+ */
937
+ async invoke(functionName, options = {}) {
938
+ const config = this.client.getConfig();
939
+ const url = new URL(
940
+ `/functions/${functionName}`,
941
+ config.apiUrl
942
+ );
943
+ const headers = {
944
+ "X-Project-ID": config.projectId,
945
+ apikey: config.apiKey,
946
+ ...options.headers
947
+ };
948
+ const token = this.client.getAccessToken();
949
+ if (token) {
950
+ headers.Authorization = `Bearer ${token}`;
951
+ }
952
+ if (options.body !== void 0) {
953
+ if (options.body instanceof FormData) {
954
+ } else if (options.body instanceof Blob) {
955
+ headers["Content-Type"] = options.body.type || "application/octet-stream";
956
+ } else {
957
+ headers["Content-Type"] = "application/json";
958
+ }
959
+ }
960
+ const timeoutMs = options.timeout ?? config.timeout;
961
+ const controller = new AbortController();
962
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
963
+ try {
964
+ const response = await fetch(url.toString(), {
965
+ method: options.method ?? "POST",
966
+ headers,
967
+ body: this.serializeBody(options.body),
968
+ signal: controller.signal
969
+ });
970
+ clearTimeout(timeoutId);
971
+ const contentType = response.headers.get("content-type");
972
+ let data = null;
973
+ if (contentType?.includes("application/json")) {
974
+ data = await response.json();
975
+ } else if (contentType?.includes("text/")) {
976
+ data = await response.text();
977
+ } else if (response.body) {
978
+ data = await response.blob();
979
+ }
980
+ if (!response.ok) {
981
+ const error = {
982
+ message: typeof data === "object" && data !== null && "message" in data ? data.message : `Function invocation failed with status ${response.status}`,
983
+ status: response.status,
984
+ name: functionName
985
+ };
986
+ return { data: null, error };
987
+ }
988
+ return { data, error: null };
989
+ } catch (err) {
990
+ clearTimeout(timeoutId);
991
+ const error = {
992
+ message: err instanceof Error ? err.name === "AbortError" ? "Function invocation timed out" : err.message : "Function invocation failed",
993
+ status: 0,
994
+ name: functionName
995
+ };
996
+ return { data: null, error };
997
+ }
998
+ }
999
+ /**
1000
+ * Create a function URL for direct invocation
1001
+ */
1002
+ createUrl(functionName) {
1003
+ const config = this.client.getConfig();
1004
+ return `${config.apiUrl}/functions/${functionName}`;
1005
+ }
1006
+ /**
1007
+ * Get list of deployed functions
1008
+ */
1009
+ async list() {
1010
+ const response = await this.client.request("/functions");
1011
+ return {
1012
+ data: response.data,
1013
+ error: response.error ? {
1014
+ message: response.error.message,
1015
+ status: response.status,
1016
+ name: "list"
1017
+ } : null
1018
+ };
1019
+ }
1020
+ /**
1021
+ * Get function details
1022
+ */
1023
+ async get(functionName) {
1024
+ const response = await this.client.request(`/functions/${functionName}`);
1025
+ return {
1026
+ data: response.data,
1027
+ error: response.error ? {
1028
+ message: response.error.message,
1029
+ status: response.status,
1030
+ name: functionName
1031
+ } : null
1032
+ };
1033
+ }
1034
+ serializeBody(body) {
1035
+ if (body === void 0) {
1036
+ return void 0;
1037
+ }
1038
+ if (body instanceof FormData || body instanceof Blob) {
1039
+ return body;
1040
+ }
1041
+ return JSON.stringify(body);
1042
+ }
1043
+ };
1044
+
1045
+ // src/lib/auth.ts
1046
+ var VaifAuth = class {
1047
+ constructor(client) {
1048
+ this.currentSession = null;
1049
+ this.currentUser = null;
1050
+ this.stateChangeCallbacks = /* @__PURE__ */ new Set();
1051
+ this.refreshTimeout = null;
1052
+ this.client = client;
1053
+ this.restoreSession();
1054
+ }
1055
+ /**
1056
+ * Get current session
1057
+ */
1058
+ async getSession() {
1059
+ return {
1060
+ data: { session: this.currentSession },
1061
+ error: null
1062
+ };
1063
+ }
1064
+ /**
1065
+ * Get current user synchronously from cache
1066
+ */
1067
+ get user() {
1068
+ return this.currentUser;
1069
+ }
1070
+ /**
1071
+ * Get current user (fetches fresh data)
1072
+ */
1073
+ async getUser() {
1074
+ if (!this.currentSession) {
1075
+ return { data: { user: this.currentUser }, error: null };
1076
+ }
1077
+ const response = await this.client.request("/auth/user");
1078
+ if (response.error) {
1079
+ return {
1080
+ data: { user: this.currentUser },
1081
+ error: {
1082
+ message: response.error.message,
1083
+ status: response.status
1084
+ }
1085
+ };
1086
+ }
1087
+ this.currentUser = response.data ?? null;
1088
+ return { data: { user: this.currentUser }, error: null };
1089
+ }
1090
+ /**
1091
+ * Sign up with email and password
1092
+ */
1093
+ async signUp(options) {
1094
+ const response = await this.client.request("/auth/signup", {
1095
+ method: "POST",
1096
+ body: {
1097
+ email: options.email,
1098
+ password: options.password,
1099
+ phone: options.phone,
1100
+ data: options.data,
1101
+ redirectTo: options.redirectTo
1102
+ }
1103
+ });
1104
+ if (response.error) {
1105
+ return {
1106
+ data: { session: null, user: null },
1107
+ error: {
1108
+ message: response.error.message,
1109
+ status: response.status
1110
+ }
1111
+ };
1112
+ }
1113
+ const { session, user } = response.data ?? { session: null, user: null };
1114
+ if (session) {
1115
+ await this.setSession(session);
1116
+ this.notifyStateChange("SIGNED_IN", session);
1117
+ }
1118
+ return {
1119
+ data: { session, user },
1120
+ error: null
1121
+ };
1122
+ }
1123
+ /**
1124
+ * Sign in with email and password
1125
+ */
1126
+ async signInWithPassword(options) {
1127
+ const response = await this.client.request("/auth/token?grant_type=password", {
1128
+ method: "POST",
1129
+ body: {
1130
+ email: options.email,
1131
+ password: options.password,
1132
+ phone: options.phone
1133
+ }
1134
+ });
1135
+ if (response.error) {
1136
+ return {
1137
+ data: { session: null, user: null },
1138
+ error: {
1139
+ message: response.error.message,
1140
+ status: response.status
1141
+ }
1142
+ };
1143
+ }
1144
+ const { session, user } = response.data ?? { session: null, user: null };
1145
+ if (session) {
1146
+ await this.setSession(session);
1147
+ this.notifyStateChange("SIGNED_IN", session);
1148
+ }
1149
+ return {
1150
+ data: { session, user },
1151
+ error: null
1152
+ };
1153
+ }
1154
+ /**
1155
+ * Sign in with OAuth provider
1156
+ */
1157
+ async signInWithOAuth(options) {
1158
+ const config = this.client.getConfig();
1159
+ const redirectTo = options.redirectTo ?? (typeof window !== "undefined" ? window.location?.origin : "") ?? "";
1160
+ const params = new URLSearchParams({
1161
+ provider: options.provider,
1162
+ redirect_to: redirectTo
1163
+ });
1164
+ if (options.scopes) {
1165
+ params.set("scopes", options.scopes);
1166
+ }
1167
+ if (options.queryParams) {
1168
+ Object.entries(options.queryParams).forEach(([key, value]) => {
1169
+ params.set(key, value);
1170
+ });
1171
+ }
1172
+ const url = `${config.apiUrl}/auth/authorize?${params.toString()}`;
1173
+ return {
1174
+ data: {
1175
+ provider: options.provider,
1176
+ url
1177
+ },
1178
+ error: null
1179
+ };
1180
+ }
1181
+ /**
1182
+ * Sign in with magic link (passwordless)
1183
+ */
1184
+ async signInWithOtp(options) {
1185
+ const response = await this.client.request("/auth/otp", {
1186
+ method: "POST",
1187
+ body: {
1188
+ email: options.email,
1189
+ phone: options.phone,
1190
+ create_user: options.createUser ?? true,
1191
+ redirect_to: options.redirectTo
1192
+ }
1193
+ });
1194
+ if (response.error) {
1195
+ return {
1196
+ data: {},
1197
+ error: {
1198
+ message: response.error.message,
1199
+ status: response.status
1200
+ }
1201
+ };
1202
+ }
1203
+ return {
1204
+ data: { messageId: response.data?.messageId },
1205
+ error: null
1206
+ };
1207
+ }
1208
+ /**
1209
+ * Verify OTP code
1210
+ */
1211
+ async verifyOtp(options) {
1212
+ const response = await this.client.request("/auth/verify", {
1213
+ method: "POST",
1214
+ body: {
1215
+ email: options.email,
1216
+ phone: options.phone,
1217
+ token: options.token,
1218
+ type: options.type
1219
+ }
1220
+ });
1221
+ if (response.error) {
1222
+ return {
1223
+ data: { session: null, user: null },
1224
+ error: {
1225
+ message: response.error.message,
1226
+ status: response.status
1227
+ }
1228
+ };
1229
+ }
1230
+ const { session, user } = response.data ?? { session: null, user: null };
1231
+ if (session) {
1232
+ await this.setSession(session);
1233
+ this.notifyStateChange("SIGNED_IN", session);
1234
+ }
1235
+ return {
1236
+ data: { session, user },
1237
+ error: null
1238
+ };
1239
+ }
1240
+ /**
1241
+ * Sign out
1242
+ */
1243
+ async signOut(options) {
1244
+ const scope = options?.scope ?? "local";
1245
+ if (scope !== "local") {
1246
+ await this.client.request("/auth/logout", {
1247
+ method: "POST",
1248
+ body: { scope }
1249
+ });
1250
+ }
1251
+ await this.clearSession();
1252
+ this.notifyStateChange("SIGNED_OUT", null);
1253
+ return { error: null };
1254
+ }
1255
+ /**
1256
+ * Reset password
1257
+ */
1258
+ async resetPasswordForEmail(options) {
1259
+ const response = await this.client.request("/auth/recover", {
1260
+ method: "POST",
1261
+ body: {
1262
+ email: options.email,
1263
+ redirect_to: options.redirectTo,
1264
+ captcha_token: options.captchaToken
1265
+ }
1266
+ });
1267
+ if (response.error) {
1268
+ return {
1269
+ error: {
1270
+ message: response.error.message,
1271
+ status: response.status
1272
+ }
1273
+ };
1274
+ }
1275
+ return { error: null };
1276
+ }
1277
+ /**
1278
+ * Update user
1279
+ */
1280
+ async updateUser(options) {
1281
+ const response = await this.client.request("/auth/user", {
1282
+ method: "PUT",
1283
+ body: {
1284
+ email: options.email,
1285
+ password: options.password,
1286
+ phone: options.phone,
1287
+ data: options.data,
1288
+ nonce: options.nonce
1289
+ }
1290
+ });
1291
+ if (response.error) {
1292
+ return {
1293
+ data: { user: null },
1294
+ error: {
1295
+ message: response.error.message,
1296
+ status: response.status
1297
+ }
1298
+ };
1299
+ }
1300
+ this.currentUser = response.data;
1301
+ this.notifyStateChange("USER_UPDATED", this.currentSession);
1302
+ return { data: { user: response.data }, error: null };
1303
+ }
1304
+ /**
1305
+ * Refresh session
1306
+ */
1307
+ async refreshSession() {
1308
+ if (!this.currentSession?.refreshToken) {
1309
+ return {
1310
+ data: { session: null, user: null },
1311
+ error: { message: "No refresh token available", status: 401 }
1312
+ };
1313
+ }
1314
+ const response = await this.client.request("/auth/token?grant_type=refresh_token", {
1315
+ method: "POST",
1316
+ body: {
1317
+ refresh_token: this.currentSession.refreshToken
1318
+ }
1319
+ });
1320
+ if (response.error) {
1321
+ await this.clearSession();
1322
+ this.notifyStateChange("TOKEN_REFRESHED", null);
1323
+ return {
1324
+ data: { session: null, user: null },
1325
+ error: {
1326
+ message: response.error.message,
1327
+ status: response.status
1328
+ }
1329
+ };
1330
+ }
1331
+ const { session, user } = response.data ?? { session: null, user: null };
1332
+ if (session) {
1333
+ await this.setSession(session);
1334
+ this.notifyStateChange("TOKEN_REFRESHED", session);
1335
+ }
1336
+ return {
1337
+ data: { session, user },
1338
+ error: null
1339
+ };
1340
+ }
1341
+ /**
1342
+ * Set session from URL (after OAuth redirect)
1343
+ */
1344
+ async setSessionFromUrl() {
1345
+ if (typeof window === "undefined") {
1346
+ return {
1347
+ data: { session: null, user: null },
1348
+ error: { message: "Not in browser environment", status: 0 }
1349
+ };
1350
+ }
1351
+ const hashParams = new URLSearchParams(window.location.hash.slice(1));
1352
+ const accessToken = hashParams.get("access_token");
1353
+ const refreshToken = hashParams.get("refresh_token");
1354
+ const expiresIn = hashParams.get("expires_in");
1355
+ const tokenType = hashParams.get("token_type");
1356
+ if (!accessToken) {
1357
+ const error = hashParams.get("error_description") || hashParams.get("error");
1358
+ return {
1359
+ data: { session: null, user: null },
1360
+ error: error ? { message: error, status: 0 } : null
1361
+ };
1362
+ }
1363
+ const session = {
1364
+ accessToken,
1365
+ refreshToken: refreshToken ?? "",
1366
+ expiresIn: expiresIn ? parseInt(expiresIn, 10) : 3600,
1367
+ expiresAt: Date.now() + (expiresIn ? parseInt(expiresIn, 10) * 1e3 : 36e5),
1368
+ tokenType: tokenType ?? "bearer",
1369
+ user: null
1370
+ };
1371
+ this.client.setAccessToken(accessToken);
1372
+ const userResponse = await this.client.request("/auth/user");
1373
+ if (userResponse.data) {
1374
+ session.user = userResponse.data;
1375
+ }
1376
+ await this.setSession(session);
1377
+ this.notifyStateChange("SIGNED_IN", session);
1378
+ window.history.replaceState(null, "", window.location.pathname);
1379
+ return {
1380
+ data: { session, user: session.user },
1381
+ error: null
1382
+ };
1383
+ }
1384
+ /**
1385
+ * Subscribe to auth state changes
1386
+ */
1387
+ onAuthStateChange(callback) {
1388
+ this.stateChangeCallbacks.add(callback);
1389
+ if (this.currentSession) {
1390
+ callback("INITIAL_SESSION", this.currentSession);
1391
+ }
1392
+ return {
1393
+ data: {
1394
+ subscription: {
1395
+ unsubscribe: () => {
1396
+ this.stateChangeCallbacks.delete(callback);
1397
+ }
1398
+ }
1399
+ }
1400
+ };
1401
+ }
1402
+ /**
1403
+ * Exchange code for session (PKCE flow)
1404
+ */
1405
+ async exchangeCodeForSession(code) {
1406
+ const response = await this.client.request("/auth/token?grant_type=authorization_code", {
1407
+ method: "POST",
1408
+ body: { code }
1409
+ });
1410
+ if (response.error) {
1411
+ return {
1412
+ data: { session: null, user: null },
1413
+ error: {
1414
+ message: response.error.message,
1415
+ status: response.status
1416
+ }
1417
+ };
1418
+ }
1419
+ const { session, user } = response.data ?? { session: null, user: null };
1420
+ if (session) {
1421
+ await this.setSession(session);
1422
+ this.notifyStateChange("SIGNED_IN", session);
1423
+ }
1424
+ return {
1425
+ data: { session, user },
1426
+ error: null
1427
+ };
1428
+ }
1429
+ /**
1430
+ * Resend confirmation email
1431
+ */
1432
+ async resend(options) {
1433
+ const response = await this.client.request("/auth/resend", {
1434
+ method: "POST",
1435
+ body: options
1436
+ });
1437
+ if (response.error) {
1438
+ return {
1439
+ error: {
1440
+ message: response.error.message,
1441
+ status: response.status
1442
+ }
1443
+ };
1444
+ }
1445
+ return { error: null };
1446
+ }
1447
+ // Private methods
1448
+ async setSession(session) {
1449
+ this.currentSession = session;
1450
+ this.currentUser = session.user;
1451
+ this.client.setAccessToken(session.accessToken);
1452
+ const config = this.client.getConfig();
1453
+ if (config.persistSession) {
1454
+ await config.storage.setItem(
1455
+ `vaif-session-${config.projectId}`,
1456
+ JSON.stringify({
1457
+ accessToken: session.accessToken,
1458
+ refreshToken: session.refreshToken,
1459
+ expiresAt: session.expiresAt
1460
+ })
1461
+ );
1462
+ }
1463
+ if (config.autoRefreshToken) {
1464
+ this.scheduleRefresh(session);
1465
+ }
1466
+ }
1467
+ async clearSession() {
1468
+ this.currentSession = null;
1469
+ this.currentUser = null;
1470
+ this.client.setAccessToken(null);
1471
+ if (this.refreshTimeout) {
1472
+ clearTimeout(this.refreshTimeout);
1473
+ this.refreshTimeout = null;
1474
+ }
1475
+ const config = this.client.getConfig();
1476
+ if (config.persistSession) {
1477
+ await config.storage.removeItem(`vaif-session-${config.projectId}`);
1478
+ }
1479
+ }
1480
+ async restoreSession() {
1481
+ const config = this.client.getConfig();
1482
+ if (!config.persistSession) return;
1483
+ try {
1484
+ const sessionStr = await config.storage.getItem(`vaif-session-${config.projectId}`);
1485
+ if (!sessionStr) return;
1486
+ const stored = JSON.parse(sessionStr);
1487
+ if (!stored.accessToken || stored.expiresAt < Date.now()) {
1488
+ await this.clearSession();
1489
+ return;
1490
+ }
1491
+ this.client.setAccessToken(stored.accessToken);
1492
+ const response = await this.client.request("/auth/session");
1493
+ if (response.data?.session) {
1494
+ await this.setSession(response.data.session);
1495
+ this.notifyStateChange("INITIAL_SESSION", response.data.session);
1496
+ } else {
1497
+ await this.clearSession();
1498
+ }
1499
+ } catch {
1500
+ await this.clearSession();
1501
+ }
1502
+ }
1503
+ scheduleRefresh(session) {
1504
+ if (this.refreshTimeout) {
1505
+ clearTimeout(this.refreshTimeout);
1506
+ }
1507
+ const expiresIn = session.expiresAt - Date.now() - 6e4;
1508
+ if (expiresIn > 0) {
1509
+ this.refreshTimeout = setTimeout(() => {
1510
+ this.refreshSession();
1511
+ }, expiresIn);
1512
+ }
1513
+ }
1514
+ notifyStateChange(event, session) {
1515
+ this.stateChangeCallbacks.forEach((callback) => {
1516
+ try {
1517
+ callback(event, session);
1518
+ } catch {
1519
+ }
1520
+ });
1521
+ }
1522
+ };
1523
+
1524
+ // src/lib/ai.ts
1525
+ var VaifAI = class {
1526
+ constructor(client) {
1527
+ this.client = client;
1528
+ }
1529
+ /**
1530
+ * Generate database schema from natural language
1531
+ *
1532
+ * @example
1533
+ * ```typescript
1534
+ * const { data, error } = await vaif.ai.generateSchema({
1535
+ * prompt: 'Create a blog with posts, authors, and comments',
1536
+ * format: 'drizzle',
1537
+ * includeRelations: true
1538
+ * });
1539
+ * ```
1540
+ */
1541
+ async generateSchema(request, options = {}) {
1542
+ const response = await this.client.request(
1543
+ "/ai/generate/schema",
1544
+ {
1545
+ method: "POST",
1546
+ body: {
1547
+ ...request,
1548
+ model: options.model,
1549
+ temperature: options.temperature,
1550
+ maxTokens: options.maxTokens
1551
+ }
1552
+ }
1553
+ );
1554
+ if (response.error) {
1555
+ return {
1556
+ data: null,
1557
+ error: {
1558
+ message: response.error.message,
1559
+ code: response.error.code,
1560
+ status: response.status
1561
+ }
1562
+ };
1563
+ }
1564
+ return { data: response.data, error: null };
1565
+ }
1566
+ /**
1567
+ * Generate edge function from natural language
1568
+ *
1569
+ * @example
1570
+ * ```typescript
1571
+ * const { data, error } = await vaif.ai.generateFunction({
1572
+ * prompt: 'Create a function that sends welcome emails to new users',
1573
+ * name: 'send-welcome-email',
1574
+ * runtime: 'edge'
1575
+ * });
1576
+ * ```
1577
+ */
1578
+ async generateFunction(request, options = {}) {
1579
+ const response = await this.client.request(
1580
+ "/ai/generate/function",
1581
+ {
1582
+ method: "POST",
1583
+ body: {
1584
+ ...request,
1585
+ model: options.model,
1586
+ temperature: options.temperature,
1587
+ maxTokens: options.maxTokens
1588
+ }
1589
+ }
1590
+ );
1591
+ if (response.error) {
1592
+ return {
1593
+ data: null,
1594
+ error: {
1595
+ message: response.error.message,
1596
+ code: response.error.code,
1597
+ status: response.status
1598
+ }
1599
+ };
1600
+ }
1601
+ return { data: response.data, error: null };
1602
+ }
1603
+ /**
1604
+ * Generate API endpoint from natural language
1605
+ *
1606
+ * @example
1607
+ * ```typescript
1608
+ * const { data, error } = await vaif.ai.generateEndpoint({
1609
+ * prompt: 'Create an endpoint to get user profile with their recent posts',
1610
+ * method: 'GET',
1611
+ * path: '/users/:id/profile',
1612
+ * requiresAuth: true
1613
+ * });
1614
+ * ```
1615
+ */
1616
+ async generateEndpoint(request, options = {}) {
1617
+ const response = await this.client.request(
1618
+ "/ai/generate/endpoint",
1619
+ {
1620
+ method: "POST",
1621
+ body: {
1622
+ ...request,
1623
+ model: options.model,
1624
+ temperature: options.temperature,
1625
+ maxTokens: options.maxTokens
1626
+ }
1627
+ }
1628
+ );
1629
+ if (response.error) {
1630
+ return {
1631
+ data: null,
1632
+ error: {
1633
+ message: response.error.message,
1634
+ code: response.error.code,
1635
+ status: response.status
1636
+ }
1637
+ };
1638
+ }
1639
+ return { data: response.data, error: null };
1640
+ }
1641
+ /**
1642
+ * Chat with AI assistant for project help
1643
+ *
1644
+ * @example
1645
+ * ```typescript
1646
+ * const { data, error } = await vaif.ai.chat({
1647
+ * messages: [
1648
+ * { role: 'user', content: 'How do I add pagination to my posts query?' }
1649
+ * ],
1650
+ * context: {
1651
+ * schema: mySchemaString
1652
+ * }
1653
+ * });
1654
+ * ```
1655
+ */
1656
+ async chat(request, options = {}) {
1657
+ const response = await this.client.request("/ai/chat", {
1658
+ method: "POST",
1659
+ body: {
1660
+ ...request,
1661
+ model: options.model,
1662
+ temperature: options.temperature,
1663
+ maxTokens: options.maxTokens
1664
+ }
1665
+ });
1666
+ if (response.error) {
1667
+ return {
1668
+ data: null,
1669
+ error: {
1670
+ message: response.error.message,
1671
+ code: response.error.code,
1672
+ status: response.status
1673
+ }
1674
+ };
1675
+ }
1676
+ return { data: response.data, error: null };
1677
+ }
1678
+ /**
1679
+ * Stream chat response
1680
+ *
1681
+ * @example
1682
+ * ```typescript
1683
+ * for await (const chunk of vaif.ai.chatStream({
1684
+ * messages: [{ role: 'user', content: 'Explain this schema' }]
1685
+ * })) {
1686
+ * process.stdout.write(chunk);
1687
+ * }
1688
+ * ```
1689
+ */
1690
+ async *chatStream(request, options = {}) {
1691
+ const config = this.client.getConfig();
1692
+ const url = new URL("/ai/chat/stream", config.apiUrl);
1693
+ const headers = {
1694
+ "Content-Type": "application/json",
1695
+ "X-Project-ID": config.projectId,
1696
+ apikey: config.apiKey
1697
+ };
1698
+ const token = this.client.getAccessToken();
1699
+ if (token) {
1700
+ headers.Authorization = `Bearer ${token}`;
1701
+ }
1702
+ const response = await fetch(url.toString(), {
1703
+ method: "POST",
1704
+ headers,
1705
+ body: JSON.stringify({
1706
+ ...request,
1707
+ model: options.model,
1708
+ temperature: options.temperature,
1709
+ maxTokens: options.maxTokens,
1710
+ stream: true
1711
+ })
1712
+ });
1713
+ if (!response.ok) {
1714
+ throw new Error(`AI chat failed: ${response.statusText}`);
1715
+ }
1716
+ const reader = response.body?.getReader();
1717
+ if (!reader) {
1718
+ throw new Error("No response body");
1719
+ }
1720
+ const decoder = new TextDecoder();
1721
+ try {
1722
+ while (true) {
1723
+ const { done, value } = await reader.read();
1724
+ if (done) break;
1725
+ const chunk = decoder.decode(value, { stream: true });
1726
+ const lines = chunk.split("\n");
1727
+ for (const line of lines) {
1728
+ if (line.startsWith("data: ")) {
1729
+ const data = line.slice(6);
1730
+ if (data === "[DONE]") {
1731
+ return;
1732
+ }
1733
+ try {
1734
+ const parsed = JSON.parse(data);
1735
+ if (parsed.content) {
1736
+ yield parsed.content;
1737
+ }
1738
+ } catch {
1739
+ }
1740
+ }
1741
+ }
1742
+ }
1743
+ } finally {
1744
+ reader.releaseLock();
1745
+ }
1746
+ }
1747
+ /**
1748
+ * Get AI credits information
1749
+ *
1750
+ * @example
1751
+ * ```typescript
1752
+ * const { data, error } = await vaif.ai.getCredits();
1753
+ * console.log(`Balance: ${data.balance} credits`);
1754
+ * ```
1755
+ */
1756
+ async getCredits() {
1757
+ const response = await this.client.request("/ai/credits");
1758
+ if (response.error) {
1759
+ return {
1760
+ data: null,
1761
+ error: {
1762
+ message: response.error.message,
1763
+ code: response.error.code,
1764
+ status: response.status
1765
+ }
1766
+ };
1767
+ }
1768
+ return { data: response.data, error: null };
1769
+ }
1770
+ /**
1771
+ * Explain code or schema
1772
+ */
1773
+ async explain(code, options = {}) {
1774
+ const response = await this.client.request(
1775
+ "/ai/explain",
1776
+ {
1777
+ method: "POST",
1778
+ body: {
1779
+ code,
1780
+ language: options.language,
1781
+ context: options.context,
1782
+ model: options.model,
1783
+ temperature: options.temperature
1784
+ }
1785
+ }
1786
+ );
1787
+ if (response.error) {
1788
+ return {
1789
+ data: null,
1790
+ error: {
1791
+ message: response.error.message,
1792
+ code: response.error.code,
1793
+ status: response.status
1794
+ }
1795
+ };
1796
+ }
1797
+ return { data: response.data, error: null };
1798
+ }
1799
+ /**
1800
+ * Review code for issues and improvements
1801
+ */
1802
+ async review(code, options = {}) {
1803
+ const response = await this.client.request("/ai/review", {
1804
+ method: "POST",
1805
+ body: {
1806
+ code,
1807
+ language: options.language,
1808
+ focus: options.focus,
1809
+ model: options.model,
1810
+ temperature: options.temperature
1811
+ }
1812
+ });
1813
+ if (response.error) {
1814
+ return {
1815
+ data: null,
1816
+ error: {
1817
+ message: response.error.message,
1818
+ code: response.error.code,
1819
+ status: response.status
1820
+ }
1821
+ };
1822
+ }
1823
+ return { data: response.data, error: null };
1824
+ }
1825
+ /**
1826
+ * Generate TypeScript types from schema
1827
+ */
1828
+ async generateTypes(schema, options = {}) {
1829
+ const response = await this.client.request(
1830
+ "/ai/generate/types",
1831
+ {
1832
+ method: "POST",
1833
+ body: {
1834
+ schema,
1835
+ format: options.format ?? "typescript",
1836
+ model: options.model
1837
+ }
1838
+ }
1839
+ );
1840
+ if (response.error) {
1841
+ return {
1842
+ data: null,
1843
+ error: {
1844
+ message: response.error.message,
1845
+ code: response.error.code,
1846
+ status: response.status
1847
+ }
1848
+ };
1849
+ }
1850
+ return { data: response.data, error: null };
1851
+ }
1852
+ /**
1853
+ * Generate SQL migration from schema diff
1854
+ */
1855
+ async generateMigration(options) {
1856
+ const response = await this.client.request("/ai/generate/migration", {
1857
+ method: "POST",
1858
+ body: {
1859
+ currentSchema: options.currentSchema,
1860
+ targetSchema: options.targetSchema,
1861
+ database: options.database ?? "postgresql",
1862
+ model: options.model
1863
+ }
1864
+ });
1865
+ if (response.error) {
1866
+ return {
1867
+ data: null,
1868
+ error: {
1869
+ message: response.error.message,
1870
+ code: response.error.code,
1871
+ status: response.status
1872
+ }
1873
+ };
1874
+ }
1875
+ return { data: response.data, error: null };
1876
+ }
1877
+ };
1878
+
1879
+ // src/lib/typegen.ts
1880
+ var PG_TO_TS_TYPES = {
1881
+ // Numeric
1882
+ smallint: "number",
1883
+ integer: "number",
1884
+ bigint: "number",
1885
+ int2: "number",
1886
+ int4: "number",
1887
+ int8: "number",
1888
+ decimal: "number",
1889
+ numeric: "number",
1890
+ real: "number",
1891
+ "double precision": "number",
1892
+ float4: "number",
1893
+ float8: "number",
1894
+ serial: "number",
1895
+ bigserial: "number",
1896
+ smallserial: "number",
1897
+ money: "number",
1898
+ // String
1899
+ character: "string",
1900
+ "character varying": "string",
1901
+ varchar: "string",
1902
+ char: "string",
1903
+ text: "string",
1904
+ name: "string",
1905
+ citext: "string",
1906
+ // Boolean
1907
+ boolean: "boolean",
1908
+ bool: "boolean",
1909
+ // Date/Time
1910
+ date: "string",
1911
+ time: "string",
1912
+ "time with time zone": "string",
1913
+ "time without time zone": "string",
1914
+ timestamp: "string",
1915
+ "timestamp with time zone": "string",
1916
+ "timestamp without time zone": "string",
1917
+ timestamptz: "string",
1918
+ timetz: "string",
1919
+ interval: "string",
1920
+ // Binary
1921
+ bytea: "string",
1922
+ // UUID
1923
+ uuid: "string",
1924
+ // JSON
1925
+ json: "unknown",
1926
+ jsonb: "unknown",
1927
+ // Arrays (handled separately)
1928
+ "ARRAY": "unknown[]",
1929
+ // Network
1930
+ inet: "string",
1931
+ cidr: "string",
1932
+ macaddr: "string",
1933
+ macaddr8: "string",
1934
+ // Geometric
1935
+ point: "{ x: number; y: number }",
1936
+ line: "string",
1937
+ lseg: "string",
1938
+ box: "string",
1939
+ path: "string",
1940
+ polygon: "string",
1941
+ circle: "string",
1942
+ // Text search
1943
+ tsvector: "string",
1944
+ tsquery: "string",
1945
+ // Other
1946
+ xml: "string",
1947
+ void: "void",
1948
+ unknown: "unknown"
1949
+ };
1950
+ var VaifTypeGen = class {
1951
+ constructor(client) {
1952
+ this.client = client;
1953
+ }
1954
+ /**
1955
+ * Fetch database schema from the API.
1956
+ */
1957
+ async fetchSchema(schema = "public") {
1958
+ const response = await this.client.request(
1959
+ `/database/schema?schema=${schema}`
1960
+ );
1961
+ if (response.error) {
1962
+ throw new Error(response.error.message);
1963
+ }
1964
+ return response.data;
1965
+ }
1966
+ /**
1967
+ * Generate TypeScript types from schema.
1968
+ */
1969
+ async generate(options = {}) {
1970
+ const {
1971
+ includeRow = true,
1972
+ includeInsert = true,
1973
+ includeUpdate = true,
1974
+ includeRelationships = true,
1975
+ schema = "public",
1976
+ tables = [],
1977
+ excludeTables = [],
1978
+ includeComments = true,
1979
+ exportFormat = "named",
1980
+ namespace = "Database"
1981
+ } = options;
1982
+ const dbSchema = await this.fetchSchema(schema);
1983
+ const lines = [];
1984
+ lines.push("/**");
1985
+ lines.push(" * VAIF Studio Database Types");
1986
+ lines.push(` * Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
1987
+ lines.push(" * DO NOT EDIT - This file is auto-generated");
1988
+ lines.push(" */");
1989
+ lines.push("");
1990
+ if (dbSchema.enums.length > 0) {
1991
+ lines.push("// Enums");
1992
+ for (const enumDef of dbSchema.enums) {
1993
+ lines.push(...this.generateEnum(enumDef, includeComments));
1994
+ lines.push("");
1995
+ }
1996
+ }
1997
+ let filteredTables = dbSchema.tables.filter((t) => {
1998
+ if (tables.length > 0 && !tables.includes(t.name)) return false;
1999
+ if (excludeTables.includes(t.name)) return false;
2000
+ return true;
2001
+ });
2002
+ const tableTypes = [];
2003
+ for (const table of filteredTables) {
2004
+ const types = this.generateTableTypes(table, {
2005
+ includeRow,
2006
+ includeInsert,
2007
+ includeUpdate,
2008
+ includeRelationships,
2009
+ includeComments,
2010
+ allTables: dbSchema.tables
2011
+ });
2012
+ tableTypes.push(types);
2013
+ }
2014
+ for (const view of dbSchema.views) {
2015
+ if (tables.length > 0 && !tables.includes(view.name)) continue;
2016
+ if (excludeTables.includes(view.name)) continue;
2017
+ const types = this.generateViewTypes(view, includeComments);
2018
+ tableTypes.push(types);
2019
+ }
2020
+ if (exportFormat === "namespace") {
2021
+ lines.push(`export namespace ${namespace} {`);
2022
+ for (const types of tableTypes) {
2023
+ lines.push(this.indent(types, 2));
2024
+ }
2025
+ lines.push("}");
2026
+ } else {
2027
+ lines.push(...tableTypes);
2028
+ }
2029
+ lines.push("");
2030
+ lines.push("// Database type helper");
2031
+ lines.push("export interface Database {");
2032
+ lines.push(" public: {");
2033
+ lines.push(" Tables: {");
2034
+ for (const table of filteredTables) {
2035
+ const pascalName = this.toPascalCase(table.name);
2036
+ lines.push(` ${table.name}: {`);
2037
+ if (includeRow) lines.push(` Row: ${pascalName}Row;`);
2038
+ if (includeInsert) lines.push(` Insert: ${pascalName}Insert;`);
2039
+ if (includeUpdate) lines.push(` Update: ${pascalName}Update;`);
2040
+ lines.push(" };");
2041
+ }
2042
+ lines.push(" };");
2043
+ lines.push(" Views: {");
2044
+ for (const view of dbSchema.views) {
2045
+ const pascalName = this.toPascalCase(view.name);
2046
+ lines.push(` ${view.name}: {`);
2047
+ lines.push(` Row: ${pascalName}Row;`);
2048
+ lines.push(" };");
2049
+ }
2050
+ lines.push(" };");
2051
+ lines.push(" Enums: {");
2052
+ for (const enumDef of dbSchema.enums) {
2053
+ lines.push(` ${enumDef.name}: ${this.toPascalCase(enumDef.name)};`);
2054
+ }
2055
+ lines.push(" };");
2056
+ lines.push(" };");
2057
+ lines.push("}");
2058
+ return lines.join("\n");
2059
+ }
2060
+ /**
2061
+ * Generate TypeScript from schema JSON directly.
2062
+ */
2063
+ generateFromSchema(schema, options = {}) {
2064
+ const {
2065
+ includeRow = true,
2066
+ includeInsert = true,
2067
+ includeUpdate = true,
2068
+ includeRelationships = true,
2069
+ tables = [],
2070
+ excludeTables = [],
2071
+ includeComments = true,
2072
+ exportFormat = "named",
2073
+ namespace = "Database"
2074
+ } = options;
2075
+ const lines = [];
2076
+ lines.push("/**");
2077
+ lines.push(" * VAIF Studio Database Types");
2078
+ lines.push(` * Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
2079
+ lines.push(" * DO NOT EDIT - This file is auto-generated");
2080
+ lines.push(" */");
2081
+ lines.push("");
2082
+ if (schema.enums && schema.enums.length > 0) {
2083
+ lines.push("// Enums");
2084
+ for (const enumDef of schema.enums) {
2085
+ lines.push(...this.generateEnum(enumDef, includeComments));
2086
+ lines.push("");
2087
+ }
2088
+ }
2089
+ let filteredTables = (schema.tables || []).filter((t) => {
2090
+ if (tables.length > 0 && !tables.includes(t.name)) return false;
2091
+ if (excludeTables.includes(t.name)) return false;
2092
+ return true;
2093
+ });
2094
+ const tableTypes = [];
2095
+ for (const table of filteredTables) {
2096
+ const types = this.generateTableTypes(table, {
2097
+ includeRow,
2098
+ includeInsert,
2099
+ includeUpdate,
2100
+ includeRelationships,
2101
+ includeComments,
2102
+ allTables: schema.tables
2103
+ });
2104
+ tableTypes.push(types);
2105
+ }
2106
+ if (exportFormat === "namespace") {
2107
+ lines.push(`export namespace ${namespace} {`);
2108
+ for (const types of tableTypes) {
2109
+ lines.push(this.indent(types, 2));
2110
+ }
2111
+ lines.push("}");
2112
+ } else {
2113
+ lines.push(...tableTypes);
2114
+ }
2115
+ return lines.join("\n");
2116
+ }
2117
+ generateEnum(enumDef, includeComments) {
2118
+ const lines = [];
2119
+ const name = this.toPascalCase(enumDef.name);
2120
+ if (includeComments) {
2121
+ lines.push(`/** Enum: ${enumDef.name} */`);
2122
+ }
2123
+ lines.push(`export type ${name} = ${enumDef.values.map((v) => `'${v}'`).join(" | ")};`);
2124
+ return lines;
2125
+ }
2126
+ generateTableTypes(table, options) {
2127
+ const lines = [];
2128
+ const pascalName = this.toPascalCase(table.name);
2129
+ if (options.includeRow) {
2130
+ if (options.includeComments && table.comment) {
2131
+ lines.push(`/** ${table.comment} */`);
2132
+ }
2133
+ lines.push(`export interface ${pascalName}Row {`);
2134
+ for (const col of table.columns) {
2135
+ const tsType = this.pgTypeToTs(col.type);
2136
+ const nullable = col.nullable ? " | null" : "";
2137
+ if (options.includeComments && col.comment) {
2138
+ lines.push(` /** ${col.comment} */`);
2139
+ }
2140
+ lines.push(` ${col.name}: ${tsType}${nullable};`);
2141
+ }
2142
+ if (options.includeRelationships) {
2143
+ for (const fk of table.foreignKeys) {
2144
+ const relatedTable = options.allTables.find(
2145
+ (t) => t.name === fk.foreignTable
2146
+ );
2147
+ if (relatedTable) {
2148
+ const relatedPascal = this.toPascalCase(fk.foreignTable);
2149
+ lines.push(` ${fk.foreignTable}?: ${relatedPascal}Row;`);
2150
+ }
2151
+ }
2152
+ }
2153
+ lines.push("}");
2154
+ lines.push("");
2155
+ }
2156
+ if (options.includeInsert) {
2157
+ lines.push(`export interface ${pascalName}Insert {`);
2158
+ for (const col of table.columns) {
2159
+ const tsType = this.pgTypeToTs(col.type);
2160
+ const optional = col.nullable || col.defaultValue !== void 0 || col.isPrimaryKey ? "?" : "";
2161
+ const nullable = col.nullable ? " | null" : "";
2162
+ lines.push(` ${col.name}${optional}: ${tsType}${nullable};`);
2163
+ }
2164
+ lines.push("}");
2165
+ lines.push("");
2166
+ }
2167
+ if (options.includeUpdate) {
2168
+ lines.push(`export interface ${pascalName}Update {`);
2169
+ for (const col of table.columns) {
2170
+ if (col.isPrimaryKey) continue;
2171
+ const tsType = this.pgTypeToTs(col.type);
2172
+ const nullable = col.nullable ? " | null" : "";
2173
+ lines.push(` ${col.name}?: ${tsType}${nullable};`);
2174
+ }
2175
+ lines.push("}");
2176
+ lines.push("");
2177
+ }
2178
+ return lines.join("\n");
2179
+ }
2180
+ generateViewTypes(view, includeComments) {
2181
+ const lines = [];
2182
+ const pascalName = this.toPascalCase(view.name);
2183
+ if (includeComments) {
2184
+ lines.push(`/** View: ${view.name} */`);
2185
+ }
2186
+ lines.push(`export interface ${pascalName}Row {`);
2187
+ for (const col of view.columns) {
2188
+ const tsType = this.pgTypeToTs(col.type);
2189
+ const nullable = col.nullable ? " | null" : "";
2190
+ lines.push(` ${col.name}: ${tsType}${nullable};`);
2191
+ }
2192
+ lines.push("}");
2193
+ lines.push("");
2194
+ return lines.join("\n");
2195
+ }
2196
+ pgTypeToTs(pgType) {
2197
+ if (pgType.endsWith("[]")) {
2198
+ const baseType = pgType.slice(0, -2);
2199
+ const tsBaseType = PG_TO_TS_TYPES[baseType.toLowerCase()] || "unknown";
2200
+ return `${tsBaseType}[]`;
2201
+ }
2202
+ const arrayMatch = pgType.match(/^(.+)\s+ARRAY$/i);
2203
+ if (arrayMatch) {
2204
+ const baseType = arrayMatch[1];
2205
+ const tsBaseType = PG_TO_TS_TYPES[baseType.toLowerCase()] || "unknown";
2206
+ return `${tsBaseType}[]`;
2207
+ }
2208
+ const precisionMatch = pgType.match(/^(\w+)\(/);
2209
+ if (precisionMatch) {
2210
+ const baseType = precisionMatch[1];
2211
+ return PG_TO_TS_TYPES[baseType.toLowerCase()] || "unknown";
2212
+ }
2213
+ return PG_TO_TS_TYPES[pgType.toLowerCase()] || "unknown";
2214
+ }
2215
+ toPascalCase(str) {
2216
+ return str.split(/[_\s-]+/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
2217
+ }
2218
+ indent(text, spaces) {
2219
+ const pad = " ".repeat(spaces);
2220
+ return text.split("\n").map((line) => line ? pad + line : line).join("\n");
2221
+ }
2222
+ };
2223
+ function createTypeGen(client) {
2224
+ return new VaifTypeGen(client);
2225
+ }
2226
+
2227
+ // src/lib/client.ts
2228
+ var DEFAULT_API_URL = "https://api.vaif.studio";
2229
+ var DEFAULT_REALTIME_URL = "wss://realtime.vaif.studio";
2230
+ var DEFAULT_TIMEOUT = 3e4;
2231
+ var memoryStorage = {
2232
+ data: /* @__PURE__ */ new Map(),
2233
+ getItem(key) {
2234
+ return this.data.get(key) ?? null;
2235
+ },
2236
+ setItem(key, value) {
2237
+ this.data.set(key, value);
2238
+ },
2239
+ removeItem(key) {
2240
+ this.data.delete(key);
2241
+ }
2242
+ };
2243
+ var browserStorage = typeof window !== "undefined" && window.localStorage ? {
2244
+ getItem: (key) => window.localStorage.getItem(key),
2245
+ setItem: (key, value) => window.localStorage.setItem(key, value),
2246
+ removeItem: (key) => window.localStorage.removeItem(key)
2247
+ } : null;
2248
+ function createClient(options) {
2249
+ return new VaifClient(options);
2250
+ }
2251
+ var VaifClient = class {
2252
+ constructor(options) {
2253
+ this.accessToken = null;
2254
+ if (!options.projectId) {
2255
+ throw new Error("projectId is required");
2256
+ }
2257
+ if (!options.apiKey) {
2258
+ throw new Error("apiKey is required");
2259
+ }
2260
+ this.config = {
2261
+ projectId: options.projectId,
2262
+ apiKey: options.apiKey,
2263
+ apiUrl: options.apiUrl ?? DEFAULT_API_URL,
2264
+ realtimeUrl: options.realtimeUrl ?? DEFAULT_REALTIME_URL,
2265
+ timeout: options.timeout ?? DEFAULT_TIMEOUT,
2266
+ headers: options.headers ?? {},
2267
+ debug: options.debug ?? false,
2268
+ autoRefreshToken: options.autoRefreshToken ?? true,
2269
+ persistSession: options.persistSession ?? true,
2270
+ storage: options.storage ?? browserStorage ?? memoryStorage
2271
+ };
2272
+ this.db = new VaifDatabase(this);
2273
+ this.realtime = new VaifRealtime(this);
2274
+ this.storage = new VaifStorage(this);
2275
+ this.functions = new VaifFunctions(this);
2276
+ this.auth = new VaifAuth(this);
2277
+ this.ai = new VaifAI(this);
2278
+ this.typegen = new VaifTypeGen(this);
2279
+ this.restoreSession();
2280
+ }
2281
+ /**
2282
+ * Get current configuration
2283
+ */
2284
+ getConfig() {
2285
+ return this.config;
2286
+ }
2287
+ /**
2288
+ * Set access token for authenticated requests
2289
+ */
2290
+ setAccessToken(token) {
2291
+ this.accessToken = token;
2292
+ }
2293
+ /**
2294
+ * Get current access token
2295
+ */
2296
+ getAccessToken() {
2297
+ return this.accessToken;
2298
+ }
2299
+ /**
2300
+ * Make an authenticated API request
2301
+ */
2302
+ async request(path, options = {}) {
2303
+ const url = new URL(path, this.config.apiUrl);
2304
+ if (options.params) {
2305
+ Object.entries(options.params).forEach(([key, value]) => {
2306
+ if (value !== void 0) {
2307
+ url.searchParams.set(key, String(value));
2308
+ }
2309
+ });
2310
+ }
2311
+ const headers = {
2312
+ "Content-Type": "application/json",
2313
+ "X-Project-ID": this.config.projectId,
2314
+ apikey: this.config.apiKey,
2315
+ ...this.config.headers,
2316
+ ...options.headers
2317
+ };
2318
+ if (this.accessToken) {
2319
+ headers.Authorization = `Bearer ${this.accessToken}`;
2320
+ }
2321
+ const fetchOptions = {
2322
+ method: options.method ?? "GET",
2323
+ headers,
2324
+ signal: options.signal
2325
+ };
2326
+ if (options.body && options.method !== "GET") {
2327
+ fetchOptions.body = JSON.stringify(options.body);
2328
+ }
2329
+ const timeoutMs = options.timeout ?? this.config.timeout;
2330
+ const timeoutController = new AbortController();
2331
+ const timeoutId = setTimeout(() => timeoutController.abort(), timeoutMs);
2332
+ if (options.signal) {
2333
+ options.signal.addEventListener("abort", () => timeoutController.abort());
2334
+ }
2335
+ fetchOptions.signal = timeoutController.signal;
2336
+ try {
2337
+ if (this.config.debug) {
2338
+ console.log(`[VAIF] ${options.method ?? "GET"} ${url.toString()}`);
2339
+ }
2340
+ const response = await fetch(url.toString(), fetchOptions);
2341
+ clearTimeout(timeoutId);
2342
+ const contentType = response.headers.get("content-type");
2343
+ let data = null;
2344
+ let error = null;
2345
+ if (contentType?.includes("application/json")) {
2346
+ const json = await response.json();
2347
+ if (response.ok) {
2348
+ data = json;
2349
+ } else {
2350
+ error = {
2351
+ message: json.message ?? json.error ?? "Request failed",
2352
+ code: json.code,
2353
+ status: response.status,
2354
+ details: json.details,
2355
+ requestId: response.headers.get("x-request-id") ?? void 0
2356
+ };
2357
+ }
2358
+ } else if (!response.ok) {
2359
+ error = {
2360
+ message: `Request failed with status ${response.status}`,
2361
+ status: response.status
2362
+ };
2363
+ }
2364
+ const responseHeaders = {};
2365
+ response.headers.forEach((value, key) => {
2366
+ responseHeaders[key] = value;
2367
+ });
2368
+ return {
2369
+ data,
2370
+ error,
2371
+ status: response.status,
2372
+ headers: responseHeaders
2373
+ };
2374
+ } catch (err) {
2375
+ clearTimeout(timeoutId);
2376
+ const error = {
2377
+ message: err instanceof Error ? err.name === "AbortError" ? "Request timed out" : err.message : "Unknown error",
2378
+ code: err instanceof Error && err.name === "AbortError" ? "TIMEOUT" : "NETWORK_ERROR"
2379
+ };
2380
+ return {
2381
+ data: null,
2382
+ error,
2383
+ status: 0,
2384
+ headers: {}
2385
+ };
2386
+ }
2387
+ }
2388
+ /**
2389
+ * Restore session from storage
2390
+ */
2391
+ async restoreSession() {
2392
+ if (!this.config.persistSession) return;
2393
+ try {
2394
+ const sessionStr = await this.config.storage.getItem(
2395
+ `vaif-session-${this.config.projectId}`
2396
+ );
2397
+ if (sessionStr) {
2398
+ const session = JSON.parse(sessionStr);
2399
+ if (session.accessToken && session.expiresAt > Date.now()) {
2400
+ this.accessToken = session.accessToken;
2401
+ }
2402
+ }
2403
+ } catch {
2404
+ }
2405
+ }
2406
+ /**
2407
+ * Log debug messages
2408
+ */
2409
+ debug(...args) {
2410
+ if (this.config.debug) {
2411
+ console.log("[VAIF]", ...args);
2412
+ }
2413
+ }
2414
+ };
2415
+ // Annotate the CommonJS export names for ESM import in node:
2416
+ 0 && (module.exports = {
2417
+ RealtimeChannel,
2418
+ VaifAI,
2419
+ VaifAuth,
2420
+ VaifClient,
2421
+ VaifDatabase,
2422
+ VaifFunctions,
2423
+ VaifRealtime,
2424
+ VaifStorage,
2425
+ VaifTypeGen,
2426
+ createClient,
2427
+ createTypeGen
2428
+ });