@pushflodev/sdk 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 PushFlo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,500 @@
1
+ # @pushflodev/sdk
2
+
3
+ Official TypeScript SDK for [PushFlo](https://pushflo.dev) real-time messaging service.
4
+
5
+ ## Features
6
+
7
+ - **Zero runtime dependencies** - Built entirely on native Web APIs
8
+ - **Full TypeScript support** - Complete type definitions included
9
+ - **Three entry points** - Browser client, server client, and React hooks
10
+ - **Auto-reconnection** - Automatic reconnection with exponential backoff
11
+ - **Lightweight** - Minimal bundle size
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ # npm
17
+ npm install @pushflodev/sdk
18
+
19
+ # yarn
20
+ yarn add @pushflodev/sdk
21
+
22
+ # pnpm
23
+ pnpm add @pushflodev/sdk
24
+ ```
25
+
26
+ ## Quick Start - Browser (60 seconds)
27
+
28
+ ```typescript
29
+ import { PushFloClient } from '@pushflodev/sdk';
30
+
31
+ // 1. Create client
32
+ const client = new PushFloClient({
33
+ publishKey: 'pub_xxxxxxxxxxxxx', // Get from console.pushflo.dev
34
+ });
35
+
36
+ // 2. Connect
37
+ await client.connect();
38
+
39
+ // 3. Subscribe to channel
40
+ const subscription = client.subscribe('notifications', {
41
+ onMessage: (message) => {
42
+ console.log('Received:', message.content);
43
+ console.log('Event type:', message.eventType);
44
+ console.log('Timestamp:', new Date(message.timestamp));
45
+ },
46
+ });
47
+
48
+ // 4. Cleanup when done
49
+ subscription.unsubscribe();
50
+ client.disconnect();
51
+ ```
52
+
53
+ ## Quick Start - Server (60 seconds)
54
+
55
+ ```typescript
56
+ import { PushFloServer } from '@pushflodev/sdk/server';
57
+
58
+ // 1. Create server client
59
+ const pushflo = new PushFloServer({
60
+ secretKey: 'sec_xxxxxxxxxxxxx', // Get from console.pushflo.dev
61
+ });
62
+
63
+ // 2. Publish a message
64
+ const result = await pushflo.publish('notifications', {
65
+ title: 'New Order',
66
+ orderId: '12345',
67
+ amount: 99.99,
68
+ });
69
+
70
+ console.log('Message ID:', result.id);
71
+ console.log('Delivered to:', result.delivered, 'subscribers');
72
+ ```
73
+
74
+ ## Quick Start - React (60 seconds)
75
+
76
+ ```tsx
77
+ // App.tsx - Wrap your app with provider
78
+ import { PushFloProvider } from '@pushflodev/sdk/react';
79
+
80
+ function App() {
81
+ return (
82
+ <PushFloProvider publishKey="pub_xxxxxxxxxxxxx">
83
+ <NotificationList />
84
+ </PushFloProvider>
85
+ );
86
+ }
87
+
88
+ // NotificationList.tsx - Subscribe to channel
89
+ import { useChannel } from '@pushflodev/sdk/react';
90
+
91
+ function NotificationList() {
92
+ const { messages, connectionState } = useChannel('notifications');
93
+
94
+ if (connectionState === 'connecting') {
95
+ return <div>Connecting...</div>;
96
+ }
97
+
98
+ return (
99
+ <ul>
100
+ {messages.map((msg) => (
101
+ <li key={msg.id}>
102
+ <strong>{msg.content.title}</strong>
103
+ <span>{new Date(msg.timestamp).toLocaleString()}</span>
104
+ </li>
105
+ ))}
106
+ </ul>
107
+ );
108
+ }
109
+ ```
110
+
111
+ ## Browser Client
112
+
113
+ ### Connection State Handling
114
+
115
+ ```typescript
116
+ import { PushFloClient, ConnectionState } from '@pushflodev/sdk';
117
+
118
+ const client = new PushFloClient({ publishKey: 'pub_xxx' });
119
+
120
+ // Listen to connection changes
121
+ client.onConnectionChange((state: ConnectionState) => {
122
+ switch (state) {
123
+ case 'disconnected':
124
+ console.log('Disconnected from PushFlo');
125
+ break;
126
+ case 'connecting':
127
+ console.log('Connecting to PushFlo...');
128
+ break;
129
+ case 'connected':
130
+ console.log('Connected to PushFlo!');
131
+ break;
132
+ case 'error':
133
+ console.log('Connection error');
134
+ break;
135
+ }
136
+ });
137
+
138
+ await client.connect();
139
+ ```
140
+
141
+ ### Client Options
142
+
143
+ ```typescript
144
+ const client = new PushFloClient({
145
+ // Required
146
+ publishKey: 'pub_xxxxxxxxxxxxx',
147
+
148
+ // Optional
149
+ baseUrl: 'https://api.pushflo.dev', // Custom API URL
150
+ autoConnect: false, // Auto-connect on creation
151
+ debug: false, // Enable debug logging
152
+ connectionTimeout: 30000, // Connection timeout (ms)
153
+ heartbeatInterval: 25000, // Heartbeat interval (ms)
154
+ autoReconnect: true, // Auto-reconnect on disconnect
155
+ maxReconnectAttempts: 0, // Max reconnect attempts (0 = infinite)
156
+ reconnectDelay: 1000, // Initial reconnect delay (ms)
157
+ maxReconnectDelay: 30000, // Max reconnect delay (ms)
158
+ });
159
+ ```
160
+
161
+ ### Subscription Options
162
+
163
+ ```typescript
164
+ const subscription = client.subscribe('notifications', {
165
+ onMessage: (message) => {
166
+ console.log('Received:', message);
167
+ },
168
+ onError: (error) => {
169
+ console.error('Subscription error:', error);
170
+ },
171
+ onSubscribed: () => {
172
+ console.log('Successfully subscribed');
173
+ },
174
+ onUnsubscribed: () => {
175
+ console.log('Unsubscribed');
176
+ },
177
+ });
178
+
179
+ // Later: unsubscribe
180
+ subscription.unsubscribe();
181
+ ```
182
+
183
+ ### Event Listeners
184
+
185
+ ```typescript
186
+ // Listen for all messages
187
+ client.on('message', (message) => {
188
+ console.log('Message on', message.channel, ':', message.content);
189
+ });
190
+
191
+ // Listen for errors
192
+ client.on('error', (error) => {
193
+ console.error('Error:', error);
194
+ });
195
+
196
+ // Listen for connection events
197
+ client.on('connected', (info) => {
198
+ console.log('Connected with client ID:', info.clientId);
199
+ });
200
+
201
+ client.on('disconnected', (reason) => {
202
+ console.log('Disconnected:', reason);
203
+ });
204
+ ```
205
+
206
+ ## Server Client
207
+
208
+ ### Channel Management
209
+
210
+ ```typescript
211
+ import { PushFloServer } from '@pushflodev/sdk/server';
212
+
213
+ const pushflo = new PushFloServer({ secretKey: 'sec_xxx' });
214
+
215
+ // List all channels
216
+ const { channels, pagination } = await pushflo.listChannels({
217
+ page: 1,
218
+ pageSize: 25,
219
+ });
220
+ console.log('Channels:', channels);
221
+ console.log('Total:', pagination.total);
222
+
223
+ // Get a specific channel
224
+ const channel = await pushflo.getChannel('notifications');
225
+ console.log('Channel:', channel.name, '- Messages:', channel.messageCount);
226
+
227
+ // Create a new channel
228
+ const newChannel = await pushflo.createChannel({
229
+ name: 'Order Updates',
230
+ slug: 'order-updates',
231
+ description: 'Real-time order status updates',
232
+ isPrivate: false,
233
+ });
234
+ console.log('Created:', newChannel.slug);
235
+
236
+ // Update a channel
237
+ const updated = await pushflo.updateChannel('order-updates', {
238
+ description: 'Updated description',
239
+ });
240
+
241
+ // Delete a channel
242
+ await pushflo.deleteChannel('order-updates');
243
+ console.log('Channel deleted');
244
+ ```
245
+
246
+ ### Message History
247
+
248
+ ```typescript
249
+ import { PushFloServer } from '@pushflodev/sdk/server';
250
+
251
+ const pushflo = new PushFloServer({ secretKey: 'sec_xxx' });
252
+
253
+ // Get message history
254
+ const { messages, pagination } = await pushflo.getMessageHistory('notifications', {
255
+ page: 1,
256
+ pageSize: 50,
257
+ });
258
+
259
+ messages.forEach((msg) => {
260
+ console.log(`[${msg.eventType}] ${JSON.stringify(msg.content)}`);
261
+ });
262
+
263
+ console.log(`Page ${pagination.page} of ${pagination.totalPages}`);
264
+ ```
265
+
266
+ ### Publishing with Event Types
267
+
268
+ ```typescript
269
+ import { PushFloServer } from '@pushflodev/sdk/server';
270
+
271
+ const pushflo = new PushFloServer({ secretKey: 'sec_xxx' });
272
+
273
+ // Publish with custom event type
274
+ await pushflo.publish('orders',
275
+ { orderId: '123', status: 'shipped' },
276
+ { eventType: 'order.shipped' }
277
+ );
278
+
279
+ // Subscribe and filter by event type (browser client)
280
+ client.subscribe('orders', {
281
+ onMessage: (message) => {
282
+ if (message.eventType === 'order.shipped') {
283
+ showShippingNotification(message.content);
284
+ } else if (message.eventType === 'order.delivered') {
285
+ showDeliveryNotification(message.content);
286
+ }
287
+ },
288
+ });
289
+ ```
290
+
291
+ ### Server Options
292
+
293
+ ```typescript
294
+ const pushflo = new PushFloServer({
295
+ // Required
296
+ secretKey: 'sec_xxxxxxxxxxxxx',
297
+
298
+ // Optional
299
+ baseUrl: 'https://api.pushflo.dev', // Custom API URL
300
+ timeout: 30000, // Request timeout (ms)
301
+ debug: false, // Enable debug logging
302
+ retryAttempts: 3, // Retry failed requests
303
+ });
304
+ ```
305
+
306
+ ## React Integration
307
+
308
+ ### Provider Options
309
+
310
+ ```tsx
311
+ import { PushFloProvider } from '@pushflodev/sdk/react';
312
+
313
+ function App() {
314
+ return (
315
+ <PushFloProvider
316
+ publishKey={process.env.NEXT_PUBLIC_PUSHFLO_PUBLISH_KEY!}
317
+ baseUrl={process.env.NEXT_PUBLIC_PUSHFLO_BASE_URL}
318
+ autoConnect={true}
319
+ debug={process.env.NODE_ENV === 'development'}
320
+ >
321
+ <Dashboard />
322
+ </PushFloProvider>
323
+ );
324
+ }
325
+ ```
326
+
327
+ ### usePushFlo Hook
328
+
329
+ ```tsx
330
+ import { usePushFlo } from '@pushflodev/sdk/react';
331
+
332
+ function Dashboard() {
333
+ const { connectionState, isConnected, connect, disconnect } = usePushFlo();
334
+
335
+ return (
336
+ <div>
337
+ <p>Status: {connectionState}</p>
338
+ <button onClick={connect} disabled={isConnected}>Connect</button>
339
+ <button onClick={disconnect} disabled={!isConnected}>Disconnect</button>
340
+ </div>
341
+ );
342
+ }
343
+ ```
344
+
345
+ ### useChannel Hook
346
+
347
+ ```tsx
348
+ import { useChannel } from '@pushflodev/sdk/react';
349
+
350
+ function NotificationBell() {
351
+ const { messages, lastMessage, clearMessages, isSubscribed } = useChannel('notifications', {
352
+ onMessage: (msg) => {
353
+ // Play sound, show toast, etc.
354
+ playNotificationSound();
355
+ },
356
+ maxMessages: 100, // Limit stored messages
357
+ });
358
+
359
+ return (
360
+ <div>
361
+ <span>({messages.length} new)</span>
362
+ {lastMessage && <p>Latest: {lastMessage.content.title}</p>}
363
+ <button onClick={clearMessages}>Clear</button>
364
+ </div>
365
+ );
366
+ }
367
+ ```
368
+
369
+ ## Error Handling
370
+
371
+ ```typescript
372
+ import {
373
+ PushFloClient,
374
+ PushFloError,
375
+ ConnectionError,
376
+ AuthenticationError
377
+ } from '@pushflodev/sdk';
378
+
379
+ const client = new PushFloClient({ publishKey: 'pub_xxx' });
380
+
381
+ try {
382
+ await client.connect();
383
+ } catch (error) {
384
+ if (error instanceof AuthenticationError) {
385
+ console.error('Invalid API key:', error.message);
386
+ } else if (error instanceof ConnectionError) {
387
+ console.error('Connection failed:', error.message);
388
+ if (error.retryable) {
389
+ console.log('Will auto-retry...');
390
+ }
391
+ } else if (error instanceof PushFloError) {
392
+ console.error('PushFlo error:', error.code, error.message);
393
+ } else {
394
+ throw error;
395
+ }
396
+ }
397
+
398
+ // Listen for runtime errors
399
+ client.on('error', (error) => {
400
+ console.error('Runtime error:', error);
401
+ });
402
+ ```
403
+
404
+ ### Error Types
405
+
406
+ | Error Class | Description | Retryable |
407
+ |-------------|-------------|-----------|
408
+ | `PushFloError` | Base error class | Varies |
409
+ | `ConnectionError` | WebSocket connection issues | Yes |
410
+ | `AuthenticationError` | Invalid/missing API key | No |
411
+ | `NetworkError` | HTTP request failures | Varies |
412
+
413
+ ## Environment Variables
414
+
415
+ ```bash
416
+ # .env.local (Next.js)
417
+ NEXT_PUBLIC_PUSHFLO_PUBLISH_KEY=pub_xxxxxxxxxxxxx
418
+ NEXT_PUBLIC_PUSHFLO_BASE_URL=https://api.pushflo.dev
419
+ PUSHFLO_SECRET_KEY=sec_xxxxxxxxxxxxx
420
+
421
+ # .env (Vite)
422
+ VITE_PUSHFLO_PUBLISH_KEY=pub_xxxxxxxxxxxxx
423
+ VITE_PUSHFLO_BASE_URL=https://api.pushflo.dev
424
+
425
+ # .env (Node.js server)
426
+ PUSHFLO_SECRET_KEY=sec_xxxxxxxxxxxxx
427
+ PUSHFLO_BASE_URL=https://api.pushflo.dev
428
+ ```
429
+
430
+ ## TypeScript Types
431
+
432
+ ```typescript
433
+ import type {
434
+ // Connection
435
+ ConnectionState,
436
+ ClientOptions,
437
+ ServerOptions,
438
+
439
+ // Channels
440
+ Channel,
441
+ ChannelInput,
442
+
443
+ // Messages
444
+ Message,
445
+ PublishOptions,
446
+ PublishResult,
447
+
448
+ // API
449
+ Pagination,
450
+
451
+ // Subscriptions
452
+ Subscription,
453
+ SubscriptionOptions,
454
+ } from '@pushflodev/sdk';
455
+ ```
456
+
457
+ ## API Keys
458
+
459
+ | Key Prefix | Permissions | Use Case |
460
+ |------------|-------------|----------|
461
+ | `pub_xxx` | Read/Subscribe | Browser clients |
462
+ | `sec_xxx` | Read/Write/Publish | Server-side code |
463
+ | `mgmt_xxx` | Full access | Channel management |
464
+
465
+ ## Troubleshooting
466
+
467
+ ### Connection Issues
468
+
469
+ ```typescript
470
+ // Enable debug logging
471
+ const client = new PushFloClient({
472
+ publishKey: 'pub_xxx',
473
+ debug: true, // Logs all WebSocket activity
474
+ });
475
+ ```
476
+
477
+ ### CORS Errors
478
+
479
+ CORS is configured on the PushFlo servers. If you see CORS errors, ensure:
480
+ - You're using the correct API endpoint
481
+ - Your domain is registered in the PushFlo console
482
+
483
+ ### React Strict Mode
484
+
485
+ The SDK handles React Strict Mode correctly. The client is cleaned up and recreated as needed during development.
486
+
487
+ ### Server-Side Rendering (SSR)
488
+
489
+ The browser client requires `WebSocket` which is not available on the server. Use the SDK only in client components or with dynamic imports:
490
+
491
+ ```tsx
492
+ // Next.js App Router
493
+ 'use client';
494
+
495
+ import { PushFloProvider } from '@pushflodev/sdk/react';
496
+ ```
497
+
498
+ ## License
499
+
500
+ MIT
@@ -0,0 +1,102 @@
1
+ import { a as ConnectionInfo, M as Message, C as ClientOptions, b as ConnectionState, e as SubscriptionOptions, S as Subscription } from './message-CVgilLwz.cjs';
2
+
3
+ /**
4
+ * Type-safe event emitter with zero dependencies
5
+ */
6
+ type EventMap = {
7
+ [key: string]: unknown[];
8
+ };
9
+ type EventHandler<T extends unknown[]> = (...args: T) => void;
10
+ declare class TypedEventEmitter<Events extends EventMap> {
11
+ private listeners;
12
+ /**
13
+ * Register an event listener
14
+ */
15
+ on<K extends keyof Events>(event: K, handler: EventHandler<Events[K]>): this;
16
+ /**
17
+ * Register a one-time event listener
18
+ */
19
+ once<K extends keyof Events>(event: K, handler: EventHandler<Events[K]>): this;
20
+ /**
21
+ * Remove an event listener
22
+ */
23
+ off<K extends keyof Events>(event: K, handler: EventHandler<Events[K]>): this;
24
+ /**
25
+ * Emit an event to all registered listeners
26
+ */
27
+ protected emit<K extends keyof Events>(event: K, ...args: Events[K]): boolean;
28
+ /**
29
+ * Remove all listeners for an event, or all listeners if no event specified
30
+ */
31
+ removeAllListeners<K extends keyof Events>(event?: K): this;
32
+ /**
33
+ * Get the number of listeners for an event
34
+ */
35
+ listenerCount<K extends keyof Events>(event: K): number;
36
+ /**
37
+ * Get all event names with registered listeners
38
+ */
39
+ eventNames(): (keyof Events)[];
40
+ }
41
+
42
+ interface PushFloClientEvents {
43
+ [key: string]: unknown[];
44
+ connected: [ConnectionInfo];
45
+ disconnected: [reason?: string];
46
+ message: [Message];
47
+ error: [Error];
48
+ }
49
+ /**
50
+ * Browser client for PushFlo real-time messaging
51
+ */
52
+ declare class PushFloClient extends TypedEventEmitter<PushFloClientEvents> {
53
+ private readonly wsManager;
54
+ private readonly subscriptions;
55
+ private readonly logger;
56
+ private connectionChangeListeners;
57
+ constructor(options: ClientOptions);
58
+ /**
59
+ * Get current connection state
60
+ */
61
+ get connectionState(): ConnectionState;
62
+ /**
63
+ * Get client ID (available after connected)
64
+ */
65
+ get clientId(): string | null;
66
+ /**
67
+ * Connect to PushFlo
68
+ */
69
+ connect(): Promise<void>;
70
+ /**
71
+ * Disconnect from PushFlo
72
+ */
73
+ disconnect(): void;
74
+ /**
75
+ * Clean up all resources
76
+ */
77
+ destroy(): void;
78
+ /**
79
+ * Subscribe to a channel
80
+ */
81
+ subscribe(channel: string, options?: SubscriptionOptions): Subscription;
82
+ /**
83
+ * Unsubscribe from a channel
84
+ */
85
+ unsubscribe(channel: string): void;
86
+ /**
87
+ * Register a connection state change listener
88
+ */
89
+ onConnectionChange(listener: (state: ConnectionState) => void): () => void;
90
+ /**
91
+ * Get list of subscribed channels
92
+ */
93
+ getSubscribedChannels(): string[];
94
+ /**
95
+ * Check if subscribed to a channel
96
+ */
97
+ isSubscribed(channel: string): boolean;
98
+ private setupEventHandlers;
99
+ private handleServerMessage;
100
+ }
101
+
102
+ export { PushFloClient as P };