@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.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,12 +308,11 @@ 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
318
|
detectAuthCallback() {
|
|
@@ -310,13 +323,19 @@ 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
|
+
}
|
|
314
332
|
const session = {
|
|
315
333
|
accessToken,
|
|
316
334
|
user: {
|
|
317
335
|
id: userId,
|
|
318
336
|
email,
|
|
319
|
-
|
|
337
|
+
profile: null,
|
|
338
|
+
metadata: null,
|
|
320
339
|
// These fields are not provided by backend OAuth callback
|
|
321
340
|
// They'll be populated when calling getCurrentUser()
|
|
322
341
|
emailVerified: false,
|
|
@@ -331,6 +350,7 @@ var Auth = class {
|
|
|
331
350
|
url.searchParams.delete("user_id");
|
|
332
351
|
url.searchParams.delete("email");
|
|
333
352
|
url.searchParams.delete("name");
|
|
353
|
+
url.searchParams.delete("csrf_token");
|
|
334
354
|
if (params.has("error")) {
|
|
335
355
|
url.searchParams.delete("error");
|
|
336
356
|
}
|
|
@@ -346,15 +366,16 @@ var Auth = class {
|
|
|
346
366
|
async signUp(request) {
|
|
347
367
|
try {
|
|
348
368
|
const response = await this.http.post("/api/auth/users", request);
|
|
349
|
-
if (response.accessToken && response.user) {
|
|
369
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
350
370
|
const session = {
|
|
351
371
|
accessToken: response.accessToken,
|
|
352
372
|
user: response.user
|
|
353
373
|
};
|
|
354
|
-
|
|
355
|
-
this.tokenManager.saveSession(session);
|
|
356
|
-
}
|
|
374
|
+
this.tokenManager.saveSession(session);
|
|
357
375
|
this.http.setAuthToken(response.accessToken);
|
|
376
|
+
if (response.csrfToken) {
|
|
377
|
+
setCsrfToken(response.csrfToken);
|
|
378
|
+
}
|
|
358
379
|
}
|
|
359
380
|
return {
|
|
360
381
|
data: response,
|
|
@@ -380,21 +401,17 @@ var Auth = class {
|
|
|
380
401
|
async signInWithPassword(request) {
|
|
381
402
|
try {
|
|
382
403
|
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
404
|
if (!isHostedAuthEnvironment()) {
|
|
405
|
+
const session = {
|
|
406
|
+
accessToken: response.accessToken,
|
|
407
|
+
user: response.user
|
|
408
|
+
};
|
|
395
409
|
this.tokenManager.saveSession(session);
|
|
410
|
+
this.http.setAuthToken(response.accessToken);
|
|
411
|
+
if (response.csrfToken) {
|
|
412
|
+
setCsrfToken(response.csrfToken);
|
|
413
|
+
}
|
|
396
414
|
}
|
|
397
|
-
this.http.setAuthToken(response.accessToken || "");
|
|
398
415
|
return {
|
|
399
416
|
data: response,
|
|
400
417
|
error: null
|
|
@@ -452,8 +469,13 @@ var Auth = class {
|
|
|
452
469
|
*/
|
|
453
470
|
async signOut() {
|
|
454
471
|
try {
|
|
472
|
+
try {
|
|
473
|
+
await this.http.post("/api/auth/logout", void 0, { credentials: "include" });
|
|
474
|
+
} catch {
|
|
475
|
+
}
|
|
455
476
|
this.tokenManager.clearSession();
|
|
456
477
|
this.http.setAuthToken(null);
|
|
478
|
+
clearCsrfToken();
|
|
457
479
|
return { error: null };
|
|
458
480
|
} catch (error) {
|
|
459
481
|
return {
|
|
@@ -514,14 +536,9 @@ var Auth = class {
|
|
|
514
536
|
}
|
|
515
537
|
this.http.setAuthToken(session.accessToken);
|
|
516
538
|
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
539
|
return {
|
|
522
540
|
data: {
|
|
523
|
-
user: authResponse.user
|
|
524
|
-
profile: profile ? convertDbProfileToCamelCase(profile) : null
|
|
541
|
+
user: authResponse.user
|
|
525
542
|
},
|
|
526
543
|
error: null
|
|
527
544
|
};
|
|
@@ -545,29 +562,80 @@ var Auth = class {
|
|
|
545
562
|
}
|
|
546
563
|
/**
|
|
547
564
|
* Get any user's profile by ID
|
|
548
|
-
* Returns profile information from the users table
|
|
565
|
+
* Returns profile information from the users table
|
|
549
566
|
*/
|
|
550
567
|
async getProfile(userId) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
return {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
568
|
+
try {
|
|
569
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
570
|
+
return {
|
|
571
|
+
data: response,
|
|
572
|
+
error: null
|
|
573
|
+
};
|
|
574
|
+
} catch (error) {
|
|
575
|
+
if (error instanceof InsForgeError) {
|
|
576
|
+
return { data: null, error };
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
data: null,
|
|
580
|
+
error: new InsForgeError(
|
|
581
|
+
"An unexpected error occurred while fetching user profile",
|
|
582
|
+
500,
|
|
583
|
+
"UNEXPECTED_ERROR"
|
|
584
|
+
)
|
|
585
|
+
};
|
|
557
586
|
}
|
|
558
|
-
return { data: null, error };
|
|
559
587
|
}
|
|
560
588
|
/**
|
|
561
589
|
* Get the current session (only session data, no API call)
|
|
562
590
|
* Returns the stored JWT token and basic user info from local storage
|
|
563
591
|
*/
|
|
564
|
-
getCurrentSession() {
|
|
592
|
+
async getCurrentSession() {
|
|
565
593
|
try {
|
|
566
594
|
const session = this.tokenManager.getSession();
|
|
567
|
-
if (session
|
|
595
|
+
if (session) {
|
|
568
596
|
this.http.setAuthToken(session.accessToken);
|
|
569
597
|
return { data: { session }, error: null };
|
|
570
598
|
}
|
|
599
|
+
if (typeof window !== "undefined") {
|
|
600
|
+
try {
|
|
601
|
+
const csrfToken = getCsrfToken();
|
|
602
|
+
const response = await this.http.post(
|
|
603
|
+
"/api/auth/refresh",
|
|
604
|
+
void 0,
|
|
605
|
+
{
|
|
606
|
+
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
607
|
+
credentials: "include"
|
|
608
|
+
}
|
|
609
|
+
);
|
|
610
|
+
if (response.accessToken) {
|
|
611
|
+
this.tokenManager.setMemoryMode();
|
|
612
|
+
this.tokenManager.setAccessToken(response.accessToken);
|
|
613
|
+
this.http.setAuthToken(response.accessToken);
|
|
614
|
+
if (response.user) {
|
|
615
|
+
this.tokenManager.setUser(response.user);
|
|
616
|
+
}
|
|
617
|
+
if (response.csrfToken) {
|
|
618
|
+
setCsrfToken(response.csrfToken);
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
data: { session: this.tokenManager.getSession() },
|
|
622
|
+
error: null
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
} catch (error) {
|
|
626
|
+
if (error instanceof InsForgeError) {
|
|
627
|
+
if (error.statusCode === 404) {
|
|
628
|
+
this.tokenManager.setStorageMode();
|
|
629
|
+
const session2 = this.tokenManager.getSession();
|
|
630
|
+
if (session2) {
|
|
631
|
+
return { data: { session: session2 }, error: null };
|
|
632
|
+
}
|
|
633
|
+
return { data: { session: null }, error: null };
|
|
634
|
+
}
|
|
635
|
+
return { data: { session: null }, error };
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
571
639
|
return { data: { session: null }, error: null };
|
|
572
640
|
} catch (error) {
|
|
573
641
|
if (error instanceof InsForgeError) {
|
|
@@ -586,42 +654,31 @@ var Auth = class {
|
|
|
586
654
|
/**
|
|
587
655
|
* Set/Update the current user's profile
|
|
588
656
|
* Updates profile information in the users table (supports any dynamic fields)
|
|
657
|
+
* Requires authentication
|
|
589
658
|
*/
|
|
590
659
|
async setProfile(profile) {
|
|
591
|
-
|
|
592
|
-
|
|
660
|
+
try {
|
|
661
|
+
const response = await this.http.patch(
|
|
662
|
+
"/api/auth/profiles/current",
|
|
663
|
+
{ profile }
|
|
664
|
+
);
|
|
665
|
+
return {
|
|
666
|
+
data: response,
|
|
667
|
+
error: null
|
|
668
|
+
};
|
|
669
|
+
} catch (error) {
|
|
670
|
+
if (error instanceof InsForgeError) {
|
|
671
|
+
return { data: null, error };
|
|
672
|
+
}
|
|
593
673
|
return {
|
|
594
674
|
data: null,
|
|
595
675
|
error: new InsForgeError(
|
|
596
|
-
"
|
|
597
|
-
|
|
598
|
-
"
|
|
676
|
+
"An unexpected error occurred while updating user profile",
|
|
677
|
+
500,
|
|
678
|
+
"UNEXPECTED_ERROR"
|
|
599
679
|
)
|
|
600
680
|
};
|
|
601
681
|
}
|
|
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
682
|
}
|
|
626
683
|
/**
|
|
627
684
|
* Send email verification (code or link based on config)
|
|
@@ -775,13 +832,16 @@ var Auth = class {
|
|
|
775
832
|
"/api/auth/email/verify",
|
|
776
833
|
request
|
|
777
834
|
);
|
|
778
|
-
if (
|
|
835
|
+
if (!isHostedAuthEnvironment()) {
|
|
779
836
|
const session = {
|
|
780
837
|
accessToken: response.accessToken,
|
|
781
|
-
user: response.user
|
|
838
|
+
user: response.user
|
|
782
839
|
};
|
|
783
840
|
this.tokenManager.saveSession(session);
|
|
784
841
|
this.http.setAuthToken(response.accessToken);
|
|
842
|
+
if (response.csrfToken) {
|
|
843
|
+
setCsrfToken(response.csrfToken);
|
|
844
|
+
}
|
|
785
845
|
}
|
|
786
846
|
return {
|
|
787
847
|
data: response,
|
|
@@ -803,6 +863,75 @@ var Auth = class {
|
|
|
803
863
|
}
|
|
804
864
|
};
|
|
805
865
|
|
|
866
|
+
// src/modules/database-postgrest.ts
|
|
867
|
+
import { PostgrestClient } from "@supabase/postgrest-js";
|
|
868
|
+
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
869
|
+
return async (input, init) => {
|
|
870
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
871
|
+
const urlObj = new URL(url);
|
|
872
|
+
const tableName = urlObj.pathname.slice(1);
|
|
873
|
+
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
874
|
+
const token = tokenManager.getAccessToken();
|
|
875
|
+
const httpHeaders = httpClient.getHeaders();
|
|
876
|
+
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
877
|
+
const headers = new Headers(init?.headers);
|
|
878
|
+
if (authToken && !headers.has("Authorization")) {
|
|
879
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
880
|
+
}
|
|
881
|
+
const response = await fetch(insforgeUrl, {
|
|
882
|
+
...init,
|
|
883
|
+
headers
|
|
884
|
+
});
|
|
885
|
+
return response;
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
var Database = class {
|
|
889
|
+
constructor(httpClient, tokenManager) {
|
|
890
|
+
this.postgrest = new PostgrestClient("http://dummy", {
|
|
891
|
+
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
892
|
+
headers: {}
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Create a query builder for a table
|
|
897
|
+
*
|
|
898
|
+
* @example
|
|
899
|
+
* // Basic query
|
|
900
|
+
* const { data, error } = await client.database
|
|
901
|
+
* .from('posts')
|
|
902
|
+
* .select('*')
|
|
903
|
+
* .eq('user_id', userId);
|
|
904
|
+
*
|
|
905
|
+
* // With count (Supabase style!)
|
|
906
|
+
* const { data, error, count } = await client.database
|
|
907
|
+
* .from('posts')
|
|
908
|
+
* .select('*', { count: 'exact' })
|
|
909
|
+
* .range(0, 9);
|
|
910
|
+
*
|
|
911
|
+
* // Just get count, no data
|
|
912
|
+
* const { count } = await client.database
|
|
913
|
+
* .from('posts')
|
|
914
|
+
* .select('*', { count: 'exact', head: true });
|
|
915
|
+
*
|
|
916
|
+
* // Complex queries with OR
|
|
917
|
+
* const { data } = await client.database
|
|
918
|
+
* .from('posts')
|
|
919
|
+
* .select('*, users!inner(*)')
|
|
920
|
+
* .or('status.eq.active,status.eq.pending');
|
|
921
|
+
*
|
|
922
|
+
* // All features work:
|
|
923
|
+
* - Nested selects
|
|
924
|
+
* - Foreign key expansion
|
|
925
|
+
* - OR/AND/NOT conditions
|
|
926
|
+
* - Count with head
|
|
927
|
+
* - Range pagination
|
|
928
|
+
* - Upserts
|
|
929
|
+
*/
|
|
930
|
+
from(table) {
|
|
931
|
+
return this.postgrest.from(table);
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
|
|
806
935
|
// src/modules/storage.ts
|
|
807
936
|
var StorageBucket = class {
|
|
808
937
|
constructor(bucketName, http) {
|
|
@@ -1564,7 +1693,7 @@ var Emails = class {
|
|
|
1564
1693
|
}
|
|
1565
1694
|
/**
|
|
1566
1695
|
* Send a custom HTML email
|
|
1567
|
-
* @param options Email options
|
|
1696
|
+
* @param options Email options including recipients, subject, and HTML content
|
|
1568
1697
|
*/
|
|
1569
1698
|
async send(options) {
|
|
1570
1699
|
try {
|
|
@@ -1597,10 +1726,7 @@ var InsForgeClient = class {
|
|
|
1597
1726
|
if (existingSession?.accessToken) {
|
|
1598
1727
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1599
1728
|
}
|
|
1600
|
-
this.auth = new Auth(
|
|
1601
|
-
this.http,
|
|
1602
|
-
this.tokenManager
|
|
1603
|
-
);
|
|
1729
|
+
this.auth = new Auth(this.http, this.tokenManager);
|
|
1604
1730
|
this.database = new Database(this.http, this.tokenManager);
|
|
1605
1731
|
this.storage = new Storage(this.http);
|
|
1606
1732
|
this.ai = new AI(this.http);
|