@blinkdotnew/sdk 0.5.1 → 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/README.md +274 -1
- package/dist/index.d.mts +57 -3
- package/dist/index.d.ts +57 -3
- package/dist/index.js +360 -0
- package/dist/index.mjs +359 -1
- package/package.json +1 -1
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,57 @@ declare class BlinkAIImpl implements BlinkAI {
|
|
|
665
666
|
transcribeAudio(options: TranscriptionRequest): Promise<TranscriptionResponse>;
|
|
666
667
|
}
|
|
667
668
|
|
|
668
|
-
|
|
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
|
+
private reconnectAttempts;
|
|
685
|
+
constructor(channelName: string, httpClient: HttpClient, projectId: string);
|
|
686
|
+
subscribe(options?: {
|
|
687
|
+
userId?: string;
|
|
688
|
+
metadata?: Record<string, any>;
|
|
689
|
+
}): Promise<void>;
|
|
690
|
+
unsubscribe(): Promise<void>;
|
|
691
|
+
publish(type: string, data: any, options?: {
|
|
692
|
+
userId?: string;
|
|
693
|
+
metadata?: Record<string, any>;
|
|
694
|
+
}): Promise<string>;
|
|
695
|
+
onMessage(callback: (message: RealtimeMessage) => void): () => void;
|
|
696
|
+
onPresence(callback: (users: PresenceUser[]) => void): () => void;
|
|
697
|
+
getPresence(): Promise<PresenceUser[]>;
|
|
698
|
+
getMessages(options?: {
|
|
699
|
+
limit?: number;
|
|
700
|
+
before?: string;
|
|
701
|
+
after?: string;
|
|
702
|
+
}): Promise<RealtimeMessage[]>;
|
|
703
|
+
private connectWebSocket;
|
|
704
|
+
private handleWebSocketMessage;
|
|
705
|
+
private startHeartbeat;
|
|
706
|
+
private scheduleReconnect;
|
|
707
|
+
private cleanup;
|
|
708
|
+
}
|
|
709
|
+
declare class BlinkRealtimeImpl implements BlinkRealtime {
|
|
710
|
+
private httpClient;
|
|
711
|
+
private projectId;
|
|
712
|
+
private channels;
|
|
713
|
+
private handlers;
|
|
714
|
+
constructor(httpClient: HttpClient, projectId: string);
|
|
715
|
+
channel(name: string): RealtimeChannel;
|
|
716
|
+
subscribe(channelName: string, callback: (message: RealtimeMessage) => void, options?: RealtimeSubscribeOptions): Promise<() => void>;
|
|
717
|
+
publish(channelName: string, type: string, data: any, options?: RealtimePublishOptions): Promise<string>;
|
|
718
|
+
presence(channelName: string): Promise<PresenceUser[]>;
|
|
719
|
+
onPresence(channelName: string, callback: (users: PresenceUser[]) => void): () => void;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
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,57 @@ declare class BlinkAIImpl implements BlinkAI {
|
|
|
665
666
|
transcribeAudio(options: TranscriptionRequest): Promise<TranscriptionResponse>;
|
|
666
667
|
}
|
|
667
668
|
|
|
668
|
-
|
|
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
|
+
private reconnectAttempts;
|
|
685
|
+
constructor(channelName: string, httpClient: HttpClient, projectId: string);
|
|
686
|
+
subscribe(options?: {
|
|
687
|
+
userId?: string;
|
|
688
|
+
metadata?: Record<string, any>;
|
|
689
|
+
}): Promise<void>;
|
|
690
|
+
unsubscribe(): Promise<void>;
|
|
691
|
+
publish(type: string, data: any, options?: {
|
|
692
|
+
userId?: string;
|
|
693
|
+
metadata?: Record<string, any>;
|
|
694
|
+
}): Promise<string>;
|
|
695
|
+
onMessage(callback: (message: RealtimeMessage) => void): () => void;
|
|
696
|
+
onPresence(callback: (users: PresenceUser[]) => void): () => void;
|
|
697
|
+
getPresence(): Promise<PresenceUser[]>;
|
|
698
|
+
getMessages(options?: {
|
|
699
|
+
limit?: number;
|
|
700
|
+
before?: string;
|
|
701
|
+
after?: string;
|
|
702
|
+
}): Promise<RealtimeMessage[]>;
|
|
703
|
+
private connectWebSocket;
|
|
704
|
+
private handleWebSocketMessage;
|
|
705
|
+
private startHeartbeat;
|
|
706
|
+
private scheduleReconnect;
|
|
707
|
+
private cleanup;
|
|
708
|
+
}
|
|
709
|
+
declare class BlinkRealtimeImpl implements BlinkRealtime {
|
|
710
|
+
private httpClient;
|
|
711
|
+
private projectId;
|
|
712
|
+
private channels;
|
|
713
|
+
private handlers;
|
|
714
|
+
constructor(httpClient: HttpClient, projectId: string);
|
|
715
|
+
channel(name: string): RealtimeChannel;
|
|
716
|
+
subscribe(channelName: string, callback: (message: RealtimeMessage) => void, options?: RealtimeSubscribeOptions): Promise<() => void>;
|
|
717
|
+
publish(channelName: string, type: string, data: any, options?: RealtimePublishOptions): Promise<string>;
|
|
718
|
+
presence(channelName: string): Promise<PresenceUser[]>;
|
|
719
|
+
onPresence(channelName: string, callback: (users: PresenceUser[]) => void): () => void;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
export { type AuthStateChangeCallback, BlinkAIImpl, type BlinkClient, type BlinkData, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, createClient };
|
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) {
|
|
@@ -46,6 +53,12 @@ var BlinkDataError = class extends BlinkError {
|
|
|
46
53
|
this.name = "BlinkDataError";
|
|
47
54
|
}
|
|
48
55
|
};
|
|
56
|
+
var BlinkRealtimeError = class extends BlinkError {
|
|
57
|
+
constructor(message, status, details) {
|
|
58
|
+
super(message, "REALTIME_ERROR", status, details);
|
|
59
|
+
this.name = "BlinkRealtimeError";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
49
62
|
|
|
50
63
|
// ../core/src/query-builder.ts
|
|
51
64
|
function buildFilterQuery(condition) {
|
|
@@ -576,6 +589,24 @@ var HttpClient = class {
|
|
|
576
589
|
async dataSearch(projectId, request) {
|
|
577
590
|
return this.post(`/api/data/${projectId}/search`, request);
|
|
578
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* Realtime-specific requests
|
|
594
|
+
*/
|
|
595
|
+
async realtimePublish(projectId, request) {
|
|
596
|
+
return this.post(`/api/realtime/${projectId}/publish`, request);
|
|
597
|
+
}
|
|
598
|
+
async realtimeGetPresence(projectId, channel) {
|
|
599
|
+
return this.get(`/api/realtime/${projectId}/presence`, { channel });
|
|
600
|
+
}
|
|
601
|
+
async realtimeGetMessages(projectId, options) {
|
|
602
|
+
const { channel, ...searchParams } = options;
|
|
603
|
+
return this.get(`/api/realtime/${projectId}/messages`, {
|
|
604
|
+
channel,
|
|
605
|
+
...Object.fromEntries(
|
|
606
|
+
Object.entries(searchParams).map(([k, v]) => [k, String(v)])
|
|
607
|
+
)
|
|
608
|
+
});
|
|
609
|
+
}
|
|
579
610
|
/**
|
|
580
611
|
* Private helper methods
|
|
581
612
|
*/
|
|
@@ -2449,6 +2480,331 @@ var BlinkDataImpl = class {
|
|
|
2449
2480
|
}
|
|
2450
2481
|
};
|
|
2451
2482
|
|
|
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
|
+
};
|
|
2495
|
+
var BlinkRealtimeChannel = class {
|
|
2496
|
+
constructor(channelName, httpClient, projectId) {
|
|
2497
|
+
this.channelName = channelName;
|
|
2498
|
+
this.httpClient = httpClient;
|
|
2499
|
+
this.projectId = projectId;
|
|
2500
|
+
}
|
|
2501
|
+
messageCallbacks = [];
|
|
2502
|
+
presenceCallbacks = [];
|
|
2503
|
+
websocket = null;
|
|
2504
|
+
isSubscribed = false;
|
|
2505
|
+
reconnectTimer = null;
|
|
2506
|
+
heartbeatTimer = null;
|
|
2507
|
+
reconnectAttempts = 0;
|
|
2508
|
+
async subscribe(options = {}) {
|
|
2509
|
+
if (this.isSubscribed) {
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
try {
|
|
2513
|
+
await this.connectWebSocket();
|
|
2514
|
+
if (this.websocket) {
|
|
2515
|
+
const subscribeMessage = {
|
|
2516
|
+
type: "subscribe",
|
|
2517
|
+
payload: {
|
|
2518
|
+
channel: this.channelName,
|
|
2519
|
+
userId: options.userId,
|
|
2520
|
+
metadata: options.metadata
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
this.websocket.send(JSON.stringify(subscribeMessage));
|
|
2524
|
+
this.isSubscribed = true;
|
|
2525
|
+
this.startHeartbeat();
|
|
2526
|
+
}
|
|
2527
|
+
} catch (error) {
|
|
2528
|
+
throw new BlinkRealtimeError(
|
|
2529
|
+
`Failed to subscribe to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
async unsubscribe() {
|
|
2534
|
+
if (!this.isSubscribed) {
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
if (this.websocket) {
|
|
2538
|
+
const unsubscribeMessage = {
|
|
2539
|
+
type: "unsubscribe",
|
|
2540
|
+
payload: {
|
|
2541
|
+
channel: this.channelName
|
|
2542
|
+
}
|
|
2543
|
+
};
|
|
2544
|
+
this.websocket.send(JSON.stringify(unsubscribeMessage));
|
|
2545
|
+
}
|
|
2546
|
+
this.cleanup();
|
|
2547
|
+
}
|
|
2548
|
+
async publish(type, data, options = {}) {
|
|
2549
|
+
try {
|
|
2550
|
+
const response = await this.httpClient.realtimePublish(this.projectId, {
|
|
2551
|
+
channel: this.channelName,
|
|
2552
|
+
type,
|
|
2553
|
+
data,
|
|
2554
|
+
userId: options.userId,
|
|
2555
|
+
metadata: options.metadata
|
|
2556
|
+
});
|
|
2557
|
+
return response.data.messageId;
|
|
2558
|
+
} catch (error) {
|
|
2559
|
+
throw new BlinkRealtimeError(
|
|
2560
|
+
`Failed to publish message to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
onMessage(callback) {
|
|
2565
|
+
this.messageCallbacks.push(callback);
|
|
2566
|
+
return () => {
|
|
2567
|
+
const index = this.messageCallbacks.indexOf(callback);
|
|
2568
|
+
if (index > -1) {
|
|
2569
|
+
this.messageCallbacks.splice(index, 1);
|
|
2570
|
+
}
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
onPresence(callback) {
|
|
2574
|
+
this.presenceCallbacks.push(callback);
|
|
2575
|
+
return () => {
|
|
2576
|
+
const index = this.presenceCallbacks.indexOf(callback);
|
|
2577
|
+
if (index > -1) {
|
|
2578
|
+
this.presenceCallbacks.splice(index, 1);
|
|
2579
|
+
}
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
async getPresence() {
|
|
2583
|
+
try {
|
|
2584
|
+
const response = await this.httpClient.realtimeGetPresence(this.projectId, this.channelName);
|
|
2585
|
+
return response.data.users;
|
|
2586
|
+
} catch (error) {
|
|
2587
|
+
throw new BlinkRealtimeError(
|
|
2588
|
+
`Failed to get presence for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2589
|
+
);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
async getMessages(options = {}) {
|
|
2593
|
+
try {
|
|
2594
|
+
const response = await this.httpClient.realtimeGetMessages(this.projectId, {
|
|
2595
|
+
channel: this.channelName,
|
|
2596
|
+
limit: options.limit,
|
|
2597
|
+
start: options.after,
|
|
2598
|
+
// after = start from this ID onwards
|
|
2599
|
+
end: options.before
|
|
2600
|
+
// before = end at this ID
|
|
2601
|
+
});
|
|
2602
|
+
return response.data.messages;
|
|
2603
|
+
} catch (error) {
|
|
2604
|
+
throw new BlinkRealtimeError(
|
|
2605
|
+
`Failed to get messages for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
async connectWebSocket() {
|
|
2610
|
+
if (this.websocket && this.websocket.readyState === 1) {
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
2613
|
+
return new Promise((resolve, reject) => {
|
|
2614
|
+
try {
|
|
2615
|
+
const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
|
|
2616
|
+
const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
|
|
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
|
+
}
|
|
2623
|
+
this.websocket.onopen = () => {
|
|
2624
|
+
console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
|
|
2625
|
+
this.reconnectAttempts = 0;
|
|
2626
|
+
resolve();
|
|
2627
|
+
};
|
|
2628
|
+
this.websocket.onmessage = (event) => {
|
|
2629
|
+
try {
|
|
2630
|
+
const message = JSON.parse(event.data);
|
|
2631
|
+
this.handleWebSocketMessage(message);
|
|
2632
|
+
} catch (error) {
|
|
2633
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
2634
|
+
}
|
|
2635
|
+
};
|
|
2636
|
+
this.websocket.onclose = () => {
|
|
2637
|
+
console.log(`\u{1F50C} Disconnected from realtime for project ${this.projectId}`);
|
|
2638
|
+
this.isSubscribed = false;
|
|
2639
|
+
this.scheduleReconnect();
|
|
2640
|
+
};
|
|
2641
|
+
this.websocket.onerror = (error) => {
|
|
2642
|
+
console.error("WebSocket error:", error);
|
|
2643
|
+
reject(new BlinkRealtimeError("WebSocket connection failed"));
|
|
2644
|
+
};
|
|
2645
|
+
setTimeout(() => {
|
|
2646
|
+
if (this.websocket?.readyState !== 1) {
|
|
2647
|
+
reject(new BlinkRealtimeError("WebSocket connection timeout"));
|
|
2648
|
+
}
|
|
2649
|
+
}, 5e3);
|
|
2650
|
+
} catch (error) {
|
|
2651
|
+
reject(new BlinkRealtimeError(`Failed to create WebSocket connection: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
2652
|
+
}
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
handleWebSocketMessage(message) {
|
|
2656
|
+
switch (message.type) {
|
|
2657
|
+
case "message":
|
|
2658
|
+
this.messageCallbacks.forEach((callback) => {
|
|
2659
|
+
try {
|
|
2660
|
+
callback(message.payload);
|
|
2661
|
+
} catch (error) {
|
|
2662
|
+
console.error("Error in message callback:", error);
|
|
2663
|
+
}
|
|
2664
|
+
});
|
|
2665
|
+
break;
|
|
2666
|
+
case "presence":
|
|
2667
|
+
this.presenceCallbacks.forEach((callback) => {
|
|
2668
|
+
try {
|
|
2669
|
+
callback(message.payload.data || []);
|
|
2670
|
+
} catch (error) {
|
|
2671
|
+
console.error("Error in presence callback:", error);
|
|
2672
|
+
}
|
|
2673
|
+
});
|
|
2674
|
+
break;
|
|
2675
|
+
case "subscribed":
|
|
2676
|
+
console.log(`\u2705 Subscribed to channel: ${message.payload.channel}`);
|
|
2677
|
+
break;
|
|
2678
|
+
case "unsubscribed":
|
|
2679
|
+
console.log(`\u274C Unsubscribed from channel: ${message.payload.channel}`);
|
|
2680
|
+
break;
|
|
2681
|
+
case "pong":
|
|
2682
|
+
break;
|
|
2683
|
+
case "error":
|
|
2684
|
+
console.error("Realtime error:", message.payload.error);
|
|
2685
|
+
break;
|
|
2686
|
+
default:
|
|
2687
|
+
console.log("Unknown message type:", message.type);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
startHeartbeat() {
|
|
2691
|
+
if (this.heartbeatTimer) {
|
|
2692
|
+
clearInterval(this.heartbeatTimer);
|
|
2693
|
+
}
|
|
2694
|
+
this.heartbeatTimer = globalThis.setInterval(() => {
|
|
2695
|
+
if (this.websocket && this.websocket.readyState === 1) {
|
|
2696
|
+
this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
|
|
2697
|
+
}
|
|
2698
|
+
}, 25e3);
|
|
2699
|
+
}
|
|
2700
|
+
scheduleReconnect() {
|
|
2701
|
+
if (this.reconnectTimer) {
|
|
2702
|
+
clearTimeout(this.reconnectTimer);
|
|
2703
|
+
}
|
|
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 () => {
|
|
2710
|
+
if (this.isSubscribed) {
|
|
2711
|
+
try {
|
|
2712
|
+
await this.connectWebSocket();
|
|
2713
|
+
if (this.websocket) {
|
|
2714
|
+
const subscribeMessage = {
|
|
2715
|
+
type: "subscribe",
|
|
2716
|
+
payload: {
|
|
2717
|
+
channel: this.channelName
|
|
2718
|
+
}
|
|
2719
|
+
};
|
|
2720
|
+
this.websocket.send(JSON.stringify(subscribeMessage));
|
|
2721
|
+
this.startHeartbeat();
|
|
2722
|
+
}
|
|
2723
|
+
} catch (error) {
|
|
2724
|
+
console.error("Reconnection failed:", error);
|
|
2725
|
+
this.scheduleReconnect();
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
}, delay);
|
|
2729
|
+
}
|
|
2730
|
+
cleanup() {
|
|
2731
|
+
this.isSubscribed = false;
|
|
2732
|
+
if (this.heartbeatTimer) {
|
|
2733
|
+
clearInterval(this.heartbeatTimer);
|
|
2734
|
+
this.heartbeatTimer = null;
|
|
2735
|
+
}
|
|
2736
|
+
if (this.reconnectTimer) {
|
|
2737
|
+
clearTimeout(this.reconnectTimer);
|
|
2738
|
+
this.reconnectTimer = null;
|
|
2739
|
+
}
|
|
2740
|
+
if (this.websocket) {
|
|
2741
|
+
this.websocket.close();
|
|
2742
|
+
this.websocket = null;
|
|
2743
|
+
}
|
|
2744
|
+
this.messageCallbacks = [];
|
|
2745
|
+
this.presenceCallbacks = [];
|
|
2746
|
+
}
|
|
2747
|
+
};
|
|
2748
|
+
var BlinkRealtimeImpl = class {
|
|
2749
|
+
constructor(httpClient, projectId) {
|
|
2750
|
+
this.httpClient = httpClient;
|
|
2751
|
+
this.projectId = projectId;
|
|
2752
|
+
}
|
|
2753
|
+
channels = /* @__PURE__ */ new Map();
|
|
2754
|
+
handlers = {};
|
|
2755
|
+
channel(name) {
|
|
2756
|
+
if (!this.channels.has(name)) {
|
|
2757
|
+
this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
|
|
2758
|
+
}
|
|
2759
|
+
return this.channels.get(name);
|
|
2760
|
+
}
|
|
2761
|
+
async subscribe(channelName, callback, options = {}) {
|
|
2762
|
+
const channel = this.channel(channelName);
|
|
2763
|
+
await channel.subscribe(options);
|
|
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
|
+
};
|
|
2779
|
+
}
|
|
2780
|
+
async publish(channelName, type, data, options = {}) {
|
|
2781
|
+
const channel = this.channel(channelName);
|
|
2782
|
+
return channel.publish(type, data, options);
|
|
2783
|
+
}
|
|
2784
|
+
async presence(channelName) {
|
|
2785
|
+
const channel = this.channel(channelName);
|
|
2786
|
+
return channel.getPresence();
|
|
2787
|
+
}
|
|
2788
|
+
onPresence(channelName, callback) {
|
|
2789
|
+
const channel = this.channel(channelName);
|
|
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
|
+
};
|
|
2805
|
+
}
|
|
2806
|
+
};
|
|
2807
|
+
|
|
2452
2808
|
// src/client.ts
|
|
2453
2809
|
var BlinkClientImpl = class {
|
|
2454
2810
|
auth;
|
|
@@ -2456,6 +2812,7 @@ var BlinkClientImpl = class {
|
|
|
2456
2812
|
storage;
|
|
2457
2813
|
ai;
|
|
2458
2814
|
data;
|
|
2815
|
+
realtime;
|
|
2459
2816
|
httpClient;
|
|
2460
2817
|
constructor(config) {
|
|
2461
2818
|
this.auth = new BlinkAuth(config);
|
|
@@ -2468,6 +2825,7 @@ var BlinkClientImpl = class {
|
|
|
2468
2825
|
this.storage = new BlinkStorageImpl(this.httpClient);
|
|
2469
2826
|
this.ai = new BlinkAIImpl(this.httpClient);
|
|
2470
2827
|
this.data = new BlinkDataImpl(this.httpClient, config.projectId);
|
|
2828
|
+
this.realtime = new BlinkRealtimeImpl(this.httpClient, config.projectId);
|
|
2471
2829
|
}
|
|
2472
2830
|
};
|
|
2473
2831
|
function createClient(config) {
|
|
@@ -2484,6 +2842,8 @@ function createClient(config) {
|
|
|
2484
2842
|
exports.BlinkAIImpl = BlinkAIImpl;
|
|
2485
2843
|
exports.BlinkDataImpl = BlinkDataImpl;
|
|
2486
2844
|
exports.BlinkDatabase = BlinkDatabase;
|
|
2845
|
+
exports.BlinkRealtimeChannel = BlinkRealtimeChannel;
|
|
2846
|
+
exports.BlinkRealtimeImpl = BlinkRealtimeImpl;
|
|
2487
2847
|
exports.BlinkStorageImpl = BlinkStorageImpl;
|
|
2488
2848
|
exports.BlinkTable = BlinkTable;
|
|
2489
2849
|
exports.createClient = createClient;
|
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) {
|
|
@@ -44,6 +51,12 @@ var BlinkDataError = class extends BlinkError {
|
|
|
44
51
|
this.name = "BlinkDataError";
|
|
45
52
|
}
|
|
46
53
|
};
|
|
54
|
+
var BlinkRealtimeError = class extends BlinkError {
|
|
55
|
+
constructor(message, status, details) {
|
|
56
|
+
super(message, "REALTIME_ERROR", status, details);
|
|
57
|
+
this.name = "BlinkRealtimeError";
|
|
58
|
+
}
|
|
59
|
+
};
|
|
47
60
|
|
|
48
61
|
// ../core/src/query-builder.ts
|
|
49
62
|
function buildFilterQuery(condition) {
|
|
@@ -574,6 +587,24 @@ var HttpClient = class {
|
|
|
574
587
|
async dataSearch(projectId, request) {
|
|
575
588
|
return this.post(`/api/data/${projectId}/search`, request);
|
|
576
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Realtime-specific requests
|
|
592
|
+
*/
|
|
593
|
+
async realtimePublish(projectId, request) {
|
|
594
|
+
return this.post(`/api/realtime/${projectId}/publish`, request);
|
|
595
|
+
}
|
|
596
|
+
async realtimeGetPresence(projectId, channel) {
|
|
597
|
+
return this.get(`/api/realtime/${projectId}/presence`, { channel });
|
|
598
|
+
}
|
|
599
|
+
async realtimeGetMessages(projectId, options) {
|
|
600
|
+
const { channel, ...searchParams } = options;
|
|
601
|
+
return this.get(`/api/realtime/${projectId}/messages`, {
|
|
602
|
+
channel,
|
|
603
|
+
...Object.fromEntries(
|
|
604
|
+
Object.entries(searchParams).map(([k, v]) => [k, String(v)])
|
|
605
|
+
)
|
|
606
|
+
});
|
|
607
|
+
}
|
|
577
608
|
/**
|
|
578
609
|
* Private helper methods
|
|
579
610
|
*/
|
|
@@ -2447,6 +2478,331 @@ var BlinkDataImpl = class {
|
|
|
2447
2478
|
}
|
|
2448
2479
|
};
|
|
2449
2480
|
|
|
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
|
+
};
|
|
2493
|
+
var BlinkRealtimeChannel = class {
|
|
2494
|
+
constructor(channelName, httpClient, projectId) {
|
|
2495
|
+
this.channelName = channelName;
|
|
2496
|
+
this.httpClient = httpClient;
|
|
2497
|
+
this.projectId = projectId;
|
|
2498
|
+
}
|
|
2499
|
+
messageCallbacks = [];
|
|
2500
|
+
presenceCallbacks = [];
|
|
2501
|
+
websocket = null;
|
|
2502
|
+
isSubscribed = false;
|
|
2503
|
+
reconnectTimer = null;
|
|
2504
|
+
heartbeatTimer = null;
|
|
2505
|
+
reconnectAttempts = 0;
|
|
2506
|
+
async subscribe(options = {}) {
|
|
2507
|
+
if (this.isSubscribed) {
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
try {
|
|
2511
|
+
await this.connectWebSocket();
|
|
2512
|
+
if (this.websocket) {
|
|
2513
|
+
const subscribeMessage = {
|
|
2514
|
+
type: "subscribe",
|
|
2515
|
+
payload: {
|
|
2516
|
+
channel: this.channelName,
|
|
2517
|
+
userId: options.userId,
|
|
2518
|
+
metadata: options.metadata
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
this.websocket.send(JSON.stringify(subscribeMessage));
|
|
2522
|
+
this.isSubscribed = true;
|
|
2523
|
+
this.startHeartbeat();
|
|
2524
|
+
}
|
|
2525
|
+
} catch (error) {
|
|
2526
|
+
throw new BlinkRealtimeError(
|
|
2527
|
+
`Failed to subscribe to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2528
|
+
);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
async unsubscribe() {
|
|
2532
|
+
if (!this.isSubscribed) {
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
if (this.websocket) {
|
|
2536
|
+
const unsubscribeMessage = {
|
|
2537
|
+
type: "unsubscribe",
|
|
2538
|
+
payload: {
|
|
2539
|
+
channel: this.channelName
|
|
2540
|
+
}
|
|
2541
|
+
};
|
|
2542
|
+
this.websocket.send(JSON.stringify(unsubscribeMessage));
|
|
2543
|
+
}
|
|
2544
|
+
this.cleanup();
|
|
2545
|
+
}
|
|
2546
|
+
async publish(type, data, options = {}) {
|
|
2547
|
+
try {
|
|
2548
|
+
const response = await this.httpClient.realtimePublish(this.projectId, {
|
|
2549
|
+
channel: this.channelName,
|
|
2550
|
+
type,
|
|
2551
|
+
data,
|
|
2552
|
+
userId: options.userId,
|
|
2553
|
+
metadata: options.metadata
|
|
2554
|
+
});
|
|
2555
|
+
return response.data.messageId;
|
|
2556
|
+
} catch (error) {
|
|
2557
|
+
throw new BlinkRealtimeError(
|
|
2558
|
+
`Failed to publish message to channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
onMessage(callback) {
|
|
2563
|
+
this.messageCallbacks.push(callback);
|
|
2564
|
+
return () => {
|
|
2565
|
+
const index = this.messageCallbacks.indexOf(callback);
|
|
2566
|
+
if (index > -1) {
|
|
2567
|
+
this.messageCallbacks.splice(index, 1);
|
|
2568
|
+
}
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
onPresence(callback) {
|
|
2572
|
+
this.presenceCallbacks.push(callback);
|
|
2573
|
+
return () => {
|
|
2574
|
+
const index = this.presenceCallbacks.indexOf(callback);
|
|
2575
|
+
if (index > -1) {
|
|
2576
|
+
this.presenceCallbacks.splice(index, 1);
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
async getPresence() {
|
|
2581
|
+
try {
|
|
2582
|
+
const response = await this.httpClient.realtimeGetPresence(this.projectId, this.channelName);
|
|
2583
|
+
return response.data.users;
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
throw new BlinkRealtimeError(
|
|
2586
|
+
`Failed to get presence for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
async getMessages(options = {}) {
|
|
2591
|
+
try {
|
|
2592
|
+
const response = await this.httpClient.realtimeGetMessages(this.projectId, {
|
|
2593
|
+
channel: this.channelName,
|
|
2594
|
+
limit: options.limit,
|
|
2595
|
+
start: options.after,
|
|
2596
|
+
// after = start from this ID onwards
|
|
2597
|
+
end: options.before
|
|
2598
|
+
// before = end at this ID
|
|
2599
|
+
});
|
|
2600
|
+
return response.data.messages;
|
|
2601
|
+
} catch (error) {
|
|
2602
|
+
throw new BlinkRealtimeError(
|
|
2603
|
+
`Failed to get messages for channel ${this.channelName}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2604
|
+
);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
async connectWebSocket() {
|
|
2608
|
+
if (this.websocket && this.websocket.readyState === 1) {
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
return new Promise((resolve, reject) => {
|
|
2612
|
+
try {
|
|
2613
|
+
const baseUrl = this.httpClient.projectId.includes("localhost") ? "ws://localhost:3000" : "wss://core.blink.new";
|
|
2614
|
+
const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
|
|
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
|
+
}
|
|
2621
|
+
this.websocket.onopen = () => {
|
|
2622
|
+
console.log(`\u{1F517} Connected to realtime for project ${this.projectId}`);
|
|
2623
|
+
this.reconnectAttempts = 0;
|
|
2624
|
+
resolve();
|
|
2625
|
+
};
|
|
2626
|
+
this.websocket.onmessage = (event) => {
|
|
2627
|
+
try {
|
|
2628
|
+
const message = JSON.parse(event.data);
|
|
2629
|
+
this.handleWebSocketMessage(message);
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
this.websocket.onclose = () => {
|
|
2635
|
+
console.log(`\u{1F50C} Disconnected from realtime for project ${this.projectId}`);
|
|
2636
|
+
this.isSubscribed = false;
|
|
2637
|
+
this.scheduleReconnect();
|
|
2638
|
+
};
|
|
2639
|
+
this.websocket.onerror = (error) => {
|
|
2640
|
+
console.error("WebSocket error:", error);
|
|
2641
|
+
reject(new BlinkRealtimeError("WebSocket connection failed"));
|
|
2642
|
+
};
|
|
2643
|
+
setTimeout(() => {
|
|
2644
|
+
if (this.websocket?.readyState !== 1) {
|
|
2645
|
+
reject(new BlinkRealtimeError("WebSocket connection timeout"));
|
|
2646
|
+
}
|
|
2647
|
+
}, 5e3);
|
|
2648
|
+
} catch (error) {
|
|
2649
|
+
reject(new BlinkRealtimeError(`Failed to create WebSocket connection: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
2650
|
+
}
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
handleWebSocketMessage(message) {
|
|
2654
|
+
switch (message.type) {
|
|
2655
|
+
case "message":
|
|
2656
|
+
this.messageCallbacks.forEach((callback) => {
|
|
2657
|
+
try {
|
|
2658
|
+
callback(message.payload);
|
|
2659
|
+
} catch (error) {
|
|
2660
|
+
console.error("Error in message callback:", error);
|
|
2661
|
+
}
|
|
2662
|
+
});
|
|
2663
|
+
break;
|
|
2664
|
+
case "presence":
|
|
2665
|
+
this.presenceCallbacks.forEach((callback) => {
|
|
2666
|
+
try {
|
|
2667
|
+
callback(message.payload.data || []);
|
|
2668
|
+
} catch (error) {
|
|
2669
|
+
console.error("Error in presence callback:", error);
|
|
2670
|
+
}
|
|
2671
|
+
});
|
|
2672
|
+
break;
|
|
2673
|
+
case "subscribed":
|
|
2674
|
+
console.log(`\u2705 Subscribed to channel: ${message.payload.channel}`);
|
|
2675
|
+
break;
|
|
2676
|
+
case "unsubscribed":
|
|
2677
|
+
console.log(`\u274C Unsubscribed from channel: ${message.payload.channel}`);
|
|
2678
|
+
break;
|
|
2679
|
+
case "pong":
|
|
2680
|
+
break;
|
|
2681
|
+
case "error":
|
|
2682
|
+
console.error("Realtime error:", message.payload.error);
|
|
2683
|
+
break;
|
|
2684
|
+
default:
|
|
2685
|
+
console.log("Unknown message type:", message.type);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
startHeartbeat() {
|
|
2689
|
+
if (this.heartbeatTimer) {
|
|
2690
|
+
clearInterval(this.heartbeatTimer);
|
|
2691
|
+
}
|
|
2692
|
+
this.heartbeatTimer = globalThis.setInterval(() => {
|
|
2693
|
+
if (this.websocket && this.websocket.readyState === 1) {
|
|
2694
|
+
this.websocket.send(JSON.stringify({ type: "ping", payload: {} }));
|
|
2695
|
+
}
|
|
2696
|
+
}, 25e3);
|
|
2697
|
+
}
|
|
2698
|
+
scheduleReconnect() {
|
|
2699
|
+
if (this.reconnectTimer) {
|
|
2700
|
+
clearTimeout(this.reconnectTimer);
|
|
2701
|
+
}
|
|
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 () => {
|
|
2708
|
+
if (this.isSubscribed) {
|
|
2709
|
+
try {
|
|
2710
|
+
await this.connectWebSocket();
|
|
2711
|
+
if (this.websocket) {
|
|
2712
|
+
const subscribeMessage = {
|
|
2713
|
+
type: "subscribe",
|
|
2714
|
+
payload: {
|
|
2715
|
+
channel: this.channelName
|
|
2716
|
+
}
|
|
2717
|
+
};
|
|
2718
|
+
this.websocket.send(JSON.stringify(subscribeMessage));
|
|
2719
|
+
this.startHeartbeat();
|
|
2720
|
+
}
|
|
2721
|
+
} catch (error) {
|
|
2722
|
+
console.error("Reconnection failed:", error);
|
|
2723
|
+
this.scheduleReconnect();
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}, delay);
|
|
2727
|
+
}
|
|
2728
|
+
cleanup() {
|
|
2729
|
+
this.isSubscribed = false;
|
|
2730
|
+
if (this.heartbeatTimer) {
|
|
2731
|
+
clearInterval(this.heartbeatTimer);
|
|
2732
|
+
this.heartbeatTimer = null;
|
|
2733
|
+
}
|
|
2734
|
+
if (this.reconnectTimer) {
|
|
2735
|
+
clearTimeout(this.reconnectTimer);
|
|
2736
|
+
this.reconnectTimer = null;
|
|
2737
|
+
}
|
|
2738
|
+
if (this.websocket) {
|
|
2739
|
+
this.websocket.close();
|
|
2740
|
+
this.websocket = null;
|
|
2741
|
+
}
|
|
2742
|
+
this.messageCallbacks = [];
|
|
2743
|
+
this.presenceCallbacks = [];
|
|
2744
|
+
}
|
|
2745
|
+
};
|
|
2746
|
+
var BlinkRealtimeImpl = class {
|
|
2747
|
+
constructor(httpClient, projectId) {
|
|
2748
|
+
this.httpClient = httpClient;
|
|
2749
|
+
this.projectId = projectId;
|
|
2750
|
+
}
|
|
2751
|
+
channels = /* @__PURE__ */ new Map();
|
|
2752
|
+
handlers = {};
|
|
2753
|
+
channel(name) {
|
|
2754
|
+
if (!this.channels.has(name)) {
|
|
2755
|
+
this.channels.set(name, new BlinkRealtimeChannel(name, this.httpClient, this.projectId));
|
|
2756
|
+
}
|
|
2757
|
+
return this.channels.get(name);
|
|
2758
|
+
}
|
|
2759
|
+
async subscribe(channelName, callback, options = {}) {
|
|
2760
|
+
const channel = this.channel(channelName);
|
|
2761
|
+
await channel.subscribe(options);
|
|
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
|
+
};
|
|
2777
|
+
}
|
|
2778
|
+
async publish(channelName, type, data, options = {}) {
|
|
2779
|
+
const channel = this.channel(channelName);
|
|
2780
|
+
return channel.publish(type, data, options);
|
|
2781
|
+
}
|
|
2782
|
+
async presence(channelName) {
|
|
2783
|
+
const channel = this.channel(channelName);
|
|
2784
|
+
return channel.getPresence();
|
|
2785
|
+
}
|
|
2786
|
+
onPresence(channelName, callback) {
|
|
2787
|
+
const channel = this.channel(channelName);
|
|
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
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
|
|
2450
2806
|
// src/client.ts
|
|
2451
2807
|
var BlinkClientImpl = class {
|
|
2452
2808
|
auth;
|
|
@@ -2454,6 +2810,7 @@ var BlinkClientImpl = class {
|
|
|
2454
2810
|
storage;
|
|
2455
2811
|
ai;
|
|
2456
2812
|
data;
|
|
2813
|
+
realtime;
|
|
2457
2814
|
httpClient;
|
|
2458
2815
|
constructor(config) {
|
|
2459
2816
|
this.auth = new BlinkAuth(config);
|
|
@@ -2466,6 +2823,7 @@ var BlinkClientImpl = class {
|
|
|
2466
2823
|
this.storage = new BlinkStorageImpl(this.httpClient);
|
|
2467
2824
|
this.ai = new BlinkAIImpl(this.httpClient);
|
|
2468
2825
|
this.data = new BlinkDataImpl(this.httpClient, config.projectId);
|
|
2826
|
+
this.realtime = new BlinkRealtimeImpl(this.httpClient, config.projectId);
|
|
2469
2827
|
}
|
|
2470
2828
|
};
|
|
2471
2829
|
function createClient(config) {
|
|
@@ -2479,6 +2837,6 @@ function createClient(config) {
|
|
|
2479
2837
|
return new BlinkClientImpl(clientConfig);
|
|
2480
2838
|
}
|
|
2481
2839
|
|
|
2482
|
-
export { BlinkAIImpl, BlinkDataImpl, BlinkDatabase, BlinkStorageImpl, BlinkTable, createClient };
|
|
2840
|
+
export { BlinkAIImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, createClient };
|
|
2483
2841
|
//# sourceMappingURL=index.mjs.map
|
|
2484
2842
|
//# sourceMappingURL=index.mjs.map
|