@devo-bmad-custom/agent-orchestration 1.0.6 → 1.0.7

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.
Files changed (63) hide show
  1. package/package.json +4 -2
  2. package/src/.agents/skills/tmux-commands/SKILL.md +1 -1
  3. package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -0
  4. package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -0
  5. package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -0
  6. package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -0
  7. package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -0
  8. package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -0
  9. package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -0
  10. package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +58 -0
  11. package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -0
  12. package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +100 -0
  13. package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -0
  14. package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -0
  15. package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -0
  16. package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -0
  17. package/src/.agents/skills/ux-audit/SKILL.md +151 -0
  18. package/src/.agents/skills/websocket-engineer/SKILL.md +168 -0
  19. package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -0
  20. package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -0
  21. package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -0
  22. package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -0
  23. package/src/.agents/skills/websocket-engineer/references/security.md +474 -0
  24. package/src/.agents/skills/writing-skills/SKILL.md +655 -0
  25. package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -0
  26. package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  27. package/src/.agents/skills/writing-skills/graphviz-conventions.dot +172 -0
  28. package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -0
  29. package/src/.agents/skills/writing-skills/render-graphs.js +168 -0
  30. package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  31. package/src/.claude/commands/bmad-track-compact.md +19 -0
  32. package/src/.claude/commands/bmad-track-extended.md +19 -0
  33. package/src/.claude/commands/bmad-track-large.md +19 -0
  34. package/src/.claude/commands/bmad-track-medium.md +19 -0
  35. package/src/.claude/commands/bmad-track-nano.md +19 -0
  36. package/src/.claude/commands/bmad-track-rv.md +18 -0
  37. package/src/.claude/commands/bmad-track-small.md +19 -0
  38. package/src/.claude/commands/master-orchestrator.md +15 -0
  39. package/src/_memory/master-orchestrator-sidecar/docs-index.md +3 -0
  40. package/src/_memory/master-orchestrator-sidecar/instructions.md +2616 -0
  41. package/src/_memory/master-orchestrator-sidecar/memories.md +8 -0
  42. package/src/_memory/master-orchestrator-sidecar/session-state.md +15 -0
  43. package/src/_memory/master-orchestrator-sidecar/triage-history.md +3 -0
  44. package/src/_memory/master-orchestrator-sidecar/workflows-overview.html +1230 -0
  45. package/src/core/agents/master-orchestrator.md +54 -0
  46. package/src/docs/dev/tmux/actions_popup.py +291 -0
  47. package/src/docs/dev/tmux/actions_popup.sh +110 -0
  48. package/src/docs/dev/tmux/claude_usage.sh +15 -0
  49. package/src/docs/dev/tmux/colors.conf +26 -0
  50. package/src/docs/dev/tmux/cpu_usage.sh +7 -0
  51. package/src/docs/dev/tmux/dispatch.sh +10 -0
  52. package/src/docs/dev/tmux/float_init.sh +13 -0
  53. package/src/docs/dev/tmux/float_term.sh +23 -0
  54. package/src/docs/dev/tmux/open_clip.sh +14 -0
  55. package/src/docs/dev/tmux/paste_claude.sh +26 -0
  56. package/src/docs/dev/tmux/paste_clipboard.sh +13 -0
  57. package/src/docs/dev/tmux/paste_image_wrapper.sh +98 -0
  58. package/src/docs/dev/tmux/ram_usage.sh +3 -0
  59. package/src/docs/dev/tmux/title_sync.sh +54 -0
  60. package/src/docs/dev/tmux/tmux-setup.md +867 -0
  61. package/src/docs/dev/tmux/tmux-test.sh +255 -0
  62. package/src/docs/dev/tmux/tmux.conf +127 -0
  63. package/src/docs/dev/tmux/xclip +18 -0
@@ -0,0 +1,400 @@
1
+ # WebSocket Patterns Reference
2
+
3
+ ## Rooms and Namespaces
4
+
5
+ ### Rooms (Channel Grouping)
6
+
7
+ ```javascript
8
+ const io = require('socket.io')(3000);
9
+
10
+ io.on('connection', (socket) => {
11
+ // Join a room
12
+ socket.on('join-room', (roomId) => {
13
+ socket.join(roomId);
14
+ socket.emit('joined', { room: roomId });
15
+
16
+ // Notify others in room
17
+ socket.to(roomId).emit('user-joined', {
18
+ userId: socket.id,
19
+ timestamp: Date.now()
20
+ });
21
+ });
22
+
23
+ // Leave a room
24
+ socket.on('leave-room', (roomId) => {
25
+ socket.leave(roomId);
26
+ socket.to(roomId).emit('user-left', { userId: socket.id });
27
+ });
28
+
29
+ // Send to specific room
30
+ socket.on('message', ({ roomId, text }) => {
31
+ io.to(roomId).emit('message', {
32
+ userId: socket.id,
33
+ text,
34
+ timestamp: Date.now()
35
+ });
36
+ });
37
+
38
+ // Get all rooms a socket is in
39
+ console.log('Socket rooms:', socket.rooms); // Set { socketId, roomId1, roomId2 }
40
+
41
+ // Disconnect from all rooms
42
+ socket.on('disconnect', () => {
43
+ // Automatically leaves all rooms
44
+ });
45
+ });
46
+
47
+ // Broadcast to all sockets in a room
48
+ io.to('room123').emit('announcement', 'Hello room!');
49
+
50
+ // Broadcast to multiple rooms
51
+ io.to('room1').to('room2').emit('multi-room', 'data');
52
+
53
+ // Broadcast to room except specific socket
54
+ socket.to('room123').emit('message', 'Others see this');
55
+
56
+ // Get all sockets in a room
57
+ const sockets = await io.in('room123').fetchSockets();
58
+ console.log(`Room has ${sockets.length} connections`);
59
+ ```
60
+
61
+ ### Namespaces (Logical Separation)
62
+
63
+ ```javascript
64
+ // Admin namespace
65
+ const adminNs = io.of('/admin');
66
+ adminNs.on('connection', (socket) => {
67
+ console.log('Admin connected:', socket.id);
68
+
69
+ socket.on('admin-action', (data) => {
70
+ // Admin-only events
71
+ });
72
+ });
73
+
74
+ // Chat namespace
75
+ const chatNs = io.of('/chat');
76
+ chatNs.on('connection', (socket) => {
77
+ console.log('Chat user connected:', socket.id);
78
+
79
+ socket.on('message', (msg) => {
80
+ chatNs.emit('message', msg); // Broadcast to all in /chat
81
+ });
82
+ });
83
+
84
+ // Dynamic namespaces
85
+ io.of(/^\/workspace-\d+$/).on('connection', (socket) => {
86
+ const namespace = socket.nsp;
87
+ console.log(`Connected to ${namespace.name}`);
88
+
89
+ socket.on('message', (data) => {
90
+ namespace.emit('message', data);
91
+ });
92
+ });
93
+ ```
94
+
95
+ ## Broadcasting Patterns
96
+
97
+ ```javascript
98
+ // Broadcast to everyone including sender
99
+ io.emit('event', data);
100
+
101
+ // Broadcast to everyone except sender
102
+ socket.broadcast.emit('event', data);
103
+
104
+ // Broadcast to specific room
105
+ io.to('room1').emit('event', data);
106
+
107
+ // Broadcast to room except sender
108
+ socket.to('room1').emit('event', data);
109
+
110
+ // Broadcast to multiple rooms
111
+ io.to('room1').to('room2').emit('event', data);
112
+
113
+ // Broadcast to all connected clients in namespace
114
+ io.of('/namespace').emit('event', data);
115
+
116
+ // Volatile messages (ok to drop if client not ready)
117
+ socket.volatile.emit('high-frequency', data);
118
+
119
+ // Broadcast with acknowledgment (to all clients)
120
+ io.timeout(5000).emit('event', data, (err, responses) => {
121
+ if (err) {
122
+ console.log('Some clients did not acknowledge');
123
+ } else {
124
+ console.log('All clients acknowledged:', responses);
125
+ }
126
+ });
127
+ ```
128
+
129
+ ## Acknowledgments
130
+
131
+ ```javascript
132
+ // Server expects acknowledgment
133
+ socket.emit('question', 'Do you agree?', (answer) => {
134
+ console.log('Client answered:', answer);
135
+ });
136
+
137
+ // Client sends acknowledgment
138
+ socket.on('question', (data, callback) => {
139
+ console.log('Server asked:', data);
140
+ callback('Yes, I agree');
141
+ });
142
+
143
+ // Timeout for acknowledgment
144
+ socket.timeout(5000).emit('request', data, (err, response) => {
145
+ if (err) {
146
+ console.log('Client did not acknowledge in time');
147
+ } else {
148
+ console.log('Got response:', response);
149
+ }
150
+ });
151
+
152
+ // Error handling in acknowledgment
153
+ socket.on('save-data', (data, callback) => {
154
+ try {
155
+ saveToDatabase(data);
156
+ callback({ success: true });
157
+ } catch (error) {
158
+ callback({ success: false, error: error.message });
159
+ }
160
+ });
161
+ ```
162
+
163
+ ## Presence System
164
+
165
+ ```javascript
166
+ const redis = require('ioredis');
167
+ const redisClient = new redis();
168
+
169
+ class PresenceManager {
170
+ async userConnected(userId, socketId) {
171
+ const key = `user:${userId}:sockets`;
172
+
173
+ // Add socket to user's socket set
174
+ await redisClient.sadd(key, socketId);
175
+
176
+ // Set TTL to auto-cleanup stale connections
177
+ await redisClient.expire(key, 3600);
178
+
179
+ // Get total connections for user
180
+ const socketCount = await redisClient.scard(key);
181
+
182
+ // If first connection, mark user as online
183
+ if (socketCount === 1) {
184
+ await redisClient.hset('presence', userId, JSON.stringify({
185
+ status: 'online',
186
+ lastSeen: Date.now()
187
+ }));
188
+
189
+ // Notify friends
190
+ const friends = await this.getUserFriends(userId);
191
+ friends.forEach(friendId => {
192
+ io.to(`user:${friendId}`).emit('presence', {
193
+ userId,
194
+ status: 'online'
195
+ });
196
+ });
197
+ }
198
+
199
+ return socketCount;
200
+ }
201
+
202
+ async userDisconnected(userId, socketId) {
203
+ const key = `user:${userId}:sockets`;
204
+
205
+ // Remove socket from set
206
+ await redisClient.srem(key, socketId);
207
+
208
+ const socketCount = await redisClient.scard(key);
209
+
210
+ // If no more connections, mark offline
211
+ if (socketCount === 0) {
212
+ await redisClient.hset('presence', userId, JSON.stringify({
213
+ status: 'offline',
214
+ lastSeen: Date.now()
215
+ }));
216
+
217
+ const friends = await this.getUserFriends(userId);
218
+ friends.forEach(friendId => {
219
+ io.to(`user:${friendId}`).emit('presence', {
220
+ userId,
221
+ status: 'offline',
222
+ lastSeen: Date.now()
223
+ });
224
+ });
225
+ }
226
+
227
+ return socketCount;
228
+ }
229
+
230
+ async getUserStatus(userId) {
231
+ const data = await redisClient.hget('presence', userId);
232
+ return data ? JSON.parse(data) : { status: 'offline' };
233
+ }
234
+
235
+ async getBulkPresence(userIds) {
236
+ const pipeline = redisClient.pipeline();
237
+ userIds.forEach(id => pipeline.hget('presence', id));
238
+
239
+ const results = await pipeline.exec();
240
+ return userIds.map((id, i) => ({
241
+ userId: id,
242
+ ...JSON.parse(results[i][1] || '{"status":"offline"}')
243
+ }));
244
+ }
245
+ }
246
+
247
+ // Usage
248
+ const presence = new PresenceManager();
249
+
250
+ io.on('connection', async (socket) => {
251
+ const userId = socket.handshake.auth.userId;
252
+
253
+ await presence.userConnected(userId, socket.id);
254
+ socket.join(`user:${userId}`);
255
+
256
+ socket.on('disconnect', async () => {
257
+ await presence.userDisconnected(userId, socket.id);
258
+ });
259
+
260
+ // Get presence for user's friends
261
+ socket.on('get-presence', async (friendIds, callback) => {
262
+ const presenceData = await presence.getBulkPresence(friendIds);
263
+ callback(presenceData);
264
+ });
265
+ });
266
+ ```
267
+
268
+ ## Message Queue Pattern
269
+
270
+ ```javascript
271
+ // Queue messages when client disconnected
272
+ const messageQueue = new Map();
273
+
274
+ io.on('connection', (socket) => {
275
+ const userId = socket.handshake.auth.userId;
276
+
277
+ // Deliver queued messages on connect
278
+ const queuedMessages = messageQueue.get(userId) || [];
279
+ if (queuedMessages.length > 0) {
280
+ socket.emit('queued-messages', queuedMessages);
281
+ messageQueue.delete(userId);
282
+ }
283
+
284
+ socket.on('disconnect', () => {
285
+ // Mark user as disconnected
286
+ setTimeout(() => {
287
+ const userSockets = io.sockets.sockets;
288
+ const hasOtherConnection = Array.from(userSockets.values())
289
+ .some(s => s.handshake.auth.userId === userId);
290
+
291
+ if (!hasOtherConnection) {
292
+ // User fully disconnected, queue new messages
293
+ messageQueue.set(userId, []);
294
+ }
295
+ }, 1000);
296
+ });
297
+ });
298
+
299
+ // Queue message if user offline
300
+ async function sendMessage(userId, message) {
301
+ const userOnline = await isUserOnline(userId);
302
+
303
+ if (userOnline) {
304
+ io.to(`user:${userId}`).emit('message', message);
305
+ } else {
306
+ const queue = messageQueue.get(userId) || [];
307
+ queue.push(message);
308
+ messageQueue.set(userId, queue);
309
+
310
+ // Persist to database for longer-term storage
311
+ await saveMessageToDb(userId, message);
312
+ }
313
+ }
314
+ ```
315
+
316
+ ## Pub/Sub Pattern
317
+
318
+ ```javascript
319
+ const EventEmitter = require('events');
320
+
321
+ class MessageBus extends EventEmitter {
322
+ constructor(io, redis) {
323
+ super();
324
+ this.io = io;
325
+ this.redis = redis;
326
+ this.setupSubscriptions();
327
+ }
328
+
329
+ setupSubscriptions() {
330
+ // Subscribe to Redis channels
331
+ this.redis.psubscribe('room:*', (err, count) => {
332
+ console.log(`Subscribed to ${count} channels`);
333
+ });
334
+
335
+ this.redis.on('pmessage', (pattern, channel, message) => {
336
+ const data = JSON.parse(message);
337
+ const roomId = channel.split(':')[1];
338
+
339
+ // Emit to Socket.IO room
340
+ this.io.to(roomId).emit(data.event, data.payload);
341
+ });
342
+ }
343
+
344
+ publish(roomId, event, payload) {
345
+ // Publish to Redis (distributed across servers)
346
+ this.redis.publish(
347
+ `room:${roomId}`,
348
+ JSON.stringify({ event, payload })
349
+ );
350
+ }
351
+ }
352
+
353
+ // Usage
354
+ const messageBus = new MessageBus(io, redisClient);
355
+
356
+ io.on('connection', (socket) => {
357
+ socket.on('send-message', ({ roomId, text }) => {
358
+ // Publish to all servers via Redis
359
+ messageBus.publish(roomId, 'message', {
360
+ userId: socket.id,
361
+ text,
362
+ timestamp: Date.now()
363
+ });
364
+ });
365
+ });
366
+ ```
367
+
368
+ ## Backpressure Handling
369
+
370
+ ```javascript
371
+ io.on('connection', (socket) => {
372
+ const MAX_BUFFER_SIZE = 10000;
373
+ let bufferSize = 0;
374
+
375
+ const originalEmit = socket.emit.bind(socket);
376
+
377
+ socket.emit = function(event, ...args) {
378
+ bufferSize++;
379
+
380
+ if (bufferSize > MAX_BUFFER_SIZE) {
381
+ console.warn('Buffer overflow, dropping message');
382
+ return false;
383
+ }
384
+
385
+ const result = originalEmit(event, ...args);
386
+
387
+ // Track buffer drain
388
+ socket.once('drain', () => {
389
+ bufferSize = 0;
390
+ });
391
+
392
+ return result;
393
+ };
394
+
395
+ // Monitor buffer size
396
+ socket.on('drain', () => {
397
+ console.log('Socket buffer drained');
398
+ });
399
+ });
400
+ ```
@@ -0,0 +1,195 @@
1
+ # WebSocket Protocol Reference
2
+
3
+ ## Protocol Basics
4
+
5
+ ### Handshake Process
6
+
7
+ ```
8
+ Client → Server: HTTP Upgrade Request
9
+ GET /socket.io/?EIO=4&transport=websocket HTTP/1.1
10
+ Host: example.com
11
+ Upgrade: websocket
12
+ Connection: Upgrade
13
+ Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
14
+ Sec-WebSocket-Version: 13
15
+
16
+ Server → Client: HTTP 101 Switching Protocols
17
+ HTTP/1.1 101 Switching Protocols
18
+ Upgrade: websocket
19
+ Connection: Upgrade
20
+ Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
21
+ ```
22
+
23
+ ### Frame Structure
24
+
25
+ ```
26
+ 0 1 2 3
27
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
28
+ +-+-+-+-+-------+-+-------------+-------------------------------+
29
+ |F|R|R|R| opcode|M| Payload len | Extended payload length |
30
+ |I|S|S|S| (4) |A| (7) | (16/64) |
31
+ |N|V|V|V| |S| | (if payload len==126/127) |
32
+ | |1|2|3| |K| | |
33
+ +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
34
+ | Extended payload length continued, if payload len == 127 |
35
+ + - - - - - - - - - - - - - - - +-------------------------------+
36
+ | |Masking-key, if MASK set to 1 |
37
+ +-------------------------------+-------------------------------+
38
+ | Masking-key (continued) | Payload Data |
39
+ +-------------------------------- - - - - - - - - - - - - - - - +
40
+ : Payload Data continued ... :
41
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
42
+ | Payload Data continued ... |
43
+ +---------------------------------------------------------------+
44
+ ```
45
+
46
+ ## Opcodes
47
+
48
+ ```javascript
49
+ const OPCODES = {
50
+ CONTINUATION: 0x0, // Continuation frame
51
+ TEXT: 0x1, // Text frame
52
+ BINARY: 0x2, // Binary frame
53
+ CLOSE: 0x8, // Connection close
54
+ PING: 0x9, // Heartbeat ping
55
+ PONG: 0xA // Heartbeat pong
56
+ };
57
+ ```
58
+
59
+ ## Ping/Pong Mechanism
60
+
61
+ ```javascript
62
+ // Server-side ping/pong with ws library
63
+ const WebSocket = require('ws');
64
+ const wss = new WebSocket.Server({ port: 8080 });
65
+
66
+ wss.on('connection', (ws) => {
67
+ ws.isAlive = true;
68
+
69
+ ws.on('pong', () => {
70
+ ws.isAlive = true;
71
+ });
72
+
73
+ ws.on('message', (data) => {
74
+ console.log('Received:', data);
75
+ });
76
+ });
77
+
78
+ // Ping clients every 30 seconds
79
+ const interval = setInterval(() => {
80
+ wss.clients.forEach((ws) => {
81
+ if (ws.isAlive === false) {
82
+ return ws.terminate();
83
+ }
84
+
85
+ ws.isAlive = false;
86
+ ws.ping(); // Send ping frame
87
+ });
88
+ }, 30000);
89
+
90
+ wss.on('close', () => {
91
+ clearInterval(interval);
92
+ });
93
+ ```
94
+
95
+ ## Close Codes
96
+
97
+ ```javascript
98
+ const CLOSE_CODES = {
99
+ 1000: 'Normal Closure',
100
+ 1001: 'Going Away',
101
+ 1002: 'Protocol Error',
102
+ 1003: 'Unsupported Data',
103
+ 1005: 'No Status Received',
104
+ 1006: 'Abnormal Closure',
105
+ 1007: 'Invalid Payload',
106
+ 1008: 'Policy Violation',
107
+ 1009: 'Message Too Big',
108
+ 1010: 'Mandatory Extension',
109
+ 1011: 'Internal Server Error',
110
+ 1015: 'TLS Handshake Fail'
111
+ };
112
+
113
+ // Proper close handling
114
+ ws.close(1000, 'Normal closure');
115
+ ```
116
+
117
+ ## Message Size Limits
118
+
119
+ ```javascript
120
+ // Set max payload size (default 100MB)
121
+ const wss = new WebSocket.Server({
122
+ port: 8080,
123
+ maxPayload: 1024 * 1024 // 1MB
124
+ });
125
+
126
+ // Handle too large messages
127
+ ws.on('error', (error) => {
128
+ if (error.message.includes('Max payload size exceeded')) {
129
+ ws.close(1009, 'Message too big');
130
+ }
131
+ });
132
+ ```
133
+
134
+ ## Compression (permessage-deflate)
135
+
136
+ ```javascript
137
+ const wss = new WebSocket.Server({
138
+ port: 8080,
139
+ perMessageDeflate: {
140
+ zlibDeflateOptions: {
141
+ chunkSize: 1024,
142
+ memLevel: 7,
143
+ level: 3
144
+ },
145
+ zlibInflateOptions: {
146
+ chunkSize: 10 * 1024
147
+ },
148
+ clientNoContextTakeover: true,
149
+ serverNoContextTakeover: true,
150
+ serverMaxWindowBits: 10,
151
+ concurrencyLimit: 10,
152
+ threshold: 1024 // Only compress messages > 1KB
153
+ }
154
+ });
155
+ ```
156
+
157
+ ## Binary Data Handling
158
+
159
+ ```javascript
160
+ // Send binary data
161
+ const buffer = Buffer.from([0x00, 0x01, 0x02, 0x03]);
162
+ ws.send(buffer, { binary: true });
163
+
164
+ // Receive binary data
165
+ ws.on('message', (data) => {
166
+ if (data instanceof Buffer) {
167
+ console.log('Received binary:', data);
168
+ } else {
169
+ console.log('Received text:', data.toString());
170
+ }
171
+ });
172
+
173
+ // ArrayBuffer in browser
174
+ socket.binaryType = 'arraybuffer';
175
+ socket.onmessage = (event) => {
176
+ if (event.data instanceof ArrayBuffer) {
177
+ const view = new Uint8Array(event.data);
178
+ console.log('Received:', view);
179
+ }
180
+ };
181
+ ```
182
+
183
+ ## Protocol Comparison
184
+
185
+ | Feature | WebSocket | Socket.IO |
186
+ |---------|-----------|-----------|
187
+ | Protocol | Native WS | WS + fallbacks |
188
+ | Handshake | HTTP Upgrade | Engine.IO handshake |
189
+ | Reconnection | Manual | Automatic |
190
+ | Broadcasting | Manual | Built-in |
191
+ | Rooms | Manual | Built-in |
192
+ | Acknowledgments | Manual | Built-in |
193
+ | Binary | Native | Converted |
194
+ | Overhead | Minimal | Higher |
195
+ | Fallback | None | Long polling, SSE |