@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.mjs
CHANGED
|
@@ -138,8 +138,31 @@ var HttpClient = class {
|
|
|
138
138
|
// src/lib/token-manager.ts
|
|
139
139
|
var TOKEN_KEY = "insforge-auth-token";
|
|
140
140
|
var USER_KEY = "insforge-auth-user";
|
|
141
|
+
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
142
|
+
function getCsrfToken() {
|
|
143
|
+
if (typeof document === "undefined") return null;
|
|
144
|
+
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
145
|
+
if (!match) return null;
|
|
146
|
+
return match.split("=")[1] || null;
|
|
147
|
+
}
|
|
148
|
+
function setCsrfToken(token) {
|
|
149
|
+
if (typeof document === "undefined") return;
|
|
150
|
+
const maxAge = 7 * 24 * 60 * 60;
|
|
151
|
+
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
152
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=${encodeURIComponent(token)}; path=/; max-age=${maxAge}; SameSite=Lax${secure}`;
|
|
153
|
+
}
|
|
154
|
+
function clearCsrfToken() {
|
|
155
|
+
if (typeof document === "undefined") return;
|
|
156
|
+
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
157
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax${secure}`;
|
|
158
|
+
}
|
|
141
159
|
var TokenManager = class {
|
|
142
160
|
constructor(storage) {
|
|
161
|
+
// In-memory storage
|
|
162
|
+
this.accessToken = null;
|
|
163
|
+
this.user = null;
|
|
164
|
+
// Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
|
|
165
|
+
this._mode = "storage";
|
|
143
166
|
if (storage) {
|
|
144
167
|
this.storage = storage;
|
|
145
168
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -157,126 +180,117 @@ var TokenManager = class {
|
|
|
157
180
|
};
|
|
158
181
|
}
|
|
159
182
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Get current mode
|
|
185
|
+
*/
|
|
186
|
+
get mode() {
|
|
187
|
+
return this._mode;
|
|
163
188
|
}
|
|
164
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Set mode to memory (new backend with cookies + memory)
|
|
191
|
+
*/
|
|
192
|
+
setMemoryMode() {
|
|
193
|
+
if (this._mode === "storage") {
|
|
194
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
195
|
+
this.storage.removeItem(USER_KEY);
|
|
196
|
+
}
|
|
197
|
+
this._mode = "memory";
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Set mode to storage (legacy backend with localStorage)
|
|
201
|
+
* Also loads existing session from localStorage
|
|
202
|
+
*/
|
|
203
|
+
setStorageMode() {
|
|
204
|
+
this._mode = "storage";
|
|
205
|
+
this.loadFromStorage();
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Load session from localStorage
|
|
209
|
+
*/
|
|
210
|
+
loadFromStorage() {
|
|
165
211
|
const token = this.storage.getItem(TOKEN_KEY);
|
|
166
212
|
const userStr = this.storage.getItem(USER_KEY);
|
|
167
|
-
if (
|
|
168
|
-
|
|
213
|
+
if (token && userStr) {
|
|
214
|
+
try {
|
|
215
|
+
this.accessToken = token;
|
|
216
|
+
this.user = JSON.parse(userStr);
|
|
217
|
+
} catch {
|
|
218
|
+
this.clearSession();
|
|
219
|
+
}
|
|
169
220
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Save session (memory always, localStorage only in storage mode)
|
|
224
|
+
*/
|
|
225
|
+
saveSession(session) {
|
|
226
|
+
this.accessToken = session.accessToken;
|
|
227
|
+
this.user = session.user;
|
|
228
|
+
if (this._mode === "storage") {
|
|
229
|
+
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
230
|
+
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
176
231
|
}
|
|
177
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Get current session
|
|
235
|
+
*/
|
|
236
|
+
getSession() {
|
|
237
|
+
this.loadFromStorage();
|
|
238
|
+
if (!this.accessToken || !this.user) return null;
|
|
239
|
+
return {
|
|
240
|
+
accessToken: this.accessToken,
|
|
241
|
+
user: this.user
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get access token
|
|
246
|
+
*/
|
|
178
247
|
getAccessToken() {
|
|
179
|
-
|
|
180
|
-
return
|
|
248
|
+
this.loadFromStorage();
|
|
249
|
+
return this.accessToken;
|
|
181
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Set access token
|
|
253
|
+
*/
|
|
254
|
+
setAccessToken(token) {
|
|
255
|
+
this.accessToken = token;
|
|
256
|
+
if (this._mode === "storage") {
|
|
257
|
+
this.storage.setItem(TOKEN_KEY, token);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get user
|
|
262
|
+
*/
|
|
263
|
+
getUser() {
|
|
264
|
+
return this.user;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Set user
|
|
268
|
+
*/
|
|
269
|
+
setUser(user) {
|
|
270
|
+
this.user = user;
|
|
271
|
+
if (this._mode === "storage") {
|
|
272
|
+
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Clear session (both memory and localStorage)
|
|
277
|
+
*/
|
|
182
278
|
clearSession() {
|
|
279
|
+
this.accessToken = null;
|
|
280
|
+
this.user = null;
|
|
183
281
|
this.storage.removeItem(TOKEN_KEY);
|
|
184
282
|
this.storage.removeItem(USER_KEY);
|
|
185
283
|
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// src/modules/database-postgrest.ts
|
|
189
|
-
import { PostgrestClient } from "@supabase/postgrest-js";
|
|
190
|
-
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
191
|
-
return async (input, init) => {
|
|
192
|
-
const url = typeof input === "string" ? input : input.toString();
|
|
193
|
-
const urlObj = new URL(url);
|
|
194
|
-
const tableName = urlObj.pathname.slice(1);
|
|
195
|
-
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
196
|
-
const token = tokenManager.getAccessToken();
|
|
197
|
-
const httpHeaders = httpClient.getHeaders();
|
|
198
|
-
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
199
|
-
const headers = new Headers(init?.headers);
|
|
200
|
-
if (authToken && !headers.has("Authorization")) {
|
|
201
|
-
headers.set("Authorization", `Bearer ${authToken}`);
|
|
202
|
-
}
|
|
203
|
-
const response = await fetch(insforgeUrl, {
|
|
204
|
-
...init,
|
|
205
|
-
headers
|
|
206
|
-
});
|
|
207
|
-
return response;
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
var Database = class {
|
|
211
|
-
constructor(httpClient, tokenManager) {
|
|
212
|
-
this.postgrest = new PostgrestClient("http://dummy", {
|
|
213
|
-
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
214
|
-
headers: {}
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
284
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* @example
|
|
221
|
-
* // Basic query
|
|
222
|
-
* const { data, error } = await client.database
|
|
223
|
-
* .from('posts')
|
|
224
|
-
* .select('*')
|
|
225
|
-
* .eq('user_id', userId);
|
|
226
|
-
*
|
|
227
|
-
* // With count (Supabase style!)
|
|
228
|
-
* const { data, error, count } = await client.database
|
|
229
|
-
* .from('posts')
|
|
230
|
-
* .select('*', { count: 'exact' })
|
|
231
|
-
* .range(0, 9);
|
|
232
|
-
*
|
|
233
|
-
* // Just get count, no data
|
|
234
|
-
* const { count } = await client.database
|
|
235
|
-
* .from('posts')
|
|
236
|
-
* .select('*', { count: 'exact', head: true });
|
|
237
|
-
*
|
|
238
|
-
* // Complex queries with OR
|
|
239
|
-
* const { data } = await client.database
|
|
240
|
-
* .from('posts')
|
|
241
|
-
* .select('*, users!inner(*)')
|
|
242
|
-
* .or('status.eq.active,status.eq.pending');
|
|
243
|
-
*
|
|
244
|
-
* // All features work:
|
|
245
|
-
* - Nested selects
|
|
246
|
-
* - Foreign key expansion
|
|
247
|
-
* - OR/AND/NOT conditions
|
|
248
|
-
* - Count with head
|
|
249
|
-
* - Range pagination
|
|
250
|
-
* - Upserts
|
|
285
|
+
* Check if there's a session in localStorage (for legacy detection)
|
|
251
286
|
*/
|
|
252
|
-
|
|
253
|
-
|
|
287
|
+
hasStoredSession() {
|
|
288
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
289
|
+
return !!token;
|
|
254
290
|
}
|
|
255
291
|
};
|
|
256
292
|
|
|
257
293
|
// src/modules/auth.ts
|
|
258
|
-
function convertDbProfileToCamelCase(dbProfile) {
|
|
259
|
-
const result = {
|
|
260
|
-
id: dbProfile.id
|
|
261
|
-
};
|
|
262
|
-
Object.keys(dbProfile).forEach((key) => {
|
|
263
|
-
result[key] = dbProfile[key];
|
|
264
|
-
if (key.includes("_")) {
|
|
265
|
-
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
266
|
-
result[camelKey] = dbProfile[key];
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
return result;
|
|
270
|
-
}
|
|
271
|
-
function convertCamelCaseToDbProfile(profile) {
|
|
272
|
-
const dbProfile = {};
|
|
273
|
-
Object.keys(profile).forEach((key) => {
|
|
274
|
-
if (profile[key] === void 0) return;
|
|
275
|
-
const snakeKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
276
|
-
dbProfile[snakeKey] = profile[key];
|
|
277
|
-
});
|
|
278
|
-
return dbProfile;
|
|
279
|
-
}
|
|
280
294
|
function isHostedAuthEnvironment() {
|
|
281
295
|
if (typeof window === "undefined") {
|
|
282
296
|
return false;
|
|
@@ -294,15 +308,14 @@ var Auth = class {
|
|
|
294
308
|
constructor(http, tokenManager) {
|
|
295
309
|
this.http = http;
|
|
296
310
|
this.tokenManager = tokenManager;
|
|
297
|
-
this.database = new Database(http, tokenManager);
|
|
298
311
|
this.detectAuthCallback();
|
|
299
312
|
}
|
|
300
313
|
/**
|
|
301
314
|
* Automatically detect and handle OAuth callback parameters in the URL
|
|
302
|
-
* This runs
|
|
315
|
+
* This runs after initialization to seamlessly complete the OAuth flow
|
|
303
316
|
* Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
|
|
304
317
|
*/
|
|
305
|
-
detectAuthCallback() {
|
|
318
|
+
async detectAuthCallback() {
|
|
306
319
|
if (typeof window === "undefined") return;
|
|
307
320
|
try {
|
|
308
321
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -310,13 +323,20 @@ var Auth = class {
|
|
|
310
323
|
const userId = params.get("user_id");
|
|
311
324
|
const email = params.get("email");
|
|
312
325
|
const name = params.get("name");
|
|
326
|
+
const csrfToken = params.get("csrf_token");
|
|
313
327
|
if (accessToken && userId && email) {
|
|
328
|
+
if (csrfToken) {
|
|
329
|
+
this.tokenManager.setMemoryMode();
|
|
330
|
+
setCsrfToken(csrfToken);
|
|
331
|
+
}
|
|
332
|
+
const { data: profileData } = await this.getProfile(userId);
|
|
314
333
|
const session = {
|
|
315
334
|
accessToken,
|
|
316
335
|
user: {
|
|
317
336
|
id: userId,
|
|
318
337
|
email,
|
|
319
|
-
name: name || "",
|
|
338
|
+
profile: profileData?.profile || { name: name || "" },
|
|
339
|
+
metadata: null,
|
|
320
340
|
// These fields are not provided by backend OAuth callback
|
|
321
341
|
// They'll be populated when calling getCurrentUser()
|
|
322
342
|
emailVerified: false,
|
|
@@ -331,6 +351,7 @@ var Auth = class {
|
|
|
331
351
|
url.searchParams.delete("user_id");
|
|
332
352
|
url.searchParams.delete("email");
|
|
333
353
|
url.searchParams.delete("name");
|
|
354
|
+
url.searchParams.delete("csrf_token");
|
|
334
355
|
if (params.has("error")) {
|
|
335
356
|
url.searchParams.delete("error");
|
|
336
357
|
}
|
|
@@ -346,15 +367,16 @@ var Auth = class {
|
|
|
346
367
|
async signUp(request) {
|
|
347
368
|
try {
|
|
348
369
|
const response = await this.http.post("/api/auth/users", request);
|
|
349
|
-
if (response.accessToken && response.user) {
|
|
370
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
350
371
|
const session = {
|
|
351
372
|
accessToken: response.accessToken,
|
|
352
373
|
user: response.user
|
|
353
374
|
};
|
|
354
|
-
|
|
355
|
-
this.tokenManager.saveSession(session);
|
|
356
|
-
}
|
|
375
|
+
this.tokenManager.saveSession(session);
|
|
357
376
|
this.http.setAuthToken(response.accessToken);
|
|
377
|
+
if (response.csrfToken) {
|
|
378
|
+
setCsrfToken(response.csrfToken);
|
|
379
|
+
}
|
|
358
380
|
}
|
|
359
381
|
return {
|
|
360
382
|
data: response,
|
|
@@ -380,21 +402,17 @@ var Auth = class {
|
|
|
380
402
|
async signInWithPassword(request) {
|
|
381
403
|
try {
|
|
382
404
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
383
|
-
const session = {
|
|
384
|
-
accessToken: response.accessToken || "",
|
|
385
|
-
user: response.user || {
|
|
386
|
-
id: "",
|
|
387
|
-
email: "",
|
|
388
|
-
name: "",
|
|
389
|
-
emailVerified: false,
|
|
390
|
-
createdAt: "",
|
|
391
|
-
updatedAt: ""
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
405
|
if (!isHostedAuthEnvironment()) {
|
|
406
|
+
const session = {
|
|
407
|
+
accessToken: response.accessToken,
|
|
408
|
+
user: response.user
|
|
409
|
+
};
|
|
395
410
|
this.tokenManager.saveSession(session);
|
|
411
|
+
this.http.setAuthToken(response.accessToken);
|
|
412
|
+
if (response.csrfToken) {
|
|
413
|
+
setCsrfToken(response.csrfToken);
|
|
414
|
+
}
|
|
396
415
|
}
|
|
397
|
-
this.http.setAuthToken(response.accessToken || "");
|
|
398
416
|
return {
|
|
399
417
|
data: response,
|
|
400
418
|
error: null
|
|
@@ -452,8 +470,13 @@ var Auth = class {
|
|
|
452
470
|
*/
|
|
453
471
|
async signOut() {
|
|
454
472
|
try {
|
|
473
|
+
try {
|
|
474
|
+
await this.http.post("/api/auth/logout", void 0, { credentials: "include" });
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
455
477
|
this.tokenManager.clearSession();
|
|
456
478
|
this.http.setAuthToken(null);
|
|
479
|
+
clearCsrfToken();
|
|
457
480
|
return { error: null };
|
|
458
481
|
} catch (error) {
|
|
459
482
|
return {
|
|
@@ -514,14 +537,9 @@ var Auth = class {
|
|
|
514
537
|
}
|
|
515
538
|
this.http.setAuthToken(session.accessToken);
|
|
516
539
|
const authResponse = await this.http.get("/api/auth/sessions/current");
|
|
517
|
-
const { data: profile, error: profileError } = await this.database.from("users").select("*").eq("id", authResponse.user.id).single();
|
|
518
|
-
if (profileError && profileError.code !== "PGRST116") {
|
|
519
|
-
return { data: null, error: profileError };
|
|
520
|
-
}
|
|
521
540
|
return {
|
|
522
541
|
data: {
|
|
523
|
-
user: authResponse.user
|
|
524
|
-
profile: profile ? convertDbProfileToCamelCase(profile) : null
|
|
542
|
+
user: authResponse.user
|
|
525
543
|
},
|
|
526
544
|
error: null
|
|
527
545
|
};
|
|
@@ -545,29 +563,80 @@ var Auth = class {
|
|
|
545
563
|
}
|
|
546
564
|
/**
|
|
547
565
|
* Get any user's profile by ID
|
|
548
|
-
* Returns profile information from the users table
|
|
566
|
+
* Returns profile information from the users table
|
|
549
567
|
*/
|
|
550
568
|
async getProfile(userId) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
return {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
569
|
+
try {
|
|
570
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
571
|
+
return {
|
|
572
|
+
data: response,
|
|
573
|
+
error: null
|
|
574
|
+
};
|
|
575
|
+
} catch (error) {
|
|
576
|
+
if (error instanceof InsForgeError) {
|
|
577
|
+
return { data: null, error };
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
data: null,
|
|
581
|
+
error: new InsForgeError(
|
|
582
|
+
"An unexpected error occurred while fetching user profile",
|
|
583
|
+
500,
|
|
584
|
+
"UNEXPECTED_ERROR"
|
|
585
|
+
)
|
|
586
|
+
};
|
|
557
587
|
}
|
|
558
|
-
return { data: null, error };
|
|
559
588
|
}
|
|
560
589
|
/**
|
|
561
590
|
* Get the current session (only session data, no API call)
|
|
562
591
|
* Returns the stored JWT token and basic user info from local storage
|
|
563
592
|
*/
|
|
564
|
-
getCurrentSession() {
|
|
593
|
+
async getCurrentSession() {
|
|
565
594
|
try {
|
|
566
595
|
const session = this.tokenManager.getSession();
|
|
567
|
-
if (session
|
|
596
|
+
if (session) {
|
|
568
597
|
this.http.setAuthToken(session.accessToken);
|
|
569
598
|
return { data: { session }, error: null };
|
|
570
599
|
}
|
|
600
|
+
if (typeof window !== "undefined") {
|
|
601
|
+
try {
|
|
602
|
+
const csrfToken = getCsrfToken();
|
|
603
|
+
const response = await this.http.post(
|
|
604
|
+
"/api/auth/refresh",
|
|
605
|
+
void 0,
|
|
606
|
+
{
|
|
607
|
+
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
608
|
+
credentials: "include"
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
if (response.accessToken) {
|
|
612
|
+
this.tokenManager.setMemoryMode();
|
|
613
|
+
this.tokenManager.setAccessToken(response.accessToken);
|
|
614
|
+
this.http.setAuthToken(response.accessToken);
|
|
615
|
+
if (response.user) {
|
|
616
|
+
this.tokenManager.setUser(response.user);
|
|
617
|
+
}
|
|
618
|
+
if (response.csrfToken) {
|
|
619
|
+
setCsrfToken(response.csrfToken);
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
data: { session: this.tokenManager.getSession() },
|
|
623
|
+
error: null
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
} catch (error) {
|
|
627
|
+
if (error instanceof InsForgeError) {
|
|
628
|
+
if (error.statusCode === 404) {
|
|
629
|
+
this.tokenManager.setStorageMode();
|
|
630
|
+
const session2 = this.tokenManager.getSession();
|
|
631
|
+
if (session2) {
|
|
632
|
+
return { data: { session: session2 }, error: null };
|
|
633
|
+
}
|
|
634
|
+
return { data: { session: null }, error: null };
|
|
635
|
+
}
|
|
636
|
+
return { data: { session: null }, error };
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
571
640
|
return { data: { session: null }, error: null };
|
|
572
641
|
} catch (error) {
|
|
573
642
|
if (error instanceof InsForgeError) {
|
|
@@ -586,42 +655,31 @@ var Auth = class {
|
|
|
586
655
|
/**
|
|
587
656
|
* Set/Update the current user's profile
|
|
588
657
|
* Updates profile information in the users table (supports any dynamic fields)
|
|
658
|
+
* Requires authentication
|
|
589
659
|
*/
|
|
590
660
|
async setProfile(profile) {
|
|
591
|
-
|
|
592
|
-
|
|
661
|
+
try {
|
|
662
|
+
const response = await this.http.patch(
|
|
663
|
+
"/api/auth/profiles/current",
|
|
664
|
+
{ profile }
|
|
665
|
+
);
|
|
666
|
+
return {
|
|
667
|
+
data: response,
|
|
668
|
+
error: null
|
|
669
|
+
};
|
|
670
|
+
} catch (error) {
|
|
671
|
+
if (error instanceof InsForgeError) {
|
|
672
|
+
return { data: null, error };
|
|
673
|
+
}
|
|
593
674
|
return {
|
|
594
675
|
data: null,
|
|
595
676
|
error: new InsForgeError(
|
|
596
|
-
"
|
|
597
|
-
|
|
598
|
-
"
|
|
677
|
+
"An unexpected error occurred while updating user profile",
|
|
678
|
+
500,
|
|
679
|
+
"UNEXPECTED_ERROR"
|
|
599
680
|
)
|
|
600
681
|
};
|
|
601
682
|
}
|
|
602
|
-
if (!session.user?.id) {
|
|
603
|
-
const { data: data2, error: error2 } = await this.getCurrentUser();
|
|
604
|
-
if (error2) {
|
|
605
|
-
return { data: null, error: error2 };
|
|
606
|
-
}
|
|
607
|
-
if (data2?.user) {
|
|
608
|
-
session.user = {
|
|
609
|
-
id: data2.user.id,
|
|
610
|
-
email: data2.user.email,
|
|
611
|
-
name: "",
|
|
612
|
-
emailVerified: false,
|
|
613
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
614
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
615
|
-
};
|
|
616
|
-
this.tokenManager.saveSession(session);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
const dbProfile = convertCamelCaseToDbProfile(profile);
|
|
620
|
-
const { data, error } = await this.database.from("users").update(dbProfile).eq("id", session.user.id).select().single();
|
|
621
|
-
if (data) {
|
|
622
|
-
return { data: convertDbProfileToCamelCase(data), error: null };
|
|
623
|
-
}
|
|
624
|
-
return { data: null, error };
|
|
625
683
|
}
|
|
626
684
|
/**
|
|
627
685
|
* Send email verification (code or link based on config)
|
|
@@ -775,13 +833,16 @@ var Auth = class {
|
|
|
775
833
|
"/api/auth/email/verify",
|
|
776
834
|
request
|
|
777
835
|
);
|
|
778
|
-
if (
|
|
836
|
+
if (!isHostedAuthEnvironment()) {
|
|
779
837
|
const session = {
|
|
780
838
|
accessToken: response.accessToken,
|
|
781
|
-
user: response.user
|
|
839
|
+
user: response.user
|
|
782
840
|
};
|
|
783
841
|
this.tokenManager.saveSession(session);
|
|
784
842
|
this.http.setAuthToken(response.accessToken);
|
|
843
|
+
if (response.csrfToken) {
|
|
844
|
+
setCsrfToken(response.csrfToken);
|
|
845
|
+
}
|
|
785
846
|
}
|
|
786
847
|
return {
|
|
787
848
|
data: response,
|
|
@@ -803,6 +864,75 @@ var Auth = class {
|
|
|
803
864
|
}
|
|
804
865
|
};
|
|
805
866
|
|
|
867
|
+
// src/modules/database-postgrest.ts
|
|
868
|
+
import { PostgrestClient } from "@supabase/postgrest-js";
|
|
869
|
+
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
870
|
+
return async (input, init) => {
|
|
871
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
872
|
+
const urlObj = new URL(url);
|
|
873
|
+
const tableName = urlObj.pathname.slice(1);
|
|
874
|
+
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
875
|
+
const token = tokenManager.getAccessToken();
|
|
876
|
+
const httpHeaders = httpClient.getHeaders();
|
|
877
|
+
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
878
|
+
const headers = new Headers(init?.headers);
|
|
879
|
+
if (authToken && !headers.has("Authorization")) {
|
|
880
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
881
|
+
}
|
|
882
|
+
const response = await fetch(insforgeUrl, {
|
|
883
|
+
...init,
|
|
884
|
+
headers
|
|
885
|
+
});
|
|
886
|
+
return response;
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
var Database = class {
|
|
890
|
+
constructor(httpClient, tokenManager) {
|
|
891
|
+
this.postgrest = new PostgrestClient("http://dummy", {
|
|
892
|
+
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
893
|
+
headers: {}
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Create a query builder for a table
|
|
898
|
+
*
|
|
899
|
+
* @example
|
|
900
|
+
* // Basic query
|
|
901
|
+
* const { data, error } = await client.database
|
|
902
|
+
* .from('posts')
|
|
903
|
+
* .select('*')
|
|
904
|
+
* .eq('user_id', userId);
|
|
905
|
+
*
|
|
906
|
+
* // With count (Supabase style!)
|
|
907
|
+
* const { data, error, count } = await client.database
|
|
908
|
+
* .from('posts')
|
|
909
|
+
* .select('*', { count: 'exact' })
|
|
910
|
+
* .range(0, 9);
|
|
911
|
+
*
|
|
912
|
+
* // Just get count, no data
|
|
913
|
+
* const { count } = await client.database
|
|
914
|
+
* .from('posts')
|
|
915
|
+
* .select('*', { count: 'exact', head: true });
|
|
916
|
+
*
|
|
917
|
+
* // Complex queries with OR
|
|
918
|
+
* const { data } = await client.database
|
|
919
|
+
* .from('posts')
|
|
920
|
+
* .select('*, users!inner(*)')
|
|
921
|
+
* .or('status.eq.active,status.eq.pending');
|
|
922
|
+
*
|
|
923
|
+
* // All features work:
|
|
924
|
+
* - Nested selects
|
|
925
|
+
* - Foreign key expansion
|
|
926
|
+
* - OR/AND/NOT conditions
|
|
927
|
+
* - Count with head
|
|
928
|
+
* - Range pagination
|
|
929
|
+
* - Upserts
|
|
930
|
+
*/
|
|
931
|
+
from(table) {
|
|
932
|
+
return this.postgrest.from(table);
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
|
|
806
936
|
// src/modules/storage.ts
|
|
807
937
|
var StorageBucket = class {
|
|
808
938
|
constructor(bucketName, http) {
|
|
@@ -1597,10 +1727,7 @@ var InsForgeClient = class {
|
|
|
1597
1727
|
if (existingSession?.accessToken) {
|
|
1598
1728
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1599
1729
|
}
|
|
1600
|
-
this.auth = new Auth(
|
|
1601
|
-
this.http,
|
|
1602
|
-
this.tokenManager
|
|
1603
|
-
);
|
|
1730
|
+
this.auth = new Auth(this.http, this.tokenManager);
|
|
1604
1731
|
this.database = new Database(this.http, this.tokenManager);
|
|
1605
1732
|
this.storage = new Storage(this.http);
|
|
1606
1733
|
this.ai = new AI(this.http);
|