aurabase-js 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.mjs ADDED
@@ -0,0 +1,741 @@
1
+ // src/QueryBuilder.ts
2
+ var QueryBuilder = class {
3
+ constructor(url, anonKey, accessToken, tableName, headers = {}) {
4
+ this.isSingle = false;
5
+ this.url = url;
6
+ this.anonKey = anonKey;
7
+ this.accessToken = accessToken;
8
+ this.tableName = tableName;
9
+ this.queryParams = new URLSearchParams();
10
+ this.headers = { ...headers };
11
+ }
12
+ /**
13
+ * Select columns
14
+ * @example
15
+ * .select('id, name, email')
16
+ * .select('*')
17
+ */
18
+ select(columns = "*") {
19
+ this.queryParams.set("select", columns);
20
+ return this;
21
+ }
22
+ /**
23
+ * Filter by column equality
24
+ * @example
25
+ * .eq('id', 1)
26
+ * .eq('status', 'active')
27
+ */
28
+ eq(column, value) {
29
+ this.queryParams.append(column, `eq.${value}`);
30
+ return this;
31
+ }
32
+ /**
33
+ * Filter by column inequality
34
+ */
35
+ neq(column, value) {
36
+ this.queryParams.append(column, `neq.${value}`);
37
+ return this;
38
+ }
39
+ /**
40
+ * Filter by greater than
41
+ */
42
+ gt(column, value) {
43
+ this.queryParams.append(column, `gt.${value}`);
44
+ return this;
45
+ }
46
+ /**
47
+ * Filter by greater than or equal
48
+ */
49
+ gte(column, value) {
50
+ this.queryParams.append(column, `gte.${value}`);
51
+ return this;
52
+ }
53
+ /**
54
+ * Filter by less than
55
+ */
56
+ lt(column, value) {
57
+ this.queryParams.append(column, `lt.${value}`);
58
+ return this;
59
+ }
60
+ /**
61
+ * Filter by less than or equal
62
+ */
63
+ lte(column, value) {
64
+ this.queryParams.append(column, `lte.${value}`);
65
+ return this;
66
+ }
67
+ /**
68
+ * Filter by like pattern
69
+ */
70
+ like(column, pattern) {
71
+ this.queryParams.append(column, `like.${pattern}`);
72
+ return this;
73
+ }
74
+ /**
75
+ * Filter by case-insensitive like pattern
76
+ */
77
+ ilike(column, pattern) {
78
+ this.queryParams.append(column, `ilike.${pattern}`);
79
+ return this;
80
+ }
81
+ /**
82
+ * Filter by array contains
83
+ */
84
+ contains(column, value) {
85
+ this.queryParams.append(column, `cs.{${value.join(",")}}`);
86
+ return this;
87
+ }
88
+ /**
89
+ * Filter by value in array
90
+ */
91
+ in(column, values) {
92
+ this.queryParams.append(column, `in.(${values.join(",")})`);
93
+ return this;
94
+ }
95
+ /**
96
+ * Filter for null values
97
+ */
98
+ isNull(column) {
99
+ this.queryParams.append(column, "is.null");
100
+ return this;
101
+ }
102
+ /**
103
+ * Filter for non-null values
104
+ */
105
+ isNotNull(column) {
106
+ this.queryParams.append(column, "is.not.null");
107
+ return this;
108
+ }
109
+ /**
110
+ * Order results
111
+ * @example
112
+ * .order('created_at', { ascending: false })
113
+ */
114
+ order(column, options = {}) {
115
+ const { ascending = true, nullsFirst } = options;
116
+ let orderStr = column;
117
+ if (!ascending) orderStr += ".desc";
118
+ if (nullsFirst !== void 0) {
119
+ orderStr += nullsFirst ? ".nullsfirst" : ".nullslast";
120
+ }
121
+ this.queryParams.append("order", orderStr);
122
+ return this;
123
+ }
124
+ /**
125
+ * Limit results
126
+ */
127
+ limit(count) {
128
+ this.queryParams.set("limit", String(count));
129
+ return this;
130
+ }
131
+ /**
132
+ * Offset results
133
+ */
134
+ offset(count) {
135
+ this.queryParams.set("offset", String(count));
136
+ return this;
137
+ }
138
+ /**
139
+ * Range of results (offset + limit)
140
+ */
141
+ range(from, to) {
142
+ this.queryParams.set("offset", String(from));
143
+ this.queryParams.set("limit", String(to - from + 1));
144
+ return this;
145
+ }
146
+ /**
147
+ * Return single result
148
+ */
149
+ single() {
150
+ this.isSingle = true;
151
+ return this;
152
+ }
153
+ /**
154
+ * Return single result or null
155
+ */
156
+ maybeSingle() {
157
+ this.isSingle = true;
158
+ return this;
159
+ }
160
+ getHeaders() {
161
+ const token = this.accessToken || this.anonKey;
162
+ return {
163
+ "Content-Type": "application/json",
164
+ "apikey": this.anonKey,
165
+ "Authorization": `Bearer ${token}`,
166
+ ...this.headers
167
+ };
168
+ }
169
+ async request(method, body) {
170
+ const queryString = this.queryParams.toString();
171
+ const fullUrl = `${this.url}/rest/v1/${this.tableName}${queryString ? `?${queryString}` : ""}`;
172
+ const options = {
173
+ method,
174
+ headers: this.getHeaders()
175
+ };
176
+ if (body && method !== "GET") {
177
+ options.body = JSON.stringify(body);
178
+ options.headers["Prefer"] = "return=representation";
179
+ }
180
+ try {
181
+ const response = await fetch(fullUrl, options);
182
+ let data = null;
183
+ let error = null;
184
+ const text = await response.text();
185
+ if (text) {
186
+ try {
187
+ const parsed = JSON.parse(text);
188
+ if (!response.ok) {
189
+ error = {
190
+ message: parsed.message || parsed.error || `HTTP ${response.status}`,
191
+ code: parsed.code,
192
+ details: parsed.details
193
+ };
194
+ } else {
195
+ data = this.isSingle && Array.isArray(parsed) ? parsed[0] ?? null : parsed;
196
+ }
197
+ } catch {
198
+ if (!response.ok) {
199
+ error = {
200
+ message: text || `HTTP ${response.status}`
201
+ };
202
+ }
203
+ }
204
+ }
205
+ return {
206
+ data,
207
+ error,
208
+ status: response.status,
209
+ statusText: response.statusText
210
+ };
211
+ } catch (err) {
212
+ return {
213
+ data: null,
214
+ error: {
215
+ message: err instanceof Error ? err.message : "Network error"
216
+ },
217
+ status: 0,
218
+ statusText: "Network Error"
219
+ };
220
+ }
221
+ }
222
+ /**
223
+ * Execute SELECT query
224
+ */
225
+ async then(resolve, reject) {
226
+ try {
227
+ const result = await this.request("GET");
228
+ await resolve(result);
229
+ } catch (err) {
230
+ if (reject) {
231
+ await reject(err);
232
+ }
233
+ }
234
+ }
235
+ /**
236
+ * Insert row(s)
237
+ */
238
+ async insert(row) {
239
+ const rows = Array.isArray(row) ? row : [row];
240
+ return this.request("POST", rows.length === 1 ? rows[0] : rows);
241
+ }
242
+ /**
243
+ * Update row(s)
244
+ */
245
+ async update(data) {
246
+ return this.request("PATCH", data);
247
+ }
248
+ /**
249
+ * Upsert row(s)
250
+ */
251
+ async upsert(row) {
252
+ const rows = Array.isArray(row) ? row : [row];
253
+ this.headers["Prefer"] = "resolution=merge-duplicates,return=representation";
254
+ return this.request("POST", rows.length === 1 ? rows[0] : rows);
255
+ }
256
+ /**
257
+ * Delete row(s)
258
+ */
259
+ async delete() {
260
+ return this.request("DELETE");
261
+ }
262
+ };
263
+
264
+ // src/Auth.ts
265
+ var AuthClient = class {
266
+ constructor(url, anonKey) {
267
+ this.listeners = /* @__PURE__ */ new Map();
268
+ this.currentSession = null;
269
+ this.storageKey = "aurabase_session";
270
+ this.url = url;
271
+ this.anonKey = anonKey;
272
+ this.loadSession();
273
+ }
274
+ loadSession() {
275
+ if (typeof window === "undefined") return;
276
+ try {
277
+ const stored = localStorage.getItem(this.storageKey);
278
+ if (stored) {
279
+ this.currentSession = JSON.parse(stored);
280
+ }
281
+ } catch {
282
+ }
283
+ }
284
+ saveSession(session) {
285
+ this.currentSession = session;
286
+ if (typeof window === "undefined") return;
287
+ if (session) {
288
+ localStorage.setItem(this.storageKey, JSON.stringify(session));
289
+ } else {
290
+ localStorage.removeItem(this.storageKey);
291
+ }
292
+ }
293
+ getHeaders() {
294
+ return {
295
+ "Content-Type": "application/json",
296
+ "apikey": this.anonKey
297
+ };
298
+ }
299
+ emitEvent(event, session) {
300
+ const listeners = this.listeners.get(event) || [];
301
+ listeners.forEach((listener) => listener(event, session));
302
+ const allListeners = this.listeners.get("*") || [];
303
+ allListeners.forEach((listener) => listener(event, session));
304
+ }
305
+ /**
306
+ * Sign in with email and password
307
+ */
308
+ async signInWithPassword(credentials) {
309
+ try {
310
+ const response = await fetch(`${this.url}/auth/login`, {
311
+ method: "POST",
312
+ headers: this.getHeaders(),
313
+ body: JSON.stringify(credentials)
314
+ });
315
+ const data = await response.json();
316
+ if (!response.ok) {
317
+ const errorMsg = typeof data.detail === "string" ? data.detail : Array.isArray(data.detail) ? data.detail.map((e) => e.msg).join(", ") : data.error || "Login failed";
318
+ return {
319
+ data: null,
320
+ error: { error: errorMsg }
321
+ };
322
+ }
323
+ const session = {
324
+ access_token: data.access_token,
325
+ token_type: "bearer",
326
+ user: data.user
327
+ };
328
+ this.saveSession(session);
329
+ this.emitEvent("SIGNED_IN", session);
330
+ return {
331
+ data: { user: data.user, session },
332
+ error: null
333
+ };
334
+ } catch (err) {
335
+ return {
336
+ data: null,
337
+ error: { error: err instanceof Error ? err.message : "Network error" }
338
+ };
339
+ }
340
+ }
341
+ /**
342
+ * Sign up with email and password
343
+ */
344
+ async signUp(credentials) {
345
+ try {
346
+ const response = await fetch(`${this.url}/auth/register`, {
347
+ method: "POST",
348
+ headers: this.getHeaders(),
349
+ body: JSON.stringify(credentials)
350
+ });
351
+ const data = await response.json();
352
+ if (!response.ok) {
353
+ return {
354
+ data: null,
355
+ error: { error: data.detail || data.error || "Registration failed" }
356
+ };
357
+ }
358
+ return {
359
+ data: { user: data.user || data },
360
+ error: null
361
+ };
362
+ } catch (err) {
363
+ return {
364
+ data: null,
365
+ error: { error: err instanceof Error ? err.message : "Network error" }
366
+ };
367
+ }
368
+ }
369
+ /**
370
+ * Sign out
371
+ */
372
+ async signOut() {
373
+ try {
374
+ const token = this.currentSession?.access_token;
375
+ if (token) {
376
+ await fetch(`${this.url}/auth/logout`, {
377
+ method: "POST",
378
+ headers: {
379
+ ...this.getHeaders(),
380
+ "Authorization": `Bearer ${token}`
381
+ }
382
+ });
383
+ }
384
+ this.saveSession(null);
385
+ this.emitEvent("SIGNED_OUT", null);
386
+ return { error: null };
387
+ } catch (err) {
388
+ return { error: { error: err instanceof Error ? err.message : "Network error" } };
389
+ }
390
+ }
391
+ /**
392
+ * Get current session
393
+ */
394
+ getSession() {
395
+ return { data: { session: this.currentSession } };
396
+ }
397
+ /**
398
+ * Get current user
399
+ */
400
+ async getUser() {
401
+ const token = this.currentSession?.access_token;
402
+ if (!token) {
403
+ return { data: { user: null }, error: null };
404
+ }
405
+ try {
406
+ const response = await fetch(`${this.url}/auth/me`, {
407
+ headers: {
408
+ ...this.getHeaders(),
409
+ "Authorization": `Bearer ${token}`
410
+ }
411
+ });
412
+ if (!response.ok) {
413
+ return {
414
+ data: { user: null },
415
+ error: { error: "Failed to get user" }
416
+ };
417
+ }
418
+ const user = await response.json();
419
+ return { data: { user }, error: null };
420
+ } catch (err) {
421
+ return {
422
+ data: { user: null },
423
+ error: { error: err instanceof Error ? err.message : "Network error" }
424
+ };
425
+ }
426
+ }
427
+ /**
428
+ * Update user
429
+ */
430
+ async updateUser(attributes) {
431
+ const token = this.currentSession?.access_token;
432
+ if (!token) {
433
+ return {
434
+ data: null,
435
+ error: { error: "Not authenticated" }
436
+ };
437
+ }
438
+ try {
439
+ const response = await fetch(`${this.url}/auth/me`, {
440
+ method: "PATCH",
441
+ headers: {
442
+ ...this.getHeaders(),
443
+ "Authorization": `Bearer ${token}`
444
+ },
445
+ body: JSON.stringify(attributes)
446
+ });
447
+ const data = await response.json();
448
+ if (!response.ok) {
449
+ return {
450
+ data: null,
451
+ error: { error: data.detail || data.error || "Update failed" }
452
+ };
453
+ }
454
+ if (this.currentSession) {
455
+ this.currentSession.user = data;
456
+ this.saveSession(this.currentSession);
457
+ }
458
+ this.emitEvent("USER_UPDATED", this.currentSession);
459
+ return { data: { user: data }, error: null };
460
+ } catch (err) {
461
+ return {
462
+ data: null,
463
+ error: { error: err instanceof Error ? err.message : "Network error" }
464
+ };
465
+ }
466
+ }
467
+ /**
468
+ * Request password reset
469
+ */
470
+ async resetPasswordForEmail(email) {
471
+ try {
472
+ const response = await fetch(`${this.url}/auth/find-password`, {
473
+ method: "POST",
474
+ headers: this.getHeaders(),
475
+ body: JSON.stringify({ email })
476
+ });
477
+ const data = await response.json();
478
+ if (!response.ok) {
479
+ return { error: { error: data.detail || data.error || "Request failed" } };
480
+ }
481
+ return { error: null };
482
+ } catch (err) {
483
+ return { error: { error: err instanceof Error ? err.message : "Network error" } };
484
+ }
485
+ }
486
+ /**
487
+ * Subscribe to auth state changes
488
+ */
489
+ onAuthStateChange(callback) {
490
+ const addListener = (event) => {
491
+ if (!this.listeners.has(event)) {
492
+ this.listeners.set(event, []);
493
+ }
494
+ this.listeners.get(event).push(callback);
495
+ };
496
+ addListener("SIGNED_IN");
497
+ addListener("SIGNED_OUT");
498
+ addListener("TOKEN_REFRESHED");
499
+ addListener("USER_UPDATED");
500
+ return {
501
+ data: {
502
+ subscription: {
503
+ unsubscribe: () => {
504
+ this.listeners.forEach((listeners) => {
505
+ const index = listeners.indexOf(callback);
506
+ if (index > -1) {
507
+ listeners.splice(index, 1);
508
+ }
509
+ });
510
+ }
511
+ }
512
+ }
513
+ };
514
+ }
515
+ };
516
+
517
+ // src/AuraBaseClient.ts
518
+ var AuraBaseClient = class {
519
+ constructor(options) {
520
+ this.accessToken = null;
521
+ this.url = options.url;
522
+ this.anonKey = options.anonKey;
523
+ this.customHeaders = options.headers || {};
524
+ this.auth = new AuthClient(this.url, this.anonKey);
525
+ }
526
+ /**
527
+ * Set access token for authenticated requests
528
+ */
529
+ setAccessToken(token) {
530
+ this.accessToken = token;
531
+ }
532
+ /**
533
+ * Get the current access token
534
+ */
535
+ getAccessToken() {
536
+ return this.accessToken;
537
+ }
538
+ /**
539
+ * Query a table
540
+ * @example
541
+ * const { data, error } = await client.from('todos').select('*')
542
+ */
543
+ from(tableName) {
544
+ return new QueryBuilder(
545
+ this.url,
546
+ this.anonKey,
547
+ this.accessToken,
548
+ tableName,
549
+ this.customHeaders
550
+ );
551
+ }
552
+ /**
553
+ * Execute raw SQL (RPC function call)
554
+ * @example
555
+ * const { data, error } = await client.rpc('my_function', { arg1: 'value' })
556
+ */
557
+ async rpc(functionName, params) {
558
+ const token = this.accessToken || this.anonKey;
559
+ try {
560
+ const response = await fetch(`${this.url}/rpc/v1/${functionName}`, {
561
+ method: "POST",
562
+ headers: {
563
+ "Content-Type": "application/json",
564
+ "apikey": this.anonKey,
565
+ "Authorization": `Bearer ${token}`,
566
+ ...this.customHeaders
567
+ },
568
+ body: JSON.stringify(params || {})
569
+ });
570
+ const text = await response.text();
571
+ let data = null;
572
+ let error = null;
573
+ if (text) {
574
+ try {
575
+ const parsed = JSON.parse(text);
576
+ if (!response.ok) {
577
+ error = { message: parsed.message || parsed.error || `HTTP ${response.status}` };
578
+ } else {
579
+ data = parsed;
580
+ }
581
+ } catch {
582
+ if (!response.ok) {
583
+ error = { message: text || `HTTP ${response.status}` };
584
+ }
585
+ }
586
+ }
587
+ return { data, error };
588
+ } catch (err) {
589
+ return {
590
+ data: null,
591
+ error: { message: err instanceof Error ? err.message : "Network error" }
592
+ };
593
+ }
594
+ }
595
+ /**
596
+ * Get storage client (if available)
597
+ */
598
+ get storage() {
599
+ return {
600
+ from: (bucket) => new StorageBucket(this.url, this.anonKey, bucket, this.accessToken)
601
+ };
602
+ }
603
+ };
604
+ var StorageBucket = class {
605
+ constructor(url, anonKey, bucket, accessToken) {
606
+ this.url = url;
607
+ this.anonKey = anonKey;
608
+ this.bucket = bucket;
609
+ this.accessToken = accessToken;
610
+ }
611
+ getHeaders() {
612
+ const token = this.accessToken || this.anonKey;
613
+ return {
614
+ "apikey": this.anonKey,
615
+ "Authorization": `Bearer ${token}`
616
+ };
617
+ }
618
+ /**
619
+ * Upload a file
620
+ */
621
+ async upload(path, file, options) {
622
+ const formData = new FormData();
623
+ formData.append("file", file);
624
+ const headers = this.getHeaders();
625
+ if (options?.contentType) {
626
+ headers["Content-Type"] = options.contentType;
627
+ }
628
+ try {
629
+ const response = await fetch(
630
+ `${this.url}/storage/v1/object/${this.bucket}/${path}`,
631
+ {
632
+ method: "POST",
633
+ headers,
634
+ body: formData
635
+ }
636
+ );
637
+ const data = await response.json();
638
+ if (!response.ok) {
639
+ return {
640
+ data: null,
641
+ error: { message: data.message || data.error || "Upload failed" }
642
+ };
643
+ }
644
+ return { data: { path: data.path || path }, error: null };
645
+ } catch (err) {
646
+ return {
647
+ data: null,
648
+ error: { message: err instanceof Error ? err.message : "Network error" }
649
+ };
650
+ }
651
+ }
652
+ /**
653
+ * Download a file
654
+ */
655
+ async download(path) {
656
+ try {
657
+ const response = await fetch(
658
+ `${this.url}/storage/v1/object/${this.bucket}/${path}`,
659
+ {
660
+ headers: this.getHeaders()
661
+ }
662
+ );
663
+ if (!response.ok) {
664
+ const data = await response.json();
665
+ return {
666
+ data: null,
667
+ error: { message: data.message || data.error || "Download failed" }
668
+ };
669
+ }
670
+ return { data: await response.blob(), error: null };
671
+ } catch (err) {
672
+ return {
673
+ data: null,
674
+ error: { message: err instanceof Error ? err.message : "Network error" }
675
+ };
676
+ }
677
+ }
678
+ /**
679
+ * Get public URL for a file
680
+ */
681
+ getPublicUrl(path) {
682
+ return `${this.url}/storage/v1/object/public/${this.bucket}/${path}`;
683
+ }
684
+ /**
685
+ * Delete a file
686
+ */
687
+ async remove(paths) {
688
+ try {
689
+ const response = await fetch(`${this.url}/storage/v1/object/${this.bucket}`, {
690
+ method: "DELETE",
691
+ headers: {
692
+ ...this.getHeaders(),
693
+ "Content-Type": "application/json"
694
+ },
695
+ body: JSON.stringify({ prefixes: paths })
696
+ });
697
+ if (!response.ok) {
698
+ const data = await response.json();
699
+ return { error: { message: data.message || data.error || "Delete failed" } };
700
+ }
701
+ return { error: null };
702
+ } catch (err) {
703
+ return { error: { message: err instanceof Error ? err.message : "Network error" } };
704
+ }
705
+ }
706
+ /**
707
+ * List files in a bucket
708
+ */
709
+ async list(prefix) {
710
+ try {
711
+ const url = prefix ? `${this.url}/storage/v1/object/list/${this.bucket}?prefix=${prefix}` : `${this.url}/storage/v1/object/list/${this.bucket}`;
712
+ const response = await fetch(url, {
713
+ headers: this.getHeaders()
714
+ });
715
+ const data = await response.json();
716
+ if (!response.ok) {
717
+ return {
718
+ data: null,
719
+ error: { message: data.message || data.error || "List failed" }
720
+ };
721
+ }
722
+ return { data, error: null };
723
+ } catch (err) {
724
+ return {
725
+ data: null,
726
+ error: { message: err instanceof Error ? err.message : "Network error" }
727
+ };
728
+ }
729
+ }
730
+ };
731
+
732
+ // src/index.ts
733
+ function createClient(options) {
734
+ return new AuraBaseClient(options);
735
+ }
736
+ var index_default = createClient;
737
+ export {
738
+ AuraBaseClient,
739
+ createClient,
740
+ index_default as default
741
+ };