@insforge/sdk 1.0.2-dev.0 → 1.0.3-dev.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/LICENSE +201 -201
- package/README.md +259 -249
- package/dist/index.d.mts +71 -39
- package/dist/index.d.ts +71 -39
- package/dist/index.js +299 -172
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +299 -172
- package/dist/index.mjs.map +1 -1
- package/package.json +68 -68
package/dist/index.js
CHANGED
|
@@ -177,8 +177,31 @@ var HttpClient = class {
|
|
|
177
177
|
// src/lib/token-manager.ts
|
|
178
178
|
var TOKEN_KEY = "insforge-auth-token";
|
|
179
179
|
var USER_KEY = "insforge-auth-user";
|
|
180
|
+
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
181
|
+
function getCsrfToken() {
|
|
182
|
+
if (typeof document === "undefined") return null;
|
|
183
|
+
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
184
|
+
if (!match) return null;
|
|
185
|
+
return match.split("=")[1] || null;
|
|
186
|
+
}
|
|
187
|
+
function setCsrfToken(token) {
|
|
188
|
+
if (typeof document === "undefined") return;
|
|
189
|
+
const maxAge = 7 * 24 * 60 * 60;
|
|
190
|
+
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
191
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=${encodeURIComponent(token)}; path=/; max-age=${maxAge}; SameSite=Lax${secure}`;
|
|
192
|
+
}
|
|
193
|
+
function clearCsrfToken() {
|
|
194
|
+
if (typeof document === "undefined") return;
|
|
195
|
+
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
196
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax${secure}`;
|
|
197
|
+
}
|
|
180
198
|
var TokenManager = class {
|
|
181
199
|
constructor(storage) {
|
|
200
|
+
// In-memory storage
|
|
201
|
+
this.accessToken = null;
|
|
202
|
+
this.user = null;
|
|
203
|
+
// Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
|
|
204
|
+
this._mode = "storage";
|
|
182
205
|
if (storage) {
|
|
183
206
|
this.storage = storage;
|
|
184
207
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -196,126 +219,117 @@ var TokenManager = class {
|
|
|
196
219
|
};
|
|
197
220
|
}
|
|
198
221
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Get current mode
|
|
224
|
+
*/
|
|
225
|
+
get mode() {
|
|
226
|
+
return this._mode;
|
|
202
227
|
}
|
|
203
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Set mode to memory (new backend with cookies + memory)
|
|
230
|
+
*/
|
|
231
|
+
setMemoryMode() {
|
|
232
|
+
if (this._mode === "storage") {
|
|
233
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
234
|
+
this.storage.removeItem(USER_KEY);
|
|
235
|
+
}
|
|
236
|
+
this._mode = "memory";
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Set mode to storage (legacy backend with localStorage)
|
|
240
|
+
* Also loads existing session from localStorage
|
|
241
|
+
*/
|
|
242
|
+
setStorageMode() {
|
|
243
|
+
this._mode = "storage";
|
|
244
|
+
this.loadFromStorage();
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Load session from localStorage
|
|
248
|
+
*/
|
|
249
|
+
loadFromStorage() {
|
|
204
250
|
const token = this.storage.getItem(TOKEN_KEY);
|
|
205
251
|
const userStr = this.storage.getItem(USER_KEY);
|
|
206
|
-
if (
|
|
207
|
-
|
|
252
|
+
if (token && userStr) {
|
|
253
|
+
try {
|
|
254
|
+
this.accessToken = token;
|
|
255
|
+
this.user = JSON.parse(userStr);
|
|
256
|
+
} catch {
|
|
257
|
+
this.clearSession();
|
|
258
|
+
}
|
|
208
259
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Save session (memory always, localStorage only in storage mode)
|
|
263
|
+
*/
|
|
264
|
+
saveSession(session) {
|
|
265
|
+
this.accessToken = session.accessToken;
|
|
266
|
+
this.user = session.user;
|
|
267
|
+
if (this._mode === "storage") {
|
|
268
|
+
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
269
|
+
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
215
270
|
}
|
|
216
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Get current session
|
|
274
|
+
*/
|
|
275
|
+
getSession() {
|
|
276
|
+
this.loadFromStorage();
|
|
277
|
+
if (!this.accessToken || !this.user) return null;
|
|
278
|
+
return {
|
|
279
|
+
accessToken: this.accessToken,
|
|
280
|
+
user: this.user
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get access token
|
|
285
|
+
*/
|
|
217
286
|
getAccessToken() {
|
|
218
|
-
|
|
219
|
-
return
|
|
287
|
+
this.loadFromStorage();
|
|
288
|
+
return this.accessToken;
|
|
220
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Set access token
|
|
292
|
+
*/
|
|
293
|
+
setAccessToken(token) {
|
|
294
|
+
this.accessToken = token;
|
|
295
|
+
if (this._mode === "storage") {
|
|
296
|
+
this.storage.setItem(TOKEN_KEY, token);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get user
|
|
301
|
+
*/
|
|
302
|
+
getUser() {
|
|
303
|
+
return this.user;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Set user
|
|
307
|
+
*/
|
|
308
|
+
setUser(user) {
|
|
309
|
+
this.user = user;
|
|
310
|
+
if (this._mode === "storage") {
|
|
311
|
+
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Clear session (both memory and localStorage)
|
|
316
|
+
*/
|
|
221
317
|
clearSession() {
|
|
318
|
+
this.accessToken = null;
|
|
319
|
+
this.user = null;
|
|
222
320
|
this.storage.removeItem(TOKEN_KEY);
|
|
223
321
|
this.storage.removeItem(USER_KEY);
|
|
224
322
|
}
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// src/modules/database-postgrest.ts
|
|
228
|
-
var import_postgrest_js = require("@supabase/postgrest-js");
|
|
229
|
-
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
230
|
-
return async (input, init) => {
|
|
231
|
-
const url = typeof input === "string" ? input : input.toString();
|
|
232
|
-
const urlObj = new URL(url);
|
|
233
|
-
const tableName = urlObj.pathname.slice(1);
|
|
234
|
-
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
235
|
-
const token = tokenManager.getAccessToken();
|
|
236
|
-
const httpHeaders = httpClient.getHeaders();
|
|
237
|
-
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
238
|
-
const headers = new Headers(init?.headers);
|
|
239
|
-
if (authToken && !headers.has("Authorization")) {
|
|
240
|
-
headers.set("Authorization", `Bearer ${authToken}`);
|
|
241
|
-
}
|
|
242
|
-
const response = await fetch(insforgeUrl, {
|
|
243
|
-
...init,
|
|
244
|
-
headers
|
|
245
|
-
});
|
|
246
|
-
return response;
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
var Database = class {
|
|
250
|
-
constructor(httpClient, tokenManager) {
|
|
251
|
-
this.postgrest = new import_postgrest_js.PostgrestClient("http://dummy", {
|
|
252
|
-
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
253
|
-
headers: {}
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
323
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* @example
|
|
260
|
-
* // Basic query
|
|
261
|
-
* const { data, error } = await client.database
|
|
262
|
-
* .from('posts')
|
|
263
|
-
* .select('*')
|
|
264
|
-
* .eq('user_id', userId);
|
|
265
|
-
*
|
|
266
|
-
* // With count (Supabase style!)
|
|
267
|
-
* const { data, error, count } = await client.database
|
|
268
|
-
* .from('posts')
|
|
269
|
-
* .select('*', { count: 'exact' })
|
|
270
|
-
* .range(0, 9);
|
|
271
|
-
*
|
|
272
|
-
* // Just get count, no data
|
|
273
|
-
* const { count } = await client.database
|
|
274
|
-
* .from('posts')
|
|
275
|
-
* .select('*', { count: 'exact', head: true });
|
|
276
|
-
*
|
|
277
|
-
* // Complex queries with OR
|
|
278
|
-
* const { data } = await client.database
|
|
279
|
-
* .from('posts')
|
|
280
|
-
* .select('*, users!inner(*)')
|
|
281
|
-
* .or('status.eq.active,status.eq.pending');
|
|
282
|
-
*
|
|
283
|
-
* // All features work:
|
|
284
|
-
* - Nested selects
|
|
285
|
-
* - Foreign key expansion
|
|
286
|
-
* - OR/AND/NOT conditions
|
|
287
|
-
* - Count with head
|
|
288
|
-
* - Range pagination
|
|
289
|
-
* - Upserts
|
|
324
|
+
* Check if there's a session in localStorage (for legacy detection)
|
|
290
325
|
*/
|
|
291
|
-
|
|
292
|
-
|
|
326
|
+
hasStoredSession() {
|
|
327
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
328
|
+
return !!token;
|
|
293
329
|
}
|
|
294
330
|
};
|
|
295
331
|
|
|
296
332
|
// src/modules/auth.ts
|
|
297
|
-
function convertDbProfileToCamelCase(dbProfile) {
|
|
298
|
-
const result = {
|
|
299
|
-
id: dbProfile.id
|
|
300
|
-
};
|
|
301
|
-
Object.keys(dbProfile).forEach((key) => {
|
|
302
|
-
result[key] = dbProfile[key];
|
|
303
|
-
if (key.includes("_")) {
|
|
304
|
-
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
305
|
-
result[camelKey] = dbProfile[key];
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
return result;
|
|
309
|
-
}
|
|
310
|
-
function convertCamelCaseToDbProfile(profile) {
|
|
311
|
-
const dbProfile = {};
|
|
312
|
-
Object.keys(profile).forEach((key) => {
|
|
313
|
-
if (profile[key] === void 0) return;
|
|
314
|
-
const snakeKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
315
|
-
dbProfile[snakeKey] = profile[key];
|
|
316
|
-
});
|
|
317
|
-
return dbProfile;
|
|
318
|
-
}
|
|
319
333
|
function isHostedAuthEnvironment() {
|
|
320
334
|
if (typeof window === "undefined") {
|
|
321
335
|
return false;
|
|
@@ -333,15 +347,14 @@ var Auth = class {
|
|
|
333
347
|
constructor(http, tokenManager) {
|
|
334
348
|
this.http = http;
|
|
335
349
|
this.tokenManager = tokenManager;
|
|
336
|
-
this.database = new Database(http, tokenManager);
|
|
337
350
|
this.detectAuthCallback();
|
|
338
351
|
}
|
|
339
352
|
/**
|
|
340
353
|
* Automatically detect and handle OAuth callback parameters in the URL
|
|
341
|
-
* This runs
|
|
354
|
+
* This runs after initialization to seamlessly complete the OAuth flow
|
|
342
355
|
* Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
|
|
343
356
|
*/
|
|
344
|
-
detectAuthCallback() {
|
|
357
|
+
async detectAuthCallback() {
|
|
345
358
|
if (typeof window === "undefined") return;
|
|
346
359
|
try {
|
|
347
360
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -349,13 +362,20 @@ var Auth = class {
|
|
|
349
362
|
const userId = params.get("user_id");
|
|
350
363
|
const email = params.get("email");
|
|
351
364
|
const name = params.get("name");
|
|
365
|
+
const csrfToken = params.get("csrf_token");
|
|
352
366
|
if (accessToken && userId && email) {
|
|
367
|
+
if (csrfToken) {
|
|
368
|
+
this.tokenManager.setMemoryMode();
|
|
369
|
+
setCsrfToken(csrfToken);
|
|
370
|
+
}
|
|
371
|
+
const { data: profileData } = await this.getProfile(userId);
|
|
353
372
|
const session = {
|
|
354
373
|
accessToken,
|
|
355
374
|
user: {
|
|
356
375
|
id: userId,
|
|
357
376
|
email,
|
|
358
|
-
name: name || "",
|
|
377
|
+
profile: profileData?.profile || { name: name || "" },
|
|
378
|
+
metadata: null,
|
|
359
379
|
// These fields are not provided by backend OAuth callback
|
|
360
380
|
// They'll be populated when calling getCurrentUser()
|
|
361
381
|
emailVerified: false,
|
|
@@ -370,6 +390,7 @@ var Auth = class {
|
|
|
370
390
|
url.searchParams.delete("user_id");
|
|
371
391
|
url.searchParams.delete("email");
|
|
372
392
|
url.searchParams.delete("name");
|
|
393
|
+
url.searchParams.delete("csrf_token");
|
|
373
394
|
if (params.has("error")) {
|
|
374
395
|
url.searchParams.delete("error");
|
|
375
396
|
}
|
|
@@ -385,15 +406,16 @@ var Auth = class {
|
|
|
385
406
|
async signUp(request) {
|
|
386
407
|
try {
|
|
387
408
|
const response = await this.http.post("/api/auth/users", request);
|
|
388
|
-
if (response.accessToken && response.user) {
|
|
409
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
389
410
|
const session = {
|
|
390
411
|
accessToken: response.accessToken,
|
|
391
412
|
user: response.user
|
|
392
413
|
};
|
|
393
|
-
|
|
394
|
-
this.tokenManager.saveSession(session);
|
|
395
|
-
}
|
|
414
|
+
this.tokenManager.saveSession(session);
|
|
396
415
|
this.http.setAuthToken(response.accessToken);
|
|
416
|
+
if (response.csrfToken) {
|
|
417
|
+
setCsrfToken(response.csrfToken);
|
|
418
|
+
}
|
|
397
419
|
}
|
|
398
420
|
return {
|
|
399
421
|
data: response,
|
|
@@ -419,21 +441,17 @@ var Auth = class {
|
|
|
419
441
|
async signInWithPassword(request) {
|
|
420
442
|
try {
|
|
421
443
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
422
|
-
const session = {
|
|
423
|
-
accessToken: response.accessToken || "",
|
|
424
|
-
user: response.user || {
|
|
425
|
-
id: "",
|
|
426
|
-
email: "",
|
|
427
|
-
name: "",
|
|
428
|
-
emailVerified: false,
|
|
429
|
-
createdAt: "",
|
|
430
|
-
updatedAt: ""
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
444
|
if (!isHostedAuthEnvironment()) {
|
|
445
|
+
const session = {
|
|
446
|
+
accessToken: response.accessToken,
|
|
447
|
+
user: response.user
|
|
448
|
+
};
|
|
434
449
|
this.tokenManager.saveSession(session);
|
|
450
|
+
this.http.setAuthToken(response.accessToken);
|
|
451
|
+
if (response.csrfToken) {
|
|
452
|
+
setCsrfToken(response.csrfToken);
|
|
453
|
+
}
|
|
435
454
|
}
|
|
436
|
-
this.http.setAuthToken(response.accessToken || "");
|
|
437
455
|
return {
|
|
438
456
|
data: response,
|
|
439
457
|
error: null
|
|
@@ -491,8 +509,13 @@ var Auth = class {
|
|
|
491
509
|
*/
|
|
492
510
|
async signOut() {
|
|
493
511
|
try {
|
|
512
|
+
try {
|
|
513
|
+
await this.http.post("/api/auth/logout", void 0, { credentials: "include" });
|
|
514
|
+
} catch {
|
|
515
|
+
}
|
|
494
516
|
this.tokenManager.clearSession();
|
|
495
517
|
this.http.setAuthToken(null);
|
|
518
|
+
clearCsrfToken();
|
|
496
519
|
return { error: null };
|
|
497
520
|
} catch (error) {
|
|
498
521
|
return {
|
|
@@ -553,14 +576,9 @@ var Auth = class {
|
|
|
553
576
|
}
|
|
554
577
|
this.http.setAuthToken(session.accessToken);
|
|
555
578
|
const authResponse = await this.http.get("/api/auth/sessions/current");
|
|
556
|
-
const { data: profile, error: profileError } = await this.database.from("users").select("*").eq("id", authResponse.user.id).single();
|
|
557
|
-
if (profileError && profileError.code !== "PGRST116") {
|
|
558
|
-
return { data: null, error: profileError };
|
|
559
|
-
}
|
|
560
579
|
return {
|
|
561
580
|
data: {
|
|
562
|
-
user: authResponse.user
|
|
563
|
-
profile: profile ? convertDbProfileToCamelCase(profile) : null
|
|
581
|
+
user: authResponse.user
|
|
564
582
|
},
|
|
565
583
|
error: null
|
|
566
584
|
};
|
|
@@ -584,29 +602,80 @@ var Auth = class {
|
|
|
584
602
|
}
|
|
585
603
|
/**
|
|
586
604
|
* Get any user's profile by ID
|
|
587
|
-
* Returns profile information from the users table
|
|
605
|
+
* Returns profile information from the users table
|
|
588
606
|
*/
|
|
589
607
|
async getProfile(userId) {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
608
|
+
try {
|
|
609
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
610
|
+
return {
|
|
611
|
+
data: response,
|
|
612
|
+
error: null
|
|
613
|
+
};
|
|
614
|
+
} catch (error) {
|
|
615
|
+
if (error instanceof InsForgeError) {
|
|
616
|
+
return { data: null, error };
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
data: null,
|
|
620
|
+
error: new InsForgeError(
|
|
621
|
+
"An unexpected error occurred while fetching user profile",
|
|
622
|
+
500,
|
|
623
|
+
"UNEXPECTED_ERROR"
|
|
624
|
+
)
|
|
625
|
+
};
|
|
596
626
|
}
|
|
597
|
-
return { data: null, error };
|
|
598
627
|
}
|
|
599
628
|
/**
|
|
600
629
|
* Get the current session (only session data, no API call)
|
|
601
630
|
* Returns the stored JWT token and basic user info from local storage
|
|
602
631
|
*/
|
|
603
|
-
getCurrentSession() {
|
|
632
|
+
async getCurrentSession() {
|
|
604
633
|
try {
|
|
605
634
|
const session = this.tokenManager.getSession();
|
|
606
|
-
if (session
|
|
635
|
+
if (session) {
|
|
607
636
|
this.http.setAuthToken(session.accessToken);
|
|
608
637
|
return { data: { session }, error: null };
|
|
609
638
|
}
|
|
639
|
+
if (typeof window !== "undefined") {
|
|
640
|
+
try {
|
|
641
|
+
const csrfToken = getCsrfToken();
|
|
642
|
+
const response = await this.http.post(
|
|
643
|
+
"/api/auth/refresh",
|
|
644
|
+
void 0,
|
|
645
|
+
{
|
|
646
|
+
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
647
|
+
credentials: "include"
|
|
648
|
+
}
|
|
649
|
+
);
|
|
650
|
+
if (response.accessToken) {
|
|
651
|
+
this.tokenManager.setMemoryMode();
|
|
652
|
+
this.tokenManager.setAccessToken(response.accessToken);
|
|
653
|
+
this.http.setAuthToken(response.accessToken);
|
|
654
|
+
if (response.user) {
|
|
655
|
+
this.tokenManager.setUser(response.user);
|
|
656
|
+
}
|
|
657
|
+
if (response.csrfToken) {
|
|
658
|
+
setCsrfToken(response.csrfToken);
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
data: { session: this.tokenManager.getSession() },
|
|
662
|
+
error: null
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
} catch (error) {
|
|
666
|
+
if (error instanceof InsForgeError) {
|
|
667
|
+
if (error.statusCode === 404) {
|
|
668
|
+
this.tokenManager.setStorageMode();
|
|
669
|
+
const session2 = this.tokenManager.getSession();
|
|
670
|
+
if (session2) {
|
|
671
|
+
return { data: { session: session2 }, error: null };
|
|
672
|
+
}
|
|
673
|
+
return { data: { session: null }, error: null };
|
|
674
|
+
}
|
|
675
|
+
return { data: { session: null }, error };
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
610
679
|
return { data: { session: null }, error: null };
|
|
611
680
|
} catch (error) {
|
|
612
681
|
if (error instanceof InsForgeError) {
|
|
@@ -625,42 +694,31 @@ var Auth = class {
|
|
|
625
694
|
/**
|
|
626
695
|
* Set/Update the current user's profile
|
|
627
696
|
* Updates profile information in the users table (supports any dynamic fields)
|
|
697
|
+
* Requires authentication
|
|
628
698
|
*/
|
|
629
699
|
async setProfile(profile) {
|
|
630
|
-
|
|
631
|
-
|
|
700
|
+
try {
|
|
701
|
+
const response = await this.http.patch(
|
|
702
|
+
"/api/auth/profiles/current",
|
|
703
|
+
{ profile }
|
|
704
|
+
);
|
|
705
|
+
return {
|
|
706
|
+
data: response,
|
|
707
|
+
error: null
|
|
708
|
+
};
|
|
709
|
+
} catch (error) {
|
|
710
|
+
if (error instanceof InsForgeError) {
|
|
711
|
+
return { data: null, error };
|
|
712
|
+
}
|
|
632
713
|
return {
|
|
633
714
|
data: null,
|
|
634
715
|
error: new InsForgeError(
|
|
635
|
-
"
|
|
636
|
-
|
|
637
|
-
"
|
|
716
|
+
"An unexpected error occurred while updating user profile",
|
|
717
|
+
500,
|
|
718
|
+
"UNEXPECTED_ERROR"
|
|
638
719
|
)
|
|
639
720
|
};
|
|
640
721
|
}
|
|
641
|
-
if (!session.user?.id) {
|
|
642
|
-
const { data: data2, error: error2 } = await this.getCurrentUser();
|
|
643
|
-
if (error2) {
|
|
644
|
-
return { data: null, error: error2 };
|
|
645
|
-
}
|
|
646
|
-
if (data2?.user) {
|
|
647
|
-
session.user = {
|
|
648
|
-
id: data2.user.id,
|
|
649
|
-
email: data2.user.email,
|
|
650
|
-
name: "",
|
|
651
|
-
emailVerified: false,
|
|
652
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
653
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
654
|
-
};
|
|
655
|
-
this.tokenManager.saveSession(session);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
const dbProfile = convertCamelCaseToDbProfile(profile);
|
|
659
|
-
const { data, error } = await this.database.from("users").update(dbProfile).eq("id", session.user.id).select().single();
|
|
660
|
-
if (data) {
|
|
661
|
-
return { data: convertDbProfileToCamelCase(data), error: null };
|
|
662
|
-
}
|
|
663
|
-
return { data: null, error };
|
|
664
722
|
}
|
|
665
723
|
/**
|
|
666
724
|
* Send email verification (code or link based on config)
|
|
@@ -814,13 +872,16 @@ var Auth = class {
|
|
|
814
872
|
"/api/auth/email/verify",
|
|
815
873
|
request
|
|
816
874
|
);
|
|
817
|
-
if (
|
|
875
|
+
if (!isHostedAuthEnvironment()) {
|
|
818
876
|
const session = {
|
|
819
877
|
accessToken: response.accessToken,
|
|
820
|
-
user: response.user
|
|
878
|
+
user: response.user
|
|
821
879
|
};
|
|
822
880
|
this.tokenManager.saveSession(session);
|
|
823
881
|
this.http.setAuthToken(response.accessToken);
|
|
882
|
+
if (response.csrfToken) {
|
|
883
|
+
setCsrfToken(response.csrfToken);
|
|
884
|
+
}
|
|
824
885
|
}
|
|
825
886
|
return {
|
|
826
887
|
data: response,
|
|
@@ -842,6 +903,75 @@ var Auth = class {
|
|
|
842
903
|
}
|
|
843
904
|
};
|
|
844
905
|
|
|
906
|
+
// src/modules/database-postgrest.ts
|
|
907
|
+
var import_postgrest_js = require("@supabase/postgrest-js");
|
|
908
|
+
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
909
|
+
return async (input, init) => {
|
|
910
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
911
|
+
const urlObj = new URL(url);
|
|
912
|
+
const tableName = urlObj.pathname.slice(1);
|
|
913
|
+
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
914
|
+
const token = tokenManager.getAccessToken();
|
|
915
|
+
const httpHeaders = httpClient.getHeaders();
|
|
916
|
+
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
917
|
+
const headers = new Headers(init?.headers);
|
|
918
|
+
if (authToken && !headers.has("Authorization")) {
|
|
919
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
920
|
+
}
|
|
921
|
+
const response = await fetch(insforgeUrl, {
|
|
922
|
+
...init,
|
|
923
|
+
headers
|
|
924
|
+
});
|
|
925
|
+
return response;
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
var Database = class {
|
|
929
|
+
constructor(httpClient, tokenManager) {
|
|
930
|
+
this.postgrest = new import_postgrest_js.PostgrestClient("http://dummy", {
|
|
931
|
+
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
932
|
+
headers: {}
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Create a query builder for a table
|
|
937
|
+
*
|
|
938
|
+
* @example
|
|
939
|
+
* // Basic query
|
|
940
|
+
* const { data, error } = await client.database
|
|
941
|
+
* .from('posts')
|
|
942
|
+
* .select('*')
|
|
943
|
+
* .eq('user_id', userId);
|
|
944
|
+
*
|
|
945
|
+
* // With count (Supabase style!)
|
|
946
|
+
* const { data, error, count } = await client.database
|
|
947
|
+
* .from('posts')
|
|
948
|
+
* .select('*', { count: 'exact' })
|
|
949
|
+
* .range(0, 9);
|
|
950
|
+
*
|
|
951
|
+
* // Just get count, no data
|
|
952
|
+
* const { count } = await client.database
|
|
953
|
+
* .from('posts')
|
|
954
|
+
* .select('*', { count: 'exact', head: true });
|
|
955
|
+
*
|
|
956
|
+
* // Complex queries with OR
|
|
957
|
+
* const { data } = await client.database
|
|
958
|
+
* .from('posts')
|
|
959
|
+
* .select('*, users!inner(*)')
|
|
960
|
+
* .or('status.eq.active,status.eq.pending');
|
|
961
|
+
*
|
|
962
|
+
* // All features work:
|
|
963
|
+
* - Nested selects
|
|
964
|
+
* - Foreign key expansion
|
|
965
|
+
* - OR/AND/NOT conditions
|
|
966
|
+
* - Count with head
|
|
967
|
+
* - Range pagination
|
|
968
|
+
* - Upserts
|
|
969
|
+
*/
|
|
970
|
+
from(table) {
|
|
971
|
+
return this.postgrest.from(table);
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
|
|
845
975
|
// src/modules/storage.ts
|
|
846
976
|
var StorageBucket = class {
|
|
847
977
|
constructor(bucketName, http) {
|
|
@@ -1636,10 +1766,7 @@ var InsForgeClient = class {
|
|
|
1636
1766
|
if (existingSession?.accessToken) {
|
|
1637
1767
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1638
1768
|
}
|
|
1639
|
-
this.auth = new Auth(
|
|
1640
|
-
this.http,
|
|
1641
|
-
this.tokenManager
|
|
1642
|
-
);
|
|
1769
|
+
this.auth = new Auth(this.http, this.tokenManager);
|
|
1643
1770
|
this.database = new Database(this.http, this.tokenManager);
|
|
1644
1771
|
this.storage = new Storage(this.http);
|
|
1645
1772
|
this.ai = new AI(this.http);
|