@blinkdotnew/sdk 0.6.0 → 0.6.2

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,24 @@ 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
- const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
2615
+ const httpClient = this.httpClient;
2616
+ const coreUrl = httpClient.coreUrl || "https://core.blink.new";
2617
+ const baseUrl = coreUrl.includes("localhost") ? "ws://localhost:3000" : coreUrl.replace("https://", "wss://").replace("http://", "ws://");
2595
2618
  const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
2596
- this.websocket = new WebSocket(wsUrl);
2619
+ const WSClass = getWebSocketClass();
2620
+ this.websocket = new WSClass(wsUrl);
2621
+ if (!this.websocket) {
2622
+ reject(new BlinkRealtimeError("Failed to create WebSocket instance"));
2623
+ return;
2624
+ }
2597
2625
  this.websocket.onopen = () => {
2598
2626
  console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
2627
+ this.reconnectAttempts = 0;
2599
2628
  resolve();
2600
2629
  };
2601
2630
  this.websocket.onmessage = (event) => {
@@ -2616,7 +2645,7 @@ var BlinkRealtimeChannel = class {
2616
2645
  reject(new BlinkRealtimeError("WebSocket connection failed"));
2617
2646
  };
2618
2647
  setTimeout(() => {
2619
- if (this.websocket?.readyState !== WebSocket.OPEN) {
2648
+ if (this.websocket?.readyState !== 1) {
2620
2649
  reject(new BlinkRealtimeError("WebSocket connection timeout"));
2621
2650
  }
2622
2651
  }, 5e3);
@@ -2664,8 +2693,8 @@ var BlinkRealtimeChannel = class {
2664
2693
  if (this.heartbeatTimer) {
2665
2694
  clearInterval(this.heartbeatTimer);
2666
2695
  }
2667
- this.heartbeatTimer = window.setInterval(() => {
2668
- if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2696
+ this.heartbeatTimer = globalThis.setInterval(() => {
2697
+ if (this.websocket && this.websocket.readyState === 1) {
2669
2698
  this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
2670
2699
  }
2671
2700
  }, 25e3);
@@ -2674,7 +2703,12 @@ var BlinkRealtimeChannel = class {
2674
2703
  if (this.reconnectTimer) {
2675
2704
  clearTimeout(this.reconnectTimer);
2676
2705
  }
2677
- this.reconnectTimer = window.setTimeout(async () => {
2706
+ this.reconnectAttempts++;
2707
+ const baseDelay = Math.min(3e4, Math.pow(2, this.reconnectAttempts) * 1e3);
2708
+ const jitter = Math.random() * 1e3;
2709
+ const delay = baseDelay + jitter;
2710
+ console.log(`\u{1F504} Scheduling reconnect attempt ${this.reconnectAttempts} in ${Math.round(delay)}ms`);
2711
+ this.reconnectTimer = globalThis.setTimeout(async () => {
2678
2712
  if (this.isSubscribed) {
2679
2713
  try {
2680
2714
  await this.connectWebSocket();
@@ -2693,7 +2727,7 @@ var BlinkRealtimeChannel = class {
2693
2727
  this.scheduleReconnect();
2694
2728
  }
2695
2729
  }
2696
- }, 2e3);
2730
+ }, delay);
2697
2731
  }
2698
2732
  cleanup() {
2699
2733
  this.isSubscribed = false;
@@ -2719,6 +2753,7 @@ var BlinkRealtimeImpl = class {
2719
2753
  this.projectId = projectId;
2720
2754
  }
2721
2755
  channels = /* @__PURE__ */ new Map();
2756
+ handlers = {};
2722
2757
  channel(name) {
2723
2758
  if (!this.channels.has(name)) {
2724
2759
  this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
@@ -2728,7 +2763,21 @@ var BlinkRealtimeImpl = class {
2728
2763
  async subscribe(channelName, callback, options = {}) {
2729
2764
  const channel = this.channel(channelName);
2730
2765
  await channel.subscribe(options);
2731
- return channel.onMessage(callback);
2766
+ const state = this.handlers[channelName] ??= {
2767
+ msgHandlers: /* @__PURE__ */ new Set(),
2768
+ presHandlers: /* @__PURE__ */ new Set(),
2769
+ subscribed: true
2770
+ };
2771
+ state.msgHandlers.add(callback);
2772
+ const messageUnsub = channel.onMessage(callback);
2773
+ return () => {
2774
+ messageUnsub();
2775
+ state.msgHandlers.delete(callback);
2776
+ if (state.msgHandlers.size === 0 && state.presHandlers.size === 0) {
2777
+ channel.unsubscribe();
2778
+ delete this.handlers[channelName];
2779
+ }
2780
+ };
2732
2781
  }
2733
2782
  async publish(channelName, type, data, options = {}) {
2734
2783
  const channel = this.channel(channelName);
@@ -2740,7 +2789,21 @@ var BlinkRealtimeImpl = class {
2740
2789
  }
2741
2790
  onPresence(channelName, callback) {
2742
2791
  const channel = this.channel(channelName);
2743
- return channel.onPresence(callback);
2792
+ const state = this.handlers[channelName] ??= {
2793
+ msgHandlers: /* @__PURE__ */ new Set(),
2794
+ presHandlers: /* @__PURE__ */ new Set(),
2795
+ subscribed: false
2796
+ };
2797
+ state.presHandlers.add(callback);
2798
+ const presenceUnsub = channel.onPresence(callback);
2799
+ return () => {
2800
+ presenceUnsub();
2801
+ state.presHandlers.delete(callback);
2802
+ if (state.msgHandlers.size === 0 && state.presHandlers.size === 0) {
2803
+ channel.unsubscribe();
2804
+ delete this.handlers[channelName];
2805
+ }
2806
+ };
2744
2807
  }
2745
2808
  };
2746
2809
 
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,24 @@ 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
- const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
2613
+ const httpClient = this.httpClient;
2614
+ const coreUrl = httpClient.coreUrl || "https://core.blink.new";
2615
+ const baseUrl = coreUrl.includes("localhost") ? "ws://localhost:3000" : coreUrl.replace("https://", "wss://").replace("http://", "ws://");
2593
2616
  const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
2594
- 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
+ }
2595
2623
  this.websocket.onopen = () => {
2596
2624
  console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
2625
+ this.reconnectAttempts = 0;
2597
2626
  resolve();
2598
2627
  };
2599
2628
  this.websocket.onmessage = (event) => {
@@ -2614,7 +2643,7 @@ var BlinkRealtimeChannel = class {
2614
2643
  reject(new BlinkRealtimeError("WebSocket connection failed"));
2615
2644
  };
2616
2645
  setTimeout(() => {
2617
- if (this.websocket?.readyState !== WebSocket.OPEN) {
2646
+ if (this.websocket?.readyState !== 1) {
2618
2647
  reject(new BlinkRealtimeError("WebSocket connection timeout"));
2619
2648
  }
2620
2649
  }, 5e3);
@@ -2662,8 +2691,8 @@ var BlinkRealtimeChannel = class {
2662
2691
  if (this.heartbeatTimer) {
2663
2692
  clearInterval(this.heartbeatTimer);
2664
2693
  }
2665
- this.heartbeatTimer = window.setInterval(() => {
2666
- if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2694
+ this.heartbeatTimer = globalThis.setInterval(() => {
2695
+ if (this.websocket && this.websocket.readyState === 1) {
2667
2696
  this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
2668
2697
  }
2669
2698
  }, 25e3);
@@ -2672,7 +2701,12 @@ var BlinkRealtimeChannel = class {
2672
2701
  if (this.reconnectTimer) {
2673
2702
  clearTimeout(this.reconnectTimer);
2674
2703
  }
2675
- 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 () => {
2676
2710
  if (this.isSubscribed) {
2677
2711
  try {
2678
2712
  await this.connectWebSocket();
@@ -2691,7 +2725,7 @@ var BlinkRealtimeChannel = class {
2691
2725
  this.scheduleReconnect();
2692
2726
  }
2693
2727
  }
2694
- }, 2e3);
2728
+ }, delay);
2695
2729
  }
2696
2730
  cleanup() {
2697
2731
  this.isSubscribed = false;
@@ -2717,6 +2751,7 @@ var BlinkRealtimeImpl = class {
2717
2751
  this.projectId = projectId;
2718
2752
  }
2719
2753
  channels = /* @__PURE__ */ new Map();
2754
+ handlers = {};
2720
2755
  channel(name) {
2721
2756
  if (!this.channels.has(name)) {
2722
2757
  this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
@@ -2726,7 +2761,21 @@ var BlinkRealtimeImpl = class {
2726
2761
  async subscribe(channelName, callback, options = {}) {
2727
2762
  const channel = this.channel(channelName);
2728
2763
  await channel.subscribe(options);
2729
- 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
+ };
2730
2779
  }
2731
2780
  async publish(channelName, type, data, options = {}) {
2732
2781
  const channel = this.channel(channelName);
@@ -2738,7 +2787,21 @@ var BlinkRealtimeImpl = class {
2738
2787
  }
2739
2788
  onPresence(channelName, callback) {
2740
2789
  const channel = this.channel(channelName);
2741
- 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
+ };
2742
2805
  }
2743
2806
  };
2744
2807
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
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",