@insforge/sdk 1.0.2 → 1.0.3-dev.1
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 +72 -40
- package/dist/index.d.ts +72 -40
- package/dist/index.js +298 -172
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +298 -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,12 +347,11 @@ 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
357
|
detectAuthCallback() {
|
|
@@ -349,13 +362,19 @@ 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
|
+
}
|
|
353
371
|
const session = {
|
|
354
372
|
accessToken,
|
|
355
373
|
user: {
|
|
356
374
|
id: userId,
|
|
357
375
|
email,
|
|
358
|
-
|
|
376
|
+
profile: null,
|
|
377
|
+
metadata: null,
|
|
359
378
|
// These fields are not provided by backend OAuth callback
|
|
360
379
|
// They'll be populated when calling getCurrentUser()
|
|
361
380
|
emailVerified: false,
|
|
@@ -370,6 +389,7 @@ var Auth = class {
|
|
|
370
389
|
url.searchParams.delete("user_id");
|
|
371
390
|
url.searchParams.delete("email");
|
|
372
391
|
url.searchParams.delete("name");
|
|
392
|
+
url.searchParams.delete("csrf_token");
|
|
373
393
|
if (params.has("error")) {
|
|
374
394
|
url.searchParams.delete("error");
|
|
375
395
|
}
|
|
@@ -385,15 +405,16 @@ var Auth = class {
|
|
|
385
405
|
async signUp(request) {
|
|
386
406
|
try {
|
|
387
407
|
const response = await this.http.post("/api/auth/users", request);
|
|
388
|
-
if (response.accessToken && response.user) {
|
|
408
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
389
409
|
const session = {
|
|
390
410
|
accessToken: response.accessToken,
|
|
391
411
|
user: response.user
|
|
392
412
|
};
|
|
393
|
-
|
|
394
|
-
this.tokenManager.saveSession(session);
|
|
395
|
-
}
|
|
413
|
+
this.tokenManager.saveSession(session);
|
|
396
414
|
this.http.setAuthToken(response.accessToken);
|
|
415
|
+
if (response.csrfToken) {
|
|
416
|
+
setCsrfToken(response.csrfToken);
|
|
417
|
+
}
|
|
397
418
|
}
|
|
398
419
|
return {
|
|
399
420
|
data: response,
|
|
@@ -419,21 +440,17 @@ var Auth = class {
|
|
|
419
440
|
async signInWithPassword(request) {
|
|
420
441
|
try {
|
|
421
442
|
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
443
|
if (!isHostedAuthEnvironment()) {
|
|
444
|
+
const session = {
|
|
445
|
+
accessToken: response.accessToken,
|
|
446
|
+
user: response.user
|
|
447
|
+
};
|
|
434
448
|
this.tokenManager.saveSession(session);
|
|
449
|
+
this.http.setAuthToken(response.accessToken);
|
|
450
|
+
if (response.csrfToken) {
|
|
451
|
+
setCsrfToken(response.csrfToken);
|
|
452
|
+
}
|
|
435
453
|
}
|
|
436
|
-
this.http.setAuthToken(response.accessToken || "");
|
|
437
454
|
return {
|
|
438
455
|
data: response,
|
|
439
456
|
error: null
|
|
@@ -491,8 +508,13 @@ var Auth = class {
|
|
|
491
508
|
*/
|
|
492
509
|
async signOut() {
|
|
493
510
|
try {
|
|
511
|
+
try {
|
|
512
|
+
await this.http.post("/api/auth/logout", void 0, { credentials: "include" });
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
494
515
|
this.tokenManager.clearSession();
|
|
495
516
|
this.http.setAuthToken(null);
|
|
517
|
+
clearCsrfToken();
|
|
496
518
|
return { error: null };
|
|
497
519
|
} catch (error) {
|
|
498
520
|
return {
|
|
@@ -553,14 +575,9 @@ var Auth = class {
|
|
|
553
575
|
}
|
|
554
576
|
this.http.setAuthToken(session.accessToken);
|
|
555
577
|
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
578
|
return {
|
|
561
579
|
data: {
|
|
562
|
-
user: authResponse.user
|
|
563
|
-
profile: profile ? convertDbProfileToCamelCase(profile) : null
|
|
580
|
+
user: authResponse.user
|
|
564
581
|
},
|
|
565
582
|
error: null
|
|
566
583
|
};
|
|
@@ -584,29 +601,80 @@ var Auth = class {
|
|
|
584
601
|
}
|
|
585
602
|
/**
|
|
586
603
|
* Get any user's profile by ID
|
|
587
|
-
* Returns profile information from the users table
|
|
604
|
+
* Returns profile information from the users table
|
|
588
605
|
*/
|
|
589
606
|
async getProfile(userId) {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
607
|
+
try {
|
|
608
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
609
|
+
return {
|
|
610
|
+
data: response,
|
|
611
|
+
error: null
|
|
612
|
+
};
|
|
613
|
+
} catch (error) {
|
|
614
|
+
if (error instanceof InsForgeError) {
|
|
615
|
+
return { data: null, error };
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
data: null,
|
|
619
|
+
error: new InsForgeError(
|
|
620
|
+
"An unexpected error occurred while fetching user profile",
|
|
621
|
+
500,
|
|
622
|
+
"UNEXPECTED_ERROR"
|
|
623
|
+
)
|
|
624
|
+
};
|
|
596
625
|
}
|
|
597
|
-
return { data: null, error };
|
|
598
626
|
}
|
|
599
627
|
/**
|
|
600
628
|
* Get the current session (only session data, no API call)
|
|
601
629
|
* Returns the stored JWT token and basic user info from local storage
|
|
602
630
|
*/
|
|
603
|
-
getCurrentSession() {
|
|
631
|
+
async getCurrentSession() {
|
|
604
632
|
try {
|
|
605
633
|
const session = this.tokenManager.getSession();
|
|
606
|
-
if (session
|
|
634
|
+
if (session) {
|
|
607
635
|
this.http.setAuthToken(session.accessToken);
|
|
608
636
|
return { data: { session }, error: null };
|
|
609
637
|
}
|
|
638
|
+
if (typeof window !== "undefined") {
|
|
639
|
+
try {
|
|
640
|
+
const csrfToken = getCsrfToken();
|
|
641
|
+
const response = await this.http.post(
|
|
642
|
+
"/api/auth/refresh",
|
|
643
|
+
void 0,
|
|
644
|
+
{
|
|
645
|
+
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
646
|
+
credentials: "include"
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
if (response.accessToken) {
|
|
650
|
+
this.tokenManager.setMemoryMode();
|
|
651
|
+
this.tokenManager.setAccessToken(response.accessToken);
|
|
652
|
+
this.http.setAuthToken(response.accessToken);
|
|
653
|
+
if (response.user) {
|
|
654
|
+
this.tokenManager.setUser(response.user);
|
|
655
|
+
}
|
|
656
|
+
if (response.csrfToken) {
|
|
657
|
+
setCsrfToken(response.csrfToken);
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
data: { session: this.tokenManager.getSession() },
|
|
661
|
+
error: null
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
} catch (error) {
|
|
665
|
+
if (error instanceof InsForgeError) {
|
|
666
|
+
if (error.statusCode === 404) {
|
|
667
|
+
this.tokenManager.setStorageMode();
|
|
668
|
+
const session2 = this.tokenManager.getSession();
|
|
669
|
+
if (session2) {
|
|
670
|
+
return { data: { session: session2 }, error: null };
|
|
671
|
+
}
|
|
672
|
+
return { data: { session: null }, error: null };
|
|
673
|
+
}
|
|
674
|
+
return { data: { session: null }, error };
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
610
678
|
return { data: { session: null }, error: null };
|
|
611
679
|
} catch (error) {
|
|
612
680
|
if (error instanceof InsForgeError) {
|
|
@@ -625,42 +693,31 @@ var Auth = class {
|
|
|
625
693
|
/**
|
|
626
694
|
* Set/Update the current user's profile
|
|
627
695
|
* Updates profile information in the users table (supports any dynamic fields)
|
|
696
|
+
* Requires authentication
|
|
628
697
|
*/
|
|
629
698
|
async setProfile(profile) {
|
|
630
|
-
|
|
631
|
-
|
|
699
|
+
try {
|
|
700
|
+
const response = await this.http.patch(
|
|
701
|
+
"/api/auth/profiles/current",
|
|
702
|
+
{ profile }
|
|
703
|
+
);
|
|
704
|
+
return {
|
|
705
|
+
data: response,
|
|
706
|
+
error: null
|
|
707
|
+
};
|
|
708
|
+
} catch (error) {
|
|
709
|
+
if (error instanceof InsForgeError) {
|
|
710
|
+
return { data: null, error };
|
|
711
|
+
}
|
|
632
712
|
return {
|
|
633
713
|
data: null,
|
|
634
714
|
error: new InsForgeError(
|
|
635
|
-
"
|
|
636
|
-
|
|
637
|
-
"
|
|
715
|
+
"An unexpected error occurred while updating user profile",
|
|
716
|
+
500,
|
|
717
|
+
"UNEXPECTED_ERROR"
|
|
638
718
|
)
|
|
639
719
|
};
|
|
640
720
|
}
|
|
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
721
|
}
|
|
665
722
|
/**
|
|
666
723
|
* Send email verification (code or link based on config)
|
|
@@ -814,13 +871,16 @@ var Auth = class {
|
|
|
814
871
|
"/api/auth/email/verify",
|
|
815
872
|
request
|
|
816
873
|
);
|
|
817
|
-
if (
|
|
874
|
+
if (!isHostedAuthEnvironment()) {
|
|
818
875
|
const session = {
|
|
819
876
|
accessToken: response.accessToken,
|
|
820
|
-
user: response.user
|
|
877
|
+
user: response.user
|
|
821
878
|
};
|
|
822
879
|
this.tokenManager.saveSession(session);
|
|
823
880
|
this.http.setAuthToken(response.accessToken);
|
|
881
|
+
if (response.csrfToken) {
|
|
882
|
+
setCsrfToken(response.csrfToken);
|
|
883
|
+
}
|
|
824
884
|
}
|
|
825
885
|
return {
|
|
826
886
|
data: response,
|
|
@@ -842,6 +902,75 @@ var Auth = class {
|
|
|
842
902
|
}
|
|
843
903
|
};
|
|
844
904
|
|
|
905
|
+
// src/modules/database-postgrest.ts
|
|
906
|
+
var import_postgrest_js = require("@supabase/postgrest-js");
|
|
907
|
+
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
908
|
+
return async (input, init) => {
|
|
909
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
910
|
+
const urlObj = new URL(url);
|
|
911
|
+
const tableName = urlObj.pathname.slice(1);
|
|
912
|
+
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
913
|
+
const token = tokenManager.getAccessToken();
|
|
914
|
+
const httpHeaders = httpClient.getHeaders();
|
|
915
|
+
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
916
|
+
const headers = new Headers(init?.headers);
|
|
917
|
+
if (authToken && !headers.has("Authorization")) {
|
|
918
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
919
|
+
}
|
|
920
|
+
const response = await fetch(insforgeUrl, {
|
|
921
|
+
...init,
|
|
922
|
+
headers
|
|
923
|
+
});
|
|
924
|
+
return response;
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
var Database = class {
|
|
928
|
+
constructor(httpClient, tokenManager) {
|
|
929
|
+
this.postgrest = new import_postgrest_js.PostgrestClient("http://dummy", {
|
|
930
|
+
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
931
|
+
headers: {}
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Create a query builder for a table
|
|
936
|
+
*
|
|
937
|
+
* @example
|
|
938
|
+
* // Basic query
|
|
939
|
+
* const { data, error } = await client.database
|
|
940
|
+
* .from('posts')
|
|
941
|
+
* .select('*')
|
|
942
|
+
* .eq('user_id', userId);
|
|
943
|
+
*
|
|
944
|
+
* // With count (Supabase style!)
|
|
945
|
+
* const { data, error, count } = await client.database
|
|
946
|
+
* .from('posts')
|
|
947
|
+
* .select('*', { count: 'exact' })
|
|
948
|
+
* .range(0, 9);
|
|
949
|
+
*
|
|
950
|
+
* // Just get count, no data
|
|
951
|
+
* const { count } = await client.database
|
|
952
|
+
* .from('posts')
|
|
953
|
+
* .select('*', { count: 'exact', head: true });
|
|
954
|
+
*
|
|
955
|
+
* // Complex queries with OR
|
|
956
|
+
* const { data } = await client.database
|
|
957
|
+
* .from('posts')
|
|
958
|
+
* .select('*, users!inner(*)')
|
|
959
|
+
* .or('status.eq.active,status.eq.pending');
|
|
960
|
+
*
|
|
961
|
+
* // All features work:
|
|
962
|
+
* - Nested selects
|
|
963
|
+
* - Foreign key expansion
|
|
964
|
+
* - OR/AND/NOT conditions
|
|
965
|
+
* - Count with head
|
|
966
|
+
* - Range pagination
|
|
967
|
+
* - Upserts
|
|
968
|
+
*/
|
|
969
|
+
from(table) {
|
|
970
|
+
return this.postgrest.from(table);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
|
|
845
974
|
// src/modules/storage.ts
|
|
846
975
|
var StorageBucket = class {
|
|
847
976
|
constructor(bucketName, http) {
|
|
@@ -1603,7 +1732,7 @@ var Emails = class {
|
|
|
1603
1732
|
}
|
|
1604
1733
|
/**
|
|
1605
1734
|
* Send a custom HTML email
|
|
1606
|
-
* @param options Email options
|
|
1735
|
+
* @param options Email options including recipients, subject, and HTML content
|
|
1607
1736
|
*/
|
|
1608
1737
|
async send(options) {
|
|
1609
1738
|
try {
|
|
@@ -1636,10 +1765,7 @@ var InsForgeClient = class {
|
|
|
1636
1765
|
if (existingSession?.accessToken) {
|
|
1637
1766
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1638
1767
|
}
|
|
1639
|
-
this.auth = new Auth(
|
|
1640
|
-
this.http,
|
|
1641
|
-
this.tokenManager
|
|
1642
|
-
);
|
|
1768
|
+
this.auth = new Auth(this.http, this.tokenManager);
|
|
1643
1769
|
this.database = new Database(this.http, this.tokenManager);
|
|
1644
1770
|
this.storage = new Storage(this.http);
|
|
1645
1771
|
this.ai = new AI(this.http);
|