@blinkdotnew/sdk 0.5.1 → 0.6.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/README.md CHANGED
@@ -64,6 +64,16 @@ const response = await blink.data.fetch({
64
64
  body: { /* email data */ }
65
65
  })
66
66
 
67
+ // Realtime operations (live messaging and presence)
68
+ const unsubscribe = await blink.realtime.subscribe('chat-room', (message) => {
69
+ console.log('New message:', message.data)
70
+ })
71
+
72
+ await blink.realtime.publish('chat-room', 'message', { text: 'Hello world!' })
73
+
74
+ const users = await blink.realtime.presence('chat-room')
75
+ console.log('Online users:', users.length)
76
+
67
77
  // Storage operations (instant - returns public URL directly)
68
78
  const { publicUrl } = await blink.storage.upload(
69
79
  file,
@@ -90,6 +100,7 @@ This SDK powers every Blink-generated app with:
90
100
  - **🤖 AI**: Text generation with web search, object generation, image creation, speech synthesis, and transcription
91
101
  - **📄 Data**: Extract text content from documents, secure API proxy with secret substitution, web scraping, screenshots, and web search
92
102
  - **📁 Storage**: File upload, download, and management
103
+ - **⚡ Realtime**: WebSocket-based pub/sub messaging, presence tracking, and live updates
93
104
  - **🌐 Universal**: Works on client-side and server-side
94
105
  - **📱 Framework Agnostic**: React, Vue, Svelte, vanilla JS, Node.js, Deno
95
106
  - **🔄 Real-time**: Built-in auth state management and token refresh
@@ -481,12 +492,150 @@ const { publicUrl } = await blink.storage.upload(
481
492
  await blink.storage.remove('file1.jpg', 'file2.jpg')
482
493
  ```
483
494
 
495
+ ### Realtime Operations
496
+
497
+ ```typescript
498
+ // 🔥 Real-time Messaging & Presence (NEW!)
499
+ // Perfect for chat apps, live collaboration, multiplayer games, and live updates
500
+
501
+ // Simple subscribe and publish (most common pattern)
502
+ const unsubscribe = await blink.realtime.subscribe('chat-room', (message) => {
503
+ console.log('New message:', message.data)
504
+ console.log('From user:', message.userId)
505
+ console.log('Message type:', message.type)
506
+ })
507
+
508
+ // Publish a message to all subscribers
509
+ const messageId = await blink.realtime.publish('chat-room', 'message', {
510
+ text: 'Hello everyone!',
511
+ timestamp: Date.now()
512
+ })
513
+
514
+ // Advanced channel usage with presence tracking
515
+ const channel = blink.realtime.channel('game-lobby')
516
+
517
+ // Subscribe with user metadata
518
+ await channel.subscribe({
519
+ userId: user.id,
520
+ metadata: {
521
+ displayName: user.name,
522
+ avatar: user.avatar,
523
+ status: 'online'
524
+ }
525
+ })
526
+
527
+ // Listen for messages
528
+ const unsubMessage = channel.onMessage((message) => {
529
+ if (message.type === 'chat') {
530
+ addChatMessage(message.data)
531
+ } else if (message.type === 'game-move') {
532
+ updateGameState(message.data)
533
+ }
534
+ })
535
+
536
+ // Listen for presence changes (who's online)
537
+ const unsubPresence = channel.onPresence((users) => {
538
+ console.log(`${users.length} users online:`)
539
+ users.forEach(user => {
540
+ console.log(`- ${user.metadata?.displayName} (${user.userId})`)
541
+ })
542
+ updateOnlineUsersList(users)
543
+ })
544
+
545
+ // Publish different types of messages
546
+ await channel.publish('chat', { text: 'Hello!' }, { userId: user.id })
547
+ await channel.publish('game-move', { x: 5, y: 3, piece: 'king' })
548
+ await channel.publish('typing', { isTyping: true })
549
+
550
+ // Get current presence (one-time check)
551
+ const currentUsers = await channel.getPresence()
552
+ console.log('Currently online:', currentUsers.length)
553
+
554
+ // Get message history
555
+ const recentMessages = await channel.getMessages({
556
+ limit: 50,
557
+ before: lastMessageId // Pagination support
558
+ })
559
+
560
+ // Cleanup when done
561
+ unsubMessage()
562
+ unsubPresence()
563
+ await channel.unsubscribe()
564
+
565
+ // Or use the simple unsubscribe from subscribe()
566
+ unsubscribe()
567
+
568
+ // Multiple channels for different features
569
+ const chatChannel = blink.realtime.channel('chat')
570
+ const notificationChannel = blink.realtime.channel('notifications')
571
+ const gameChannel = blink.realtime.channel('game-state')
572
+
573
+ // Each channel is independent with its own subscribers and presence
574
+ await chatChannel.subscribe({ userId: user.id })
575
+ await notificationChannel.subscribe({ userId: user.id })
576
+ await gameChannel.subscribe({ userId: user.id, metadata: { team: 'red' } })
577
+
578
+ // Real-time collaboration example
579
+ const docChannel = blink.realtime.channel(`document-${docId}`)
580
+
581
+ await docChannel.subscribe({
582
+ userId: user.id,
583
+ metadata: {
584
+ name: user.name,
585
+ cursor: { line: 1, column: 0 }
586
+ }
587
+ })
588
+
589
+ // Broadcast cursor movements
590
+ docChannel.onMessage((message) => {
591
+ if (message.type === 'cursor-move') {
592
+ updateUserCursor(message.userId, message.data.position)
593
+ } else if (message.type === 'text-change') {
594
+ applyTextChange(message.data.delta)
595
+ }
596
+ })
597
+
598
+ // Send cursor updates
599
+ await docChannel.publish('cursor-move', {
600
+ position: { line: 5, column: 10 }
601
+ }, { userId: user.id })
602
+
603
+ // Send text changes
604
+ await docChannel.publish('text-change', {
605
+ delta: { insert: 'Hello', retain: 5 },
606
+ timestamp: Date.now()
607
+ })
608
+
609
+ // Presence with live cursor positions
610
+ docChannel.onPresence((users) => {
611
+ users.forEach(user => {
612
+ if (user.metadata?.cursor) {
613
+ showUserCursor(user.userId, user.metadata.cursor)
614
+ }
615
+ })
616
+ })
617
+
618
+ // Auto-cleanup on page unload
619
+ window.addEventListener('beforeunload', () => {
620
+ docChannel.unsubscribe()
621
+ })
622
+
623
+ // Error handling
624
+ try {
625
+ await blink.realtime.publish('restricted-channel', 'message', { data: 'test' })
626
+ } catch (error) {
627
+ if (error instanceof BlinkRealtimeError) {
628
+ console.error('Realtime error:', error.message)
629
+ }
630
+ }
631
+ ```
632
+
484
633
  ## 🔧 Advanced Usage
485
634
 
486
635
  ### Error Handling
487
636
 
488
637
  ```typescript
489
- import { BlinkAuthError, BlinkAIError, BlinkStorageError, BlinkDataError } from '@blinkdotnew/sdk'
638
+ import { BlinkAuthError, BlinkAIError, BlinkStorageError, BlinkDataError, BlinkRealtimeError } from '@blinkdotnew/sdk'
490
639
 
491
640
  try {
492
641
  const user = await blink.auth.me()
@@ -710,6 +859,130 @@ function EmailSender() {
710
859
  </div>
711
860
  )
712
861
  }
862
+
863
+ // React example with realtime chat
864
+ function RealtimeChat() {
865
+ const [messages, setMessages] = useState([])
866
+ const [newMessage, setNewMessage] = useState('')
867
+ const [onlineUsers, setOnlineUsers] = useState([])
868
+ const [user] = useState({ id: 'user123', name: 'John Doe' }) // From auth
869
+
870
+ useEffect(() => {
871
+ const channel = blink.realtime.channel('chat-room')
872
+
873
+ // Subscribe and listen for messages
874
+ const setupRealtime = async () => {
875
+ await channel.subscribe({
876
+ userId: user.id,
877
+ metadata: { displayName: user.name, avatar: '/avatar.png' }
878
+ })
879
+
880
+ // Listen for new messages
881
+ channel.onMessage((message) => {
882
+ if (message.type === 'chat') {
883
+ setMessages(prev => [...prev, {
884
+ id: message.id,
885
+ text: message.data.text,
886
+ userId: message.userId,
887
+ timestamp: message.timestamp,
888
+ user: message.metadata?.displayName || 'Unknown'
889
+ }])
890
+ }
891
+ })
892
+
893
+ // Listen for presence changes
894
+ channel.onPresence((users) => {
895
+ setOnlineUsers(users.map(u => ({
896
+ id: u.userId,
897
+ name: u.metadata?.displayName || 'Anonymous',
898
+ avatar: u.metadata?.avatar
899
+ })))
900
+ })
901
+
902
+ // Load recent messages
903
+ const recentMessages = await channel.getMessages({ limit: 50 })
904
+ setMessages(recentMessages.map(msg => ({
905
+ id: msg.id,
906
+ text: msg.data.text,
907
+ userId: msg.userId,
908
+ timestamp: msg.timestamp,
909
+ user: msg.metadata?.displayName || 'Unknown'
910
+ })))
911
+ }
912
+
913
+ setupRealtime()
914
+
915
+ // Cleanup on unmount
916
+ return () => {
917
+ channel.unsubscribe()
918
+ }
919
+ }, [user.id, user.name])
920
+
921
+ const sendMessage = async () => {
922
+ if (!newMessage.trim()) return
923
+
924
+ try {
925
+ await blink.realtime.publish('chat-room', 'chat', {
926
+ text: newMessage,
927
+ timestamp: Date.now()
928
+ }, {
929
+ userId: user.id,
930
+ metadata: { displayName: user.name }
931
+ })
932
+
933
+ setNewMessage('')
934
+ } catch (error) {
935
+ console.error('Failed to send message:', error)
936
+ }
937
+ }
938
+
939
+ return (
940
+ <div style={{ display: 'flex', height: '400px' }}>
941
+ {/* Chat messages */}
942
+ <div style={{ flex: 1, padding: '1rem' }}>
943
+ <h3>Chat Room</h3>
944
+ <div style={{ height: '250px', overflowY: 'auto', border: '1px solid #ccc', padding: '0.5rem' }}>
945
+ {messages.map((msg) => (
946
+ <div key={msg.id} style={{ marginBottom: '0.5rem' }}>
947
+ <strong>{msg.user}:</strong> {msg.text}
948
+ <small style={{ color: '#666', marginLeft: '0.5rem' }}>
949
+ {new Date(msg.timestamp).toLocaleTimeString()}
950
+ </small>
951
+ </div>
952
+ ))}
953
+ </div>
954
+
955
+ <div style={{ marginTop: '1rem', display: 'flex' }}>
956
+ <input
957
+ value={newMessage}
958
+ onChange={(e) => setNewMessage(e.target.value)}
959
+ placeholder="Type a message..."
960
+ style={{ flex: 1, marginRight: '0.5rem' }}
961
+ onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
962
+ />
963
+ <button onClick={sendMessage}>Send</button>
964
+ </div>
965
+ </div>
966
+
967
+ {/* Online users sidebar */}
968
+ <div style={{ width: '200px', borderLeft: '1px solid #ccc', padding: '1rem' }}>
969
+ <h4>Online ({onlineUsers.length})</h4>
970
+ {onlineUsers.map((user) => (
971
+ <div key={user.id} style={{ display: 'flex', alignItems: 'center', marginBottom: '0.5rem' }}>
972
+ <div style={{
973
+ width: '8px',
974
+ height: '8px',
975
+ backgroundColor: '#22c55e',
976
+ borderRadius: '50%',
977
+ marginRight: '0.5rem'
978
+ }} />
979
+ {user.name}
980
+ </div>
981
+ ))}
982
+ </div>
983
+ </div>
984
+ )
985
+ }
713
986
  ```
714
987
 
715
988
  ### Next.js API Routes
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, FetchRequest, FetchResponse, AsyncFetchResponse, SearchResponse, BlinkStorage, BlinkAI, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse } from '@blink/core';
2
- export { AuthState, AuthTokens, BlinkAI, BlinkClientConfig, BlinkStorage, BlinkUser, CreateOptions, DataExtraction, FileObject, FilterCondition, ImageGenerationRequest, ImageGenerationResponse, ListResponse, Message, ObjectGenerationRequest, ObjectGenerationResponse, QueryOptions, SearchRequest, SearchResponse, SpeechGenerationRequest, SpeechGenerationResponse, StorageUploadOptions, StorageUploadResponse, TableOperations, TextGenerationRequest, TextGenerationResponse, TokenUsage, TranscriptionRequest, TranscriptionResponse, UpdateOptions, UpsertOptions } from '@blink/core';
1
+ import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, FetchRequest, FetchResponse, AsyncFetchResponse, SearchResponse, BlinkStorage, BlinkAI, BlinkRealtime, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse, RealtimeChannel, RealtimeMessage, RealtimeSubscribeOptions, RealtimePublishOptions, PresenceUser } from '@blink/core';
2
+ export { AuthState, AuthTokens, BlinkAI, BlinkClientConfig, BlinkRealtime, BlinkRealtimeError, BlinkStorage, BlinkUser, CreateOptions, DataExtraction, FileObject, FilterCondition, ImageGenerationRequest, ImageGenerationResponse, ListResponse, Message, ObjectGenerationRequest, ObjectGenerationResponse, PresenceUser, QueryOptions, RealtimeChannel, RealtimeGetMessagesOptions, RealtimeMessage, RealtimePublishOptions, RealtimeSubscribeOptions, SearchRequest, SearchResponse, SpeechGenerationRequest, SpeechGenerationResponse, StorageUploadOptions, StorageUploadResponse, TableOperations, TextGenerationRequest, TextGenerationResponse, TokenUsage, TranscriptionRequest, TranscriptionResponse, UpdateOptions, UpsertOptions } from '@blink/core';
3
3
 
4
4
  /**
5
5
  * Blink Auth Module - Client-side authentication management
@@ -259,6 +259,7 @@ interface BlinkClient {
259
259
  storage: BlinkStorage;
260
260
  ai: BlinkAI;
261
261
  data: BlinkData;
262
+ realtime: BlinkRealtime;
262
263
  }
263
264
  /**
264
265
  * Create a new Blink client instance
@@ -665,4 +666,55 @@ declare class BlinkAIImpl implements BlinkAI {
665
666
  transcribeAudio(options: TranscriptionRequest): Promise<TranscriptionResponse>;
666
667
  }
667
668
 
668
- export { type AuthStateChangeCallback, BlinkAIImpl, type BlinkClient, type BlinkData, BlinkDataImpl, BlinkDatabase, BlinkStorageImpl, BlinkTable, createClient };
669
+ /**
670
+ * Blink Realtime Module - Real-time messaging and presence
671
+ * Provides pub/sub messaging, presence tracking, and live updates
672
+ */
673
+
674
+ declare class BlinkRealtimeChannel implements RealtimeChannel {
675
+ private channelName;
676
+ private httpClient;
677
+ private projectId;
678
+ private messageCallbacks;
679
+ private presenceCallbacks;
680
+ private websocket;
681
+ private isSubscribed;
682
+ private reconnectTimer;
683
+ private heartbeatTimer;
684
+ constructor(channelName: string, httpClient: HttpClient, projectId: string);
685
+ subscribe(options?: {
686
+ userId?: string;
687
+ metadata?: Record<string, any>;
688
+ }): Promise<void>;
689
+ unsubscribe(): Promise<void>;
690
+ publish(type: string, data: any, options?: {
691
+ userId?: string;
692
+ metadata?: Record<string, any>;
693
+ }): Promise<string>;
694
+ onMessage(callback: (message: RealtimeMessage) => void): () => void;
695
+ onPresence(callback: (users: PresenceUser[]) => void): () => void;
696
+ getPresence(): Promise<PresenceUser[]>;
697
+ getMessages(options?: {
698
+ limit?: number;
699
+ before?: string;
700
+ after?: string;
701
+ }): Promise<RealtimeMessage[]>;
702
+ private connectWebSocket;
703
+ private handleWebSocketMessage;
704
+ private startHeartbeat;
705
+ private scheduleReconnect;
706
+ private cleanup;
707
+ }
708
+ declare class BlinkRealtimeImpl implements BlinkRealtime {
709
+ private httpClient;
710
+ private projectId;
711
+ private channels;
712
+ constructor(httpClient: HttpClient, projectId: string);
713
+ channel(name: string): RealtimeChannel;
714
+ subscribe(channelName: string, callback: (message: RealtimeMessage) => void, options?: RealtimeSubscribeOptions): Promise<() => void>;
715
+ publish(channelName: string, type: string, data: any, options?: RealtimePublishOptions): Promise<string>;
716
+ presence(channelName: string): Promise<PresenceUser[]>;
717
+ onPresence(channelName: string, callback: (users: PresenceUser[]) => void): () => void;
718
+ }
719
+
720
+ export { type AuthStateChangeCallback, BlinkAIImpl, type BlinkClient, type BlinkData, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, createClient };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, FetchRequest, FetchResponse, AsyncFetchResponse, SearchResponse, BlinkStorage, BlinkAI, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse } from '@blink/core';
2
- export { AuthState, AuthTokens, BlinkAI, BlinkClientConfig, BlinkStorage, BlinkUser, CreateOptions, DataExtraction, FileObject, FilterCondition, ImageGenerationRequest, ImageGenerationResponse, ListResponse, Message, ObjectGenerationRequest, ObjectGenerationResponse, QueryOptions, SearchRequest, SearchResponse, SpeechGenerationRequest, SpeechGenerationResponse, StorageUploadOptions, StorageUploadResponse, TableOperations, TextGenerationRequest, TextGenerationResponse, TokenUsage, TranscriptionRequest, TranscriptionResponse, UpdateOptions, UpsertOptions } from '@blink/core';
1
+ import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, FetchRequest, FetchResponse, AsyncFetchResponse, SearchResponse, BlinkStorage, BlinkAI, BlinkRealtime, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse, RealtimeChannel, RealtimeMessage, RealtimeSubscribeOptions, RealtimePublishOptions, PresenceUser } from '@blink/core';
2
+ export { AuthState, AuthTokens, BlinkAI, BlinkClientConfig, BlinkRealtime, BlinkRealtimeError, BlinkStorage, BlinkUser, CreateOptions, DataExtraction, FileObject, FilterCondition, ImageGenerationRequest, ImageGenerationResponse, ListResponse, Message, ObjectGenerationRequest, ObjectGenerationResponse, PresenceUser, QueryOptions, RealtimeChannel, RealtimeGetMessagesOptions, RealtimeMessage, RealtimePublishOptions, RealtimeSubscribeOptions, SearchRequest, SearchResponse, SpeechGenerationRequest, SpeechGenerationResponse, StorageUploadOptions, StorageUploadResponse, TableOperations, TextGenerationRequest, TextGenerationResponse, TokenUsage, TranscriptionRequest, TranscriptionResponse, UpdateOptions, UpsertOptions } from '@blink/core';
3
3
 
4
4
  /**
5
5
  * Blink Auth Module - Client-side authentication management
@@ -259,6 +259,7 @@ interface BlinkClient {
259
259
  storage: BlinkStorage;
260
260
  ai: BlinkAI;
261
261
  data: BlinkData;
262
+ realtime: BlinkRealtime;
262
263
  }
263
264
  /**
264
265
  * Create a new Blink client instance
@@ -665,4 +666,55 @@ declare class BlinkAIImpl implements BlinkAI {
665
666
  transcribeAudio(options: TranscriptionRequest): Promise<TranscriptionResponse>;
666
667
  }
667
668
 
668
- export { type AuthStateChangeCallback, BlinkAIImpl, type BlinkClient, type BlinkData, BlinkDataImpl, BlinkDatabase, BlinkStorageImpl, BlinkTable, createClient };
669
+ /**
670
+ * Blink Realtime Module - Real-time messaging and presence
671
+ * Provides pub/sub messaging, presence tracking, and live updates
672
+ */
673
+
674
+ declare class BlinkRealtimeChannel implements RealtimeChannel {
675
+ private channelName;
676
+ private httpClient;
677
+ private projectId;
678
+ private messageCallbacks;
679
+ private presenceCallbacks;
680
+ private websocket;
681
+ private isSubscribed;
682
+ private reconnectTimer;
683
+ private heartbeatTimer;
684
+ constructor(channelName: string, httpClient: HttpClient, projectId: string);
685
+ subscribe(options?: {
686
+ userId?: string;
687
+ metadata?: Record<string, any>;
688
+ }): Promise<void>;
689
+ unsubscribe(): Promise<void>;
690
+ publish(type: string, data: any, options?: {
691
+ userId?: string;
692
+ metadata?: Record<string, any>;
693
+ }): Promise<string>;
694
+ onMessage(callback: (message: RealtimeMessage) => void): () => void;
695
+ onPresence(callback: (users: PresenceUser[]) => void): () => void;
696
+ getPresence(): Promise<PresenceUser[]>;
697
+ getMessages(options?: {
698
+ limit?: number;
699
+ before?: string;
700
+ after?: string;
701
+ }): Promise<RealtimeMessage[]>;
702
+ private connectWebSocket;
703
+ private handleWebSocketMessage;
704
+ private startHeartbeat;
705
+ private scheduleReconnect;
706
+ private cleanup;
707
+ }
708
+ declare class BlinkRealtimeImpl implements BlinkRealtime {
709
+ private httpClient;
710
+ private projectId;
711
+ private channels;
712
+ constructor(httpClient: HttpClient, projectId: string);
713
+ channel(name: string): RealtimeChannel;
714
+ subscribe(channelName: string, callback: (message: RealtimeMessage) => void, options?: RealtimeSubscribeOptions): Promise<() => void>;
715
+ publish(channelName: string, type: string, data: any, options?: RealtimePublishOptions): Promise<string>;
716
+ presence(channelName: string): Promise<PresenceUser[]>;
717
+ onPresence(channelName: string, callback: (users: PresenceUser[]) => void): () => void;
718
+ }
719
+
720
+ export { type AuthStateChangeCallback, BlinkAIImpl, type BlinkClient, type BlinkData, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, createClient };
package/dist/index.js CHANGED
@@ -46,6 +46,12 @@ var BlinkDataError = class extends BlinkError {
46
46
  this.name = "BlinkDataError";
47
47
  }
48
48
  };
49
+ var BlinkRealtimeError = class extends BlinkError {
50
+ constructor(message, status, details) {
51
+ super(message, "REALTIME_ERROR", status, details);
52
+ this.name = "BlinkRealtimeError";
53
+ }
54
+ };
49
55
 
50
56
  // ../core/src/query-builder.ts
51
57
  function buildFilterQuery(condition) {
@@ -576,6 +582,24 @@ var HttpClient = class {
576
582
  async dataSearch(projectId, request) {
577
583
  return this.post(`/api/data/${projectId}/search`, request);
578
584
  }
585
+ /**
586
+ * Realtime-specific requests
587
+ */
588
+ async realtimePublish(projectId, request) {
589
+ return this.post(`/api/realtime/${projectId}/publish`, request);
590
+ }
591
+ async realtimeGetPresence(projectId, channel) {
592
+ return this.get(`/api/realtime/${projectId}/presence`, { channel });
593
+ }
594
+ async realtimeGetMessages(projectId, options) {
595
+ const { channel, ...searchParams } = options;
596
+ return this.get(`/api/realtime/${projectId}/messages`, {
597
+ channel,
598
+ ...Object.fromEntries(
599
+ Object.entries(searchParams).map(([k, v]) => [k, String(v)])
600
+ )
601
+ });
602
+ }
579
603
  /**
580
604
  * Private helper methods
581
605
  */
@@ -2449,6 +2473,277 @@ var BlinkDataImpl = class {
2449
2473
  }
2450
2474
  };
2451
2475
 
2476
+ // src/realtime.ts
2477
+ var BlinkRealtimeChannel = class {
2478
+ constructor(channelName, httpClient, projectId) {
2479
+ this.channelName = channelName;
2480
+ this.httpClient = httpClient;
2481
+ this.projectId = projectId;
2482
+ }
2483
+ messageCallbacks = [];
2484
+ presenceCallbacks = [];
2485
+ websocket = null;
2486
+ isSubscribed = false;
2487
+ reconnectTimer = null;
2488
+ heartbeatTimer = null;
2489
+ async subscribe(options = {}) {
2490
+ if (this.isSubscribed) {
2491
+ return;
2492
+ }
2493
+ try {
2494
+ await this.connectWebSocket();
2495
+ if (this.websocket) {
2496
+ const subscribeMessage = {
2497
+ type: "subscribe",
2498
+ payload: {
2499
+ channel: this.channelName,
2500
+ userId: options.userId,
2501
+ metadata: options.metadata
2502
+ }
2503
+ };
2504
+ this.websocket.send(JSON.stringify(subscribeMessage));
2505
+ this.isSubscribed = true;
2506
+ this.startHeartbeat();
2507
+ }
2508
+ } catch (error) {
2509
+ throw new BlinkRealtimeError(
2510
+ `Failed to subscribe to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2511
+ );
2512
+ }
2513
+ }
2514
+ async unsubscribe() {
2515
+ if (!this.isSubscribed) {
2516
+ return;
2517
+ }
2518
+ if (this.websocket) {
2519
+ const unsubscribeMessage = {
2520
+ type: "unsubscribe",
2521
+ payload: {
2522
+ channel: this.channelName
2523
+ }
2524
+ };
2525
+ this.websocket.send(JSON.stringify(unsubscribeMessage));
2526
+ }
2527
+ this.cleanup();
2528
+ }
2529
+ async publish(type, data, options = {}) {
2530
+ try {
2531
+ const response = await this.httpClient.realtimePublish(this.projectId, {
2532
+ channel: this.channelName,
2533
+ type,
2534
+ data,
2535
+ userId: options.userId,
2536
+ metadata: options.metadata
2537
+ });
2538
+ return response.data.messageId;
2539
+ } catch (error) {
2540
+ throw new BlinkRealtimeError(
2541
+ `Failed to publish message to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2542
+ );
2543
+ }
2544
+ }
2545
+ onMessage(callback) {
2546
+ this.messageCallbacks.push(callback);
2547
+ return () => {
2548
+ const index = this.messageCallbacks.indexOf(callback);
2549
+ if (index > -1) {
2550
+ this.messageCallbacks.splice(index, 1);
2551
+ }
2552
+ };
2553
+ }
2554
+ onPresence(callback) {
2555
+ this.presenceCallbacks.push(callback);
2556
+ return () => {
2557
+ const index = this.presenceCallbacks.indexOf(callback);
2558
+ if (index > -1) {
2559
+ this.presenceCallbacks.splice(index, 1);
2560
+ }
2561
+ };
2562
+ }
2563
+ async getPresence() {
2564
+ try {
2565
+ const response = await this.httpClient.realtimeGetPresence(this.projectId, this.channelName);
2566
+ return response.data.users;
2567
+ } catch (error) {
2568
+ throw new BlinkRealtimeError(
2569
+ `Failed to get presence for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2570
+ );
2571
+ }
2572
+ }
2573
+ async getMessages(options = {}) {
2574
+ try {
2575
+ const response = await this.httpClient.realtimeGetMessages(this.projectId, {
2576
+ channel: this.channelName,
2577
+ limit: options.limit,
2578
+ start: options.after,
2579
+ end: options.before
2580
+ });
2581
+ return response.data.messages;
2582
+ } catch (error) {
2583
+ throw new BlinkRealtimeError(
2584
+ `Failed to get messages for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2585
+ );
2586
+ }
2587
+ }
2588
+ async connectWebSocket() {
2589
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2590
+ return;
2591
+ }
2592
+ return new Promise((resolve, reject) => {
2593
+ try {
2594
+ const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
2595
+ const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
2596
+ this.websocket = new WebSocket(wsUrl);
2597
+ this.websocket.onopen = () => {
2598
+ console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
2599
+ resolve();
2600
+ };
2601
+ this.websocket.onmessage = (event) => {
2602
+ try {
2603
+ const message = JSON.parse(event.data);
2604
+ this.handleWebSocketMessage(message);
2605
+ } catch (error) {
2606
+ console.error("Failed to parse WebSocket message:", error);
2607
+ }
2608
+ };
2609
+ this.websocket.onclose = () => {
2610
+ console.log(`\u{1F50C} Disconnected from realtime for project ${this.projectId}`);
2611
+ this.isSubscribed = false;
2612
+ this.scheduleReconnect();
2613
+ };
2614
+ this.websocket.onerror = (error) => {
2615
+ console.error("WebSocket error:", error);
2616
+ reject(new BlinkRealtimeError("WebSocket connection failed"));
2617
+ };
2618
+ setTimeout(() => {
2619
+ if (this.websocket?.readyState !== WebSocket.OPEN) {
2620
+ reject(new BlinkRealtimeError("WebSocket connection timeout"));
2621
+ }
2622
+ }, 5e3);
2623
+ } catch (error) {
2624
+ reject(new BlinkRealtimeError(`Failed to create WebSocket connection: ${error instanceof Error ? error.message : "Unknown error"}`));
2625
+ }
2626
+ });
2627
+ }
2628
+ handleWebSocketMessage(message) {
2629
+ switch (message.type) {
2630
+ case "message":
2631
+ this.messageCallbacks.forEach((callback) => {
2632
+ try {
2633
+ callback(message.payload);
2634
+ } catch (error) {
2635
+ console.error("Error in message callback:", error);
2636
+ }
2637
+ });
2638
+ break;
2639
+ case "presence":
2640
+ this.presenceCallbacks.forEach((callback) => {
2641
+ try {
2642
+ callback(message.payload.data || []);
2643
+ } catch (error) {
2644
+ console.error("Error in presence callback:", error);
2645
+ }
2646
+ });
2647
+ break;
2648
+ case "subscribed":
2649
+ console.log(`\u2705 Subscribed to channel: ${message.payload.channel}`);
2650
+ break;
2651
+ case "unsubscribed":
2652
+ console.log(`\u274C Unsubscribed from channel: ${message.payload.channel}`);
2653
+ break;
2654
+ case "pong":
2655
+ break;
2656
+ case "error":
2657
+ console.error("Realtime error:", message.payload.error);
2658
+ break;
2659
+ default:
2660
+ console.log("Unknown message type:", message.type);
2661
+ }
2662
+ }
2663
+ startHeartbeat() {
2664
+ if (this.heartbeatTimer) {
2665
+ clearInterval(this.heartbeatTimer);
2666
+ }
2667
+ this.heartbeatTimer = window.setInterval(() => {
2668
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2669
+ this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
2670
+ }
2671
+ }, 25e3);
2672
+ }
2673
+ scheduleReconnect() {
2674
+ if (this.reconnectTimer) {
2675
+ clearTimeout(this.reconnectTimer);
2676
+ }
2677
+ this.reconnectTimer = window.setTimeout(async () => {
2678
+ if (this.isSubscribed) {
2679
+ try {
2680
+ await this.connectWebSocket();
2681
+ if (this.websocket) {
2682
+ const subscribeMessage = {
2683
+ type: "subscribe",
2684
+ payload: {
2685
+ channel: this.channelName
2686
+ }
2687
+ };
2688
+ this.websocket.send(JSON.stringify(subscribeMessage));
2689
+ this.startHeartbeat();
2690
+ }
2691
+ } catch (error) {
2692
+ console.error("Reconnection failed:", error);
2693
+ this.scheduleReconnect();
2694
+ }
2695
+ }
2696
+ }, 2e3);
2697
+ }
2698
+ cleanup() {
2699
+ this.isSubscribed = false;
2700
+ if (this.heartbeatTimer) {
2701
+ clearInterval(this.heartbeatTimer);
2702
+ this.heartbeatTimer = null;
2703
+ }
2704
+ if (this.reconnectTimer) {
2705
+ clearTimeout(this.reconnectTimer);
2706
+ this.reconnectTimer = null;
2707
+ }
2708
+ if (this.websocket) {
2709
+ this.websocket.close();
2710
+ this.websocket = null;
2711
+ }
2712
+ this.messageCallbacks = [];
2713
+ this.presenceCallbacks = [];
2714
+ }
2715
+ };
2716
+ var BlinkRealtimeImpl = class {
2717
+ constructor(httpClient, projectId) {
2718
+ this.httpClient = httpClient;
2719
+ this.projectId = projectId;
2720
+ }
2721
+ channels = /* @__PURE__ */ new Map();
2722
+ channel(name) {
2723
+ if (!this.channels.has(name)) {
2724
+ this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
2725
+ }
2726
+ return this.channels.get(name);
2727
+ }
2728
+ async subscribe(channelName, callback, options = {}) {
2729
+ const channel = this.channel(channelName);
2730
+ await channel.subscribe(options);
2731
+ return channel.onMessage(callback);
2732
+ }
2733
+ async publish(channelName, type, data, options = {}) {
2734
+ const channel = this.channel(channelName);
2735
+ return channel.publish(type, data, options);
2736
+ }
2737
+ async presence(channelName) {
2738
+ const channel = this.channel(channelName);
2739
+ return channel.getPresence();
2740
+ }
2741
+ onPresence(channelName, callback) {
2742
+ const channel = this.channel(channelName);
2743
+ return channel.onPresence(callback);
2744
+ }
2745
+ };
2746
+
2452
2747
  // src/client.ts
2453
2748
  var BlinkClientImpl = class {
2454
2749
  auth;
@@ -2456,6 +2751,7 @@ var BlinkClientImpl = class {
2456
2751
  storage;
2457
2752
  ai;
2458
2753
  data;
2754
+ realtime;
2459
2755
  httpClient;
2460
2756
  constructor(config) {
2461
2757
  this.auth = new BlinkAuth(config);
@@ -2468,6 +2764,7 @@ var BlinkClientImpl = class {
2468
2764
  this.storage = new BlinkStorageImpl(this.httpClient);
2469
2765
  this.ai = new BlinkAIImpl(this.httpClient);
2470
2766
  this.data = new BlinkDataImpl(this.httpClient, config.projectId);
2767
+ this.realtime = new BlinkRealtimeImpl(this.httpClient, config.projectId);
2471
2768
  }
2472
2769
  };
2473
2770
  function createClient(config) {
@@ -2484,6 +2781,8 @@ function createClient(config) {
2484
2781
  exports.BlinkAIImpl = BlinkAIImpl;
2485
2782
  exports.BlinkDataImpl = BlinkDataImpl;
2486
2783
  exports.BlinkDatabase = BlinkDatabase;
2784
+ exports.BlinkRealtimeChannel = BlinkRealtimeChannel;
2785
+ exports.BlinkRealtimeImpl = BlinkRealtimeImpl;
2487
2786
  exports.BlinkStorageImpl = BlinkStorageImpl;
2488
2787
  exports.BlinkTable = BlinkTable;
2489
2788
  exports.createClient = createClient;
package/dist/index.mjs CHANGED
@@ -44,6 +44,12 @@ var BlinkDataError = class extends BlinkError {
44
44
  this.name = "BlinkDataError";
45
45
  }
46
46
  };
47
+ var BlinkRealtimeError = class extends BlinkError {
48
+ constructor(message, status, details) {
49
+ super(message, "REALTIME_ERROR", status, details);
50
+ this.name = "BlinkRealtimeError";
51
+ }
52
+ };
47
53
 
48
54
  // ../core/src/query-builder.ts
49
55
  function buildFilterQuery(condition) {
@@ -574,6 +580,24 @@ var HttpClient = class {
574
580
  async dataSearch(projectId, request) {
575
581
  return this.post(`/api/data/${projectId}/search`, request);
576
582
  }
583
+ /**
584
+ * Realtime-specific requests
585
+ */
586
+ async realtimePublish(projectId, request) {
587
+ return this.post(`/api/realtime/${projectId}/publish`, request);
588
+ }
589
+ async realtimeGetPresence(projectId, channel) {
590
+ return this.get(`/api/realtime/${projectId}/presence`, { channel });
591
+ }
592
+ async realtimeGetMessages(projectId, options) {
593
+ const { channel, ...searchParams } = options;
594
+ return this.get(`/api/realtime/${projectId}/messages`, {
595
+ channel,
596
+ ...Object.fromEntries(
597
+ Object.entries(searchParams).map(([k, v]) => [k, String(v)])
598
+ )
599
+ });
600
+ }
577
601
  /**
578
602
  * Private helper methods
579
603
  */
@@ -2447,6 +2471,277 @@ var BlinkDataImpl = class {
2447
2471
  }
2448
2472
  };
2449
2473
 
2474
+ // src/realtime.ts
2475
+ var BlinkRealtimeChannel = class {
2476
+ constructor(channelName, httpClient, projectId) {
2477
+ this.channelName = channelName;
2478
+ this.httpClient = httpClient;
2479
+ this.projectId = projectId;
2480
+ }
2481
+ messageCallbacks = [];
2482
+ presenceCallbacks = [];
2483
+ websocket = null;
2484
+ isSubscribed = false;
2485
+ reconnectTimer = null;
2486
+ heartbeatTimer = null;
2487
+ async subscribe(options = {}) {
2488
+ if (this.isSubscribed) {
2489
+ return;
2490
+ }
2491
+ try {
2492
+ await this.connectWebSocket();
2493
+ if (this.websocket) {
2494
+ const subscribeMessage = {
2495
+ type: "subscribe",
2496
+ payload: {
2497
+ channel: this.channelName,
2498
+ userId: options.userId,
2499
+ metadata: options.metadata
2500
+ }
2501
+ };
2502
+ this.websocket.send(JSON.stringify(subscribeMessage));
2503
+ this.isSubscribed = true;
2504
+ this.startHeartbeat();
2505
+ }
2506
+ } catch (error) {
2507
+ throw new BlinkRealtimeError(
2508
+ `Failed to subscribe to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2509
+ );
2510
+ }
2511
+ }
2512
+ async unsubscribe() {
2513
+ if (!this.isSubscribed) {
2514
+ return;
2515
+ }
2516
+ if (this.websocket) {
2517
+ const unsubscribeMessage = {
2518
+ type: "unsubscribe",
2519
+ payload: {
2520
+ channel: this.channelName
2521
+ }
2522
+ };
2523
+ this.websocket.send(JSON.stringify(unsubscribeMessage));
2524
+ }
2525
+ this.cleanup();
2526
+ }
2527
+ async publish(type, data, options = {}) {
2528
+ try {
2529
+ const response = await this.httpClient.realtimePublish(this.projectId, {
2530
+ channel: this.channelName,
2531
+ type,
2532
+ data,
2533
+ userId: options.userId,
2534
+ metadata: options.metadata
2535
+ });
2536
+ return response.data.messageId;
2537
+ } catch (error) {
2538
+ throw new BlinkRealtimeError(
2539
+ `Failed to publish message to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2540
+ );
2541
+ }
2542
+ }
2543
+ onMessage(callback) {
2544
+ this.messageCallbacks.push(callback);
2545
+ return () => {
2546
+ const index = this.messageCallbacks.indexOf(callback);
2547
+ if (index > -1) {
2548
+ this.messageCallbacks.splice(index, 1);
2549
+ }
2550
+ };
2551
+ }
2552
+ onPresence(callback) {
2553
+ this.presenceCallbacks.push(callback);
2554
+ return () => {
2555
+ const index = this.presenceCallbacks.indexOf(callback);
2556
+ if (index > -1) {
2557
+ this.presenceCallbacks.splice(index, 1);
2558
+ }
2559
+ };
2560
+ }
2561
+ async getPresence() {
2562
+ try {
2563
+ const response = await this.httpClient.realtimeGetPresence(this.projectId, this.channelName);
2564
+ return response.data.users;
2565
+ } catch (error) {
2566
+ throw new BlinkRealtimeError(
2567
+ `Failed to get presence for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2568
+ );
2569
+ }
2570
+ }
2571
+ async getMessages(options = {}) {
2572
+ try {
2573
+ const response = await this.httpClient.realtimeGetMessages(this.projectId, {
2574
+ channel: this.channelName,
2575
+ limit: options.limit,
2576
+ start: options.after,
2577
+ end: options.before
2578
+ });
2579
+ return response.data.messages;
2580
+ } catch (error) {
2581
+ throw new BlinkRealtimeError(
2582
+ `Failed to get messages for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
2583
+ );
2584
+ }
2585
+ }
2586
+ async connectWebSocket() {
2587
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2588
+ return;
2589
+ }
2590
+ return new Promise((resolve, reject) => {
2591
+ try {
2592
+ const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
2593
+ const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
2594
+ this.websocket = new WebSocket(wsUrl);
2595
+ this.websocket.onopen = () => {
2596
+ console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
2597
+ resolve();
2598
+ };
2599
+ this.websocket.onmessage = (event) => {
2600
+ try {
2601
+ const message = JSON.parse(event.data);
2602
+ this.handleWebSocketMessage(message);
2603
+ } catch (error) {
2604
+ console.error("Failed to parse WebSocket message:", error);
2605
+ }
2606
+ };
2607
+ this.websocket.onclose = () => {
2608
+ console.log(`\u{1F50C} Disconnected from realtime for project ${this.projectId}`);
2609
+ this.isSubscribed = false;
2610
+ this.scheduleReconnect();
2611
+ };
2612
+ this.websocket.onerror = (error) => {
2613
+ console.error("WebSocket error:", error);
2614
+ reject(new BlinkRealtimeError("WebSocket connection failed"));
2615
+ };
2616
+ setTimeout(() => {
2617
+ if (this.websocket?.readyState !== WebSocket.OPEN) {
2618
+ reject(new BlinkRealtimeError("WebSocket connection timeout"));
2619
+ }
2620
+ }, 5e3);
2621
+ } catch (error) {
2622
+ reject(new BlinkRealtimeError(`Failed to create WebSocket connection: ${error instanceof Error ? error.message : "Unknown error"}`));
2623
+ }
2624
+ });
2625
+ }
2626
+ handleWebSocketMessage(message) {
2627
+ switch (message.type) {
2628
+ case "message":
2629
+ this.messageCallbacks.forEach((callback) => {
2630
+ try {
2631
+ callback(message.payload);
2632
+ } catch (error) {
2633
+ console.error("Error in message callback:", error);
2634
+ }
2635
+ });
2636
+ break;
2637
+ case "presence":
2638
+ this.presenceCallbacks.forEach((callback) => {
2639
+ try {
2640
+ callback(message.payload.data || []);
2641
+ } catch (error) {
2642
+ console.error("Error in presence callback:", error);
2643
+ }
2644
+ });
2645
+ break;
2646
+ case "subscribed":
2647
+ console.log(`\u2705 Subscribed to channel: ${message.payload.channel}`);
2648
+ break;
2649
+ case "unsubscribed":
2650
+ console.log(`\u274C Unsubscribed from channel: ${message.payload.channel}`);
2651
+ break;
2652
+ case "pong":
2653
+ break;
2654
+ case "error":
2655
+ console.error("Realtime error:", message.payload.error);
2656
+ break;
2657
+ default:
2658
+ console.log("Unknown message type:", message.type);
2659
+ }
2660
+ }
2661
+ startHeartbeat() {
2662
+ if (this.heartbeatTimer) {
2663
+ clearInterval(this.heartbeatTimer);
2664
+ }
2665
+ this.heartbeatTimer = window.setInterval(() => {
2666
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
2667
+ this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
2668
+ }
2669
+ }, 25e3);
2670
+ }
2671
+ scheduleReconnect() {
2672
+ if (this.reconnectTimer) {
2673
+ clearTimeout(this.reconnectTimer);
2674
+ }
2675
+ this.reconnectTimer = window.setTimeout(async () => {
2676
+ if (this.isSubscribed) {
2677
+ try {
2678
+ await this.connectWebSocket();
2679
+ if (this.websocket) {
2680
+ const subscribeMessage = {
2681
+ type: "subscribe",
2682
+ payload: {
2683
+ channel: this.channelName
2684
+ }
2685
+ };
2686
+ this.websocket.send(JSON.stringify(subscribeMessage));
2687
+ this.startHeartbeat();
2688
+ }
2689
+ } catch (error) {
2690
+ console.error("Reconnection failed:", error);
2691
+ this.scheduleReconnect();
2692
+ }
2693
+ }
2694
+ }, 2e3);
2695
+ }
2696
+ cleanup() {
2697
+ this.isSubscribed = false;
2698
+ if (this.heartbeatTimer) {
2699
+ clearInterval(this.heartbeatTimer);
2700
+ this.heartbeatTimer = null;
2701
+ }
2702
+ if (this.reconnectTimer) {
2703
+ clearTimeout(this.reconnectTimer);
2704
+ this.reconnectTimer = null;
2705
+ }
2706
+ if (this.websocket) {
2707
+ this.websocket.close();
2708
+ this.websocket = null;
2709
+ }
2710
+ this.messageCallbacks = [];
2711
+ this.presenceCallbacks = [];
2712
+ }
2713
+ };
2714
+ var BlinkRealtimeImpl = class {
2715
+ constructor(httpClient, projectId) {
2716
+ this.httpClient = httpClient;
2717
+ this.projectId = projectId;
2718
+ }
2719
+ channels = /* @__PURE__ */ new Map();
2720
+ channel(name) {
2721
+ if (!this.channels.has(name)) {
2722
+ this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
2723
+ }
2724
+ return this.channels.get(name);
2725
+ }
2726
+ async subscribe(channelName, callback, options = {}) {
2727
+ const channel = this.channel(channelName);
2728
+ await channel.subscribe(options);
2729
+ return channel.onMessage(callback);
2730
+ }
2731
+ async publish(channelName, type, data, options = {}) {
2732
+ const channel = this.channel(channelName);
2733
+ return channel.publish(type, data, options);
2734
+ }
2735
+ async presence(channelName) {
2736
+ const channel = this.channel(channelName);
2737
+ return channel.getPresence();
2738
+ }
2739
+ onPresence(channelName, callback) {
2740
+ const channel = this.channel(channelName);
2741
+ return channel.onPresence(callback);
2742
+ }
2743
+ };
2744
+
2450
2745
  // src/client.ts
2451
2746
  var BlinkClientImpl = class {
2452
2747
  auth;
@@ -2454,6 +2749,7 @@ var BlinkClientImpl = class {
2454
2749
  storage;
2455
2750
  ai;
2456
2751
  data;
2752
+ realtime;
2457
2753
  httpClient;
2458
2754
  constructor(config) {
2459
2755
  this.auth = new BlinkAuth(config);
@@ -2466,6 +2762,7 @@ var BlinkClientImpl = class {
2466
2762
  this.storage = new BlinkStorageImpl(this.httpClient);
2467
2763
  this.ai = new BlinkAIImpl(this.httpClient);
2468
2764
  this.data = new BlinkDataImpl(this.httpClient, config.projectId);
2765
+ this.realtime = new BlinkRealtimeImpl(this.httpClient, config.projectId);
2469
2766
  }
2470
2767
  };
2471
2768
  function createClient(config) {
@@ -2479,6 +2776,6 @@ function createClient(config) {
2479
2776
  return new BlinkClientImpl(clientConfig);
2480
2777
  }
2481
2778
 
2482
- export { BlinkAIImpl, BlinkDataImpl, BlinkDatabase, BlinkStorageImpl, BlinkTable, createClient };
2779
+ export { BlinkAIImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, createClient };
2483
2780
  //# sourceMappingURL=index.mjs.map
2484
2781
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
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",