@insforge/sdk 1.0.1-refresh.9 → 1.0.2-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 +249 -249
- package/dist/index.d.mts +174 -78
- package/dist/index.d.ts +174 -78
- package/dist/index.js +299 -240
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +297 -240
- package/dist/index.mjs.map +1 -1
- package/package.json +68 -67
package/dist/index.mjs
CHANGED
|
@@ -74,7 +74,6 @@ var HttpClient = class {
|
|
|
74
74
|
method,
|
|
75
75
|
headers: requestHeaders,
|
|
76
76
|
body: processedBody,
|
|
77
|
-
credentials: "include",
|
|
78
77
|
...fetchOptions
|
|
79
78
|
});
|
|
80
79
|
if (response.status === 204) {
|
|
@@ -139,45 +138,8 @@ var HttpClient = class {
|
|
|
139
138
|
// src/lib/token-manager.ts
|
|
140
139
|
var TOKEN_KEY = "insforge-auth-token";
|
|
141
140
|
var USER_KEY = "insforge-auth-user";
|
|
142
|
-
var AUTH_FLAG_COOKIE = "isAuthenticated";
|
|
143
|
-
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
144
|
-
function hasAuthCookie() {
|
|
145
|
-
if (typeof document === "undefined") return false;
|
|
146
|
-
return document.cookie.split(";").some(
|
|
147
|
-
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
function setAuthCookie() {
|
|
151
|
-
if (typeof document === "undefined") return;
|
|
152
|
-
const maxAge = 7 * 24 * 60 * 60;
|
|
153
|
-
document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
154
|
-
}
|
|
155
|
-
function clearAuthCookie() {
|
|
156
|
-
if (typeof document === "undefined") return;
|
|
157
|
-
document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
158
|
-
}
|
|
159
|
-
function getCsrfToken() {
|
|
160
|
-
if (typeof document === "undefined") return null;
|
|
161
|
-
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
162
|
-
if (!match) return null;
|
|
163
|
-
return match.split("=")[1] || null;
|
|
164
|
-
}
|
|
165
|
-
function setCsrfToken(token) {
|
|
166
|
-
if (typeof document === "undefined") return;
|
|
167
|
-
const maxAge = 7 * 24 * 60 * 60;
|
|
168
|
-
document.cookie = `${CSRF_TOKEN_COOKIE}=${token}; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
169
|
-
}
|
|
170
|
-
function clearCsrfToken() {
|
|
171
|
-
if (typeof document === "undefined") return;
|
|
172
|
-
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
173
|
-
}
|
|
174
141
|
var TokenManager = class {
|
|
175
142
|
constructor(storage) {
|
|
176
|
-
// In-memory storage
|
|
177
|
-
this.accessToken = null;
|
|
178
|
-
this.user = null;
|
|
179
|
-
// Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
|
|
180
|
-
this._mode = "storage";
|
|
181
143
|
if (storage) {
|
|
182
144
|
this.storage = storage;
|
|
183
145
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -195,112 +157,32 @@ var TokenManager = class {
|
|
|
195
157
|
};
|
|
196
158
|
}
|
|
197
159
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
get mode() {
|
|
202
|
-
return this._mode;
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Set mode to memory (new backend with cookies + memory)
|
|
206
|
-
*/
|
|
207
|
-
setMemoryMode() {
|
|
208
|
-
if (this._mode === "storage") {
|
|
209
|
-
this.storage.removeItem(TOKEN_KEY);
|
|
210
|
-
this.storage.removeItem(USER_KEY);
|
|
211
|
-
}
|
|
212
|
-
this._mode = "memory";
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Set mode to storage (legacy backend with localStorage)
|
|
216
|
-
* Also loads existing session from localStorage
|
|
217
|
-
*/
|
|
218
|
-
setStorageMode() {
|
|
219
|
-
this._mode = "storage";
|
|
220
|
-
this.loadFromStorage();
|
|
160
|
+
saveSession(session) {
|
|
161
|
+
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
162
|
+
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
221
163
|
}
|
|
222
|
-
|
|
223
|
-
* Load session from localStorage
|
|
224
|
-
*/
|
|
225
|
-
loadFromStorage() {
|
|
164
|
+
getSession() {
|
|
226
165
|
const token = this.storage.getItem(TOKEN_KEY);
|
|
227
166
|
const userStr = this.storage.getItem(USER_KEY);
|
|
228
|
-
if (token
|
|
229
|
-
|
|
230
|
-
this.accessToken = token;
|
|
231
|
-
this.user = JSON.parse(userStr);
|
|
232
|
-
} catch {
|
|
233
|
-
this.clearSession();
|
|
234
|
-
}
|
|
167
|
+
if (!token || !userStr) {
|
|
168
|
+
return null;
|
|
235
169
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
this.user = session.user;
|
|
243
|
-
if (this._mode === "storage") {
|
|
244
|
-
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
245
|
-
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
170
|
+
try {
|
|
171
|
+
const user = JSON.parse(userStr);
|
|
172
|
+
return { accessToken: token, user };
|
|
173
|
+
} catch {
|
|
174
|
+
this.clearSession();
|
|
175
|
+
return null;
|
|
246
176
|
}
|
|
247
177
|
}
|
|
248
|
-
/**
|
|
249
|
-
* Get current session
|
|
250
|
-
*/
|
|
251
|
-
getSession() {
|
|
252
|
-
if (!this.accessToken || !this.user) return null;
|
|
253
|
-
return {
|
|
254
|
-
accessToken: this.accessToken,
|
|
255
|
-
user: this.user
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Get access token
|
|
260
|
-
*/
|
|
261
178
|
getAccessToken() {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Set access token
|
|
266
|
-
*/
|
|
267
|
-
setAccessToken(token) {
|
|
268
|
-
this.accessToken = token;
|
|
269
|
-
if (this._mode === "storage") {
|
|
270
|
-
this.storage.setItem(TOKEN_KEY, token);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Get user
|
|
275
|
-
*/
|
|
276
|
-
getUser() {
|
|
277
|
-
return this.user;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Set user
|
|
281
|
-
*/
|
|
282
|
-
setUser(user) {
|
|
283
|
-
this.user = user;
|
|
284
|
-
if (this._mode === "storage") {
|
|
285
|
-
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
286
|
-
}
|
|
179
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
180
|
+
return typeof token === "string" ? token : null;
|
|
287
181
|
}
|
|
288
|
-
/**
|
|
289
|
-
* Clear session (both memory and localStorage)
|
|
290
|
-
*/
|
|
291
182
|
clearSession() {
|
|
292
|
-
this.accessToken = null;
|
|
293
|
-
this.user = null;
|
|
294
183
|
this.storage.removeItem(TOKEN_KEY);
|
|
295
184
|
this.storage.removeItem(USER_KEY);
|
|
296
185
|
}
|
|
297
|
-
/**
|
|
298
|
-
* Check if there's a session in localStorage (for legacy detection)
|
|
299
|
-
*/
|
|
300
|
-
hasStoredSession() {
|
|
301
|
-
const token = this.storage.getItem(TOKEN_KEY);
|
|
302
|
-
return !!token;
|
|
303
|
-
}
|
|
304
186
|
};
|
|
305
187
|
|
|
306
188
|
// src/modules/database-postgrest.ts
|
|
@@ -415,79 +297,6 @@ var Auth = class {
|
|
|
415
297
|
this.database = new Database(http, tokenManager);
|
|
416
298
|
this.detectAuthCallback();
|
|
417
299
|
}
|
|
418
|
-
/**
|
|
419
|
-
* Restore session on app initialization
|
|
420
|
-
*
|
|
421
|
-
* @returns Object with isLoggedIn status
|
|
422
|
-
*
|
|
423
|
-
* @example
|
|
424
|
-
* ```typescript
|
|
425
|
-
* const client = new InsForgeClient({ baseUrl: '...' });
|
|
426
|
-
* const { isLoggedIn } = await client.auth.restoreSession();
|
|
427
|
-
*
|
|
428
|
-
* if (isLoggedIn) {
|
|
429
|
-
* const { data } = await client.auth.getCurrentUser();
|
|
430
|
-
* }
|
|
431
|
-
* ```
|
|
432
|
-
*/
|
|
433
|
-
async restoreSession() {
|
|
434
|
-
if (typeof window === "undefined") {
|
|
435
|
-
return { isLoggedIn: false };
|
|
436
|
-
}
|
|
437
|
-
if (this.tokenManager.getAccessToken()) {
|
|
438
|
-
return { isLoggedIn: true };
|
|
439
|
-
}
|
|
440
|
-
if (hasAuthCookie()) {
|
|
441
|
-
try {
|
|
442
|
-
const csrfToken = getCsrfToken();
|
|
443
|
-
const response = await this.http.post(
|
|
444
|
-
"/api/auth/refresh",
|
|
445
|
-
{
|
|
446
|
-
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {}
|
|
447
|
-
}
|
|
448
|
-
);
|
|
449
|
-
if (response.accessToken) {
|
|
450
|
-
this.tokenManager.setMemoryMode();
|
|
451
|
-
this.tokenManager.setAccessToken(response.accessToken);
|
|
452
|
-
this.http.setAuthToken(response.accessToken);
|
|
453
|
-
if (response.user) {
|
|
454
|
-
this.tokenManager.setUser(response.user);
|
|
455
|
-
}
|
|
456
|
-
if (response.csrfToken) {
|
|
457
|
-
setCsrfToken(response.csrfToken);
|
|
458
|
-
}
|
|
459
|
-
return { isLoggedIn: true };
|
|
460
|
-
}
|
|
461
|
-
} catch (error) {
|
|
462
|
-
if (error instanceof InsForgeError) {
|
|
463
|
-
if (error.statusCode === 404) {
|
|
464
|
-
this.tokenManager.setStorageMode();
|
|
465
|
-
const token = this.tokenManager.getAccessToken();
|
|
466
|
-
if (token) {
|
|
467
|
-
this.http.setAuthToken(token);
|
|
468
|
-
return { isLoggedIn: true };
|
|
469
|
-
}
|
|
470
|
-
return { isLoggedIn: false };
|
|
471
|
-
}
|
|
472
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
473
|
-
clearAuthCookie();
|
|
474
|
-
clearCsrfToken();
|
|
475
|
-
return { isLoggedIn: false };
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return { isLoggedIn: false };
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
if (this.tokenManager.hasStoredSession()) {
|
|
482
|
-
this.tokenManager.setStorageMode();
|
|
483
|
-
const token = this.tokenManager.getAccessToken();
|
|
484
|
-
if (token) {
|
|
485
|
-
this.http.setAuthToken(token);
|
|
486
|
-
return { isLoggedIn: true };
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
return { isLoggedIn: false };
|
|
490
|
-
}
|
|
491
300
|
/**
|
|
492
301
|
* Automatically detect and handle OAuth callback parameters in the URL
|
|
493
302
|
* This runs on initialization to seamlessly complete the OAuth flow
|
|
@@ -501,7 +310,6 @@ var Auth = class {
|
|
|
501
310
|
const userId = params.get("user_id");
|
|
502
311
|
const email = params.get("email");
|
|
503
312
|
const name = params.get("name");
|
|
504
|
-
const csrfToken = params.get("csrf_token");
|
|
505
313
|
if (accessToken && userId && email) {
|
|
506
314
|
const session = {
|
|
507
315
|
accessToken,
|
|
@@ -516,18 +324,13 @@ var Auth = class {
|
|
|
516
324
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
517
325
|
}
|
|
518
326
|
};
|
|
519
|
-
this.http.setAuthToken(accessToken);
|
|
520
327
|
this.tokenManager.saveSession(session);
|
|
521
|
-
|
|
522
|
-
if (csrfToken) {
|
|
523
|
-
setCsrfToken(csrfToken);
|
|
524
|
-
}
|
|
328
|
+
this.http.setAuthToken(accessToken);
|
|
525
329
|
const url = new URL(window.location.href);
|
|
526
330
|
url.searchParams.delete("access_token");
|
|
527
331
|
url.searchParams.delete("user_id");
|
|
528
332
|
url.searchParams.delete("email");
|
|
529
333
|
url.searchParams.delete("name");
|
|
530
|
-
url.searchParams.delete("csrf_token");
|
|
531
334
|
if (params.has("error")) {
|
|
532
335
|
url.searchParams.delete("error");
|
|
533
336
|
}
|
|
@@ -543,17 +346,15 @@ var Auth = class {
|
|
|
543
346
|
async signUp(request) {
|
|
544
347
|
try {
|
|
545
348
|
const response = await this.http.post("/api/auth/users", request);
|
|
546
|
-
if (response.accessToken && response.user
|
|
349
|
+
if (response.accessToken && response.user) {
|
|
547
350
|
const session = {
|
|
548
351
|
accessToken: response.accessToken,
|
|
549
352
|
user: response.user
|
|
550
353
|
};
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
this.http.setAuthToken(response.accessToken);
|
|
554
|
-
if (response.csrfToken) {
|
|
555
|
-
setCsrfToken(response.csrfToken);
|
|
354
|
+
if (!isHostedAuthEnvironment()) {
|
|
355
|
+
this.tokenManager.saveSession(session);
|
|
556
356
|
}
|
|
357
|
+
this.http.setAuthToken(response.accessToken);
|
|
557
358
|
}
|
|
558
359
|
return {
|
|
559
360
|
data: response,
|
|
@@ -579,18 +380,21 @@ var Auth = class {
|
|
|
579
380
|
async signInWithPassword(request) {
|
|
580
381
|
try {
|
|
581
382
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
setCsrfToken(response.csrfToken);
|
|
383
|
+
const session = {
|
|
384
|
+
accessToken: response.accessToken || "",
|
|
385
|
+
user: response.user || {
|
|
386
|
+
id: "",
|
|
387
|
+
email: "",
|
|
388
|
+
name: "",
|
|
389
|
+
emailVerified: false,
|
|
390
|
+
createdAt: "",
|
|
391
|
+
updatedAt: ""
|
|
592
392
|
}
|
|
393
|
+
};
|
|
394
|
+
if (!isHostedAuthEnvironment()) {
|
|
395
|
+
this.tokenManager.saveSession(session);
|
|
593
396
|
}
|
|
397
|
+
this.http.setAuthToken(response.accessToken || "");
|
|
594
398
|
return {
|
|
595
399
|
data: response,
|
|
596
400
|
error: null
|
|
@@ -648,14 +452,8 @@ var Auth = class {
|
|
|
648
452
|
*/
|
|
649
453
|
async signOut() {
|
|
650
454
|
try {
|
|
651
|
-
try {
|
|
652
|
-
await this.http.post("/api/auth/logout");
|
|
653
|
-
} catch {
|
|
654
|
-
}
|
|
655
455
|
this.tokenManager.clearSession();
|
|
656
456
|
this.http.setAuthToken(null);
|
|
657
|
-
clearAuthCookie();
|
|
658
|
-
clearCsrfToken();
|
|
659
457
|
return { error: null };
|
|
660
458
|
} catch (error) {
|
|
661
459
|
return {
|
|
@@ -977,17 +775,13 @@ var Auth = class {
|
|
|
977
775
|
"/api/auth/email/verify",
|
|
978
776
|
request
|
|
979
777
|
);
|
|
980
|
-
if (response.accessToken
|
|
778
|
+
if (response.accessToken) {
|
|
981
779
|
const session = {
|
|
982
780
|
accessToken: response.accessToken,
|
|
983
781
|
user: response.user || {}
|
|
984
782
|
};
|
|
985
783
|
this.tokenManager.saveSession(session);
|
|
986
784
|
this.http.setAuthToken(response.accessToken);
|
|
987
|
-
setAuthCookie();
|
|
988
|
-
if (response.csrfToken) {
|
|
989
|
-
setCsrfToken(response.csrfToken);
|
|
990
|
-
}
|
|
991
785
|
}
|
|
992
786
|
return {
|
|
993
787
|
data: response,
|
|
@@ -1530,6 +1324,262 @@ var Functions = class {
|
|
|
1530
1324
|
}
|
|
1531
1325
|
};
|
|
1532
1326
|
|
|
1327
|
+
// src/modules/realtime.ts
|
|
1328
|
+
import { io } from "socket.io-client";
|
|
1329
|
+
var CONNECT_TIMEOUT = 1e4;
|
|
1330
|
+
var Realtime = class {
|
|
1331
|
+
constructor(baseUrl, tokenManager) {
|
|
1332
|
+
this.socket = null;
|
|
1333
|
+
this.connectPromise = null;
|
|
1334
|
+
this.subscribedChannels = /* @__PURE__ */ new Set();
|
|
1335
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
1336
|
+
this.baseUrl = baseUrl;
|
|
1337
|
+
this.tokenManager = tokenManager;
|
|
1338
|
+
}
|
|
1339
|
+
notifyListeners(event, payload) {
|
|
1340
|
+
const listeners = this.eventListeners.get(event);
|
|
1341
|
+
if (!listeners) return;
|
|
1342
|
+
for (const cb of listeners) {
|
|
1343
|
+
try {
|
|
1344
|
+
cb(payload);
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
console.error(`Error in ${event} callback:`, err);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Connect to the realtime server
|
|
1352
|
+
* @returns Promise that resolves when connected
|
|
1353
|
+
*/
|
|
1354
|
+
connect() {
|
|
1355
|
+
if (this.socket?.connected) {
|
|
1356
|
+
return Promise.resolve();
|
|
1357
|
+
}
|
|
1358
|
+
if (this.connectPromise) {
|
|
1359
|
+
return this.connectPromise;
|
|
1360
|
+
}
|
|
1361
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
1362
|
+
const session = this.tokenManager.getSession();
|
|
1363
|
+
const token = session?.accessToken;
|
|
1364
|
+
this.socket = io(this.baseUrl, {
|
|
1365
|
+
transports: ["websocket"],
|
|
1366
|
+
auth: token ? { token } : void 0
|
|
1367
|
+
});
|
|
1368
|
+
let initialConnection = true;
|
|
1369
|
+
let timeoutId = null;
|
|
1370
|
+
const cleanup = () => {
|
|
1371
|
+
if (timeoutId) {
|
|
1372
|
+
clearTimeout(timeoutId);
|
|
1373
|
+
timeoutId = null;
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
timeoutId = setTimeout(() => {
|
|
1377
|
+
if (initialConnection) {
|
|
1378
|
+
initialConnection = false;
|
|
1379
|
+
this.connectPromise = null;
|
|
1380
|
+
this.socket?.disconnect();
|
|
1381
|
+
this.socket = null;
|
|
1382
|
+
reject(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`));
|
|
1383
|
+
}
|
|
1384
|
+
}, CONNECT_TIMEOUT);
|
|
1385
|
+
this.socket.on("connect", () => {
|
|
1386
|
+
cleanup();
|
|
1387
|
+
for (const channel of this.subscribedChannels) {
|
|
1388
|
+
this.socket.emit("realtime:subscribe", { channel });
|
|
1389
|
+
}
|
|
1390
|
+
this.notifyListeners("connect");
|
|
1391
|
+
if (initialConnection) {
|
|
1392
|
+
initialConnection = false;
|
|
1393
|
+
this.connectPromise = null;
|
|
1394
|
+
resolve();
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
this.socket.on("connect_error", (error) => {
|
|
1398
|
+
cleanup();
|
|
1399
|
+
this.notifyListeners("connect_error", error);
|
|
1400
|
+
if (initialConnection) {
|
|
1401
|
+
initialConnection = false;
|
|
1402
|
+
this.connectPromise = null;
|
|
1403
|
+
reject(error);
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
this.socket.on("disconnect", (reason) => {
|
|
1407
|
+
this.notifyListeners("disconnect", reason);
|
|
1408
|
+
});
|
|
1409
|
+
this.socket.on("realtime:error", (error) => {
|
|
1410
|
+
this.notifyListeners("error", error);
|
|
1411
|
+
});
|
|
1412
|
+
this.socket.onAny((event, message) => {
|
|
1413
|
+
if (event === "realtime:error") return;
|
|
1414
|
+
this.notifyListeners(event, message);
|
|
1415
|
+
});
|
|
1416
|
+
});
|
|
1417
|
+
return this.connectPromise;
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Disconnect from the realtime server
|
|
1421
|
+
*/
|
|
1422
|
+
disconnect() {
|
|
1423
|
+
if (this.socket) {
|
|
1424
|
+
this.socket.disconnect();
|
|
1425
|
+
this.socket = null;
|
|
1426
|
+
}
|
|
1427
|
+
this.subscribedChannels.clear();
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Check if connected to the realtime server
|
|
1431
|
+
*/
|
|
1432
|
+
get isConnected() {
|
|
1433
|
+
return this.socket?.connected ?? false;
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Get the current connection state
|
|
1437
|
+
*/
|
|
1438
|
+
get connectionState() {
|
|
1439
|
+
if (!this.socket) return "disconnected";
|
|
1440
|
+
if (this.socket.connected) return "connected";
|
|
1441
|
+
return "connecting";
|
|
1442
|
+
}
|
|
1443
|
+
/**
|
|
1444
|
+
* Get the socket ID (if connected)
|
|
1445
|
+
*/
|
|
1446
|
+
get socketId() {
|
|
1447
|
+
return this.socket?.id;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Subscribe to a channel
|
|
1451
|
+
*
|
|
1452
|
+
* Automatically connects if not already connected.
|
|
1453
|
+
*
|
|
1454
|
+
* @param channel - Channel name (e.g., 'orders:123', 'broadcast')
|
|
1455
|
+
* @returns Promise with the subscription response
|
|
1456
|
+
*/
|
|
1457
|
+
async subscribe(channel) {
|
|
1458
|
+
if (this.subscribedChannels.has(channel)) {
|
|
1459
|
+
return { ok: true, channel };
|
|
1460
|
+
}
|
|
1461
|
+
if (!this.socket?.connected) {
|
|
1462
|
+
try {
|
|
1463
|
+
await this.connect();
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
const message = error instanceof Error ? error.message : "Connection failed";
|
|
1466
|
+
return { ok: false, channel, error: { code: "CONNECTION_FAILED", message } };
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return new Promise((resolve) => {
|
|
1470
|
+
this.socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
1471
|
+
if (response.ok) {
|
|
1472
|
+
this.subscribedChannels.add(channel);
|
|
1473
|
+
}
|
|
1474
|
+
resolve(response);
|
|
1475
|
+
});
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Unsubscribe from a channel (fire-and-forget)
|
|
1480
|
+
*
|
|
1481
|
+
* @param channel - Channel name to unsubscribe from
|
|
1482
|
+
*/
|
|
1483
|
+
unsubscribe(channel) {
|
|
1484
|
+
this.subscribedChannels.delete(channel);
|
|
1485
|
+
if (this.socket?.connected) {
|
|
1486
|
+
this.socket.emit("realtime:unsubscribe", { channel });
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Publish a message to a channel
|
|
1491
|
+
*
|
|
1492
|
+
* @param channel - Channel name
|
|
1493
|
+
* @param event - Event name
|
|
1494
|
+
* @param payload - Message payload
|
|
1495
|
+
*/
|
|
1496
|
+
async publish(channel, event, payload) {
|
|
1497
|
+
if (!this.socket?.connected) {
|
|
1498
|
+
throw new Error("Not connected to realtime server. Call connect() first.");
|
|
1499
|
+
}
|
|
1500
|
+
this.socket.emit("realtime:publish", { channel, event, payload });
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Listen for events
|
|
1504
|
+
*
|
|
1505
|
+
* Reserved event names:
|
|
1506
|
+
* - 'connect' - Fired when connected to the server
|
|
1507
|
+
* - 'connect_error' - Fired when connection fails (payload: Error)
|
|
1508
|
+
* - 'disconnect' - Fired when disconnected (payload: reason string)
|
|
1509
|
+
* - 'error' - Fired when a realtime error occurs (payload: RealtimeErrorPayload)
|
|
1510
|
+
*
|
|
1511
|
+
* All other events receive a `SocketMessage` payload with metadata.
|
|
1512
|
+
*
|
|
1513
|
+
* @param event - Event name to listen for
|
|
1514
|
+
* @param callback - Callback function when event is received
|
|
1515
|
+
*/
|
|
1516
|
+
on(event, callback) {
|
|
1517
|
+
if (!this.eventListeners.has(event)) {
|
|
1518
|
+
this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
1519
|
+
}
|
|
1520
|
+
this.eventListeners.get(event).add(callback);
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Remove a listener for a specific event
|
|
1524
|
+
*
|
|
1525
|
+
* @param event - Event name
|
|
1526
|
+
* @param callback - The callback function to remove
|
|
1527
|
+
*/
|
|
1528
|
+
off(event, callback) {
|
|
1529
|
+
const listeners = this.eventListeners.get(event);
|
|
1530
|
+
if (listeners) {
|
|
1531
|
+
listeners.delete(callback);
|
|
1532
|
+
if (listeners.size === 0) {
|
|
1533
|
+
this.eventListeners.delete(event);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Listen for an event only once, then automatically remove the listener
|
|
1539
|
+
*
|
|
1540
|
+
* @param event - Event name to listen for
|
|
1541
|
+
* @param callback - Callback function when event is received
|
|
1542
|
+
*/
|
|
1543
|
+
once(event, callback) {
|
|
1544
|
+
const wrapper = (payload) => {
|
|
1545
|
+
this.off(event, wrapper);
|
|
1546
|
+
callback(payload);
|
|
1547
|
+
};
|
|
1548
|
+
this.on(event, wrapper);
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Get all currently subscribed channels
|
|
1552
|
+
*
|
|
1553
|
+
* @returns Array of channel names
|
|
1554
|
+
*/
|
|
1555
|
+
getSubscribedChannels() {
|
|
1556
|
+
return Array.from(this.subscribedChannels);
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
|
|
1560
|
+
// src/modules/email.ts
|
|
1561
|
+
var Emails = class {
|
|
1562
|
+
constructor(http) {
|
|
1563
|
+
this.http = http;
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Send a custom HTML email
|
|
1567
|
+
* @param options Email options including recipients, subject, and HTML content
|
|
1568
|
+
*/
|
|
1569
|
+
async send(options) {
|
|
1570
|
+
try {
|
|
1571
|
+
const data = await this.http.post(
|
|
1572
|
+
"/api/email/send-raw",
|
|
1573
|
+
options
|
|
1574
|
+
);
|
|
1575
|
+
return { data, error: null };
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
1578
|
+
return { data: null, error: normalizedError };
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1533
1583
|
// src/client.ts
|
|
1534
1584
|
var InsForgeClient = class {
|
|
1535
1585
|
constructor(config = {}) {
|
|
@@ -1547,11 +1597,16 @@ var InsForgeClient = class {
|
|
|
1547
1597
|
if (existingSession?.accessToken) {
|
|
1548
1598
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1549
1599
|
}
|
|
1550
|
-
this.auth = new Auth(
|
|
1600
|
+
this.auth = new Auth(
|
|
1601
|
+
this.http,
|
|
1602
|
+
this.tokenManager
|
|
1603
|
+
);
|
|
1551
1604
|
this.database = new Database(this.http, this.tokenManager);
|
|
1552
1605
|
this.storage = new Storage(this.http);
|
|
1553
1606
|
this.ai = new AI(this.http);
|
|
1554
1607
|
this.functions = new Functions(this.http);
|
|
1608
|
+
this.realtime = new Realtime(this.http.baseUrl, this.tokenManager);
|
|
1609
|
+
this.emails = new Emails(this.http);
|
|
1555
1610
|
}
|
|
1556
1611
|
/**
|
|
1557
1612
|
* Get the underlying HTTP client for custom requests
|
|
@@ -1584,10 +1639,12 @@ export {
|
|
|
1584
1639
|
AI,
|
|
1585
1640
|
Auth,
|
|
1586
1641
|
Database,
|
|
1642
|
+
Emails,
|
|
1587
1643
|
Functions,
|
|
1588
1644
|
HttpClient,
|
|
1589
1645
|
InsForgeClient,
|
|
1590
1646
|
InsForgeError,
|
|
1647
|
+
Realtime,
|
|
1591
1648
|
Storage,
|
|
1592
1649
|
StorageBucket,
|
|
1593
1650
|
TokenManager,
|