@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.js CHANGED
@@ -23,10 +23,12 @@ __export(index_exports, {
23
23
  AI: () => AI,
24
24
  Auth: () => Auth,
25
25
  Database: () => Database,
26
+ Emails: () => Emails,
26
27
  Functions: () => Functions,
27
28
  HttpClient: () => HttpClient,
28
29
  InsForgeClient: () => InsForgeClient,
29
30
  InsForgeError: () => InsForgeError,
31
+ Realtime: () => Realtime,
30
32
  Storage: () => Storage,
31
33
  StorageBucket: () => StorageBucket,
32
34
  TokenManager: () => TokenManager,
@@ -111,7 +113,6 @@ var HttpClient = class {
111
113
  method,
112
114
  headers: requestHeaders,
113
115
  body: processedBody,
114
- credentials: "include",
115
116
  ...fetchOptions
116
117
  });
117
118
  if (response.status === 204) {
@@ -176,45 +177,8 @@ var HttpClient = class {
176
177
  // src/lib/token-manager.ts
177
178
  var TOKEN_KEY = "insforge-auth-token";
178
179
  var USER_KEY = "insforge-auth-user";
179
- var AUTH_FLAG_COOKIE = "isAuthenticated";
180
- var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
181
- function hasAuthCookie() {
182
- if (typeof document === "undefined") return false;
183
- return document.cookie.split(";").some(
184
- (c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
185
- );
186
- }
187
- function setAuthCookie() {
188
- if (typeof document === "undefined") return;
189
- const maxAge = 7 * 24 * 60 * 60;
190
- document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
191
- }
192
- function clearAuthCookie() {
193
- if (typeof document === "undefined") return;
194
- document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
195
- }
196
- function getCsrfToken() {
197
- if (typeof document === "undefined") return null;
198
- const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
199
- if (!match) return null;
200
- return match.split("=")[1] || null;
201
- }
202
- function setCsrfToken(token) {
203
- if (typeof document === "undefined") return;
204
- const maxAge = 7 * 24 * 60 * 60;
205
- document.cookie = `${CSRF_TOKEN_COOKIE}=${token}; path=/; max-age=${maxAge}; SameSite=Lax`;
206
- }
207
- function clearCsrfToken() {
208
- if (typeof document === "undefined") return;
209
- document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
210
- }
211
180
  var TokenManager = class {
212
181
  constructor(storage) {
213
- // In-memory storage
214
- this.accessToken = null;
215
- this.user = null;
216
- // Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
217
- this._mode = "storage";
218
182
  if (storage) {
219
183
  this.storage = storage;
220
184
  } else if (typeof window !== "undefined" && window.localStorage) {
@@ -232,112 +196,32 @@ var TokenManager = class {
232
196
  };
233
197
  }
234
198
  }
235
- /**
236
- * Get current mode
237
- */
238
- get mode() {
239
- return this._mode;
240
- }
241
- /**
242
- * Set mode to memory (new backend with cookies + memory)
243
- */
244
- setMemoryMode() {
245
- if (this._mode === "storage") {
246
- this.storage.removeItem(TOKEN_KEY);
247
- this.storage.removeItem(USER_KEY);
248
- }
249
- this._mode = "memory";
250
- }
251
- /**
252
- * Set mode to storage (legacy backend with localStorage)
253
- * Also loads existing session from localStorage
254
- */
255
- setStorageMode() {
256
- this._mode = "storage";
257
- this.loadFromStorage();
199
+ saveSession(session) {
200
+ this.storage.setItem(TOKEN_KEY, session.accessToken);
201
+ this.storage.setItem(USER_KEY, JSON.stringify(session.user));
258
202
  }
259
- /**
260
- * Load session from localStorage
261
- */
262
- loadFromStorage() {
203
+ getSession() {
263
204
  const token = this.storage.getItem(TOKEN_KEY);
264
205
  const userStr = this.storage.getItem(USER_KEY);
265
- if (token && userStr) {
266
- try {
267
- this.accessToken = token;
268
- this.user = JSON.parse(userStr);
269
- } catch {
270
- this.clearSession();
271
- }
206
+ if (!token || !userStr) {
207
+ return null;
272
208
  }
273
- }
274
- /**
275
- * Save session (memory always, localStorage only in storage mode)
276
- */
277
- saveSession(session) {
278
- this.accessToken = session.accessToken;
279
- this.user = session.user;
280
- if (this._mode === "storage") {
281
- this.storage.setItem(TOKEN_KEY, session.accessToken);
282
- this.storage.setItem(USER_KEY, JSON.stringify(session.user));
209
+ try {
210
+ const user = JSON.parse(userStr);
211
+ return { accessToken: token, user };
212
+ } catch {
213
+ this.clearSession();
214
+ return null;
283
215
  }
284
216
  }
285
- /**
286
- * Get current session
287
- */
288
- getSession() {
289
- if (!this.accessToken || !this.user) return null;
290
- return {
291
- accessToken: this.accessToken,
292
- user: this.user
293
- };
294
- }
295
- /**
296
- * Get access token
297
- */
298
217
  getAccessToken() {
299
- return this.accessToken;
300
- }
301
- /**
302
- * Set access token
303
- */
304
- setAccessToken(token) {
305
- this.accessToken = token;
306
- if (this._mode === "storage") {
307
- this.storage.setItem(TOKEN_KEY, token);
308
- }
309
- }
310
- /**
311
- * Get user
312
- */
313
- getUser() {
314
- return this.user;
315
- }
316
- /**
317
- * Set user
318
- */
319
- setUser(user) {
320
- this.user = user;
321
- if (this._mode === "storage") {
322
- this.storage.setItem(USER_KEY, JSON.stringify(user));
323
- }
218
+ const token = this.storage.getItem(TOKEN_KEY);
219
+ return typeof token === "string" ? token : null;
324
220
  }
325
- /**
326
- * Clear session (both memory and localStorage)
327
- */
328
221
  clearSession() {
329
- this.accessToken = null;
330
- this.user = null;
331
222
  this.storage.removeItem(TOKEN_KEY);
332
223
  this.storage.removeItem(USER_KEY);
333
224
  }
334
- /**
335
- * Check if there's a session in localStorage (for legacy detection)
336
- */
337
- hasStoredSession() {
338
- const token = this.storage.getItem(TOKEN_KEY);
339
- return !!token;
340
- }
341
225
  };
342
226
 
343
227
  // src/modules/database-postgrest.ts
@@ -452,79 +336,6 @@ var Auth = class {
452
336
  this.database = new Database(http, tokenManager);
453
337
  this.detectAuthCallback();
454
338
  }
455
- /**
456
- * Restore session on app initialization
457
- *
458
- * @returns Object with isLoggedIn status
459
- *
460
- * @example
461
- * ```typescript
462
- * const client = new InsForgeClient({ baseUrl: '...' });
463
- * const { isLoggedIn } = await client.auth.restoreSession();
464
- *
465
- * if (isLoggedIn) {
466
- * const { data } = await client.auth.getCurrentUser();
467
- * }
468
- * ```
469
- */
470
- async restoreSession() {
471
- if (typeof window === "undefined") {
472
- return { isLoggedIn: false };
473
- }
474
- if (this.tokenManager.getAccessToken()) {
475
- return { isLoggedIn: true };
476
- }
477
- if (hasAuthCookie()) {
478
- try {
479
- const csrfToken = getCsrfToken();
480
- const response = await this.http.post(
481
- "/api/auth/refresh",
482
- {
483
- headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {}
484
- }
485
- );
486
- if (response.accessToken) {
487
- this.tokenManager.setMemoryMode();
488
- this.tokenManager.setAccessToken(response.accessToken);
489
- this.http.setAuthToken(response.accessToken);
490
- if (response.user) {
491
- this.tokenManager.setUser(response.user);
492
- }
493
- if (response.csrfToken) {
494
- setCsrfToken(response.csrfToken);
495
- }
496
- return { isLoggedIn: true };
497
- }
498
- } catch (error) {
499
- if (error instanceof InsForgeError) {
500
- if (error.statusCode === 404) {
501
- this.tokenManager.setStorageMode();
502
- const token = this.tokenManager.getAccessToken();
503
- if (token) {
504
- this.http.setAuthToken(token);
505
- return { isLoggedIn: true };
506
- }
507
- return { isLoggedIn: false };
508
- }
509
- if (error.statusCode === 401 || error.statusCode === 403) {
510
- clearAuthCookie();
511
- clearCsrfToken();
512
- return { isLoggedIn: false };
513
- }
514
- }
515
- return { isLoggedIn: false };
516
- }
517
- }
518
- if (this.tokenManager.hasStoredSession()) {
519
- this.tokenManager.setStorageMode();
520
- const token = this.tokenManager.getAccessToken();
521
- if (token) {
522
- this.http.setAuthToken(token);
523
- return { isLoggedIn: true };
524
- }
525
- }
526
- return { isLoggedIn: false };
527
- }
528
339
  /**
529
340
  * Automatically detect and handle OAuth callback parameters in the URL
530
341
  * This runs on initialization to seamlessly complete the OAuth flow
@@ -538,7 +349,6 @@ var Auth = class {
538
349
  const userId = params.get("user_id");
539
350
  const email = params.get("email");
540
351
  const name = params.get("name");
541
- const csrfToken = params.get("csrf_token");
542
352
  if (accessToken && userId && email) {
543
353
  const session = {
544
354
  accessToken,
@@ -553,18 +363,13 @@ var Auth = class {
553
363
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
554
364
  }
555
365
  };
556
- this.http.setAuthToken(accessToken);
557
366
  this.tokenManager.saveSession(session);
558
- setAuthCookie();
559
- if (csrfToken) {
560
- setCsrfToken(csrfToken);
561
- }
367
+ this.http.setAuthToken(accessToken);
562
368
  const url = new URL(window.location.href);
563
369
  url.searchParams.delete("access_token");
564
370
  url.searchParams.delete("user_id");
565
371
  url.searchParams.delete("email");
566
372
  url.searchParams.delete("name");
567
- url.searchParams.delete("csrf_token");
568
373
  if (params.has("error")) {
569
374
  url.searchParams.delete("error");
570
375
  }
@@ -580,17 +385,15 @@ var Auth = class {
580
385
  async signUp(request) {
581
386
  try {
582
387
  const response = await this.http.post("/api/auth/users", request);
583
- if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
388
+ if (response.accessToken && response.user) {
584
389
  const session = {
585
390
  accessToken: response.accessToken,
586
391
  user: response.user
587
392
  };
588
- this.tokenManager.saveSession(session);
589
- setAuthCookie();
590
- this.http.setAuthToken(response.accessToken);
591
- if (response.csrfToken) {
592
- setCsrfToken(response.csrfToken);
393
+ if (!isHostedAuthEnvironment()) {
394
+ this.tokenManager.saveSession(session);
593
395
  }
396
+ this.http.setAuthToken(response.accessToken);
594
397
  }
595
398
  return {
596
399
  data: response,
@@ -616,18 +419,21 @@ var Auth = class {
616
419
  async signInWithPassword(request) {
617
420
  try {
618
421
  const response = await this.http.post("/api/auth/sessions", request);
619
- if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
620
- const session = {
621
- accessToken: response.accessToken,
622
- user: response.user
623
- };
624
- this.tokenManager.saveSession(session);
625
- setAuthCookie();
626
- this.http.setAuthToken(response.accessToken);
627
- if (response.csrfToken) {
628
- setCsrfToken(response.csrfToken);
422
+ const session = {
423
+ accessToken: response.accessToken || "",
424
+ user: response.user || {
425
+ id: "",
426
+ email: "",
427
+ name: "",
428
+ emailVerified: false,
429
+ createdAt: "",
430
+ updatedAt: ""
629
431
  }
432
+ };
433
+ if (!isHostedAuthEnvironment()) {
434
+ this.tokenManager.saveSession(session);
630
435
  }
436
+ this.http.setAuthToken(response.accessToken || "");
631
437
  return {
632
438
  data: response,
633
439
  error: null
@@ -685,14 +491,8 @@ var Auth = class {
685
491
  */
686
492
  async signOut() {
687
493
  try {
688
- try {
689
- await this.http.post("/api/auth/logout");
690
- } catch {
691
- }
692
494
  this.tokenManager.clearSession();
693
495
  this.http.setAuthToken(null);
694
- clearAuthCookie();
695
- clearCsrfToken();
696
496
  return { error: null };
697
497
  } catch (error) {
698
498
  return {
@@ -1014,17 +814,13 @@ var Auth = class {
1014
814
  "/api/auth/email/verify",
1015
815
  request
1016
816
  );
1017
- if (response.accessToken && !isHostedAuthEnvironment()) {
817
+ if (response.accessToken) {
1018
818
  const session = {
1019
819
  accessToken: response.accessToken,
1020
820
  user: response.user || {}
1021
821
  };
1022
822
  this.tokenManager.saveSession(session);
1023
823
  this.http.setAuthToken(response.accessToken);
1024
- setAuthCookie();
1025
- if (response.csrfToken) {
1026
- setCsrfToken(response.csrfToken);
1027
- }
1028
824
  }
1029
825
  return {
1030
826
  data: response,
@@ -1567,6 +1363,262 @@ var Functions = class {
1567
1363
  }
1568
1364
  };
1569
1365
 
1366
+ // src/modules/realtime.ts
1367
+ var import_socket = require("socket.io-client");
1368
+ var CONNECT_TIMEOUT = 1e4;
1369
+ var Realtime = class {
1370
+ constructor(baseUrl, tokenManager) {
1371
+ this.socket = null;
1372
+ this.connectPromise = null;
1373
+ this.subscribedChannels = /* @__PURE__ */ new Set();
1374
+ this.eventListeners = /* @__PURE__ */ new Map();
1375
+ this.baseUrl = baseUrl;
1376
+ this.tokenManager = tokenManager;
1377
+ }
1378
+ notifyListeners(event, payload) {
1379
+ const listeners = this.eventListeners.get(event);
1380
+ if (!listeners) return;
1381
+ for (const cb of listeners) {
1382
+ try {
1383
+ cb(payload);
1384
+ } catch (err) {
1385
+ console.error(`Error in ${event} callback:`, err);
1386
+ }
1387
+ }
1388
+ }
1389
+ /**
1390
+ * Connect to the realtime server
1391
+ * @returns Promise that resolves when connected
1392
+ */
1393
+ connect() {
1394
+ if (this.socket?.connected) {
1395
+ return Promise.resolve();
1396
+ }
1397
+ if (this.connectPromise) {
1398
+ return this.connectPromise;
1399
+ }
1400
+ this.connectPromise = new Promise((resolve, reject) => {
1401
+ const session = this.tokenManager.getSession();
1402
+ const token = session?.accessToken;
1403
+ this.socket = (0, import_socket.io)(this.baseUrl, {
1404
+ transports: ["websocket"],
1405
+ auth: token ? { token } : void 0
1406
+ });
1407
+ let initialConnection = true;
1408
+ let timeoutId = null;
1409
+ const cleanup = () => {
1410
+ if (timeoutId) {
1411
+ clearTimeout(timeoutId);
1412
+ timeoutId = null;
1413
+ }
1414
+ };
1415
+ timeoutId = setTimeout(() => {
1416
+ if (initialConnection) {
1417
+ initialConnection = false;
1418
+ this.connectPromise = null;
1419
+ this.socket?.disconnect();
1420
+ this.socket = null;
1421
+ reject(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`));
1422
+ }
1423
+ }, CONNECT_TIMEOUT);
1424
+ this.socket.on("connect", () => {
1425
+ cleanup();
1426
+ for (const channel of this.subscribedChannels) {
1427
+ this.socket.emit("realtime:subscribe", { channel });
1428
+ }
1429
+ this.notifyListeners("connect");
1430
+ if (initialConnection) {
1431
+ initialConnection = false;
1432
+ this.connectPromise = null;
1433
+ resolve();
1434
+ }
1435
+ });
1436
+ this.socket.on("connect_error", (error) => {
1437
+ cleanup();
1438
+ this.notifyListeners("connect_error", error);
1439
+ if (initialConnection) {
1440
+ initialConnection = false;
1441
+ this.connectPromise = null;
1442
+ reject(error);
1443
+ }
1444
+ });
1445
+ this.socket.on("disconnect", (reason) => {
1446
+ this.notifyListeners("disconnect", reason);
1447
+ });
1448
+ this.socket.on("realtime:error", (error) => {
1449
+ this.notifyListeners("error", error);
1450
+ });
1451
+ this.socket.onAny((event, message) => {
1452
+ if (event === "realtime:error") return;
1453
+ this.notifyListeners(event, message);
1454
+ });
1455
+ });
1456
+ return this.connectPromise;
1457
+ }
1458
+ /**
1459
+ * Disconnect from the realtime server
1460
+ */
1461
+ disconnect() {
1462
+ if (this.socket) {
1463
+ this.socket.disconnect();
1464
+ this.socket = null;
1465
+ }
1466
+ this.subscribedChannels.clear();
1467
+ }
1468
+ /**
1469
+ * Check if connected to the realtime server
1470
+ */
1471
+ get isConnected() {
1472
+ return this.socket?.connected ?? false;
1473
+ }
1474
+ /**
1475
+ * Get the current connection state
1476
+ */
1477
+ get connectionState() {
1478
+ if (!this.socket) return "disconnected";
1479
+ if (this.socket.connected) return "connected";
1480
+ return "connecting";
1481
+ }
1482
+ /**
1483
+ * Get the socket ID (if connected)
1484
+ */
1485
+ get socketId() {
1486
+ return this.socket?.id;
1487
+ }
1488
+ /**
1489
+ * Subscribe to a channel
1490
+ *
1491
+ * Automatically connects if not already connected.
1492
+ *
1493
+ * @param channel - Channel name (e.g., 'orders:123', 'broadcast')
1494
+ * @returns Promise with the subscription response
1495
+ */
1496
+ async subscribe(channel) {
1497
+ if (this.subscribedChannels.has(channel)) {
1498
+ return { ok: true, channel };
1499
+ }
1500
+ if (!this.socket?.connected) {
1501
+ try {
1502
+ await this.connect();
1503
+ } catch (error) {
1504
+ const message = error instanceof Error ? error.message : "Connection failed";
1505
+ return { ok: false, channel, error: { code: "CONNECTION_FAILED", message } };
1506
+ }
1507
+ }
1508
+ return new Promise((resolve) => {
1509
+ this.socket.emit("realtime:subscribe", { channel }, (response) => {
1510
+ if (response.ok) {
1511
+ this.subscribedChannels.add(channel);
1512
+ }
1513
+ resolve(response);
1514
+ });
1515
+ });
1516
+ }
1517
+ /**
1518
+ * Unsubscribe from a channel (fire-and-forget)
1519
+ *
1520
+ * @param channel - Channel name to unsubscribe from
1521
+ */
1522
+ unsubscribe(channel) {
1523
+ this.subscribedChannels.delete(channel);
1524
+ if (this.socket?.connected) {
1525
+ this.socket.emit("realtime:unsubscribe", { channel });
1526
+ }
1527
+ }
1528
+ /**
1529
+ * Publish a message to a channel
1530
+ *
1531
+ * @param channel - Channel name
1532
+ * @param event - Event name
1533
+ * @param payload - Message payload
1534
+ */
1535
+ async publish(channel, event, payload) {
1536
+ if (!this.socket?.connected) {
1537
+ throw new Error("Not connected to realtime server. Call connect() first.");
1538
+ }
1539
+ this.socket.emit("realtime:publish", { channel, event, payload });
1540
+ }
1541
+ /**
1542
+ * Listen for events
1543
+ *
1544
+ * Reserved event names:
1545
+ * - 'connect' - Fired when connected to the server
1546
+ * - 'connect_error' - Fired when connection fails (payload: Error)
1547
+ * - 'disconnect' - Fired when disconnected (payload: reason string)
1548
+ * - 'error' - Fired when a realtime error occurs (payload: RealtimeErrorPayload)
1549
+ *
1550
+ * All other events receive a `SocketMessage` payload with metadata.
1551
+ *
1552
+ * @param event - Event name to listen for
1553
+ * @param callback - Callback function when event is received
1554
+ */
1555
+ on(event, callback) {
1556
+ if (!this.eventListeners.has(event)) {
1557
+ this.eventListeners.set(event, /* @__PURE__ */ new Set());
1558
+ }
1559
+ this.eventListeners.get(event).add(callback);
1560
+ }
1561
+ /**
1562
+ * Remove a listener for a specific event
1563
+ *
1564
+ * @param event - Event name
1565
+ * @param callback - The callback function to remove
1566
+ */
1567
+ off(event, callback) {
1568
+ const listeners = this.eventListeners.get(event);
1569
+ if (listeners) {
1570
+ listeners.delete(callback);
1571
+ if (listeners.size === 0) {
1572
+ this.eventListeners.delete(event);
1573
+ }
1574
+ }
1575
+ }
1576
+ /**
1577
+ * Listen for an event only once, then automatically remove the listener
1578
+ *
1579
+ * @param event - Event name to listen for
1580
+ * @param callback - Callback function when event is received
1581
+ */
1582
+ once(event, callback) {
1583
+ const wrapper = (payload) => {
1584
+ this.off(event, wrapper);
1585
+ callback(payload);
1586
+ };
1587
+ this.on(event, wrapper);
1588
+ }
1589
+ /**
1590
+ * Get all currently subscribed channels
1591
+ *
1592
+ * @returns Array of channel names
1593
+ */
1594
+ getSubscribedChannels() {
1595
+ return Array.from(this.subscribedChannels);
1596
+ }
1597
+ };
1598
+
1599
+ // src/modules/email.ts
1600
+ var Emails = class {
1601
+ constructor(http) {
1602
+ this.http = http;
1603
+ }
1604
+ /**
1605
+ * Send a custom HTML email
1606
+ * @param options Email options including recipients, subject, and HTML content
1607
+ */
1608
+ async send(options) {
1609
+ try {
1610
+ const data = await this.http.post(
1611
+ "/api/email/send-raw",
1612
+ options
1613
+ );
1614
+ return { data, error: null };
1615
+ } catch (error) {
1616
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
1617
+ return { data: null, error: normalizedError };
1618
+ }
1619
+ }
1620
+ };
1621
+
1570
1622
  // src/client.ts
1571
1623
  var InsForgeClient = class {
1572
1624
  constructor(config = {}) {
@@ -1584,11 +1636,16 @@ var InsForgeClient = class {
1584
1636
  if (existingSession?.accessToken) {
1585
1637
  this.http.setAuthToken(existingSession.accessToken);
1586
1638
  }
1587
- this.auth = new Auth(this.http, this.tokenManager);
1639
+ this.auth = new Auth(
1640
+ this.http,
1641
+ this.tokenManager
1642
+ );
1588
1643
  this.database = new Database(this.http, this.tokenManager);
1589
1644
  this.storage = new Storage(this.http);
1590
1645
  this.ai = new AI(this.http);
1591
1646
  this.functions = new Functions(this.http);
1647
+ this.realtime = new Realtime(this.http.baseUrl, this.tokenManager);
1648
+ this.emails = new Emails(this.http);
1592
1649
  }
1593
1650
  /**
1594
1651
  * Get the underlying HTTP client for custom requests
@@ -1622,10 +1679,12 @@ var index_default = InsForgeClient;
1622
1679
  AI,
1623
1680
  Auth,
1624
1681
  Database,
1682
+ Emails,
1625
1683
  Functions,
1626
1684
  HttpClient,
1627
1685
  InsForgeClient,
1628
1686
  InsForgeError,
1687
+ Realtime,
1629
1688
  Storage,
1630
1689
  StorageBucket,
1631
1690
  TokenManager,