@insforge/sdk 1.0.1-refresh.8 → 1.0.1-schema.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 +249 -249
- package/dist/index.d.mts +140 -106
- package/dist/index.d.ts +140 -106
- package/dist/index.js +379 -335
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +378 -335
- package/dist/index.mjs.map +1 -1
- package/package.json +68 -67
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
HttpClient: () => HttpClient,
|
|
28
28
|
InsForgeClient: () => InsForgeClient,
|
|
29
29
|
InsForgeError: () => InsForgeError,
|
|
30
|
+
Realtime: () => Realtime,
|
|
30
31
|
Storage: () => Storage,
|
|
31
32
|
StorageBucket: () => StorageBucket,
|
|
32
33
|
TokenManager: () => TokenManager,
|
|
@@ -111,7 +112,6 @@ var HttpClient = class {
|
|
|
111
112
|
method,
|
|
112
113
|
headers: requestHeaders,
|
|
113
114
|
body: processedBody,
|
|
114
|
-
credentials: "include",
|
|
115
115
|
...fetchOptions
|
|
116
116
|
});
|
|
117
117
|
if (response.status === 204) {
|
|
@@ -176,29 +176,8 @@ var HttpClient = class {
|
|
|
176
176
|
// src/lib/token-manager.ts
|
|
177
177
|
var TOKEN_KEY = "insforge-auth-token";
|
|
178
178
|
var USER_KEY = "insforge-auth-user";
|
|
179
|
-
var AUTH_FLAG_COOKIE = "isAuthenticated";
|
|
180
|
-
function hasAuthCookie() {
|
|
181
|
-
if (typeof document === "undefined") return false;
|
|
182
|
-
return document.cookie.split(";").some(
|
|
183
|
-
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
function setAuthCookie() {
|
|
187
|
-
if (typeof document === "undefined") return;
|
|
188
|
-
const maxAge = 7 * 24 * 60 * 60;
|
|
189
|
-
document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
190
|
-
}
|
|
191
|
-
function clearAuthCookie() {
|
|
192
|
-
if (typeof document === "undefined") return;
|
|
193
|
-
document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
194
|
-
}
|
|
195
179
|
var TokenManager = class {
|
|
196
180
|
constructor(storage) {
|
|
197
|
-
// In-memory storage
|
|
198
|
-
this.accessToken = null;
|
|
199
|
-
this.user = null;
|
|
200
|
-
// Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
|
|
201
|
-
this._mode = "storage";
|
|
202
181
|
if (storage) {
|
|
203
182
|
this.storage = storage;
|
|
204
183
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -216,206 +195,35 @@ var TokenManager = class {
|
|
|
216
195
|
};
|
|
217
196
|
}
|
|
218
197
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
get mode() {
|
|
223
|
-
return this._mode;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Set mode to memory (new backend with cookies + memory)
|
|
227
|
-
*/
|
|
228
|
-
setMemoryMode() {
|
|
229
|
-
if (this._mode === "storage") {
|
|
230
|
-
this.storage.removeItem(TOKEN_KEY);
|
|
231
|
-
this.storage.removeItem(USER_KEY);
|
|
232
|
-
}
|
|
233
|
-
this._mode = "memory";
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Set mode to storage (legacy backend with localStorage)
|
|
237
|
-
* Also loads existing session from localStorage
|
|
238
|
-
*/
|
|
239
|
-
setStorageMode() {
|
|
240
|
-
this._mode = "storage";
|
|
241
|
-
this.loadFromStorage();
|
|
198
|
+
saveSession(session) {
|
|
199
|
+
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
200
|
+
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
242
201
|
}
|
|
243
|
-
|
|
244
|
-
* Load session from localStorage
|
|
245
|
-
*/
|
|
246
|
-
loadFromStorage() {
|
|
202
|
+
getSession() {
|
|
247
203
|
const token = this.storage.getItem(TOKEN_KEY);
|
|
248
204
|
const userStr = this.storage.getItem(USER_KEY);
|
|
249
|
-
if (token
|
|
250
|
-
|
|
251
|
-
this.accessToken = token;
|
|
252
|
-
this.user = JSON.parse(userStr);
|
|
253
|
-
} catch {
|
|
254
|
-
this.clearSession();
|
|
255
|
-
}
|
|
205
|
+
if (!token || !userStr) {
|
|
206
|
+
return null;
|
|
256
207
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
this.user = session.user;
|
|
264
|
-
if (this._mode === "storage") {
|
|
265
|
-
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
266
|
-
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
208
|
+
try {
|
|
209
|
+
const user = JSON.parse(userStr);
|
|
210
|
+
return { accessToken: token, user };
|
|
211
|
+
} catch {
|
|
212
|
+
this.clearSession();
|
|
213
|
+
return null;
|
|
267
214
|
}
|
|
268
215
|
}
|
|
269
|
-
/**
|
|
270
|
-
* Get current session
|
|
271
|
-
*/
|
|
272
|
-
getSession() {
|
|
273
|
-
if (!this.accessToken || !this.user) return null;
|
|
274
|
-
return {
|
|
275
|
-
accessToken: this.accessToken,
|
|
276
|
-
user: this.user
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Get access token
|
|
281
|
-
*/
|
|
282
216
|
getAccessToken() {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Set access token
|
|
287
|
-
*/
|
|
288
|
-
setAccessToken(token) {
|
|
289
|
-
this.accessToken = token;
|
|
290
|
-
if (this._mode === "storage") {
|
|
291
|
-
this.storage.setItem(TOKEN_KEY, token);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Get user
|
|
296
|
-
*/
|
|
297
|
-
getUser() {
|
|
298
|
-
return this.user;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Set user
|
|
302
|
-
*/
|
|
303
|
-
setUser(user) {
|
|
304
|
-
this.user = user;
|
|
305
|
-
if (this._mode === "storage") {
|
|
306
|
-
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
307
|
-
}
|
|
217
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
218
|
+
return typeof token === "string" ? token : null;
|
|
308
219
|
}
|
|
309
|
-
/**
|
|
310
|
-
* Clear session (both memory and localStorage)
|
|
311
|
-
*/
|
|
312
220
|
clearSession() {
|
|
313
|
-
this.accessToken = null;
|
|
314
|
-
this.user = null;
|
|
315
221
|
this.storage.removeItem(TOKEN_KEY);
|
|
316
222
|
this.storage.removeItem(USER_KEY);
|
|
317
223
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Check if there's a session in localStorage (for legacy detection)
|
|
320
|
-
*/
|
|
321
|
-
hasStoredSession() {
|
|
322
|
-
const token = this.storage.getItem(TOKEN_KEY);
|
|
323
|
-
return !!token;
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
// src/modules/database-postgrest.ts
|
|
328
|
-
var import_postgrest_js = require("@supabase/postgrest-js");
|
|
329
|
-
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
330
|
-
return async (input, init) => {
|
|
331
|
-
const url = typeof input === "string" ? input : input.toString();
|
|
332
|
-
const urlObj = new URL(url);
|
|
333
|
-
const tableName = urlObj.pathname.slice(1);
|
|
334
|
-
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
335
|
-
const token = tokenManager.getAccessToken();
|
|
336
|
-
const httpHeaders = httpClient.getHeaders();
|
|
337
|
-
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
338
|
-
const headers = new Headers(init?.headers);
|
|
339
|
-
if (authToken && !headers.has("Authorization")) {
|
|
340
|
-
headers.set("Authorization", `Bearer ${authToken}`);
|
|
341
|
-
}
|
|
342
|
-
const response = await fetch(insforgeUrl, {
|
|
343
|
-
...init,
|
|
344
|
-
headers
|
|
345
|
-
});
|
|
346
|
-
return response;
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
var Database = class {
|
|
350
|
-
constructor(httpClient, tokenManager) {
|
|
351
|
-
this.postgrest = new import_postgrest_js.PostgrestClient("http://dummy", {
|
|
352
|
-
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
353
|
-
headers: {}
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Create a query builder for a table
|
|
358
|
-
*
|
|
359
|
-
* @example
|
|
360
|
-
* // Basic query
|
|
361
|
-
* const { data, error } = await client.database
|
|
362
|
-
* .from('posts')
|
|
363
|
-
* .select('*')
|
|
364
|
-
* .eq('user_id', userId);
|
|
365
|
-
*
|
|
366
|
-
* // With count (Supabase style!)
|
|
367
|
-
* const { data, error, count } = await client.database
|
|
368
|
-
* .from('posts')
|
|
369
|
-
* .select('*', { count: 'exact' })
|
|
370
|
-
* .range(0, 9);
|
|
371
|
-
*
|
|
372
|
-
* // Just get count, no data
|
|
373
|
-
* const { count } = await client.database
|
|
374
|
-
* .from('posts')
|
|
375
|
-
* .select('*', { count: 'exact', head: true });
|
|
376
|
-
*
|
|
377
|
-
* // Complex queries with OR
|
|
378
|
-
* const { data } = await client.database
|
|
379
|
-
* .from('posts')
|
|
380
|
-
* .select('*, users!inner(*)')
|
|
381
|
-
* .or('status.eq.active,status.eq.pending');
|
|
382
|
-
*
|
|
383
|
-
* // All features work:
|
|
384
|
-
* - Nested selects
|
|
385
|
-
* - Foreign key expansion
|
|
386
|
-
* - OR/AND/NOT conditions
|
|
387
|
-
* - Count with head
|
|
388
|
-
* - Range pagination
|
|
389
|
-
* - Upserts
|
|
390
|
-
*/
|
|
391
|
-
from(table) {
|
|
392
|
-
return this.postgrest.from(table);
|
|
393
|
-
}
|
|
394
224
|
};
|
|
395
225
|
|
|
396
226
|
// src/modules/auth.ts
|
|
397
|
-
function convertDbProfileToCamelCase(dbProfile) {
|
|
398
|
-
const result = {
|
|
399
|
-
id: dbProfile.id
|
|
400
|
-
};
|
|
401
|
-
Object.keys(dbProfile).forEach((key) => {
|
|
402
|
-
result[key] = dbProfile[key];
|
|
403
|
-
if (key.includes("_")) {
|
|
404
|
-
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
405
|
-
result[camelKey] = dbProfile[key];
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
return result;
|
|
409
|
-
}
|
|
410
|
-
function convertCamelCaseToDbProfile(profile) {
|
|
411
|
-
const dbProfile = {};
|
|
412
|
-
Object.keys(profile).forEach((key) => {
|
|
413
|
-
if (profile[key] === void 0) return;
|
|
414
|
-
const snakeKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
415
|
-
dbProfile[snakeKey] = profile[key];
|
|
416
|
-
});
|
|
417
|
-
return dbProfile;
|
|
418
|
-
}
|
|
419
227
|
function isHostedAuthEnvironment() {
|
|
420
228
|
if (typeof window === "undefined") {
|
|
421
229
|
return false;
|
|
@@ -433,74 +241,8 @@ var Auth = class {
|
|
|
433
241
|
constructor(http, tokenManager) {
|
|
434
242
|
this.http = http;
|
|
435
243
|
this.tokenManager = tokenManager;
|
|
436
|
-
this.database = new Database(http, tokenManager);
|
|
437
244
|
this.detectAuthCallback();
|
|
438
245
|
}
|
|
439
|
-
/**
|
|
440
|
-
* Restore session on app initialization
|
|
441
|
-
*
|
|
442
|
-
* @returns Object with isLoggedIn status
|
|
443
|
-
*
|
|
444
|
-
* @example
|
|
445
|
-
* ```typescript
|
|
446
|
-
* const client = new InsForgeClient({ baseUrl: '...' });
|
|
447
|
-
* const { isLoggedIn } = await client.auth.restoreSession();
|
|
448
|
-
*
|
|
449
|
-
* if (isLoggedIn) {
|
|
450
|
-
* const { data } = await client.auth.getCurrentUser();
|
|
451
|
-
* }
|
|
452
|
-
* ```
|
|
453
|
-
*/
|
|
454
|
-
async restoreSession() {
|
|
455
|
-
if (typeof window === "undefined") {
|
|
456
|
-
return { isLoggedIn: false };
|
|
457
|
-
}
|
|
458
|
-
if (this.tokenManager.getAccessToken()) {
|
|
459
|
-
return { isLoggedIn: true };
|
|
460
|
-
}
|
|
461
|
-
if (hasAuthCookie()) {
|
|
462
|
-
try {
|
|
463
|
-
const response = await this.http.post(
|
|
464
|
-
"/api/auth/refresh"
|
|
465
|
-
);
|
|
466
|
-
if (response.accessToken) {
|
|
467
|
-
this.tokenManager.setMemoryMode();
|
|
468
|
-
this.tokenManager.setAccessToken(response.accessToken);
|
|
469
|
-
this.http.setAuthToken(response.accessToken);
|
|
470
|
-
if (response.user) {
|
|
471
|
-
this.tokenManager.setUser(response.user);
|
|
472
|
-
}
|
|
473
|
-
return { isLoggedIn: true };
|
|
474
|
-
}
|
|
475
|
-
} catch (error) {
|
|
476
|
-
if (error instanceof InsForgeError) {
|
|
477
|
-
if (error.statusCode === 404) {
|
|
478
|
-
this.tokenManager.setStorageMode();
|
|
479
|
-
const token = this.tokenManager.getAccessToken();
|
|
480
|
-
if (token) {
|
|
481
|
-
this.http.setAuthToken(token);
|
|
482
|
-
return { isLoggedIn: true };
|
|
483
|
-
}
|
|
484
|
-
return { isLoggedIn: false };
|
|
485
|
-
}
|
|
486
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
487
|
-
clearAuthCookie();
|
|
488
|
-
return { isLoggedIn: false };
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
return { isLoggedIn: false };
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
if (this.tokenManager.hasStoredSession()) {
|
|
495
|
-
this.tokenManager.setStorageMode();
|
|
496
|
-
const token = this.tokenManager.getAccessToken();
|
|
497
|
-
if (token) {
|
|
498
|
-
this.http.setAuthToken(token);
|
|
499
|
-
return { isLoggedIn: true };
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return { isLoggedIn: false };
|
|
503
|
-
}
|
|
504
246
|
/**
|
|
505
247
|
* Automatically detect and handle OAuth callback parameters in the URL
|
|
506
248
|
* This runs on initialization to seamlessly complete the OAuth flow
|
|
@@ -528,9 +270,8 @@ var Auth = class {
|
|
|
528
270
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
529
271
|
}
|
|
530
272
|
};
|
|
531
|
-
this.http.setAuthToken(accessToken);
|
|
532
273
|
this.tokenManager.saveSession(session);
|
|
533
|
-
|
|
274
|
+
this.http.setAuthToken(accessToken);
|
|
534
275
|
const url = new URL(window.location.href);
|
|
535
276
|
url.searchParams.delete("access_token");
|
|
536
277
|
url.searchParams.delete("user_id");
|
|
@@ -551,13 +292,14 @@ var Auth = class {
|
|
|
551
292
|
async signUp(request) {
|
|
552
293
|
try {
|
|
553
294
|
const response = await this.http.post("/api/auth/users", request);
|
|
554
|
-
if (response.accessToken && response.user
|
|
295
|
+
if (response.accessToken && response.user) {
|
|
555
296
|
const session = {
|
|
556
297
|
accessToken: response.accessToken,
|
|
557
298
|
user: response.user
|
|
558
299
|
};
|
|
559
|
-
|
|
560
|
-
|
|
300
|
+
if (!isHostedAuthEnvironment()) {
|
|
301
|
+
this.tokenManager.saveSession(session);
|
|
302
|
+
}
|
|
561
303
|
this.http.setAuthToken(response.accessToken);
|
|
562
304
|
}
|
|
563
305
|
return {
|
|
@@ -584,15 +326,21 @@ var Auth = class {
|
|
|
584
326
|
async signInWithPassword(request) {
|
|
585
327
|
try {
|
|
586
328
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
329
|
+
const session = {
|
|
330
|
+
accessToken: response.accessToken || "",
|
|
331
|
+
user: response.user || {
|
|
332
|
+
id: "",
|
|
333
|
+
email: "",
|
|
334
|
+
name: "",
|
|
335
|
+
emailVerified: false,
|
|
336
|
+
createdAt: "",
|
|
337
|
+
updatedAt: ""
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
if (!isHostedAuthEnvironment()) {
|
|
592
341
|
this.tokenManager.saveSession(session);
|
|
593
|
-
setAuthCookie();
|
|
594
|
-
this.http.setAuthToken(response.accessToken);
|
|
595
342
|
}
|
|
343
|
+
this.http.setAuthToken(response.accessToken || "");
|
|
596
344
|
return {
|
|
597
345
|
data: response,
|
|
598
346
|
error: null
|
|
@@ -650,13 +398,8 @@ var Auth = class {
|
|
|
650
398
|
*/
|
|
651
399
|
async signOut() {
|
|
652
400
|
try {
|
|
653
|
-
try {
|
|
654
|
-
await this.http.post("/api/auth/logout");
|
|
655
|
-
} catch {
|
|
656
|
-
}
|
|
657
401
|
this.tokenManager.clearSession();
|
|
658
402
|
this.http.setAuthToken(null);
|
|
659
|
-
clearAuthCookie();
|
|
660
403
|
return { error: null };
|
|
661
404
|
} catch (error) {
|
|
662
405
|
return {
|
|
@@ -717,14 +460,9 @@ var Auth = class {
|
|
|
717
460
|
}
|
|
718
461
|
this.http.setAuthToken(session.accessToken);
|
|
719
462
|
const authResponse = await this.http.get("/api/auth/sessions/current");
|
|
720
|
-
const { data: profile, error: profileError } = await this.database.from("users").select("*").eq("id", authResponse.user.id).single();
|
|
721
|
-
if (profileError && profileError.code !== "PGRST116") {
|
|
722
|
-
return { data: null, error: profileError };
|
|
723
|
-
}
|
|
724
463
|
return {
|
|
725
464
|
data: {
|
|
726
|
-
user: authResponse.user
|
|
727
|
-
profile: profile ? convertDbProfileToCamelCase(profile) : null
|
|
465
|
+
user: authResponse.user
|
|
728
466
|
},
|
|
729
467
|
error: null
|
|
730
468
|
};
|
|
@@ -748,17 +486,28 @@ var Auth = class {
|
|
|
748
486
|
}
|
|
749
487
|
/**
|
|
750
488
|
* Get any user's profile by ID
|
|
751
|
-
* Returns profile information from the users table
|
|
489
|
+
* Returns profile information from the users table
|
|
752
490
|
*/
|
|
753
491
|
async getProfile(userId) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
return {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
492
|
+
try {
|
|
493
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
494
|
+
return {
|
|
495
|
+
data: response,
|
|
496
|
+
error: null
|
|
497
|
+
};
|
|
498
|
+
} catch (error) {
|
|
499
|
+
if (error instanceof InsForgeError) {
|
|
500
|
+
return { data: null, error };
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
data: null,
|
|
504
|
+
error: new InsForgeError(
|
|
505
|
+
"An unexpected error occurred while fetching user profile",
|
|
506
|
+
500,
|
|
507
|
+
"UNEXPECTED_ERROR"
|
|
508
|
+
)
|
|
509
|
+
};
|
|
760
510
|
}
|
|
761
|
-
return { data: null, error };
|
|
762
511
|
}
|
|
763
512
|
/**
|
|
764
513
|
* Get the current session (only session data, no API call)
|
|
@@ -789,42 +538,31 @@ var Auth = class {
|
|
|
789
538
|
/**
|
|
790
539
|
* Set/Update the current user's profile
|
|
791
540
|
* Updates profile information in the users table (supports any dynamic fields)
|
|
541
|
+
* Requires authentication
|
|
792
542
|
*/
|
|
793
543
|
async setProfile(profile) {
|
|
794
|
-
|
|
795
|
-
|
|
544
|
+
try {
|
|
545
|
+
const response = await this.http.patch(
|
|
546
|
+
"/api/auth/profiles/current",
|
|
547
|
+
{ profile }
|
|
548
|
+
);
|
|
549
|
+
return {
|
|
550
|
+
data: response,
|
|
551
|
+
error: null
|
|
552
|
+
};
|
|
553
|
+
} catch (error) {
|
|
554
|
+
if (error instanceof InsForgeError) {
|
|
555
|
+
return { data: null, error };
|
|
556
|
+
}
|
|
796
557
|
return {
|
|
797
558
|
data: null,
|
|
798
559
|
error: new InsForgeError(
|
|
799
|
-
"
|
|
800
|
-
|
|
801
|
-
"
|
|
560
|
+
"An unexpected error occurred while updating user profile",
|
|
561
|
+
500,
|
|
562
|
+
"UNEXPECTED_ERROR"
|
|
802
563
|
)
|
|
803
564
|
};
|
|
804
565
|
}
|
|
805
|
-
if (!session.user?.id) {
|
|
806
|
-
const { data: data2, error: error2 } = await this.getCurrentUser();
|
|
807
|
-
if (error2) {
|
|
808
|
-
return { data: null, error: error2 };
|
|
809
|
-
}
|
|
810
|
-
if (data2?.user) {
|
|
811
|
-
session.user = {
|
|
812
|
-
id: data2.user.id,
|
|
813
|
-
email: data2.user.email,
|
|
814
|
-
name: "",
|
|
815
|
-
emailVerified: false,
|
|
816
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
817
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
818
|
-
};
|
|
819
|
-
this.tokenManager.saveSession(session);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
const dbProfile = convertCamelCaseToDbProfile(profile);
|
|
823
|
-
const { data, error } = await this.database.from("users").update(dbProfile).eq("id", session.user.id).select().single();
|
|
824
|
-
if (data) {
|
|
825
|
-
return { data: convertDbProfileToCamelCase(data), error: null };
|
|
826
|
-
}
|
|
827
|
-
return { data: null, error };
|
|
828
566
|
}
|
|
829
567
|
/**
|
|
830
568
|
* Send email verification (code or link based on config)
|
|
@@ -978,14 +716,13 @@ var Auth = class {
|
|
|
978
716
|
"/api/auth/email/verify",
|
|
979
717
|
request
|
|
980
718
|
);
|
|
981
|
-
if (response.accessToken
|
|
719
|
+
if (response.accessToken) {
|
|
982
720
|
const session = {
|
|
983
721
|
accessToken: response.accessToken,
|
|
984
722
|
user: response.user || {}
|
|
985
723
|
};
|
|
986
724
|
this.tokenManager.saveSession(session);
|
|
987
725
|
this.http.setAuthToken(response.accessToken);
|
|
988
|
-
setAuthCookie();
|
|
989
726
|
}
|
|
990
727
|
return {
|
|
991
728
|
data: response,
|
|
@@ -1007,6 +744,75 @@ var Auth = class {
|
|
|
1007
744
|
}
|
|
1008
745
|
};
|
|
1009
746
|
|
|
747
|
+
// src/modules/database-postgrest.ts
|
|
748
|
+
var import_postgrest_js = require("@supabase/postgrest-js");
|
|
749
|
+
function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
750
|
+
return async (input, init) => {
|
|
751
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
752
|
+
const urlObj = new URL(url);
|
|
753
|
+
const tableName = urlObj.pathname.slice(1);
|
|
754
|
+
const insforgeUrl = `${httpClient.baseUrl}/api/database/records/${tableName}${urlObj.search}`;
|
|
755
|
+
const token = tokenManager.getAccessToken();
|
|
756
|
+
const httpHeaders = httpClient.getHeaders();
|
|
757
|
+
const authToken = token || httpHeaders["Authorization"]?.replace("Bearer ", "");
|
|
758
|
+
const headers = new Headers(init?.headers);
|
|
759
|
+
if (authToken && !headers.has("Authorization")) {
|
|
760
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
761
|
+
}
|
|
762
|
+
const response = await fetch(insforgeUrl, {
|
|
763
|
+
...init,
|
|
764
|
+
headers
|
|
765
|
+
});
|
|
766
|
+
return response;
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
var Database = class {
|
|
770
|
+
constructor(httpClient, tokenManager) {
|
|
771
|
+
this.postgrest = new import_postgrest_js.PostgrestClient("http://dummy", {
|
|
772
|
+
fetch: createInsForgePostgrestFetch(httpClient, tokenManager),
|
|
773
|
+
headers: {}
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Create a query builder for a table
|
|
778
|
+
*
|
|
779
|
+
* @example
|
|
780
|
+
* // Basic query
|
|
781
|
+
* const { data, error } = await client.database
|
|
782
|
+
* .from('posts')
|
|
783
|
+
* .select('*')
|
|
784
|
+
* .eq('user_id', userId);
|
|
785
|
+
*
|
|
786
|
+
* // With count (Supabase style!)
|
|
787
|
+
* const { data, error, count } = await client.database
|
|
788
|
+
* .from('posts')
|
|
789
|
+
* .select('*', { count: 'exact' })
|
|
790
|
+
* .range(0, 9);
|
|
791
|
+
*
|
|
792
|
+
* // Just get count, no data
|
|
793
|
+
* const { count } = await client.database
|
|
794
|
+
* .from('posts')
|
|
795
|
+
* .select('*', { count: 'exact', head: true });
|
|
796
|
+
*
|
|
797
|
+
* // Complex queries with OR
|
|
798
|
+
* const { data } = await client.database
|
|
799
|
+
* .from('posts')
|
|
800
|
+
* .select('*, users!inner(*)')
|
|
801
|
+
* .or('status.eq.active,status.eq.pending');
|
|
802
|
+
*
|
|
803
|
+
* // All features work:
|
|
804
|
+
* - Nested selects
|
|
805
|
+
* - Foreign key expansion
|
|
806
|
+
* - OR/AND/NOT conditions
|
|
807
|
+
* - Count with head
|
|
808
|
+
* - Range pagination
|
|
809
|
+
* - Upserts
|
|
810
|
+
*/
|
|
811
|
+
from(table) {
|
|
812
|
+
return this.postgrest.from(table);
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
|
|
1010
816
|
// src/modules/storage.ts
|
|
1011
817
|
var StorageBucket = class {
|
|
1012
818
|
constructor(bucketName, http) {
|
|
@@ -1528,6 +1334,239 @@ var Functions = class {
|
|
|
1528
1334
|
}
|
|
1529
1335
|
};
|
|
1530
1336
|
|
|
1337
|
+
// src/modules/realtime.ts
|
|
1338
|
+
var import_socket = require("socket.io-client");
|
|
1339
|
+
var CONNECT_TIMEOUT = 1e4;
|
|
1340
|
+
var Realtime = class {
|
|
1341
|
+
constructor(baseUrl, tokenManager) {
|
|
1342
|
+
this.socket = null;
|
|
1343
|
+
this.connectPromise = null;
|
|
1344
|
+
this.subscribedChannels = /* @__PURE__ */ new Set();
|
|
1345
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
1346
|
+
this.baseUrl = baseUrl;
|
|
1347
|
+
this.tokenManager = tokenManager;
|
|
1348
|
+
}
|
|
1349
|
+
notifyListeners(event, payload) {
|
|
1350
|
+
const listeners = this.eventListeners.get(event);
|
|
1351
|
+
if (!listeners) return;
|
|
1352
|
+
for (const cb of listeners) {
|
|
1353
|
+
try {
|
|
1354
|
+
cb(payload);
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
console.error(`Error in ${event} callback:`, err);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Connect to the realtime server
|
|
1362
|
+
* @returns Promise that resolves when connected
|
|
1363
|
+
*/
|
|
1364
|
+
connect() {
|
|
1365
|
+
if (this.socket?.connected) {
|
|
1366
|
+
return Promise.resolve();
|
|
1367
|
+
}
|
|
1368
|
+
if (this.connectPromise) {
|
|
1369
|
+
return this.connectPromise;
|
|
1370
|
+
}
|
|
1371
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
1372
|
+
const session = this.tokenManager.getSession();
|
|
1373
|
+
const token = session?.accessToken;
|
|
1374
|
+
this.socket = (0, import_socket.io)(this.baseUrl, {
|
|
1375
|
+
transports: ["websocket"],
|
|
1376
|
+
auth: token ? { token } : void 0
|
|
1377
|
+
});
|
|
1378
|
+
let initialConnection = true;
|
|
1379
|
+
let timeoutId = null;
|
|
1380
|
+
const cleanup = () => {
|
|
1381
|
+
if (timeoutId) {
|
|
1382
|
+
clearTimeout(timeoutId);
|
|
1383
|
+
timeoutId = null;
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
timeoutId = setTimeout(() => {
|
|
1387
|
+
if (initialConnection) {
|
|
1388
|
+
initialConnection = false;
|
|
1389
|
+
this.connectPromise = null;
|
|
1390
|
+
this.socket?.disconnect();
|
|
1391
|
+
this.socket = null;
|
|
1392
|
+
reject(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`));
|
|
1393
|
+
}
|
|
1394
|
+
}, CONNECT_TIMEOUT);
|
|
1395
|
+
this.socket.on("connect", () => {
|
|
1396
|
+
cleanup();
|
|
1397
|
+
for (const channel of this.subscribedChannels) {
|
|
1398
|
+
this.socket.emit("realtime:subscribe", { channel });
|
|
1399
|
+
}
|
|
1400
|
+
this.notifyListeners("connect");
|
|
1401
|
+
if (initialConnection) {
|
|
1402
|
+
initialConnection = false;
|
|
1403
|
+
this.connectPromise = null;
|
|
1404
|
+
resolve();
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
this.socket.on("connect_error", (error) => {
|
|
1408
|
+
cleanup();
|
|
1409
|
+
this.notifyListeners("connect_error", error);
|
|
1410
|
+
if (initialConnection) {
|
|
1411
|
+
initialConnection = false;
|
|
1412
|
+
this.connectPromise = null;
|
|
1413
|
+
reject(error);
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1416
|
+
this.socket.on("disconnect", (reason) => {
|
|
1417
|
+
this.notifyListeners("disconnect", reason);
|
|
1418
|
+
});
|
|
1419
|
+
this.socket.on("realtime:error", (error) => {
|
|
1420
|
+
this.notifyListeners("error", error);
|
|
1421
|
+
});
|
|
1422
|
+
this.socket.onAny((event, message) => {
|
|
1423
|
+
if (event === "realtime:error") return;
|
|
1424
|
+
this.notifyListeners(event, message);
|
|
1425
|
+
});
|
|
1426
|
+
});
|
|
1427
|
+
return this.connectPromise;
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Disconnect from the realtime server
|
|
1431
|
+
*/
|
|
1432
|
+
disconnect() {
|
|
1433
|
+
if (this.socket) {
|
|
1434
|
+
this.socket.disconnect();
|
|
1435
|
+
this.socket = null;
|
|
1436
|
+
}
|
|
1437
|
+
this.subscribedChannels.clear();
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Check if connected to the realtime server
|
|
1441
|
+
*/
|
|
1442
|
+
get isConnected() {
|
|
1443
|
+
return this.socket?.connected ?? false;
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Get the current connection state
|
|
1447
|
+
*/
|
|
1448
|
+
get connectionState() {
|
|
1449
|
+
if (!this.socket) return "disconnected";
|
|
1450
|
+
if (this.socket.connected) return "connected";
|
|
1451
|
+
return "connecting";
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Get the socket ID (if connected)
|
|
1455
|
+
*/
|
|
1456
|
+
get socketId() {
|
|
1457
|
+
return this.socket?.id;
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Subscribe to a channel
|
|
1461
|
+
*
|
|
1462
|
+
* Automatically connects if not already connected.
|
|
1463
|
+
*
|
|
1464
|
+
* @param channel - Channel name (e.g., 'orders:123', 'broadcast')
|
|
1465
|
+
* @returns Promise with the subscription response
|
|
1466
|
+
*/
|
|
1467
|
+
async subscribe(channel) {
|
|
1468
|
+
if (this.subscribedChannels.has(channel)) {
|
|
1469
|
+
return { ok: true, channel };
|
|
1470
|
+
}
|
|
1471
|
+
if (!this.socket?.connected) {
|
|
1472
|
+
try {
|
|
1473
|
+
await this.connect();
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
const message = error instanceof Error ? error.message : "Connection failed";
|
|
1476
|
+
return { ok: false, channel, error: { code: "CONNECTION_FAILED", message } };
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return new Promise((resolve) => {
|
|
1480
|
+
this.socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
1481
|
+
if (response.ok) {
|
|
1482
|
+
this.subscribedChannels.add(channel);
|
|
1483
|
+
}
|
|
1484
|
+
resolve(response);
|
|
1485
|
+
});
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Unsubscribe from a channel (fire-and-forget)
|
|
1490
|
+
*
|
|
1491
|
+
* @param channel - Channel name to unsubscribe from
|
|
1492
|
+
*/
|
|
1493
|
+
unsubscribe(channel) {
|
|
1494
|
+
this.subscribedChannels.delete(channel);
|
|
1495
|
+
if (this.socket?.connected) {
|
|
1496
|
+
this.socket.emit("realtime:unsubscribe", { channel });
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Publish a message to a channel
|
|
1501
|
+
*
|
|
1502
|
+
* @param channel - Channel name
|
|
1503
|
+
* @param event - Event name
|
|
1504
|
+
* @param payload - Message payload
|
|
1505
|
+
*/
|
|
1506
|
+
async publish(channel, event, payload) {
|
|
1507
|
+
if (!this.socket?.connected) {
|
|
1508
|
+
throw new Error("Not connected to realtime server. Call connect() first.");
|
|
1509
|
+
}
|
|
1510
|
+
this.socket.emit("realtime:publish", { channel, event, payload });
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Listen for events
|
|
1514
|
+
*
|
|
1515
|
+
* Reserved event names:
|
|
1516
|
+
* - 'connect' - Fired when connected to the server
|
|
1517
|
+
* - 'connect_error' - Fired when connection fails (payload: Error)
|
|
1518
|
+
* - 'disconnect' - Fired when disconnected (payload: reason string)
|
|
1519
|
+
* - 'error' - Fired when a realtime error occurs (payload: RealtimeErrorPayload)
|
|
1520
|
+
*
|
|
1521
|
+
* All other events receive a `SocketMessage` payload with metadata.
|
|
1522
|
+
*
|
|
1523
|
+
* @param event - Event name to listen for
|
|
1524
|
+
* @param callback - Callback function when event is received
|
|
1525
|
+
*/
|
|
1526
|
+
on(event, callback) {
|
|
1527
|
+
if (!this.eventListeners.has(event)) {
|
|
1528
|
+
this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
1529
|
+
}
|
|
1530
|
+
this.eventListeners.get(event).add(callback);
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Remove a listener for a specific event
|
|
1534
|
+
*
|
|
1535
|
+
* @param event - Event name
|
|
1536
|
+
* @param callback - The callback function to remove
|
|
1537
|
+
*/
|
|
1538
|
+
off(event, callback) {
|
|
1539
|
+
const listeners = this.eventListeners.get(event);
|
|
1540
|
+
if (listeners) {
|
|
1541
|
+
listeners.delete(callback);
|
|
1542
|
+
if (listeners.size === 0) {
|
|
1543
|
+
this.eventListeners.delete(event);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Listen for an event only once, then automatically remove the listener
|
|
1549
|
+
*
|
|
1550
|
+
* @param event - Event name to listen for
|
|
1551
|
+
* @param callback - Callback function when event is received
|
|
1552
|
+
*/
|
|
1553
|
+
once(event, callback) {
|
|
1554
|
+
const wrapper = (payload) => {
|
|
1555
|
+
this.off(event, wrapper);
|
|
1556
|
+
callback(payload);
|
|
1557
|
+
};
|
|
1558
|
+
this.on(event, wrapper);
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Get all currently subscribed channels
|
|
1562
|
+
*
|
|
1563
|
+
* @returns Array of channel names
|
|
1564
|
+
*/
|
|
1565
|
+
getSubscribedChannels() {
|
|
1566
|
+
return Array.from(this.subscribedChannels);
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1531
1570
|
// src/client.ts
|
|
1532
1571
|
var InsForgeClient = class {
|
|
1533
1572
|
constructor(config = {}) {
|
|
@@ -1545,11 +1584,15 @@ var InsForgeClient = class {
|
|
|
1545
1584
|
if (existingSession?.accessToken) {
|
|
1546
1585
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1547
1586
|
}
|
|
1548
|
-
this.auth = new Auth(
|
|
1587
|
+
this.auth = new Auth(
|
|
1588
|
+
this.http,
|
|
1589
|
+
this.tokenManager
|
|
1590
|
+
);
|
|
1549
1591
|
this.database = new Database(this.http, this.tokenManager);
|
|
1550
1592
|
this.storage = new Storage(this.http);
|
|
1551
1593
|
this.ai = new AI(this.http);
|
|
1552
1594
|
this.functions = new Functions(this.http);
|
|
1595
|
+
this.realtime = new Realtime(this.http.baseUrl, this.tokenManager);
|
|
1553
1596
|
}
|
|
1554
1597
|
/**
|
|
1555
1598
|
* Get the underlying HTTP client for custom requests
|
|
@@ -1587,6 +1630,7 @@ var index_default = InsForgeClient;
|
|
|
1587
1630
|
HttpClient,
|
|
1588
1631
|
InsForgeClient,
|
|
1589
1632
|
InsForgeError,
|
|
1633
|
+
Realtime,
|
|
1590
1634
|
Storage,
|
|
1591
1635
|
StorageBucket,
|
|
1592
1636
|
TokenManager,
|