@ceylonitsolutions/pushstream-js 1.0.0 → 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/README.md CHANGED
@@ -1 +1,149 @@
1
- # pushstream-js
1
+ # PushStream JavaScript SDK
2
+
3
+ Real-time messaging SDK for JavaScript/Node.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @ceylonitsolutions/pushstream-js
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Browser
14
+
15
+ ```html
16
+ <script src="pushstream.js"></script>
17
+ <script>
18
+ const client = new PushStream('your-app-key');
19
+
20
+ client.connect().then(socketId => {
21
+ console.log('Connected:', socketId);
22
+
23
+ const channel = client.subscribe('my-channel');
24
+ channel.bind('my-event', (data) => {
25
+ console.log('Received:', data);
26
+ });
27
+ });
28
+ </script>
29
+ ```
30
+
31
+ ### Node.js
32
+
33
+ ```javascript
34
+ const PushStream = require('@ceylonitsolutions/pushstream-js');
35
+
36
+ const client = new PushStream('your-app-key');
37
+
38
+ client.connect().then(socketId => {
39
+ console.log('Connected:', socketId);
40
+
41
+ const channel = client.subscribe('my-channel');
42
+ channel.bind('my-event', (data) => {
43
+ console.log('Received:', data);
44
+ });
45
+ });
46
+ ```
47
+
48
+ ## Configuration
49
+
50
+ ```javascript
51
+ const client = new PushStream('your-app-key', {
52
+ wsUrl: 'wss://ws.pushstream.ceylonitsolutions.online',
53
+ apiUrl: 'https://api.pushstream.ceylonitsolutions.online'
54
+ });
55
+ ```
56
+
57
+ ## API Reference
58
+
59
+ ### Client Methods
60
+
61
+ #### `connect()`
62
+ Establishes WebSocket connection.
63
+
64
+ ```javascript
65
+ client.connect()
66
+ .then(socketId => console.log('Connected'))
67
+ .catch(error => console.error('Failed to connect'));
68
+ ```
69
+
70
+ #### `subscribe(channelName)`
71
+ Subscribe to a channel.
72
+
73
+ ```javascript
74
+ const channel = client.subscribe('my-channel');
75
+ ```
76
+
77
+ #### `unsubscribe(channelName)`
78
+ Unsubscribe from a channel.
79
+
80
+ ```javascript
81
+ client.unsubscribe('my-channel');
82
+ ```
83
+
84
+ #### `disconnect()`
85
+ Close the connection.
86
+
87
+ ```javascript
88
+ client.disconnect();
89
+ ```
90
+
91
+ #### `publish(appId, appSecret, channel, event, data)`
92
+ Publish events via REST API.
93
+
94
+ ```javascript
95
+ await client.publish(
96
+ 'app-id',
97
+ 'app-secret',
98
+ 'my-channel',
99
+ 'my-event',
100
+ { message: 'Hello' }
101
+ );
102
+ ```
103
+
104
+ ### Channel Methods
105
+
106
+ #### `bind(event, callback)`
107
+ Listen for events on the channel.
108
+
109
+ ```javascript
110
+ channel.bind('my-event', (data) => {
111
+ console.log(data);
112
+ });
113
+ ```
114
+
115
+ #### `unbind(event, callback)`
116
+ Remove event listener.
117
+
118
+ ```javascript
119
+ channel.unbind('my-event', callback);
120
+ ```
121
+
122
+ #### `unsubscribe()`
123
+ Unsubscribe from the channel.
124
+
125
+ ```javascript
126
+ channel.unsubscribe();
127
+ ```
128
+
129
+ ## Features
130
+
131
+ - WebSocket real-time messaging
132
+ - Automatic reconnection with exponential backoff
133
+ - Channel subscriptions
134
+ - Event binding
135
+ - REST API publishing
136
+ - Browser and Node.js support
137
+
138
+ ## Requirements
139
+
140
+ - Node.js >= 14.0.0 (for Node.js usage)
141
+ - Modern browser with WebSocket support
142
+
143
+ ## License
144
+
145
+ MIT
146
+
147
+ ## Author
148
+
149
+ Ceylon IT Solutions
@@ -0,0 +1,225 @@
1
+ class PushStream {
2
+ constructor(appKey, options = {}) {
3
+ this.appKey = appKey;
4
+ this.wsUrl = options.wsUrl || 'wss://ws.pushstream.ceylonitsolutions.online';
5
+ this.apiUrl = options.apiUrl || 'https://api.pushstream.ceylonitsolutions.online';
6
+ this.ws = null;
7
+ this.socketId = null;
8
+ this.channels = new Map();
9
+ this.reconnectAttempts = 0;
10
+ this.maxReconnectAttempts = 5;
11
+ }
12
+
13
+ connect() {
14
+ return new Promise((resolve, reject) => {
15
+ try {
16
+ this.ws = new WebSocket(this.wsUrl);
17
+ } catch (error) {
18
+ reject(error);
19
+ this.attemptReconnect();
20
+ return;
21
+ }
22
+
23
+ const timeout = setTimeout(() => {
24
+ if (!this.socketId) {
25
+ reject(new Error('Connection timeout'));
26
+ this.attemptReconnect();
27
+ }
28
+ }, 10000);
29
+
30
+ this.ws.onopen = () => {
31
+ console.log('[PushStream] Connected');
32
+ this.reconnectAttempts = 0;
33
+ };
34
+
35
+ this.ws.onmessage = (event) => {
36
+ const message = JSON.parse(event.data);
37
+
38
+ if (message.event === 'pusher:connection_established') {
39
+ const data = JSON.parse(message.data);
40
+ this.socketId = data.socket_id;
41
+ clearTimeout(timeout);
42
+ resolve(this.socketId);
43
+ } else if (message.event === 'pusher:error') {
44
+ console.error('[PushStream] Error:', message.data);
45
+ } else {
46
+ this.handleMessage(message);
47
+ }
48
+ };
49
+
50
+ this.ws.onclose = () => {
51
+ console.log('[PushStream] Disconnected');
52
+ clearTimeout(timeout);
53
+ this.socketId = null;
54
+ this.attemptReconnect();
55
+ };
56
+
57
+ this.ws.onerror = (error) => {
58
+ console.error('[PushStream] WebSocket error:', error);
59
+ clearTimeout(timeout);
60
+ if (!this.socketId) reject(error);
61
+ };
62
+ });
63
+ }
64
+
65
+ attemptReconnect() {
66
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
67
+ console.error('[PushStream] Max reconnection attempts reached');
68
+ return;
69
+ }
70
+
71
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
72
+ this.reconnectAttempts++;
73
+
74
+ console.log(`[PushStream] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
75
+ setTimeout(() => this.connect(), delay);
76
+ }
77
+
78
+ subscribe(channelName, callbacks = {}) {
79
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
80
+ throw new Error('Not connected');
81
+ }
82
+
83
+ const channel = new Channel(channelName, this, callbacks);
84
+ this.channels.set(channelName, channel);
85
+
86
+ this.send({
87
+ event: 'pusher:subscribe',
88
+ data: { channel: channelName }
89
+ });
90
+
91
+ return channel;
92
+ }
93
+
94
+ unsubscribe(channelName) {
95
+ this.send({
96
+ event: 'pusher:unsubscribe',
97
+ data: { channel: channelName }
98
+ });
99
+ this.channels.delete(channelName);
100
+ }
101
+
102
+ send(data) {
103
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
104
+ this.ws.send(JSON.stringify(data));
105
+ }
106
+ }
107
+
108
+ handleMessage(message) {
109
+ const channel = this.channels.get(message.channel);
110
+ if (channel) {
111
+ channel.handleEvent(message.event, JSON.parse(message.data));
112
+ }
113
+ }
114
+
115
+ disconnect() {
116
+ if (this.ws) {
117
+ this.ws.close();
118
+ this.ws = null;
119
+ }
120
+ }
121
+
122
+ // REST API methods
123
+ async publish(appId, appSecret, channel, event, data) {
124
+ const timestamp = Math.floor(Date.now() / 1000);
125
+ const body = JSON.stringify({ name: event, channel, data });
126
+ const path = `/api/apps/${appId}/events`;
127
+ const queryString = `auth_timestamp=${timestamp}`;
128
+ const stringToSign = `POST\n${path}\n${queryString}\n${body}`;
129
+
130
+ const signature = await this.hmacSha256(stringToSign, appSecret);
131
+ const authHeader = `${appId}:${signature}`;
132
+
133
+ const response = await fetch(`${this.apiUrl}${path}?${queryString}`, {
134
+ method: 'POST',
135
+ headers: {
136
+ 'Authorization': authHeader,
137
+ 'Content-Type': 'application/json'
138
+ },
139
+ body
140
+ });
141
+
142
+ if (!response.ok) {
143
+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
144
+ }
145
+
146
+ return response.json();
147
+ }
148
+
149
+ async hmacSha256(message, secret) {
150
+ // Node.js
151
+ if (typeof window === 'undefined') {
152
+ const crypto = require('crypto');
153
+ return crypto.createHmac('sha256', secret).update(message).digest('hex');
154
+ }
155
+
156
+ // Browser
157
+ const encoder = new TextEncoder();
158
+ const keyData = encoder.encode(secret);
159
+ const messageData = encoder.encode(message);
160
+
161
+ const key = await crypto.subtle.importKey(
162
+ 'raw',
163
+ keyData,
164
+ { name: 'HMAC', hash: 'SHA-256' },
165
+ false,
166
+ ['sign']
167
+ );
168
+
169
+ const signature = await crypto.subtle.sign('HMAC', key, messageData);
170
+ return Array.from(new Uint8Array(signature))
171
+ .map(b => b.toString(16).padStart(2, '0'))
172
+ .join('');
173
+ }
174
+ }
175
+
176
+ class Channel {
177
+ constructor(name, client, callbacks = {}) {
178
+ this.name = name;
179
+ this.client = client;
180
+ this.callbacks = callbacks;
181
+ this.eventHandlers = new Map();
182
+ }
183
+
184
+ bind(event, callback) {
185
+ if (!this.eventHandlers.has(event)) {
186
+ this.eventHandlers.set(event, []);
187
+ }
188
+ this.eventHandlers.get(event).push(callback);
189
+ return this;
190
+ }
191
+
192
+ unbind(event, callback) {
193
+ if (!this.eventHandlers.has(event)) return;
194
+
195
+ if (callback) {
196
+ const handlers = this.eventHandlers.get(event);
197
+ const index = handlers.indexOf(callback);
198
+ if (index > -1) handlers.splice(index, 1);
199
+ } else {
200
+ this.eventHandlers.delete(event);
201
+ }
202
+ return this;
203
+ }
204
+
205
+ handleEvent(event, data) {
206
+ const handlers = this.eventHandlers.get(event);
207
+ if (handlers) {
208
+ handlers.forEach(handler => handler(data));
209
+ }
210
+ }
211
+
212
+ unsubscribe() {
213
+ this.client.unsubscribe(this.name);
214
+ }
215
+ }
216
+
217
+ // Node.js support
218
+ if (typeof module !== 'undefined' && module.exports) {
219
+ module.exports = PushStream;
220
+ }
221
+
222
+ // Browser support
223
+ if (typeof window !== 'undefined') {
224
+ window.PushStream = PushStream;
225
+ }
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@ceylonitsolutions/pushstream-js",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "PushStream JavaScript SDK for real-time messaging",
5
5
  "main": "pushstream.js",
6
+ "browser": "dist/pushstream.min.js",
7
+ "files": [
8
+ "pushstream.js",
9
+ "dist/pushstream.min.js"
10
+ ],
6
11
  "scripts": {
7
12
  "test": "echo \"Error: no test specified\" && exit 1"
8
13
  },