@blinkdotnew/sdk 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -681,6 +681,7 @@ declare class BlinkRealtimeChannel implements RealtimeChannel {
681
681
  private isSubscribed;
682
682
  private reconnectTimer;
683
683
  private heartbeatTimer;
684
+ private reconnectAttempts;
684
685
  constructor(channelName: string, httpClient: HttpClient, projectId: string);
685
686
  subscribe(options?: {
686
687
  userId?: string;
@@ -709,6 +710,7 @@ declare class BlinkRealtimeImpl implements BlinkRealtime {
709
710
  private httpClient;
710
711
  private projectId;
711
712
  private channels;
713
+ private handlers;
712
714
  constructor(httpClient: HttpClient, projectId: string);
713
715
  channel(name: string): RealtimeChannel;
714
716
  subscribe(channelName: string, callback: (message: RealtimeMessage) => void, options?: RealtimeSubscribeOptions): Promise<() => void>;
package/dist/index.d.ts CHANGED
@@ -681,6 +681,7 @@ declare class BlinkRealtimeChannel implements RealtimeChannel {
681
681
  private isSubscribed;
682
682
  private reconnectTimer;
683
683
  private heartbeatTimer;
684
+ private reconnectAttempts;
684
685
  constructor(channelName: string, httpClient: HttpClient, projectId: string);
685
686
  subscribe(options?: {
686
687
  userId?: string;
@@ -709,6 +710,7 @@ declare class BlinkRealtimeImpl implements BlinkRealtime {
709
710
  private httpClient;
710
711
  private projectId;
711
712
  private channels;
713
+ private handlers;
712
714
  constructor(httpClient: HttpClient, projectId: string);
713
715
  channel(name: string): RealtimeChannel;
714
716
  subscribe(channelName: string, callback: (message: RealtimeMessage) => void, options?: RealtimeSubscribeOptions): Promise<() => void>;
package/dist/index.js CHANGED
@@ -1,5 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
9
+
3
10
  // ../core/src/types.ts
4
11
  var BlinkError = class extends Error {
5
12
  constructor(message, code, status, details) {
@@ -2474,6 +2481,17 @@ var BlinkDataImpl = class {
2474
2481
  };
2475
2482
 
2476
2483
  // src/realtime.ts
2484
+ var getWebSocketClass = () => {
2485
+ if (typeof WebSocket !== "undefined") {
2486
+ return WebSocket;
2487
+ }
2488
+ try {
2489
+ const WS = __require("ws");
2490
+ return WS;
2491
+ } catch (error) {
2492
+ throw new BlinkRealtimeError('WebSocket is not available. Install "ws" package for Node.js environments.');
2493
+ }
2494
+ };
2477
2495
  var BlinkRealtimeChannel = class {
2478
2496
  constructor(channelName, httpClient, projectId) {
2479
2497
  this.channelName = channelName;
@@ -2486,6 +2504,7 @@ var BlinkRealtimeChannel = class {
2486
2504
  isSubscribed = false;
2487
2505
  reconnectTimer = null;
2488
2506
  heartbeatTimer = null;
2507
+ reconnectAttempts = 0;
2489
2508
  async subscribe(options = {}) {
2490
2509
  if (this.isSubscribed) {
2491
2510
  return;
@@ -2576,7 +2595,9 @@ var BlinkRealtimeChannel = class {
2576
2595
  channel: this.channelName,
2577
2596
  limit: options.limit,
2578
2597
  start: options.after,
2598
+ // after = start from this ID onwards
2579
2599
  end: options.before
2600
+ // before = end at this ID
2580
2601
  });
2581
2602
  return response.data.messages;
2582
2603
  } catch (error) {
@@ -2586,16 +2607,22 @@ var BlinkRealtimeChannel = class {
2586
2607
  }
2587
2608
  }
2588
2609
  async connectWebSocket() {
2589
- if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2610
+ if (this.websocket && this.websocket.readyState === 1) {
2590
2611
  return;
2591
2612
  }
2592
2613
  return new Promise((resolve, reject) => {
2593
2614
  try {
2594
2615
  const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
2595
2616
  const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
2596
- this.websocket = new WebSocket(wsUrl);
2617
+ const WSClass = getWebSocketClass();
2618
+ this.websocket = new WSClass(wsUrl);
2619
+ if (!this.websocket) {
2620
+ reject(new BlinkRealtimeError("Failed to create WebSocket instance"));
2621
+ return;
2622
+ }
2597
2623
  this.websocket.onopen = () => {
2598
2624
  console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
2625
+ this.reconnectAttempts = 0;
2599
2626
  resolve();
2600
2627
  };
2601
2628
  this.websocket.onmessage = (event) => {
@@ -2616,7 +2643,7 @@ var BlinkRealtimeChannel = class {
2616
2643
  reject(new BlinkRealtimeError("WebSocket connection failed"));
2617
2644
  };
2618
2645
  setTimeout(() => {
2619
- if (this.websocket?.readyState !== WebSocket.OPEN) {
2646
+ if (this.websocket?.readyState !== 1) {
2620
2647
  reject(new BlinkRealtimeError("WebSocket connection timeout"));
2621
2648
  }
2622
2649
  }, 5e3);
@@ -2664,8 +2691,8 @@ var BlinkRealtimeChannel = class {
2664
2691
  if (this.heartbeatTimer) {
2665
2692
  clearInterval(this.heartbeatTimer);
2666
2693
  }
2667
- this.heartbeatTimer = window.setInterval(() => {
2668
- if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2694
+ this.heartbeatTimer = globalThis.setInterval(() => {
2695
+ if (this.websocket && this.websocket.readyState === 1) {
2669
2696
  this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
2670
2697
  }
2671
2698
  }, 25e3);
@@ -2674,7 +2701,12 @@ var BlinkRealtimeChannel = class {
2674
2701
  if (this.reconnectTimer) {
2675
2702
  clearTimeout(this.reconnectTimer);
2676
2703
  }
2677
- this.reconnectTimer = window.setTimeout(async () => {
2704
+ this.reconnectAttempts++;
2705
+ const baseDelay = Math.min(3e4, Math.pow(2, this.reconnectAttempts) * 1e3);
2706
+ const jitter = Math.random() * 1e3;
2707
+ const delay = baseDelay + jitter;
2708
+ console.log(`\u{1F504} Scheduling reconnect attempt ${this.reconnectAttempts} in ${Math.round(delay)}ms`);
2709
+ this.reconnectTimer = globalThis.setTimeout(async () => {
2678
2710
  if (this.isSubscribed) {
2679
2711
  try {
2680
2712
  await this.connectWebSocket();
@@ -2693,7 +2725,7 @@ var BlinkRealtimeChannel = class {
2693
2725
  this.scheduleReconnect();
2694
2726
  }
2695
2727
  }
2696
- }, 2e3);
2728
+ }, delay);
2697
2729
  }
2698
2730
  cleanup() {
2699
2731
  this.isSubscribed = false;
@@ -2719,6 +2751,7 @@ var BlinkRealtimeImpl = class {
2719
2751
  this.projectId = projectId;
2720
2752
  }
2721
2753
  channels = /* @__PURE__ */ new Map();
2754
+ handlers = {};
2722
2755
  channel(name) {
2723
2756
  if (!this.channels.has(name)) {
2724
2757
  this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
@@ -2728,7 +2761,21 @@ var BlinkRealtimeImpl = class {
2728
2761
  async subscribe(channelName, callback, options = {}) {
2729
2762
  const channel = this.channel(channelName);
2730
2763
  await channel.subscribe(options);
2731
- return channel.onMessage(callback);
2764
+ const state = this.handlers[channelName] ??= {
2765
+ msgHandlers: /* @__PURE__ */ new Set(),
2766
+ presHandlers: /* @__PURE__ */ new Set(),
2767
+ subscribed: true
2768
+ };
2769
+ state.msgHandlers.add(callback);
2770
+ const messageUnsub = channel.onMessage(callback);
2771
+ return () => {
2772
+ messageUnsub();
2773
+ state.msgHandlers.delete(callback);
2774
+ if (state.msgHandlers.size === 0 && state.presHandlers.size === 0) {
2775
+ channel.unsubscribe();
2776
+ delete this.handlers[channelName];
2777
+ }
2778
+ };
2732
2779
  }
2733
2780
  async publish(channelName, type, data, options = {}) {
2734
2781
  const channel = this.channel(channelName);
@@ -2740,7 +2787,21 @@ var BlinkRealtimeImpl = class {
2740
2787
  }
2741
2788
  onPresence(channelName, callback) {
2742
2789
  const channel = this.channel(channelName);
2743
- return channel.onPresence(callback);
2790
+ const state = this.handlers[channelName] ??= {
2791
+ msgHandlers: /* @__PURE__ */ new Set(),
2792
+ presHandlers: /* @__PURE__ */ new Set(),
2793
+ subscribed: false
2794
+ };
2795
+ state.presHandlers.add(callback);
2796
+ const presenceUnsub = channel.onPresence(callback);
2797
+ return () => {
2798
+ presenceUnsub();
2799
+ state.presHandlers.delete(callback);
2800
+ if (state.msgHandlers.size === 0 && state.presHandlers.size === 0) {
2801
+ channel.unsubscribe();
2802
+ delete this.handlers[channelName];
2803
+ }
2804
+ };
2744
2805
  }
2745
2806
  };
2746
2807
 
package/dist/index.mjs CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // ../core/src/types.ts
2
9
  var BlinkError = class extends Error {
3
10
  constructor(message, code, status, details) {
@@ -2472,6 +2479,17 @@ var BlinkDataImpl = class {
2472
2479
  };
2473
2480
 
2474
2481
  // src/realtime.ts
2482
+ var getWebSocketClass = () => {
2483
+ if (typeof WebSocket !== "undefined") {
2484
+ return WebSocket;
2485
+ }
2486
+ try {
2487
+ const WS = __require("ws");
2488
+ return WS;
2489
+ } catch (error) {
2490
+ throw new BlinkRealtimeError('WebSocket is not available. Install "ws" package for Node.js environments.');
2491
+ }
2492
+ };
2475
2493
  var BlinkRealtimeChannel = class {
2476
2494
  constructor(channelName, httpClient, projectId) {
2477
2495
  this.channelName = channelName;
@@ -2484,6 +2502,7 @@ var BlinkRealtimeChannel = class {
2484
2502
  isSubscribed = false;
2485
2503
  reconnectTimer = null;
2486
2504
  heartbeatTimer = null;
2505
+ reconnectAttempts = 0;
2487
2506
  async subscribe(options = {}) {
2488
2507
  if (this.isSubscribed) {
2489
2508
  return;
@@ -2574,7 +2593,9 @@ var BlinkRealtimeChannel = class {
2574
2593
  channel: this.channelName,
2575
2594
  limit: options.limit,
2576
2595
  start: options.after,
2596
+ // after = start from this ID onwards
2577
2597
  end: options.before
2598
+ // before = end at this ID
2578
2599
  });
2579
2600
  return response.data.messages;
2580
2601
  } catch (error) {
@@ -2584,16 +2605,22 @@ var BlinkRealtimeChannel = class {
2584
2605
  }
2585
2606
  }
2586
2607
  async connectWebSocket() {
2587
- if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2608
+ if (this.websocket && this.websocket.readyState === 1) {
2588
2609
  return;
2589
2610
  }
2590
2611
  return new Promise((resolve, reject) => {
2591
2612
  try {
2592
2613
  const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
2593
2614
  const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
2594
- this.websocket = new WebSocket(wsUrl);
2615
+ const WSClass = getWebSocketClass();
2616
+ this.websocket = new WSClass(wsUrl);
2617
+ if (!this.websocket) {
2618
+ reject(new BlinkRealtimeError("Failed to create WebSocket instance"));
2619
+ return;
2620
+ }
2595
2621
  this.websocket.onopen = () => {
2596
2622
  console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
2623
+ this.reconnectAttempts = 0;
2597
2624
  resolve();
2598
2625
  };
2599
2626
  this.websocket.onmessage = (event) => {
@@ -2614,7 +2641,7 @@ var BlinkRealtimeChannel = class {
2614
2641
  reject(new BlinkRealtimeError("WebSocket connection failed"));
2615
2642
  };
2616
2643
  setTimeout(() => {
2617
- if (this.websocket?.readyState !== WebSocket.OPEN) {
2644
+ if (this.websocket?.readyState !== 1) {
2618
2645
  reject(new BlinkRealtimeError("WebSocket connection timeout"));
2619
2646
  }
2620
2647
  }, 5e3);
@@ -2662,8 +2689,8 @@ var BlinkRealtimeChannel = class {
2662
2689
  if (this.heartbeatTimer) {
2663
2690
  clearInterval(this.heartbeatTimer);
2664
2691
  }
2665
- this.heartbeatTimer = window.setInterval(() => {
2666
- if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2692
+ this.heartbeatTimer = globalThis.setInterval(() => {
2693
+ if (this.websocket && this.websocket.readyState === 1) {
2667
2694
  this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
2668
2695
  }
2669
2696
  }, 25e3);
@@ -2672,7 +2699,12 @@ var BlinkRealtimeChannel = class {
2672
2699
  if (this.reconnectTimer) {
2673
2700
  clearTimeout(this.reconnectTimer);
2674
2701
  }
2675
- this.reconnectTimer = window.setTimeout(async () => {
2702
+ this.reconnectAttempts++;
2703
+ const baseDelay = Math.min(3e4, Math.pow(2, this.reconnectAttempts) * 1e3);
2704
+ const jitter = Math.random() * 1e3;
2705
+ const delay = baseDelay + jitter;
2706
+ console.log(`\u{1F504} Scheduling reconnect attempt ${this.reconnectAttempts} in ${Math.round(delay)}ms`);
2707
+ this.reconnectTimer = globalThis.setTimeout(async () => {
2676
2708
  if (this.isSubscribed) {
2677
2709
  try {
2678
2710
  await this.connectWebSocket();
@@ -2691,7 +2723,7 @@ var BlinkRealtimeChannel = class {
2691
2723
  this.scheduleReconnect();
2692
2724
  }
2693
2725
  }
2694
- }, 2e3);
2726
+ }, delay);
2695
2727
  }
2696
2728
  cleanup() {
2697
2729
  this.isSubscribed = false;
@@ -2717,6 +2749,7 @@ var BlinkRealtimeImpl = class {
2717
2749
  this.projectId = projectId;
2718
2750
  }
2719
2751
  channels = /* @__PURE__ */ new Map();
2752
+ handlers = {};
2720
2753
  channel(name) {
2721
2754
  if (!this.channels.has(name)) {
2722
2755
  this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
@@ -2726,7 +2759,21 @@ var BlinkRealtimeImpl = class {
2726
2759
  async subscribe(channelName, callback, options = {}) {
2727
2760
  const channel = this.channel(channelName);
2728
2761
  await channel.subscribe(options);
2729
- return channel.onMessage(callback);
2762
+ const state = this.handlers[channelName] ??= {
2763
+ msgHandlers: /* @__PURE__ */ new Set(),
2764
+ presHandlers: /* @__PURE__ */ new Set(),
2765
+ subscribed: true
2766
+ };
2767
+ state.msgHandlers.add(callback);
2768
+ const messageUnsub = channel.onMessage(callback);
2769
+ return () => {
2770
+ messageUnsub();
2771
+ state.msgHandlers.delete(callback);
2772
+ if (state.msgHandlers.size === 0 && state.presHandlers.size === 0) {
2773
+ channel.unsubscribe();
2774
+ delete this.handlers[channelName];
2775
+ }
2776
+ };
2730
2777
  }
2731
2778
  async publish(channelName, type, data, options = {}) {
2732
2779
  const channel = this.channel(channelName);
@@ -2738,7 +2785,21 @@ var BlinkRealtimeImpl = class {
2738
2785
  }
2739
2786
  onPresence(channelName, callback) {
2740
2787
  const channel = this.channel(channelName);
2741
- return channel.onPresence(callback);
2788
+ const state = this.handlers[channelName] ??= {
2789
+ msgHandlers: /* @__PURE__ */ new Set(),
2790
+ presHandlers: /* @__PURE__ */ new Set(),
2791
+ subscribed: false
2792
+ };
2793
+ state.presHandlers.add(callback);
2794
+ const presenceUnsub = channel.onPresence(callback);
2795
+ return () => {
2796
+ presenceUnsub();
2797
+ state.presHandlers.delete(callback);
2798
+ if (state.msgHandlers.size === 0 && state.presHandlers.size === 0) {
2799
+ channel.unsubscribe();
2800
+ delete this.handlers[channelName];
2801
+ }
2802
+ };
2742
2803
  }
2743
2804
  };
2744
2805
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Blink TypeScript SDK for client-side applications - Zero-boilerplate CRUD + auth for modern SaaS/AI apps",
5
5
  "keywords": [
6
6
  "blink",