@neyugn/agent-kits 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +514 -0
- package/README.vi.md +410 -0
- package/README.zh.md +410 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +422 -0
- package/kits/coder/ARCHITECTURE.md +289 -0
- package/kits/coder/agents/ai-engineer.md +344 -0
- package/kits/coder/agents/backend-specialist.md +270 -0
- package/kits/coder/agents/cloud-architect.md +363 -0
- package/kits/coder/agents/code-reviewer.md +284 -0
- package/kits/coder/agents/data-engineer.md +401 -0
- package/kits/coder/agents/database-specialist.md +251 -0
- package/kits/coder/agents/debugger.md +209 -0
- package/kits/coder/agents/devops-engineer.md +281 -0
- package/kits/coder/agents/documentation-writer.md +296 -0
- package/kits/coder/agents/frontend-specialist.md +298 -0
- package/kits/coder/agents/i18n-specialist.md +348 -0
- package/kits/coder/agents/integration-specialist.md +314 -0
- package/kits/coder/agents/mobile-developer.md +271 -0
- package/kits/coder/agents/multi-tenant-architect.md +281 -0
- package/kits/coder/agents/orchestrator.md +263 -0
- package/kits/coder/agents/performance-analyst.md +327 -0
- package/kits/coder/agents/project-planner.md +277 -0
- package/kits/coder/agents/queue-specialist.md +282 -0
- package/kits/coder/agents/realtime-specialist.md +267 -0
- package/kits/coder/agents/security-auditor.md +253 -0
- package/kits/coder/agents/test-engineer.md +315 -0
- package/kits/coder/agents/ux-researcher.md +388 -0
- package/kits/coder/rules/.cursorrules +287 -0
- package/kits/coder/rules/CLAUDE.md +287 -0
- package/kits/coder/rules/CODEX.md +287 -0
- package/kits/coder/rules/GEMINI.md +287 -0
- package/kits/coder/scripts/checklist.py +318 -0
- package/kits/coder/scripts/kit_status.py +292 -0
- package/kits/coder/scripts/skills_manager.py +243 -0
- package/kits/coder/scripts/verify_all.py +391 -0
- package/kits/coder/skills/accessibility-patterns/SKILL.md +372 -0
- package/kits/coder/skills/accessibility-patterns/scripts/a11y_checker.py +211 -0
- package/kits/coder/skills/ai-rag-patterns/SKILL.md +444 -0
- package/kits/coder/skills/api-patterns/SKILL.md +316 -0
- package/kits/coder/skills/api-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/api-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/api-patterns/scripts/api_validator.py +253 -0
- package/kits/coder/skills/api-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/auth-patterns/SKILL.md +267 -0
- package/kits/coder/skills/aws-patterns/SKILL.md +576 -0
- package/kits/coder/skills/brainstorming/SKILL.md +370 -0
- package/kits/coder/skills/brainstorming/assets/.gitkeep +1 -0
- package/kits/coder/skills/brainstorming/references/deep-dive.md +21 -0
- package/kits/coder/skills/brainstorming/scripts/validate.py +56 -0
- package/kits/coder/skills/clean-code/SKILL.md +240 -0
- package/kits/coder/skills/clean-code/assets/.gitkeep +1 -0
- package/kits/coder/skills/clean-code/references/deep-dive.md +21 -0
- package/kits/coder/skills/clean-code/scripts/lint_runner.py +186 -0
- package/kits/coder/skills/clean-code/scripts/validate.py +56 -0
- package/kits/coder/skills/database-design/SKILL.md +255 -0
- package/kits/coder/skills/database-design/assets/.gitkeep +1 -0
- package/kits/coder/skills/database-design/references/deep-dive.md +21 -0
- package/kits/coder/skills/database-design/scripts/schema_validator.py +272 -0
- package/kits/coder/skills/database-design/scripts/validate.py +56 -0
- package/kits/coder/skills/docker-patterns/SKILL.md +240 -0
- package/kits/coder/skills/documentation-templates/SKILL.md +441 -0
- package/kits/coder/skills/e2e-testing/SKILL.md +457 -0
- package/kits/coder/skills/flutter-patterns/SKILL.md +330 -0
- package/kits/coder/skills/frontend-design/SKILL.md +127 -0
- package/kits/coder/skills/github-actions/SKILL.md +349 -0
- package/kits/coder/skills/gitlab-ci-patterns/SKILL.md +466 -0
- package/kits/coder/skills/graphql-patterns/SKILL.md +558 -0
- package/kits/coder/skills/i18n-localization/SKILL.md +345 -0
- package/kits/coder/skills/i18n-localization/scripts/i18n_checker.py +267 -0
- package/kits/coder/skills/kubernetes-patterns/SKILL.md +357 -0
- package/kits/coder/skills/mermaid-diagrams/SKILL.md +351 -0
- package/kits/coder/skills/mobile-design/SKILL.md +305 -0
- package/kits/coder/skills/monitoring-observability/SKILL.md +458 -0
- package/kits/coder/skills/multi-tenancy/SKILL.md +317 -0
- package/kits/coder/skills/multi-tenancy/assets/.gitkeep +1 -0
- package/kits/coder/skills/multi-tenancy/references/deep-dive.md +21 -0
- package/kits/coder/skills/multi-tenancy/scripts/validate.py +56 -0
- package/kits/coder/skills/nodejs-best-practices/SKILL.md +220 -0
- package/kits/coder/skills/performance-profiling/SKILL.md +333 -0
- package/kits/coder/skills/performance-profiling/assets/.gitkeep +1 -0
- package/kits/coder/skills/performance-profiling/references/deep-dive.md +21 -0
- package/kits/coder/skills/performance-profiling/scripts/validate.py +56 -0
- package/kits/coder/skills/plan-writing/SKILL.md +360 -0
- package/kits/coder/skills/plan-writing/assets/.gitkeep +1 -0
- package/kits/coder/skills/plan-writing/references/deep-dive.md +21 -0
- package/kits/coder/skills/plan-writing/scripts/validate.py +56 -0
- package/kits/coder/skills/postgres-patterns/SKILL.md +361 -0
- package/kits/coder/skills/prompt-engineering/SKILL.md +277 -0
- package/kits/coder/skills/queue-patterns/SKILL.md +359 -0
- package/kits/coder/skills/queue-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/queue-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/queue-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/react-native-patterns/SKILL.md +393 -0
- package/kits/coder/skills/react-patterns/SKILL.md +319 -0
- package/kits/coder/skills/realtime-patterns/SKILL.md +506 -0
- package/kits/coder/skills/realtime-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/realtime-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/realtime-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/redis-patterns/SKILL.md +484 -0
- package/kits/coder/skills/security-fundamentals/SKILL.md +363 -0
- package/kits/coder/skills/security-fundamentals/assets/.gitkeep +1 -0
- package/kits/coder/skills/security-fundamentals/references/deep-dive.md +21 -0
- package/kits/coder/skills/security-fundamentals/scripts/security_scan.py +326 -0
- package/kits/coder/skills/security-fundamentals/scripts/validate.py +56 -0
- package/kits/coder/skills/seo-patterns/SKILL.md +262 -0
- package/kits/coder/skills/seo-patterns/scripts/seo_checker.py +211 -0
- package/kits/coder/skills/systematic-debugging/SKILL.md +478 -0
- package/kits/coder/skills/systematic-debugging/assets/.gitkeep +1 -0
- package/kits/coder/skills/systematic-debugging/references/deep-dive.md +21 -0
- package/kits/coder/skills/systematic-debugging/scripts/validate.py +56 -0
- package/kits/coder/skills/tailwind-patterns/SKILL.md +395 -0
- package/kits/coder/skills/terraform-patterns/SKILL.md +470 -0
- package/kits/coder/skills/testing-patterns/SKILL.md +285 -0
- package/kits/coder/skills/testing-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/testing-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/kits/coder/skills/testing-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/typescript-patterns/SKILL.md +417 -0
- package/kits/coder/skills/ui-ux-pro-max/SKILL.md +364 -0
- package/kits/coder/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/kits/coder/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/kits/coder/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/kits/coder/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/kits/coder/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/kits/coder/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/kits/coder/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/kits/coder/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/kits/coder/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/kits/coder/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/kits/coder/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/kits/coder/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/core.py +257 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/design_system.py +488 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/search.py +76 -0
- package/kits/coder/workflows/.gitkeep +20 -0
- package/kits/coder/workflows/create.md +152 -0
- package/kits/coder/workflows/debug.md +223 -0
- package/kits/coder/workflows/deploy.md +283 -0
- package/kits/coder/workflows/orchestrate.md +243 -0
- package/kits/coder/workflows/plan.md +134 -0
- package/kits/coder/workflows/test.md +237 -0
- package/kits/coder/workflows/ui-ux-pro-max.md +109 -0
- package/package.json +49 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: realtime-patterns
|
|
3
|
+
description: WebSocket, Socket.IO, and event-driven architecture patterns. Use when building real-time features like chat, notifications, live updates, or collaborative editing. Covers connection management, rooms, scaling, and event design.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Realtime Patterns - Event-Driven Communication Architecture
|
|
8
|
+
|
|
9
|
+
> **Philosophy:** Real-time isn't just fastāit's instantaneous perceived response. Design for resilience, not just speed.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## šÆ Core Principle: Reliable Real-Time
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
ā WRONG: Open WebSocket ā Send data ā Hope it arrives
|
|
17
|
+
ā
CORRECT: Open WebSocket ā Handle disconnects ā Retry ā Confirm delivery ā Graceful degradation
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**The Realtime Approach:**
|
|
21
|
+
|
|
22
|
+
- Connections will failāplan for reconnection
|
|
23
|
+
- Order and delivery matterādesign for reliability
|
|
24
|
+
- Scale horizontallyādon't rely on single server
|
|
25
|
+
- Events are the contractādefine them clearly
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## š Technology Selection
|
|
30
|
+
|
|
31
|
+
### When to Use What
|
|
32
|
+
|
|
33
|
+
| Technology | Best For | Trade-offs |
|
|
34
|
+
| ---------------------- | ---------------------------------- | ------------------------------ |
|
|
35
|
+
| **WebSocket** | Two-way, low-latency communication | Manual reconnection handling |
|
|
36
|
+
| **Socket.IO** | Browser-friendly with fallbacks | Slight overhead, easier to use |
|
|
37
|
+
| **Server-Sent Events** | Server ā Client only (one-way) | Simpler, no full-duplex |
|
|
38
|
+
| **HTTP Polling** | Legacy support, simple needs | Higher latency, more resources |
|
|
39
|
+
| **HTTP Long Polling** | Fallback when others unavailable | Resource intensive |
|
|
40
|
+
| **Webhooks** | Server-to-server events | Requires public endpoint |
|
|
41
|
+
|
|
42
|
+
### Decision Tree
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Need two-way communication?
|
|
46
|
+
āā Yes ā Need browser fallbacks?
|
|
47
|
+
ā āā Yes ā Socket.IO
|
|
48
|
+
ā āā No ā Raw WebSocket
|
|
49
|
+
āā No ā Server ā Client only?
|
|
50
|
+
āā Yes ā Server-Sent Events (SSE)
|
|
51
|
+
āā No ā HTTP Polling / Webhooks
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## š Connection Management
|
|
57
|
+
|
|
58
|
+
### Connection Lifecycle
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
CONNECTING ā CONNECTED ā ACTIVE
|
|
62
|
+
ā ā ā
|
|
63
|
+
ā ā ā
|
|
64
|
+
āāā DISCONNECTED ā RECONNECTING
|
|
65
|
+
ā
|
|
66
|
+
CLOSED (user action / max retries)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Client-Side Connection State
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
interface ConnectionState {
|
|
73
|
+
status:
|
|
74
|
+
| "connecting"
|
|
75
|
+
| "connected"
|
|
76
|
+
| "reconnecting"
|
|
77
|
+
| "disconnected"
|
|
78
|
+
| "closed";
|
|
79
|
+
lastConnected: Date | null;
|
|
80
|
+
reconnectAttempts: number;
|
|
81
|
+
latency: number | null;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Keep-Alive (Heartbeat)
|
|
86
|
+
|
|
87
|
+
| Aspect | Recommendation |
|
|
88
|
+
| ---------------- | ----------------------------------------- |
|
|
89
|
+
| **Interval** | 25-30 seconds (under typical 60s timeout) |
|
|
90
|
+
| **Pong Timeout** | 5-10 seconds after ping sent |
|
|
91
|
+
| **On Timeout** | Trigger reconnection |
|
|
92
|
+
| **Payload** | Minimal (empty or timestamp only) |
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Heartbeat pattern
|
|
96
|
+
const HEARTBEAT_INTERVAL = 25000;
|
|
97
|
+
const PONG_TIMEOUT = 5000;
|
|
98
|
+
|
|
99
|
+
setInterval(() => {
|
|
100
|
+
socket.emit("ping");
|
|
101
|
+
|
|
102
|
+
const timeout = setTimeout(() => {
|
|
103
|
+
console.log("Connection dead, reconnecting...");
|
|
104
|
+
socket.disconnect();
|
|
105
|
+
socket.connect();
|
|
106
|
+
}, PONG_TIMEOUT);
|
|
107
|
+
|
|
108
|
+
socket.once("pong", () => clearTimeout(timeout));
|
|
109
|
+
}, HEARTBEAT_INTERVAL);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## š Reconnection Strategies
|
|
115
|
+
|
|
116
|
+
### Exponential Backoff with Jitter
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Attempt 1: Wait 1s
|
|
120
|
+
Attempt 2: Wait 2s (+ random 0-500ms)
|
|
121
|
+
Attempt 3: Wait 4s (+ random 0-500ms)
|
|
122
|
+
Attempt 4: Wait 8s (+ random 0-500ms)
|
|
123
|
+
Attempt 5: Wait 16s (+ random 0-500ms)
|
|
124
|
+
... capped at 30s max
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Reconnection Configuration
|
|
128
|
+
|
|
129
|
+
| Parameter | Recommended Value | Purpose |
|
|
130
|
+
| ------------------ | ----------------- | ---------------------------- |
|
|
131
|
+
| **Initial Delay** | 1000ms | First retry wait |
|
|
132
|
+
| **Max Delay** | 30000ms | Cap on retry wait time |
|
|
133
|
+
| **Max Retries** | 10-15 | Before showing error to user |
|
|
134
|
+
| **Backoff Factor** | 2 | Multiplier for each attempt |
|
|
135
|
+
| **Jitter** | 0-500ms random | Prevent thundering herd |
|
|
136
|
+
|
|
137
|
+
### Post-Reconnection Sync
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
socket.on("connect", () => {
|
|
141
|
+
if (wasReconnection) {
|
|
142
|
+
// Re-subscribe to rooms/topics
|
|
143
|
+
socket.emit("rejoin-rooms", { rooms: currentRooms });
|
|
144
|
+
|
|
145
|
+
// Request missed events since lastEventId
|
|
146
|
+
socket.emit("sync-events", { since: lastEventId });
|
|
147
|
+
|
|
148
|
+
// Or fetch current state via HTTP and apply
|
|
149
|
+
await fetchAndSyncState();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## š Rooms & Namespaces (Socket.IO)
|
|
157
|
+
|
|
158
|
+
### Rooms vs Namespaces
|
|
159
|
+
|
|
160
|
+
| Feature | Rooms | Namespaces |
|
|
161
|
+
| --------------- | --------------------------- | --------------------------- |
|
|
162
|
+
| **Purpose** | Dynamic grouping of sockets | Logical endpoint separation |
|
|
163
|
+
| **Created** | Dynamically at runtime | Defined in code |
|
|
164
|
+
| **Client Join** | Server-side only | Client connects directly |
|
|
165
|
+
| **Use Case** | Chat rooms, user groups | Different app modules |
|
|
166
|
+
|
|
167
|
+
### Room Patterns
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Join room server-side
|
|
171
|
+
socket.join(`user:${userId}`);
|
|
172
|
+
socket.join(`conversation:${conversationId}`);
|
|
173
|
+
socket.join(`tenant:${tenantId}:agents`);
|
|
174
|
+
|
|
175
|
+
// Emit to room
|
|
176
|
+
io.to(`conversation:${conversationId}`).emit("message:new", messageData);
|
|
177
|
+
|
|
178
|
+
// Leave room
|
|
179
|
+
socket.leave(`conversation:${conversationId}`);
|
|
180
|
+
|
|
181
|
+
// Room naming convention
|
|
182
|
+
// Format: entity:id or entity:id:subgroup
|
|
183
|
+
// Examples:
|
|
184
|
+
// user:123
|
|
185
|
+
// conversation:456
|
|
186
|
+
// team:789:admins
|
|
187
|
+
// tenant:abc:online-agents
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Namespace Patterns
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// Define namespaces for different features
|
|
194
|
+
const chatNsp = io.of("/chat");
|
|
195
|
+
const notificationNsp = io.of("/notifications");
|
|
196
|
+
const adminNsp = io.of("/admin");
|
|
197
|
+
|
|
198
|
+
// Each namespace has its own middleware
|
|
199
|
+
adminNsp.use(requireAdminAuth);
|
|
200
|
+
|
|
201
|
+
// Client connects to specific namespace
|
|
202
|
+
const adminSocket = io("/admin");
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## šØ Event Design
|
|
208
|
+
|
|
209
|
+
### Event Naming Convention
|
|
210
|
+
|
|
211
|
+
| Pattern | Example | Use Case |
|
|
212
|
+
| ------------------------ | ---------------------- | ---------------------- |
|
|
213
|
+
| `resource:action` | `message:send` | Client ā Server action |
|
|
214
|
+
| `resource:action:result` | `message:send:success` | Server response |
|
|
215
|
+
| `resource:event` | `message:received` | Server ā Client event |
|
|
216
|
+
| `error:resource` | `error:message` | Error events |
|
|
217
|
+
|
|
218
|
+
### Event Payload Structure
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Request (client ā server)
|
|
222
|
+
interface SocketRequest<T> {
|
|
223
|
+
requestId: string; // For correlation
|
|
224
|
+
timestamp: number; // Unix ms
|
|
225
|
+
payload: T;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Response (server ā client)
|
|
229
|
+
interface SocketResponse<T> {
|
|
230
|
+
requestId: string; // Correlation with request
|
|
231
|
+
success: boolean;
|
|
232
|
+
data?: T;
|
|
233
|
+
error?: {
|
|
234
|
+
code: string;
|
|
235
|
+
message: string;
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Event (server ā client, broadcast)
|
|
240
|
+
interface SocketEvent<T> {
|
|
241
|
+
eventId: string; // For deduplication
|
|
242
|
+
timestamp: number;
|
|
243
|
+
type: string; // Event type
|
|
244
|
+
payload: T;
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Event Acknowledgment
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// Client sends with callback
|
|
252
|
+
socket.emit("message:send", payload, (response) => {
|
|
253
|
+
if (response.success) {
|
|
254
|
+
// Handle success
|
|
255
|
+
} else {
|
|
256
|
+
// Handle error
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Server responds via callback
|
|
261
|
+
socket.on("message:send", async (payload, callback) => {
|
|
262
|
+
try {
|
|
263
|
+
const result = await processMessage(payload);
|
|
264
|
+
callback({ success: true, data: result });
|
|
265
|
+
} catch (error) {
|
|
266
|
+
callback({
|
|
267
|
+
success: false,
|
|
268
|
+
error: { code: "SEND_FAILED", message: error.message },
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## āļø Scaling Patterns
|
|
277
|
+
|
|
278
|
+
### Horizontal Scaling Architecture
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
āāāāāāāāāāāāāāāāāāā
|
|
282
|
+
ā Load Balancer ā
|
|
283
|
+
ā (Sticky Session)ā
|
|
284
|
+
āāāāāāāāāā¬āāāāāāāāā
|
|
285
|
+
āāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāā
|
|
286
|
+
ā ā ā
|
|
287
|
+
āāāāāāāāāāāā āāāāāāāāāāāā āāāāāāāāāāāā
|
|
288
|
+
āSocket.IO ā āSocket.IO ā āSocket.IO ā
|
|
289
|
+
āServer 1 ā āServer 2 ā āServer 3 ā
|
|
290
|
+
āāāāāā¬āāāāāā āāāāāā¬āāāāāā āāāāāā¬āāāāāā
|
|
291
|
+
ā ā ā
|
|
292
|
+
āāāāāāāāāāāāāā¬āāāāā“āāāāā¬āāāāāāāāāāāāā
|
|
293
|
+
ā ā
|
|
294
|
+
āāāāāāāāāāāāāāāāāāāāāāā
|
|
295
|
+
ā Redis (Pub/Sub) ā
|
|
296
|
+
ā Adapter Layer ā
|
|
297
|
+
āāāāāāāāāāāāāāāāāāāāāāā
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Redis Adapter Setup
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { createAdapter } from "@socket.io/redis-adapter";
|
|
304
|
+
import { createClient } from "redis";
|
|
305
|
+
|
|
306
|
+
const pubClient = createClient({ url: process.env.REDIS_URL });
|
|
307
|
+
const subClient = pubClient.duplicate();
|
|
308
|
+
|
|
309
|
+
await Promise.all([pubClient.connect(), subClient.connect()]);
|
|
310
|
+
|
|
311
|
+
io.adapter(createAdapter(pubClient, subClient));
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Scaling Considerations
|
|
315
|
+
|
|
316
|
+
| Challenge | Solution |
|
|
317
|
+
| --------------------- | ----------------------------------------- |
|
|
318
|
+
| **Sticky Sessions** | Use consistent hashing or client IP-based |
|
|
319
|
+
| **Cross-Server Emit** | Redis Pub/Sub adapter |
|
|
320
|
+
| **Connection State** | Store in Redis, not in-memory |
|
|
321
|
+
| **N-Squared Problem** | Sharded Redis adapter (Redis 7.0+) |
|
|
322
|
+
| **Message Order** | Use sequence numbers or timestamps |
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## š Security
|
|
327
|
+
|
|
328
|
+
### Authentication Patterns
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Token in query string (connection time)
|
|
332
|
+
const socket = io({
|
|
333
|
+
auth: {
|
|
334
|
+
token: accessToken,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Server middleware validates
|
|
339
|
+
io.use(async (socket, next) => {
|
|
340
|
+
try {
|
|
341
|
+
const token = socket.handshake.auth.token;
|
|
342
|
+
const user = await verifyToken(token);
|
|
343
|
+
socket.data.user = user;
|
|
344
|
+
next();
|
|
345
|
+
} catch (error) {
|
|
346
|
+
next(new Error("unauthorized"));
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Security Checklist
|
|
352
|
+
|
|
353
|
+
| Concern | Mitigation |
|
|
354
|
+
| ---------------------- | --------------------------------------- |
|
|
355
|
+
| **Authentication** | Validate token before accepting socket |
|
|
356
|
+
| **Authorization** | Check permissions before joining rooms |
|
|
357
|
+
| **Rate Limiting** | Limit events per second per client |
|
|
358
|
+
| **Payload Validation** | Validate and sanitize all incoming data |
|
|
359
|
+
| **Message Size** | Limit max payload size |
|
|
360
|
+
| **Origin Check** | Configure CORS properly |
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## š”ļø Error Handling
|
|
365
|
+
|
|
366
|
+
### Error Categories
|
|
367
|
+
|
|
368
|
+
| Category | Example | Client Action |
|
|
369
|
+
| -------------------- | ------------------------ | --------------------------- |
|
|
370
|
+
| **Connection Error** | Network failure | Reconnect with backoff |
|
|
371
|
+
| **Authentication** | Token expired | Refresh token, reconnect |
|
|
372
|
+
| **Authorization** | Not allowed to join room | Show error, don't retry |
|
|
373
|
+
| **Validation** | Invalid payload | Show error, fix input |
|
|
374
|
+
| **Server Error** | Internal server error | Retry once, then show error |
|
|
375
|
+
|
|
376
|
+
### Error Event Structure
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
socket.on("error", (error) => {
|
|
380
|
+
switch (error.code) {
|
|
381
|
+
case "TOKEN_EXPIRED":
|
|
382
|
+
await refreshTokenAndReconnect();
|
|
383
|
+
break;
|
|
384
|
+
case "RATE_LIMITED":
|
|
385
|
+
showNotification("Slow down!");
|
|
386
|
+
break;
|
|
387
|
+
case "ROOM_NOT_FOUND":
|
|
388
|
+
navigateToLobby();
|
|
389
|
+
break;
|
|
390
|
+
default:
|
|
391
|
+
console.error("Socket error:", error);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## š Monitoring & Debugging
|
|
399
|
+
|
|
400
|
+
### Key Metrics
|
|
401
|
+
|
|
402
|
+
| Metric | What It Tells You |
|
|
403
|
+
| ---------------------- | ----------------------------- |
|
|
404
|
+
| **Active Connections** | Current load, scaling needs |
|
|
405
|
+
| **Connection Rate** | Traffic patterns, spikes |
|
|
406
|
+
| **Reconnection Rate** | Connection stability issues |
|
|
407
|
+
| **Message Latency** | System responsiveness |
|
|
408
|
+
| **Messages/Second** | Throughput, capacity planning |
|
|
409
|
+
| **Error Rate** | System health |
|
|
410
|
+
|
|
411
|
+
### Debugging Tips
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// Enable debug logs (development only)
|
|
415
|
+
localStorage.debug = "socket.io-client:socket";
|
|
416
|
+
|
|
417
|
+
// Log all events
|
|
418
|
+
socket.onAny((eventName, ...args) => {
|
|
419
|
+
console.log(`[Socket] ${eventName}`, args);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Track connection state
|
|
423
|
+
socket.io.on("reconnect_attempt", (attempt) => {
|
|
424
|
+
console.log(`Reconnect attempt ${attempt}`);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
socket.io.on("reconnect_error", (error) => {
|
|
428
|
+
console.error("Reconnect failed:", error);
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## šØ Anti-Patterns
|
|
435
|
+
|
|
436
|
+
| ā Don't | ā
Do |
|
|
437
|
+
| ----------------------------------- | ----------------------------------- |
|
|
438
|
+
| Send large objects over socket | Send IDs, fetch data via HTTP |
|
|
439
|
+
| Block in event handlers | Process async, return quickly |
|
|
440
|
+
| Trust client-sent room names | Validate and authorize room access |
|
|
441
|
+
| Reconnect immediately on failure | Use exponential backoff with jitter |
|
|
442
|
+
| Store state in single server memory | Use Redis for cross-server state |
|
|
443
|
+
| Ignore connection state | Track and display to user |
|
|
444
|
+
| Send sensitive data in events | Encrypt or use HTTPS/WSS only |
|
|
445
|
+
| Process without validation | Validate all incoming payloads |
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## š Implementation Checklist
|
|
450
|
+
|
|
451
|
+
### Before Going Live
|
|
452
|
+
|
|
453
|
+
```markdown
|
|
454
|
+
## Realtime Feature Checklist
|
|
455
|
+
|
|
456
|
+
### Connection Management
|
|
457
|
+
|
|
458
|
+
- [ ] Reconnection with exponential backoff
|
|
459
|
+
- [ ] Heartbeat/keep-alive implemented
|
|
460
|
+
- [ ] Connection state displayed to user
|
|
461
|
+
- [ ] Graceful disconnect handling
|
|
462
|
+
|
|
463
|
+
### Event Design
|
|
464
|
+
|
|
465
|
+
- [ ] Events follow naming convention
|
|
466
|
+
- [ ] Payloads have requestId/eventId
|
|
467
|
+
- [ ] Acknowledgments for critical actions
|
|
468
|
+
- [ ] Error events defined and handled
|
|
469
|
+
|
|
470
|
+
### Scaling
|
|
471
|
+
|
|
472
|
+
- [ ] Redis adapter configured
|
|
473
|
+
- [ ] Sticky sessions in load balancer
|
|
474
|
+
- [ ] State stored in Redis (not memory)
|
|
475
|
+
- [ ] Room-based broadcasting optimized
|
|
476
|
+
|
|
477
|
+
### Security
|
|
478
|
+
|
|
479
|
+
- [ ] Authentication on connection
|
|
480
|
+
- [ ] Authorization on room join
|
|
481
|
+
- [ ] Rate limiting implemented
|
|
482
|
+
- [ ] Payload validation
|
|
483
|
+
- [ ] CORS properly configured
|
|
484
|
+
|
|
485
|
+
### Reliability
|
|
486
|
+
|
|
487
|
+
- [ ] Missed message sync strategy
|
|
488
|
+
- [ ] Deduplication by eventId
|
|
489
|
+
- [ ] Fallback to HTTP if needed
|
|
490
|
+
- [ ] Graceful degradation
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## š Related Skills
|
|
496
|
+
|
|
497
|
+
| Need | Skill |
|
|
498
|
+
| ------------------------- | ----------------------- |
|
|
499
|
+
| API design for HTTP calls | `api-patterns` |
|
|
500
|
+
| Performance optimization | `performance-profiling` |
|
|
501
|
+
| Queue/worker patterns | `queue-patterns` |
|
|
502
|
+
| Database for state | `database-design` |
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
> **Remember:** Real-time systems are about perceived responsiveness. A well-handled reconnection is better than a dropped message the user never sees.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Assets directory - add templates, images, etc.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Reference Documentation for Realtime Patterns
|
|
2
|
+
|
|
3
|
+
[TODO: Add detailed reference content here]
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
[Detailed explanation of concepts]
|
|
8
|
+
|
|
9
|
+
## Deep Dive Topics
|
|
10
|
+
|
|
11
|
+
### Topic 1
|
|
12
|
+
|
|
13
|
+
[Content]
|
|
14
|
+
|
|
15
|
+
### Topic 2
|
|
16
|
+
|
|
17
|
+
[Content]
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
[Real-world examples]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example validator for realtime-patterns
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python validate.py <project_path>
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate(project_path: str) -> dict:
|
|
14
|
+
"""Main validation logic"""
|
|
15
|
+
results = {
|
|
16
|
+
'errors': [],
|
|
17
|
+
'warnings': [],
|
|
18
|
+
'passed': []
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# TODO: Add validation logic
|
|
22
|
+
results['passed'].append('Placeholder validation passed')
|
|
23
|
+
|
|
24
|
+
return results
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def print_results(results: dict):
|
|
28
|
+
"""Pretty print results"""
|
|
29
|
+
print("\nš Validation Results\n")
|
|
30
|
+
|
|
31
|
+
if results['errors']:
|
|
32
|
+
print(f"ā Errors ({len(results['errors'])})")
|
|
33
|
+
for error in results['errors']:
|
|
34
|
+
print(f" - {error}")
|
|
35
|
+
|
|
36
|
+
if results['warnings']:
|
|
37
|
+
print(f"\nā ļø Warnings ({len(results['warnings'])})")
|
|
38
|
+
for warning in results['warnings']:
|
|
39
|
+
print(f" - {warning}")
|
|
40
|
+
|
|
41
|
+
if results['passed']:
|
|
42
|
+
print(f"\nā
Passed ({len(results['passed'])})")
|
|
43
|
+
for passed in results['passed']:
|
|
44
|
+
print(f" - {passed}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
if len(sys.argv) < 2:
|
|
49
|
+
print("Usage: python validate.py <project_path>")
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
|
|
52
|
+
project_path = sys.argv[1]
|
|
53
|
+
results = validate(project_path)
|
|
54
|
+
print_results(results)
|
|
55
|
+
|
|
56
|
+
sys.exit(1 if results['errors'] else 0)
|