@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/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
- * Get current mode
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 && userStr) {
229
- try {
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
- * Save session (memory always, localStorage only in storage mode)
239
- */
240
- saveSession(session) {
241
- this.accessToken = session.accessToken;
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
- return this.accessToken;
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
- setAuthCookie();
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 && !isHostedAuthEnvironment()) {
349
+ if (response.accessToken && response.user) {
547
350
  const session = {
548
351
  accessToken: response.accessToken,
549
352
  user: response.user
550
353
  };
551
- this.tokenManager.saveSession(session);
552
- setAuthCookie();
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
- if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
583
- const session = {
584
- accessToken: response.accessToken,
585
- user: response.user
586
- };
587
- this.tokenManager.saveSession(session);
588
- setAuthCookie();
589
- this.http.setAuthToken(response.accessToken);
590
- if (response.csrfToken) {
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 && !isHostedAuthEnvironment()) {
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(this.http, this.tokenManager);
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,