krisspy-sdk 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,890 @@
1
+ // src/http.ts
2
+ var HttpClient = class {
3
+ constructor(options) {
4
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
5
+ this.headers = {
6
+ "Content-Type": "application/json",
7
+ ...options.headers
8
+ };
9
+ this.debug = options.debug ?? false;
10
+ }
11
+ /**
12
+ * Set authorization header
13
+ */
14
+ setAuth(token) {
15
+ this.headers["Authorization"] = `Bearer ${token}`;
16
+ }
17
+ /**
18
+ * Remove authorization header
19
+ */
20
+ clearAuth() {
21
+ delete this.headers["Authorization"];
22
+ }
23
+ /**
24
+ * Update headers
25
+ */
26
+ setHeaders(headers) {
27
+ this.headers = { ...this.headers, ...headers };
28
+ }
29
+ /**
30
+ * Build query string from params
31
+ */
32
+ buildQueryString(params) {
33
+ const parts = [];
34
+ for (const [key, value] of Object.entries(params)) {
35
+ if (value === void 0 || value === null) continue;
36
+ if (Array.isArray(value)) {
37
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.join(","))}`);
38
+ } else {
39
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
40
+ }
41
+ }
42
+ return parts.length > 0 ? `?${parts.join("&")}` : "";
43
+ }
44
+ /**
45
+ * Make HTTP request
46
+ */
47
+ async request(method, path, options) {
48
+ const url = `${this.baseUrl}${path}${options?.params ? this.buildQueryString(options.params) : ""}`;
49
+ const requestHeaders = {
50
+ ...this.headers,
51
+ ...options?.headers
52
+ };
53
+ if (this.debug) {
54
+ console.log(`[Krisspy SDK] ${method} ${url}`);
55
+ if (options?.body) {
56
+ console.log("[Krisspy SDK] Body:", JSON.stringify(options.body, null, 2));
57
+ }
58
+ }
59
+ try {
60
+ const response = await fetch(url, {
61
+ method,
62
+ headers: requestHeaders,
63
+ body: options?.body ? JSON.stringify(options.body) : void 0
64
+ });
65
+ const contentType = response.headers.get("content-type");
66
+ let data = null;
67
+ if (contentType?.includes("application/json")) {
68
+ data = await response.json();
69
+ } else {
70
+ data = await response.text();
71
+ }
72
+ if (this.debug) {
73
+ console.log(`[Krisspy SDK] Response ${response.status}:`, data);
74
+ }
75
+ if (!response.ok) {
76
+ const error = {
77
+ message: data?.error || data?.message || "Request failed",
78
+ code: data?.code,
79
+ details: data?.details,
80
+ hint: data?.hint,
81
+ status: response.status
82
+ };
83
+ return { data: null, error, status: response.status };
84
+ }
85
+ return { data, error: null, status: response.status };
86
+ } catch (err) {
87
+ if (this.debug) {
88
+ console.error("[Krisspy SDK] Error:", err);
89
+ }
90
+ const error = {
91
+ message: err.message || "Network error",
92
+ code: "NETWORK_ERROR",
93
+ status: 0
94
+ };
95
+ return { data: null, error, status: 0 };
96
+ }
97
+ }
98
+ /**
99
+ * GET request
100
+ */
101
+ async get(path, params) {
102
+ return this.request("GET", path, { params });
103
+ }
104
+ /**
105
+ * POST request
106
+ */
107
+ async post(path, body, params) {
108
+ return this.request("POST", path, { body, params });
109
+ }
110
+ /**
111
+ * PATCH request
112
+ */
113
+ async patch(path, body, params) {
114
+ return this.request("PATCH", path, { body, params });
115
+ }
116
+ /**
117
+ * PUT request
118
+ */
119
+ async put(path, body, params) {
120
+ return this.request("PUT", path, { body, params });
121
+ }
122
+ /**
123
+ * DELETE request
124
+ */
125
+ async delete(path, params) {
126
+ return this.request("DELETE", path, { params });
127
+ }
128
+ };
129
+
130
+ // src/auth.ts
131
+ var STORAGE_KEY = "krisspy-auth-session";
132
+ var KrisspyAuth = class {
133
+ constructor(http, backendId) {
134
+ this.currentSession = null;
135
+ this.currentUser = null;
136
+ this.listeners = [];
137
+ this.http = http;
138
+ this.backendId = backendId;
139
+ this.restoreSession();
140
+ }
141
+ /**
142
+ * Get current session from storage
143
+ */
144
+ restoreSession() {
145
+ if (typeof window === "undefined") return;
146
+ try {
147
+ const stored = localStorage.getItem(`${STORAGE_KEY}-${this.backendId}`);
148
+ if (stored) {
149
+ const session = JSON.parse(stored);
150
+ if (session.expires_at && Date.now() > session.expires_at * 1e3) {
151
+ this.clearSession();
152
+ return;
153
+ }
154
+ this.setSession(session);
155
+ }
156
+ } catch (err) {
157
+ console.warn("[Krisspy Auth] Failed to restore session:", err);
158
+ }
159
+ }
160
+ /**
161
+ * Store session
162
+ */
163
+ saveSession(session) {
164
+ if (typeof window === "undefined") return;
165
+ try {
166
+ localStorage.setItem(`${STORAGE_KEY}-${this.backendId}`, JSON.stringify(session));
167
+ } catch (err) {
168
+ console.warn("[Krisspy Auth] Failed to save session:", err);
169
+ }
170
+ }
171
+ /**
172
+ * Clear session from storage
173
+ */
174
+ clearSession() {
175
+ this.currentSession = null;
176
+ this.currentUser = null;
177
+ this.http.clearAuth();
178
+ if (this.refreshInterval) {
179
+ clearInterval(this.refreshInterval);
180
+ this.refreshInterval = void 0;
181
+ }
182
+ if (typeof window !== "undefined") {
183
+ localStorage.removeItem(`${STORAGE_KEY}-${this.backendId}`);
184
+ }
185
+ }
186
+ /**
187
+ * Set current session
188
+ */
189
+ setSession(session) {
190
+ this.currentSession = session;
191
+ this.currentUser = session.user;
192
+ this.http.setAuth(session.access_token);
193
+ this.saveSession(session);
194
+ this.setupAutoRefresh(session);
195
+ this.notifyListeners("SIGNED_IN");
196
+ }
197
+ /**
198
+ * Setup auto-refresh of token
199
+ */
200
+ setupAutoRefresh(session) {
201
+ if (this.refreshInterval) {
202
+ clearInterval(this.refreshInterval);
203
+ }
204
+ if (!session.refresh_token || !session.expires_in) return;
205
+ const refreshIn = (session.expires_in - 300) * 1e3;
206
+ if (refreshIn > 0) {
207
+ this.refreshInterval = setInterval(() => {
208
+ this.refreshSession();
209
+ }, refreshIn);
210
+ }
211
+ }
212
+ /**
213
+ * Notify auth state change listeners
214
+ */
215
+ notifyListeners(event) {
216
+ for (const listener of this.listeners) {
217
+ try {
218
+ listener(event);
219
+ } catch (err) {
220
+ console.error("[Krisspy Auth] Listener error:", err);
221
+ }
222
+ }
223
+ }
224
+ /**
225
+ * Sign up with email and password
226
+ */
227
+ async signUp(credentials) {
228
+ const path = `/api/v1/cloud-backends/${this.backendId}/auth/signup`;
229
+ const response = await this.http.post(path, {
230
+ email: credentials.email,
231
+ password: credentials.password,
232
+ metadata: credentials.metadata
233
+ });
234
+ if (response.error) {
235
+ return {
236
+ data: { user: null, session: null },
237
+ error: response.error
238
+ };
239
+ }
240
+ if (response.data?.session) {
241
+ this.setSession(response.data.session);
242
+ }
243
+ return {
244
+ data: {
245
+ user: response.data?.user ?? null,
246
+ session: response.data?.session ?? null
247
+ },
248
+ error: null
249
+ };
250
+ }
251
+ /**
252
+ * Sign in with email and password
253
+ */
254
+ async signInWithPassword(credentials) {
255
+ const path = `/api/v1/cloud-backends/${this.backendId}/auth/login`;
256
+ const response = await this.http.post(path, {
257
+ email: credentials.email,
258
+ password: credentials.password
259
+ });
260
+ if (response.error) {
261
+ return {
262
+ data: { user: null, session: null },
263
+ error: response.error
264
+ };
265
+ }
266
+ if (response.data?.session) {
267
+ this.setSession(response.data.session);
268
+ }
269
+ return {
270
+ data: {
271
+ user: response.data?.user ?? null,
272
+ session: response.data?.session ?? null
273
+ },
274
+ error: null
275
+ };
276
+ }
277
+ /**
278
+ * Sign in with OAuth provider
279
+ */
280
+ async signInWithOAuth(options) {
281
+ const path = `/api/v1/cloud-backends/${this.backendId}/auth/oauth/${options.provider}`;
282
+ const response = await this.http.post(path, {
283
+ redirect_to: options.redirectTo
284
+ });
285
+ if (response.error) {
286
+ return { data: null, error: response.error };
287
+ }
288
+ if (typeof window !== "undefined" && response.data?.url) {
289
+ window.location.href = response.data.url;
290
+ }
291
+ return { data: response.data, error: null };
292
+ }
293
+ /**
294
+ * Sign out
295
+ */
296
+ async signOut() {
297
+ const path = `/api/v1/cloud-backends/${this.backendId}/auth/logout`;
298
+ await this.http.post(path);
299
+ this.clearSession();
300
+ this.notifyListeners("SIGNED_OUT");
301
+ return { error: null };
302
+ }
303
+ /**
304
+ * Refresh the session token
305
+ */
306
+ async refreshSession() {
307
+ if (!this.currentSession?.refresh_token) {
308
+ return {
309
+ data: { user: null, session: null },
310
+ error: { message: "No refresh token available" }
311
+ };
312
+ }
313
+ const path = `/api/v1/cloud-backends/${this.backendId}/auth/refresh`;
314
+ const response = await this.http.post(path, {
315
+ refresh_token: this.currentSession.refresh_token
316
+ });
317
+ if (response.error) {
318
+ this.clearSession();
319
+ this.notifyListeners("SIGNED_OUT");
320
+ return {
321
+ data: { user: null, session: null },
322
+ error: response.error
323
+ };
324
+ }
325
+ if (response.data?.session) {
326
+ this.setSession(response.data.session);
327
+ }
328
+ return {
329
+ data: {
330
+ user: response.data?.session?.user ?? null,
331
+ session: response.data?.session ?? null
332
+ },
333
+ error: null
334
+ };
335
+ }
336
+ /**
337
+ * Get current session
338
+ */
339
+ async getSession() {
340
+ return {
341
+ data: { session: this.currentSession },
342
+ error: null
343
+ };
344
+ }
345
+ /**
346
+ * Get current user
347
+ */
348
+ async getUser() {
349
+ return {
350
+ data: { user: this.currentUser },
351
+ error: null
352
+ };
353
+ }
354
+ /**
355
+ * Get current user synchronously
356
+ */
357
+ user() {
358
+ return this.currentUser;
359
+ }
360
+ /**
361
+ * Get current session synchronously
362
+ */
363
+ session() {
364
+ return this.currentSession;
365
+ }
366
+ /**
367
+ * Update user metadata
368
+ */
369
+ async updateUser(data) {
370
+ const path = `/api/v1/cloud-backends/${this.backendId}/auth/user`;
371
+ const response = await this.http.patch(path, data);
372
+ if (response.error) {
373
+ return { data: { user: null }, error: response.error };
374
+ }
375
+ if (response.data?.user) {
376
+ this.currentUser = response.data.user;
377
+ if (this.currentSession) {
378
+ this.currentSession.user = response.data.user;
379
+ this.saveSession(this.currentSession);
380
+ }
381
+ }
382
+ return { data: { user: response.data?.user ?? null }, error: null };
383
+ }
384
+ /**
385
+ * Request password reset
386
+ */
387
+ async resetPasswordForEmail(email) {
388
+ const path = `/api/v1/cloud-backends/${this.backendId}/auth/reset-password`;
389
+ const response = await this.http.post(path, { email });
390
+ return { error: response.error };
391
+ }
392
+ /**
393
+ * Listen for auth state changes
394
+ */
395
+ onAuthStateChange(callback) {
396
+ this.listeners.push(callback);
397
+ if (this.currentSession) {
398
+ callback("SIGNED_IN");
399
+ }
400
+ return {
401
+ unsubscribe: () => {
402
+ const index = this.listeners.indexOf(callback);
403
+ if (index > -1) {
404
+ this.listeners.splice(index, 1);
405
+ }
406
+ }
407
+ };
408
+ }
409
+ /**
410
+ * Set session from external source (e.g., OAuth callback)
411
+ */
412
+ async setSessionFromUrl() {
413
+ if (typeof window === "undefined") {
414
+ return { data: { user: null, session: null }, error: { message: "Not in browser" } };
415
+ }
416
+ const hash = window.location.hash.substring(1);
417
+ const params = new URLSearchParams(hash);
418
+ const accessToken = params.get("access_token");
419
+ const refreshToken = params.get("refresh_token");
420
+ if (!accessToken) {
421
+ return { data: { user: null, session: null }, error: { message: "No access token in URL" } };
422
+ }
423
+ try {
424
+ const payload = JSON.parse(atob(accessToken.split(".")[1]));
425
+ const session = {
426
+ access_token: accessToken,
427
+ refresh_token: refreshToken ?? void 0,
428
+ expires_at: payload.exp,
429
+ expires_in: payload.exp ? payload.exp - Math.floor(Date.now() / 1e3) : 3600,
430
+ user: {
431
+ id: payload.sub,
432
+ email: payload.email,
433
+ metadata: payload.metadata
434
+ }
435
+ };
436
+ this.setSession(session);
437
+ window.history.replaceState({}, document.title, window.location.pathname);
438
+ return {
439
+ data: { user: session.user, session },
440
+ error: null
441
+ };
442
+ } catch (err) {
443
+ return {
444
+ data: { user: null, session: null },
445
+ error: { message: "Failed to parse token" }
446
+ };
447
+ }
448
+ }
449
+ };
450
+
451
+ // src/query-builder.ts
452
+ var QueryBuilder = class {
453
+ constructor(http, backendId, tableName, useRLS = true) {
454
+ this.queryParams = {};
455
+ this.selectColumns = [];
456
+ this.orderClauses = [];
457
+ this.isSingle = false;
458
+ this.http = http;
459
+ this.backendId = backendId;
460
+ this.tableName = tableName;
461
+ this.useRLS = useRLS;
462
+ }
463
+ /**
464
+ * Get the base path for data endpoints
465
+ * Uses /rls/data for RLS-enabled queries (requires auth)
466
+ * Uses /data for legacy queries (admin/owner only)
467
+ */
468
+ getBasePath() {
469
+ if (this.useRLS) {
470
+ return `/api/v1/cloud-backends/${this.backendId}/rls/data/${this.tableName}`;
471
+ }
472
+ return `/api/v1/cloud-backends/${this.backendId}/data/${this.tableName}`;
473
+ }
474
+ /**
475
+ * Select specific columns
476
+ * @example .select('id, name, price')
477
+ * @example .select('*')
478
+ */
479
+ select(columns = "*") {
480
+ this.selectColumns = columns.split(",").map((c) => c.trim());
481
+ return this;
482
+ }
483
+ /**
484
+ * Filter: equals
485
+ * @example .eq('id', 123)
486
+ */
487
+ eq(column, value) {
488
+ this.queryParams[column] = `eq.${value}`;
489
+ return this;
490
+ }
491
+ /**
492
+ * Filter: not equals
493
+ * @example .neq('status', 'deleted')
494
+ */
495
+ neq(column, value) {
496
+ this.queryParams[column] = `neq.${value}`;
497
+ return this;
498
+ }
499
+ /**
500
+ * Filter: greater than
501
+ * @example .gt('price', 100)
502
+ */
503
+ gt(column, value) {
504
+ this.queryParams[column] = `gt.${value}`;
505
+ return this;
506
+ }
507
+ /**
508
+ * Filter: greater than or equal
509
+ * @example .gte('age', 18)
510
+ */
511
+ gte(column, value) {
512
+ this.queryParams[column] = `gte.${value}`;
513
+ return this;
514
+ }
515
+ /**
516
+ * Filter: less than
517
+ * @example .lt('stock', 10)
518
+ */
519
+ lt(column, value) {
520
+ this.queryParams[column] = `lt.${value}`;
521
+ return this;
522
+ }
523
+ /**
524
+ * Filter: less than or equal
525
+ * @example .lte('price', 1000)
526
+ */
527
+ lte(column, value) {
528
+ this.queryParams[column] = `lte.${value}`;
529
+ return this;
530
+ }
531
+ /**
532
+ * Filter: pattern matching (case sensitive)
533
+ * @example .like('name', '%iPhone%')
534
+ */
535
+ like(column, pattern) {
536
+ this.queryParams[column] = `like.${pattern}`;
537
+ return this;
538
+ }
539
+ /**
540
+ * Filter: pattern matching (case insensitive)
541
+ * @example .ilike('name', '%iphone%')
542
+ */
543
+ ilike(column, pattern) {
544
+ this.queryParams[column] = `ilike.${pattern}`;
545
+ return this;
546
+ }
547
+ /**
548
+ * Filter: value in list
549
+ * @example .in('status', ['active', 'pending'])
550
+ */
551
+ in(column, values) {
552
+ this.queryParams[column] = `in.${values.join(",")}`;
553
+ return this;
554
+ }
555
+ /**
556
+ * Filter: is null/true/false
557
+ * @example .is('deleted_at', null)
558
+ */
559
+ is(column, value) {
560
+ const strValue = value === null ? "null" : value ? "true" : "false";
561
+ this.queryParams[column] = `is.${strValue}`;
562
+ return this;
563
+ }
564
+ /**
565
+ * Order by column
566
+ * @example .order('created_at', { ascending: false })
567
+ */
568
+ order(column, options) {
569
+ const direction = options?.ascending === false ? "desc" : "asc";
570
+ this.orderClauses.push(`${column}.${direction}`);
571
+ return this;
572
+ }
573
+ /**
574
+ * Limit number of rows
575
+ * @example .limit(10)
576
+ */
577
+ limit(count) {
578
+ this.limitValue = count;
579
+ return this;
580
+ }
581
+ /**
582
+ * Skip rows (for pagination)
583
+ * @example .range(0, 9) // First 10 rows
584
+ */
585
+ range(from, to) {
586
+ this.offsetValue = from;
587
+ this.limitValue = to - from + 1;
588
+ return this;
589
+ }
590
+ /**
591
+ * Return single row (throws if multiple or none)
592
+ */
593
+ single() {
594
+ this.isSingle = true;
595
+ this.limitValue = 1;
596
+ return this;
597
+ }
598
+ /**
599
+ * Return first row or null
600
+ */
601
+ maybeSingle() {
602
+ this.isSingle = true;
603
+ this.limitValue = 1;
604
+ return this;
605
+ }
606
+ /**
607
+ * Build query params for the request
608
+ */
609
+ buildParams() {
610
+ const params = { ...this.queryParams };
611
+ if (this.selectColumns.length > 0 && !this.selectColumns.includes("*")) {
612
+ params["select"] = this.selectColumns.join(",");
613
+ }
614
+ if (this.orderClauses.length > 0) {
615
+ params["order"] = this.orderClauses.join(",");
616
+ }
617
+ if (this.limitValue !== void 0) {
618
+ params["limit"] = String(this.limitValue);
619
+ }
620
+ if (this.offsetValue !== void 0) {
621
+ params["offset"] = String(this.offsetValue);
622
+ }
623
+ return params;
624
+ }
625
+ /**
626
+ * Execute SELECT query
627
+ */
628
+ async then(resolve, reject) {
629
+ try {
630
+ const result = await this.execute();
631
+ resolve(result);
632
+ } catch (err) {
633
+ if (reject) reject(err);
634
+ }
635
+ }
636
+ /**
637
+ * Execute the query
638
+ */
639
+ async execute() {
640
+ const path = this.getBasePath();
641
+ const params = this.buildParams();
642
+ const response = await this.http.get(path, params);
643
+ if (response.error) {
644
+ if (this.isSingle) {
645
+ return { data: null, error: response.error };
646
+ }
647
+ return { data: null, error: response.error, count: null };
648
+ }
649
+ if (this.isSingle) {
650
+ const item = response.data?.data?.[0] ?? null;
651
+ return { data: item, error: null };
652
+ }
653
+ return {
654
+ data: response.data?.data ?? [],
655
+ error: null,
656
+ count: response.data?.count ?? 0
657
+ };
658
+ }
659
+ /**
660
+ * Insert rows
661
+ * @example .insert({ name: 'iPhone', price: 999 })
662
+ * @example .insert([{ name: 'iPhone' }, { name: 'iPad' }])
663
+ */
664
+ async insert(data) {
665
+ const path = this.getBasePath();
666
+ const response = await this.http.post(path, data);
667
+ if (response.error) {
668
+ return { data: null, error: response.error, count: 0 };
669
+ }
670
+ return {
671
+ data: response.data?.data ?? [],
672
+ error: null,
673
+ count: response.data?.count ?? 0
674
+ };
675
+ }
676
+ /**
677
+ * Update rows (requires filters)
678
+ * @example .update({ price: 899 }).eq('id', 123)
679
+ */
680
+ async update(data) {
681
+ const path = this.getBasePath();
682
+ const params = this.buildParams();
683
+ delete params["select"];
684
+ delete params["order"];
685
+ delete params["limit"];
686
+ delete params["offset"];
687
+ const response = await this.http.patch(path, data, params);
688
+ if (response.error) {
689
+ return { data: null, error: response.error, count: 0 };
690
+ }
691
+ return {
692
+ data: response.data?.data ?? [],
693
+ error: null,
694
+ count: response.data?.count ?? 0
695
+ };
696
+ }
697
+ /**
698
+ * Delete rows (requires filters)
699
+ * @example .delete().eq('id', 123)
700
+ */
701
+ async delete() {
702
+ const path = this.getBasePath();
703
+ const params = this.buildParams();
704
+ delete params["select"];
705
+ delete params["order"];
706
+ delete params["limit"];
707
+ delete params["offset"];
708
+ const response = await this.http.delete(path, params);
709
+ if (response.error) {
710
+ return { data: null, error: response.error, count: 0 };
711
+ }
712
+ return {
713
+ data: response.data?.data ?? [],
714
+ error: null,
715
+ count: response.data?.count ?? 0
716
+ };
717
+ }
718
+ /**
719
+ * Upsert rows (insert or update on conflict)
720
+ * @example .upsert({ id: 1, name: 'iPhone' })
721
+ */
722
+ async upsert(data, options) {
723
+ return this.insert(data);
724
+ }
725
+ };
726
+
727
+ // src/client.ts
728
+ var KrisspyClient = class {
729
+ constructor(options) {
730
+ const baseUrl = options.url || "https://api.krisspy.ai";
731
+ this.backendId = options.backendId;
732
+ this.useRLS = options.useRLS !== false;
733
+ this.http = new HttpClient({
734
+ baseUrl,
735
+ headers: {
736
+ ...options.headers,
737
+ ...options.apiKey ? { "Authorization": `Bearer ${options.apiKey}` } : {}
738
+ },
739
+ debug: options.debug
740
+ });
741
+ this._auth = new KrisspyAuth(this.http, this.backendId);
742
+ }
743
+ /**
744
+ * Auth module for user authentication
745
+ *
746
+ * @example
747
+ * // Sign up
748
+ * const { data, error } = await krisspy.auth.signUp({
749
+ * email: 'user@example.com',
750
+ * password: 'secret123'
751
+ * })
752
+ *
753
+ * // Sign in
754
+ * const { data, error } = await krisspy.auth.signInWithPassword({
755
+ * email: 'user@example.com',
756
+ * password: 'secret123'
757
+ * })
758
+ *
759
+ * // Get current user
760
+ * const user = krisspy.auth.user()
761
+ *
762
+ * // Sign out
763
+ * await krisspy.auth.signOut()
764
+ */
765
+ get auth() {
766
+ return this._auth;
767
+ }
768
+ /**
769
+ * Create a query builder for a table
770
+ *
771
+ * @example
772
+ * // Select all rows
773
+ * const { data, error } = await krisspy.from('products').select('*')
774
+ *
775
+ * // Select with filters
776
+ * const { data, error } = await krisspy
777
+ * .from('products')
778
+ * .select('id, name, price')
779
+ * .eq('active', true)
780
+ * .order('created_at', { ascending: false })
781
+ * .limit(10)
782
+ *
783
+ * // Insert
784
+ * const { data, error } = await krisspy
785
+ * .from('products')
786
+ * .insert({ name: 'iPhone', price: 999 })
787
+ *
788
+ * // Update
789
+ * const { data, error } = await krisspy
790
+ * .from('products')
791
+ * .update({ price: 899 })
792
+ * .eq('id', 123)
793
+ *
794
+ * // Delete
795
+ * const { data, error } = await krisspy
796
+ * .from('products')
797
+ * .delete()
798
+ * .eq('id', 123)
799
+ */
800
+ from(table) {
801
+ return new QueryBuilder(this.http, this.backendId, table, this.useRLS);
802
+ }
803
+ /**
804
+ * Execute raw SQL query
805
+ *
806
+ * @example
807
+ * const { data, error } = await krisspy.rpc('SELECT * FROM products WHERE price > $1', [100])
808
+ */
809
+ async rpc(sql, params) {
810
+ const path = `/api/v1/cloud-backends/${this.backendId}/sql`;
811
+ const response = await this.http.post(path, { sql, params });
812
+ if (response.error) {
813
+ return { data: null, error: response.error };
814
+ }
815
+ return { data: response.data?.result ?? null, error: null };
816
+ }
817
+ /**
818
+ * Invoke a serverless function
819
+ *
820
+ * @example
821
+ * const { data, error } = await krisspy.functions.invoke('hello-world', {
822
+ * body: { name: 'John' }
823
+ * })
824
+ */
825
+ get functions() {
826
+ return {
827
+ invoke: async (functionName, options) => {
828
+ const path = `/api/v1/cloud-backends/${this.backendId}/functions/${functionName}/invoke`;
829
+ const response = await this.http.post(path, options?.body, void 0);
830
+ if (response.error) {
831
+ return { data: null, error: response.error };
832
+ }
833
+ return { data: response.data, error: null };
834
+ },
835
+ list: async () => {
836
+ const path = `/api/v1/cloud-backends/${this.backendId}/functions`;
837
+ const response = await this.http.get(path);
838
+ if (response.error) {
839
+ return { data: null, error: response.error };
840
+ }
841
+ return { data: response.data?.functions ?? [], error: null };
842
+ }
843
+ };
844
+ }
845
+ /**
846
+ * Get backend info
847
+ */
848
+ async getBackendInfo() {
849
+ const path = `/api/v1/cloud-backends/${this.backendId}`;
850
+ const response = await this.http.get(path);
851
+ if (response.error) {
852
+ return { data: null, error: response.error };
853
+ }
854
+ return { data: response.data, error: null };
855
+ }
856
+ /**
857
+ * List tables in the backend
858
+ */
859
+ async listTables() {
860
+ const path = `/api/v1/cloud-backends/${this.backendId}/tables`;
861
+ const response = await this.http.get(path);
862
+ if (response.error) {
863
+ return { data: null, error: response.error };
864
+ }
865
+ return { data: response.data?.tables ?? [], error: null };
866
+ }
867
+ /**
868
+ * Get table schema (columns)
869
+ */
870
+ async getTableSchema(table) {
871
+ const path = `/api/v1/cloud-backends/${this.backendId}/tables/${table}`;
872
+ const response = await this.http.get(path);
873
+ if (response.error) {
874
+ return { data: null, error: response.error };
875
+ }
876
+ return { data: response.data?.columns ?? [], error: null };
877
+ }
878
+ };
879
+
880
+ // src/index.ts
881
+ function createClient(options) {
882
+ return new KrisspyClient(options);
883
+ }
884
+ export {
885
+ HttpClient,
886
+ KrisspyAuth,
887
+ KrisspyClient,
888
+ QueryBuilder,
889
+ createClient
890
+ };