javi-forge 1.1.0 → 1.3.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.
Files changed (238) hide show
  1. package/ci-local/ci-local.sh +38 -10
  2. package/ci-local/hooks/pre-commit +10 -155
  3. package/ci-local/hooks/pre-push +12 -29
  4. package/dist/commands/ci.d.ts +33 -0
  5. package/dist/commands/ci.js +341 -0
  6. package/dist/commands/init.js +5 -0
  7. package/dist/index.js +39 -5
  8. package/dist/lib/docker.d.ts +43 -0
  9. package/dist/lib/docker.js +223 -0
  10. package/dist/ui/CI.d.ts +9 -0
  11. package/dist/ui/CI.js +91 -0
  12. package/package.json +9 -1
  13. package/ai-config/.skillignore +0 -15
  14. package/ai-config/AUTO_INVOKE.md +0 -300
  15. package/ai-config/agents/_TEMPLATE.md +0 -93
  16. package/ai-config/agents/business/api-designer.md +0 -1657
  17. package/ai-config/agents/business/business-analyst.md +0 -1331
  18. package/ai-config/agents/business/product-strategist.md +0 -206
  19. package/ai-config/agents/business/project-manager.md +0 -178
  20. package/ai-config/agents/business/requirements-analyst.md +0 -1277
  21. package/ai-config/agents/business/technical-writer.md +0 -1679
  22. package/ai-config/agents/creative/ux-designer.md +0 -205
  23. package/ai-config/agents/data-ai/ai-engineer.md +0 -487
  24. package/ai-config/agents/data-ai/analytics-engineer.md +0 -953
  25. package/ai-config/agents/data-ai/data-engineer.md +0 -173
  26. package/ai-config/agents/data-ai/data-scientist.md +0 -672
  27. package/ai-config/agents/data-ai/mlops-engineer.md +0 -814
  28. package/ai-config/agents/data-ai/prompt-engineer.md +0 -772
  29. package/ai-config/agents/development/angular-expert.md +0 -620
  30. package/ai-config/agents/development/backend-architect.md +0 -795
  31. package/ai-config/agents/development/database-specialist.md +0 -212
  32. package/ai-config/agents/development/frontend-specialist.md +0 -686
  33. package/ai-config/agents/development/fullstack-engineer.md +0 -668
  34. package/ai-config/agents/development/golang-pro.md +0 -338
  35. package/ai-config/agents/development/java-enterprise.md +0 -400
  36. package/ai-config/agents/development/javascript-pro.md +0 -422
  37. package/ai-config/agents/development/nextjs-pro.md +0 -474
  38. package/ai-config/agents/development/python-pro.md +0 -570
  39. package/ai-config/agents/development/react-pro.md +0 -487
  40. package/ai-config/agents/development/rust-pro.md +0 -246
  41. package/ai-config/agents/development/spring-boot-4-expert.md +0 -326
  42. package/ai-config/agents/development/typescript-pro.md +0 -336
  43. package/ai-config/agents/development/vue-specialist.md +0 -605
  44. package/ai-config/agents/infrastructure/cloud-architect.md +0 -472
  45. package/ai-config/agents/infrastructure/deployment-manager.md +0 -358
  46. package/ai-config/agents/infrastructure/devops-engineer.md +0 -455
  47. package/ai-config/agents/infrastructure/incident-responder.md +0 -519
  48. package/ai-config/agents/infrastructure/kubernetes-expert.md +0 -705
  49. package/ai-config/agents/infrastructure/monitoring-specialist.md +0 -674
  50. package/ai-config/agents/infrastructure/performance-engineer.md +0 -658
  51. package/ai-config/agents/orchestrator.md +0 -241
  52. package/ai-config/agents/quality/accessibility-auditor.md +0 -1204
  53. package/ai-config/agents/quality/code-reviewer-compact.md +0 -123
  54. package/ai-config/agents/quality/code-reviewer.md +0 -363
  55. package/ai-config/agents/quality/dependency-manager.md +0 -743
  56. package/ai-config/agents/quality/e2e-test-specialist.md +0 -1005
  57. package/ai-config/agents/quality/performance-tester.md +0 -1086
  58. package/ai-config/agents/quality/security-auditor.md +0 -133
  59. package/ai-config/agents/quality/test-engineer.md +0 -453
  60. package/ai-config/agents/specialists/api-designer.md +0 -87
  61. package/ai-config/agents/specialists/backend-architect.md +0 -73
  62. package/ai-config/agents/specialists/code-reviewer.md +0 -77
  63. package/ai-config/agents/specialists/db-optimizer.md +0 -75
  64. package/ai-config/agents/specialists/devops-engineer.md +0 -83
  65. package/ai-config/agents/specialists/documentation-writer.md +0 -78
  66. package/ai-config/agents/specialists/frontend-developer.md +0 -75
  67. package/ai-config/agents/specialists/performance-analyst.md +0 -82
  68. package/ai-config/agents/specialists/refactor-specialist.md +0 -74
  69. package/ai-config/agents/specialists/security-auditor.md +0 -74
  70. package/ai-config/agents/specialists/test-engineer.md +0 -81
  71. package/ai-config/agents/specialists/ux-consultant.md +0 -76
  72. package/ai-config/agents/specialized/agent-generator.md +0 -1190
  73. package/ai-config/agents/specialized/blockchain-developer.md +0 -149
  74. package/ai-config/agents/specialized/code-migrator.md +0 -892
  75. package/ai-config/agents/specialized/context-manager.md +0 -978
  76. package/ai-config/agents/specialized/documentation-writer.md +0 -1078
  77. package/ai-config/agents/specialized/ecommerce-expert.md +0 -1756
  78. package/ai-config/agents/specialized/embedded-engineer.md +0 -1714
  79. package/ai-config/agents/specialized/error-detective.md +0 -1034
  80. package/ai-config/agents/specialized/fintech-specialist.md +0 -1659
  81. package/ai-config/agents/specialized/freelance-project-planner-v2.md +0 -1988
  82. package/ai-config/agents/specialized/freelance-project-planner-v3.md +0 -2136
  83. package/ai-config/agents/specialized/freelance-project-planner-v4.md +0 -4503
  84. package/ai-config/agents/specialized/freelance-project-planner.md +0 -722
  85. package/ai-config/agents/specialized/game-developer.md +0 -1963
  86. package/ai-config/agents/specialized/healthcare-dev.md +0 -1620
  87. package/ai-config/agents/specialized/mobile-developer.md +0 -188
  88. package/ai-config/agents/specialized/parallel-plan-executor.md +0 -506
  89. package/ai-config/agents/specialized/plan-executor.md +0 -485
  90. package/ai-config/agents/specialized/solo-dev-planner-modular/00-INDEX.md +0 -485
  91. package/ai-config/agents/specialized/solo-dev-planner-modular/01-CORE.md +0 -3493
  92. package/ai-config/agents/specialized/solo-dev-planner-modular/02-SELF-CORRECTION.md +0 -778
  93. package/ai-config/agents/specialized/solo-dev-planner-modular/03-PROGRESSIVE-SETUP.md +0 -918
  94. package/ai-config/agents/specialized/solo-dev-planner-modular/04-DEPLOYMENT.md +0 -1537
  95. package/ai-config/agents/specialized/solo-dev-planner-modular/05-TESTING.md +0 -2633
  96. package/ai-config/agents/specialized/solo-dev-planner-modular/06-OPERATIONS.md +0 -5610
  97. package/ai-config/agents/specialized/solo-dev-planner-modular/INSTALL.md +0 -335
  98. package/ai-config/agents/specialized/solo-dev-planner-modular/QUICK-REFERENCE.txt +0 -215
  99. package/ai-config/agents/specialized/solo-dev-planner-modular/README.md +0 -260
  100. package/ai-config/agents/specialized/solo-dev-planner-modular/START-HERE.md +0 -379
  101. package/ai-config/agents/specialized/solo-dev-planner-modular/WORKFLOW-DIAGRAM.md +0 -355
  102. package/ai-config/agents/specialized/solo-dev-planner-modular/solo-dev-planner.md +0 -279
  103. package/ai-config/agents/specialized/template-writer.md +0 -347
  104. package/ai-config/agents/specialized/test-runner.md +0 -99
  105. package/ai-config/agents/specialized/vibekanban-smart-worker.md +0 -244
  106. package/ai-config/agents/specialized/wave-executor.md +0 -138
  107. package/ai-config/agents/specialized/workflow-optimizer.md +0 -1114
  108. package/ai-config/commands/git/changelog.md +0 -32
  109. package/ai-config/commands/git/ci-local.md +0 -70
  110. package/ai-config/commands/git/commit.md +0 -35
  111. package/ai-config/commands/git/fix-issue.md +0 -23
  112. package/ai-config/commands/git/pr-create.md +0 -42
  113. package/ai-config/commands/git/pr-review.md +0 -50
  114. package/ai-config/commands/git/worktree.md +0 -39
  115. package/ai-config/commands/refactoring/cleanup.md +0 -24
  116. package/ai-config/commands/refactoring/dead-code.md +0 -40
  117. package/ai-config/commands/refactoring/extract.md +0 -31
  118. package/ai-config/commands/testing/e2e.md +0 -30
  119. package/ai-config/commands/testing/tdd.md +0 -36
  120. package/ai-config/commands/testing/test-coverage.md +0 -30
  121. package/ai-config/commands/testing/test-fix.md +0 -24
  122. package/ai-config/commands/workflow/generate-agents-md.md +0 -85
  123. package/ai-config/commands/workflow/planning.md +0 -47
  124. package/ai-config/commands/workflows/compound.md +0 -89
  125. package/ai-config/commands/workflows/diagnose.md +0 -70
  126. package/ai-config/commands/workflows/discover.md +0 -86
  127. package/ai-config/commands/workflows/plan.md +0 -77
  128. package/ai-config/commands/workflows/review.md +0 -78
  129. package/ai-config/commands/workflows/work.md +0 -75
  130. package/ai-config/config.yaml +0 -18
  131. package/ai-config/hooks/_TEMPLATE.md +0 -96
  132. package/ai-config/hooks/block-dangerous-commands.md +0 -75
  133. package/ai-config/hooks/commit-guard.md +0 -90
  134. package/ai-config/hooks/context-loader.md +0 -73
  135. package/ai-config/hooks/improve-prompt.md +0 -91
  136. package/ai-config/hooks/learning-log.md +0 -72
  137. package/ai-config/hooks/model-router.md +0 -86
  138. package/ai-config/hooks/secret-scanner.md +0 -64
  139. package/ai-config/hooks/skill-validator.md +0 -102
  140. package/ai-config/hooks/task-artifact.md +0 -114
  141. package/ai-config/hooks/validate-workflow.md +0 -100
  142. package/ai-config/prompts/base.md +0 -71
  143. package/ai-config/prompts/modes/debug.md +0 -34
  144. package/ai-config/prompts/modes/deploy.md +0 -40
  145. package/ai-config/prompts/modes/research.md +0 -32
  146. package/ai-config/prompts/modes/review.md +0 -33
  147. package/ai-config/prompts/review-policy.md +0 -79
  148. package/ai-config/skills/_TEMPLATE.md +0 -157
  149. package/ai-config/skills/backend/api-gateway/SKILL.md +0 -254
  150. package/ai-config/skills/backend/bff-concepts/SKILL.md +0 -239
  151. package/ai-config/skills/backend/bff-spring/SKILL.md +0 -364
  152. package/ai-config/skills/backend/chi-router/SKILL.md +0 -396
  153. package/ai-config/skills/backend/error-handling/SKILL.md +0 -255
  154. package/ai-config/skills/backend/exceptions-spring/SKILL.md +0 -323
  155. package/ai-config/skills/backend/fastapi/SKILL.md +0 -302
  156. package/ai-config/skills/backend/gateway-spring/SKILL.md +0 -390
  157. package/ai-config/skills/backend/go-backend/SKILL.md +0 -457
  158. package/ai-config/skills/backend/gradle-multimodule/SKILL.md +0 -274
  159. package/ai-config/skills/backend/graphql-concepts/SKILL.md +0 -352
  160. package/ai-config/skills/backend/graphql-spring/SKILL.md +0 -398
  161. package/ai-config/skills/backend/grpc-concepts/SKILL.md +0 -283
  162. package/ai-config/skills/backend/grpc-spring/SKILL.md +0 -445
  163. package/ai-config/skills/backend/jwt-auth/SKILL.md +0 -412
  164. package/ai-config/skills/backend/notifications-concepts/SKILL.md +0 -259
  165. package/ai-config/skills/backend/recommendations-concepts/SKILL.md +0 -261
  166. package/ai-config/skills/backend/search-concepts/SKILL.md +0 -263
  167. package/ai-config/skills/backend/search-spring/SKILL.md +0 -375
  168. package/ai-config/skills/backend/spring-boot-4/SKILL.md +0 -172
  169. package/ai-config/skills/backend/websockets/SKILL.md +0 -532
  170. package/ai-config/skills/data-ai/ai-ml/SKILL.md +0 -423
  171. package/ai-config/skills/data-ai/analytics-concepts/SKILL.md +0 -195
  172. package/ai-config/skills/data-ai/analytics-spring/SKILL.md +0 -340
  173. package/ai-config/skills/data-ai/duckdb-analytics/SKILL.md +0 -440
  174. package/ai-config/skills/data-ai/langchain/SKILL.md +0 -238
  175. package/ai-config/skills/data-ai/mlflow/SKILL.md +0 -302
  176. package/ai-config/skills/data-ai/onnx-inference/SKILL.md +0 -290
  177. package/ai-config/skills/data-ai/powerbi/SKILL.md +0 -352
  178. package/ai-config/skills/data-ai/pytorch/SKILL.md +0 -274
  179. package/ai-config/skills/data-ai/scikit-learn/SKILL.md +0 -321
  180. package/ai-config/skills/data-ai/vector-db/SKILL.md +0 -301
  181. package/ai-config/skills/database/graph-databases/SKILL.md +0 -218
  182. package/ai-config/skills/database/graph-spring/SKILL.md +0 -361
  183. package/ai-config/skills/database/pgx-postgres/SKILL.md +0 -512
  184. package/ai-config/skills/database/redis-cache/SKILL.md +0 -343
  185. package/ai-config/skills/database/sqlite-embedded/SKILL.md +0 -388
  186. package/ai-config/skills/database/timescaledb/SKILL.md +0 -320
  187. package/ai-config/skills/docs/api-documentation/SKILL.md +0 -293
  188. package/ai-config/skills/docs/docs-spring/SKILL.md +0 -377
  189. package/ai-config/skills/docs/mustache-templates/SKILL.md +0 -190
  190. package/ai-config/skills/docs/technical-docs/SKILL.md +0 -447
  191. package/ai-config/skills/frontend/astro-ssr/SKILL.md +0 -441
  192. package/ai-config/skills/frontend/frontend-design/SKILL.md +0 -54
  193. package/ai-config/skills/frontend/frontend-web/SKILL.md +0 -368
  194. package/ai-config/skills/frontend/mantine-ui/SKILL.md +0 -396
  195. package/ai-config/skills/frontend/tanstack-query/SKILL.md +0 -439
  196. package/ai-config/skills/frontend/zod-validation/SKILL.md +0 -417
  197. package/ai-config/skills/frontend/zustand-state/SKILL.md +0 -350
  198. package/ai-config/skills/infrastructure/chaos-engineering/SKILL.md +0 -244
  199. package/ai-config/skills/infrastructure/chaos-spring/SKILL.md +0 -378
  200. package/ai-config/skills/infrastructure/devops-infra/SKILL.md +0 -435
  201. package/ai-config/skills/infrastructure/docker-containers/SKILL.md +0 -420
  202. package/ai-config/skills/infrastructure/kubernetes/SKILL.md +0 -456
  203. package/ai-config/skills/infrastructure/opentelemetry/SKILL.md +0 -546
  204. package/ai-config/skills/infrastructure/traefik-proxy/SKILL.md +0 -474
  205. package/ai-config/skills/infrastructure/woodpecker-ci/SKILL.md +0 -315
  206. package/ai-config/skills/mobile/ionic-capacitor/SKILL.md +0 -504
  207. package/ai-config/skills/mobile/mobile-ionic/SKILL.md +0 -448
  208. package/ai-config/skills/prompt-improver/SKILL.md +0 -125
  209. package/ai-config/skills/quality/ghagga-review/SKILL.md +0 -216
  210. package/ai-config/skills/references/hooks-patterns/SKILL.md +0 -238
  211. package/ai-config/skills/references/mcp-servers/SKILL.md +0 -275
  212. package/ai-config/skills/references/plugins-reference/SKILL.md +0 -110
  213. package/ai-config/skills/references/skills-reference/SKILL.md +0 -420
  214. package/ai-config/skills/references/subagent-templates/SKILL.md +0 -193
  215. package/ai-config/skills/systems-iot/modbus-protocol/SKILL.md +0 -410
  216. package/ai-config/skills/systems-iot/mqtt-rumqttc/SKILL.md +0 -408
  217. package/ai-config/skills/systems-iot/rust-systems/SKILL.md +0 -386
  218. package/ai-config/skills/systems-iot/tokio-async/SKILL.md +0 -324
  219. package/ai-config/skills/testing/playwright-e2e/SKILL.md +0 -289
  220. package/ai-config/skills/testing/testcontainers/SKILL.md +0 -299
  221. package/ai-config/skills/testing/vitest-testing/SKILL.md +0 -381
  222. package/ai-config/skills/workflow/ci-local-guide/SKILL.md +0 -118
  223. package/ai-config/skills/workflow/claude-automation-recommender/SKILL.md +0 -299
  224. package/ai-config/skills/workflow/claude-md-improver/SKILL.md +0 -158
  225. package/ai-config/skills/workflow/finishing-a-development-branch/SKILL.md +0 -117
  226. package/ai-config/skills/workflow/git-github/SKILL.md +0 -334
  227. package/ai-config/skills/workflow/git-github/references/examples.md +0 -160
  228. package/ai-config/skills/workflow/git-workflow/SKILL.md +0 -214
  229. package/ai-config/skills/workflow/ide-plugins/SKILL.md +0 -277
  230. package/ai-config/skills/workflow/ide-plugins-intellij/SKILL.md +0 -401
  231. package/ai-config/skills/workflow/obsidian-brain-workflow/SKILL.md +0 -199
  232. package/ai-config/skills/workflow/using-git-worktrees/SKILL.md +0 -100
  233. package/ai-config/skills/workflow/verification-before-completion/SKILL.md +0 -73
  234. package/ai-config/skills/workflow/wave-workflow/SKILL.md +0 -178
  235. package/schemas/agent.schema.json +0 -34
  236. package/schemas/ai-config.schema.json +0 -28
  237. package/schemas/plugin.schema.json +0 -62
  238. package/schemas/skill.schema.json +0 -44
@@ -1,532 +0,0 @@
1
- ---
2
- name: websockets
3
- description: >
4
- WebSocket real-time communication patterns for Go and TypeScript.
5
- Trigger: websocket, real-time, pubsub, hub pattern, live updates
6
- tools:
7
- - Read
8
- - Write
9
- - Bash
10
- - Grep
11
- metadata:
12
- author: plataforma-industrial
13
- version: "2.0"
14
- tags: [websocket, realtime, go, typescript, pubsub]
15
- language: go
16
- updated: "2026-02"
17
- ---
18
-
19
- # WebSockets
20
-
21
- > Real-time bidirectional communication with Hub pattern
22
-
23
- ## When to Use
24
-
25
- - [ ] Building live dashboards with real-time updates
26
- - [ ] Implementing chat or notification systems
27
- - [ ] Streaming sensor data to web clients
28
- - [ ] Push updates without polling
29
-
30
- ## Stack
31
-
32
- ```yaml
33
- # Go Server
34
- gorilla/websocket: "1.5+"
35
- nhooyr.io/websocket: "1.8+"
36
-
37
- # TypeScript Client
38
- Native WebSocket API
39
- @tanstack/react-query (for cache sync)
40
-
41
- # Rust
42
- tokio-tungstenite: "0.21+"
43
- ```
44
-
45
- ## Go Server - Hub Pattern
46
-
47
- ### Hub Structure
48
-
49
- ```go
50
- package ws
51
-
52
- import (
53
- "encoding/json"
54
- "sync"
55
- "time"
56
-
57
- "github.com/gorilla/websocket"
58
- )
59
-
60
- const (
61
- writeWait = 10 * time.Second
62
- pongWait = 60 * time.Second
63
- pingPeriod = (pongWait * 9) / 10
64
- maxMessageSize = 512 * 1024
65
- )
66
-
67
- type Message struct {
68
- Type string `json:"type"`
69
- Payload json.RawMessage `json:"payload"`
70
- }
71
-
72
- type Client struct {
73
- hub *Hub
74
- conn *websocket.Conn
75
- send chan []byte
76
- tenantID string
77
- userID string
78
- rooms map[string]bool
79
- mu sync.RWMutex
80
- }
81
-
82
- type Hub struct {
83
- clients map[*Client]bool
84
- byTenant map[string]map[*Client]bool
85
- byRoom map[string]map[*Client]bool
86
- register chan *Client
87
- unregister chan *Client
88
- broadcast chan BroadcastMessage
89
- mu sync.RWMutex
90
- }
91
-
92
- type BroadcastMessage struct {
93
- TenantID string
94
- Room string
95
- Data []byte
96
- }
97
-
98
- func NewHub() *Hub {
99
- return &Hub{
100
- clients: make(map[*Client]bool),
101
- byTenant: make(map[string]map[*Client]bool),
102
- byRoom: make(map[string]map[*Client]bool),
103
- register: make(chan *Client),
104
- unregister: make(chan *Client),
105
- broadcast: make(chan BroadcastMessage, 256),
106
- }
107
- }
108
- ```
109
-
110
- ### Hub Run Loop
111
-
112
- ```go
113
- func (h *Hub) Run(ctx context.Context) {
114
- for {
115
- select {
116
- case <-ctx.Done():
117
- return
118
-
119
- case client := <-h.register:
120
- h.mu.Lock()
121
- h.clients[client] = true
122
- if h.byTenant[client.tenantID] == nil {
123
- h.byTenant[client.tenantID] = make(map[*Client]bool)
124
- }
125
- h.byTenant[client.tenantID][client] = true
126
- h.mu.Unlock()
127
-
128
- case client := <-h.unregister:
129
- h.mu.Lock()
130
- if _, ok := h.clients[client]; ok {
131
- delete(h.clients, client)
132
- delete(h.byTenant[client.tenantID], client)
133
- for room := range client.rooms {
134
- delete(h.byRoom[room], client)
135
- }
136
- close(client.send)
137
- }
138
- h.mu.Unlock()
139
-
140
- case msg := <-h.broadcast:
141
- h.mu.RLock()
142
- var targets map[*Client]bool
143
- if msg.Room != "" {
144
- targets = h.byRoom[msg.Room]
145
- } else {
146
- targets = h.byTenant[msg.TenantID]
147
- }
148
- for client := range targets {
149
- select {
150
- case client.send <- msg.Data:
151
- default:
152
- close(client.send)
153
- delete(h.clients, client)
154
- }
155
- }
156
- h.mu.RUnlock()
157
- }
158
- }
159
- }
160
- ```
161
-
162
- ### Room Management
163
-
164
- ```go
165
- func (h *Hub) JoinRoom(client *Client, room string) {
166
- h.mu.Lock()
167
- defer h.mu.Unlock()
168
-
169
- if h.byRoom[room] == nil {
170
- h.byRoom[room] = make(map[*Client]bool)
171
- }
172
- h.byRoom[room][client] = true
173
-
174
- client.mu.Lock()
175
- client.rooms[room] = true
176
- client.mu.Unlock()
177
- }
178
-
179
- func (h *Hub) BroadcastToRoom(room string, msgType string, payload interface{}) error {
180
- data, err := json.Marshal(Message{Type: msgType, Payload: mustMarshal(payload)})
181
- if err != nil { return err }
182
-
183
- h.broadcast <- BroadcastMessage{Room: room, Data: data}
184
- return nil
185
- }
186
- ```
187
-
188
- ### Client Read/Write Pumps
189
-
190
- ```go
191
- func (c *Client) readPump() {
192
- defer func() {
193
- c.hub.unregister <- c
194
- c.conn.Close()
195
- }()
196
-
197
- c.conn.SetReadLimit(maxMessageSize)
198
- c.conn.SetReadDeadline(time.Now().Add(pongWait))
199
- c.conn.SetPongHandler(func(string) error {
200
- c.conn.SetReadDeadline(time.Now().Add(pongWait))
201
- return nil
202
- })
203
-
204
- for {
205
- _, message, err := c.conn.ReadMessage()
206
- if err != nil { break }
207
- c.handleMessage(message)
208
- }
209
- }
210
-
211
- func (c *Client) writePump() {
212
- ticker := time.NewTicker(pingPeriod)
213
- defer func() {
214
- ticker.Stop()
215
- c.conn.Close()
216
- }()
217
-
218
- for {
219
- select {
220
- case message, ok := <-c.send:
221
- c.conn.SetWriteDeadline(time.Now().Add(writeWait))
222
- if !ok {
223
- c.conn.WriteMessage(websocket.CloseMessage, []byte{})
224
- return
225
- }
226
- w, _ := c.conn.NextWriter(websocket.TextMessage)
227
- w.Write(message)
228
- // Coalesce queued messages
229
- for i := 0; i < len(c.send); i++ {
230
- w.Write([]byte{'\n'})
231
- w.Write(<-c.send)
232
- }
233
- w.Close()
234
-
235
- case <-ticker.C:
236
- c.conn.SetWriteDeadline(time.Now().Add(writeWait))
237
- c.conn.WriteMessage(websocket.PingMessage, nil)
238
- }
239
- }
240
- }
241
- ```
242
-
243
- ### HTTP Upgrade Handler
244
-
245
- ```go
246
- var upgrader = websocket.Upgrader{
247
- ReadBufferSize: 1024,
248
- WriteBufferSize: 1024,
249
- CheckOrigin: func(r *http.Request) bool {
250
- origin := r.Header.Get("Origin")
251
- return origin == "https://app.example.com" || origin == "http://localhost:4321"
252
- },
253
- }
254
-
255
- func (h *Hub) HandleWebSocket(w http.ResponseWriter, r *http.Request) {
256
- tenantID := r.Context().Value("tenant_id").(string)
257
- userID := r.Context().Value("user_id").(string)
258
-
259
- conn, err := upgrader.Upgrade(w, r, nil)
260
- if err != nil { return }
261
-
262
- client := &Client{
263
- hub: h,
264
- conn: conn,
265
- send: make(chan []byte, 256),
266
- tenantID: tenantID,
267
- userID: userID,
268
- rooms: make(map[string]bool),
269
- }
270
-
271
- h.register <- client
272
- go client.writePump()
273
- go client.readPump()
274
- }
275
- ```
276
-
277
- ## TypeScript Client
278
-
279
- ### WebSocket Hook
280
-
281
- ```typescript
282
- import { useEffect, useRef, useState, useCallback } from 'react';
283
-
284
- interface WebSocketMessage<T = unknown> {
285
- type: string;
286
- payload: T;
287
- }
288
-
289
- export function useWebSocket(options: {
290
- onMessage?: (msg: WebSocketMessage) => void;
291
- onConnect?: () => void;
292
- reconnect?: boolean;
293
- } = {}) {
294
- const { onMessage, onConnect, reconnect = true } = options;
295
- const ws = useRef<WebSocket | null>(null);
296
- const [isConnected, setIsConnected] = useState(false);
297
-
298
- const connect = useCallback(() => {
299
- if (ws.current?.readyState === WebSocket.OPEN) return;
300
-
301
- ws.current = new WebSocket(import.meta.env.PUBLIC_WS_URL);
302
-
303
- ws.current.onopen = () => {
304
- setIsConnected(true);
305
- onConnect?.();
306
- };
307
-
308
- ws.current.onclose = () => {
309
- setIsConnected(false);
310
- if (reconnect) setTimeout(connect, 3000);
311
- };
312
-
313
- ws.current.onmessage = (event) => {
314
- const message = JSON.parse(event.data);
315
- onMessage?.(message);
316
- };
317
- }, [onMessage, onConnect, reconnect]);
318
-
319
- const send = useCallback((type: string, payload: unknown) => {
320
- if (ws.current?.readyState === WebSocket.OPEN) {
321
- ws.current.send(JSON.stringify({ type, payload }));
322
- }
323
- }, []);
324
-
325
- const subscribe = useCallback((room: string) => send('subscribe', { room }), [send]);
326
- const unsubscribe = useCallback((room: string) => send('unsubscribe', { room }), [send]);
327
-
328
- useEffect(() => {
329
- connect();
330
- return () => ws.current?.close();
331
- }, [connect]);
332
-
333
- return { isConnected, send, subscribe, unsubscribe };
334
- }
335
- ```
336
-
337
- ### React Query Integration
338
-
339
- ```typescript
340
- import { useQueryClient } from '@tanstack/react-query';
341
- import { useWebSocket } from './useWebSocket';
342
-
343
- interface SensorReading {
344
- sensorId: string;
345
- value: number;
346
- timestamp: number;
347
- }
348
-
349
- export function useRealtimeSensors(sensorIds: string[]) {
350
- const queryClient = useQueryClient();
351
-
352
- const handleMessage = useCallback((msg: { type: string; payload: unknown }) => {
353
- if (msg.type === 'sensor.reading') {
354
- const reading = msg.payload as SensorReading;
355
-
356
- // Update individual sensor cache
357
- queryClient.setQueryData(['sensor', reading.sensorId, 'latest'], reading);
358
-
359
- // Update list cache
360
- queryClient.setQueryData<SensorReading[]>(['sensors', 'latest'], (old) => {
361
- if (!old) return [reading];
362
- const index = old.findIndex((r) => r.sensorId === reading.sensorId);
363
- if (index >= 0) {
364
- const updated = [...old];
365
- updated[index] = reading;
366
- return updated;
367
- }
368
- return [...old, reading];
369
- });
370
- }
371
- }, [queryClient]);
372
-
373
- const { isConnected, subscribe, unsubscribe } = useWebSocket({
374
- onMessage: handleMessage,
375
- onConnect: () => sensorIds.forEach((id) => subscribe(`sensor:${id}`)),
376
- });
377
-
378
- useEffect(() => {
379
- if (isConnected) sensorIds.forEach((id) => subscribe(`sensor:${id}`));
380
- return () => sensorIds.forEach((id) => unsubscribe(`sensor:${id}`));
381
- }, [sensorIds, isConnected]);
382
-
383
- return { isConnected };
384
- }
385
- ```
386
-
387
- ## Reconnection with Exponential Backoff
388
-
389
- ```typescript
390
- class ReconnectingWebSocket {
391
- private ws: WebSocket | null = null;
392
- private reconnectAttempts = 0;
393
- private maxAttempts = 10;
394
- private heartbeatInterval: NodeJS.Timeout | null = null;
395
- private missedHeartbeats = 0;
396
-
397
- constructor(
398
- private url: string,
399
- private onMessage: (data: unknown) => void,
400
- private onStateChange: (connected: boolean) => void
401
- ) {
402
- this.connect();
403
- }
404
-
405
- private connect() {
406
- this.ws = new WebSocket(this.url);
407
-
408
- this.ws.onopen = () => {
409
- this.reconnectAttempts = 0;
410
- this.missedHeartbeats = 0;
411
- this.onStateChange(true);
412
- this.startHeartbeat();
413
- };
414
-
415
- this.ws.onclose = () => {
416
- this.onStateChange(false);
417
- this.stopHeartbeat();
418
- this.scheduleReconnect();
419
- };
420
-
421
- this.ws.onmessage = (event) => {
422
- const data = JSON.parse(event.data);
423
- if (data.type === 'pong') {
424
- this.missedHeartbeats = 0;
425
- return;
426
- }
427
- this.onMessage(data);
428
- };
429
- }
430
-
431
- private startHeartbeat() {
432
- this.heartbeatInterval = setInterval(() => {
433
- if (this.missedHeartbeats >= 3) {
434
- this.ws?.close();
435
- return;
436
- }
437
- this.ws?.send(JSON.stringify({ type: 'ping' }));
438
- this.missedHeartbeats++;
439
- }, 30000);
440
- }
441
-
442
- private scheduleReconnect() {
443
- if (this.reconnectAttempts >= this.maxAttempts) return;
444
-
445
- const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
446
- this.reconnectAttempts++;
447
- setTimeout(() => this.connect(), delay);
448
- }
449
- }
450
- ```
451
-
452
- ## Best Practices
453
-
454
- ### Use Rooms for Targeted Broadcasts
455
-
456
- ```go
457
- // ✅ Send only to interested clients
458
- hub.BroadcastToRoom("sensor:temp-1", "reading", reading)
459
-
460
- // ❌ Broadcast to everyone
461
- hub.BroadcastToTenant(tenantID, "reading", reading)
462
- ```
463
-
464
- ### Implement Heartbeats
465
-
466
- ```typescript
467
- // Detect stale connections
468
- setInterval(() => ws.send(JSON.stringify({ type: 'ping' })), 30000);
469
- ```
470
-
471
- ### Coalesce Messages
472
-
473
- ```go
474
- // Combine pending messages in single write
475
- n := len(c.send)
476
- for i := 0; i < n; i++ {
477
- w.Write(<-c.send)
478
- }
479
- ```
480
-
481
- ### Exponential Backoff
482
-
483
- ```typescript
484
- const delay = Math.min(1000 * Math.pow(2, attempts), 30000);
485
- ```
486
-
487
- ## Quick Reference
488
-
489
- | Task | Go Server |
490
- |------|-----------|
491
- | Create hub | `hub := NewHub()` |
492
- | Run hub | `go hub.Run(ctx)` |
493
- | Join room | `hub.JoinRoom(client, "room")` |
494
- | Broadcast | `hub.BroadcastToRoom("room", "type", data)` |
495
- | Upgrade | `upgrader.Upgrade(w, r, nil)` |
496
-
497
- | Task | TypeScript Client |
498
- |------|-------------------|
499
- | Connect | `new WebSocket(url)` |
500
- | Send | `ws.send(JSON.stringify(msg))` |
501
- | Subscribe | `send('subscribe', { room })` |
502
- | Close | `ws.close()` |
503
-
504
- ## Message Protocol
505
-
506
- ```json
507
- // Subscribe to room
508
- {"type": "subscribe", "payload": {"room": "sensor:temp-1"}}
509
-
510
- // Unsubscribe
511
- {"type": "unsubscribe", "payload": {"room": "sensor:temp-1"}}
512
-
513
- // Heartbeat
514
- {"type": "ping"}
515
- {"type": "pong"}
516
-
517
- // Data message
518
- {"type": "sensor.reading", "payload": {"sensorId": "temp-1", "value": 25.5}}
519
- ```
520
-
521
- ## Resources
522
-
523
- - [gorilla/websocket](https://github.com/gorilla/websocket)
524
- - [MDN WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
525
- - [RFC 6455 WebSocket Protocol](https://tools.ietf.org/html/rfc6455)
526
-
527
- ## Related Skills
528
-
529
- - `tokio-async`: Async WebSocket handling
530
- - `chi-router`: Go WebSocket integration
531
- - `fastapi`: Python WebSocket endpoints
532
- - `mqtt-rumqttc`: IoT data streaming