@telemetryos/cli 1.7.4 → 1.8.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/CHANGELOG.md +22 -0
- package/dist/commands/init.js +11 -0
- package/dist/services/generate-application.d.ts +1 -0
- package/dist/services/generate-application.js +127 -4
- package/dist/utils/validate-project-name.d.ts +19 -0
- package/dist/utils/validate-project-name.js +44 -0
- package/package.json +2 -2
- package/templates/vite-react-typescript/CLAUDE.md +68 -1244
- package/templates/vite-react-typescript/_claude/settings.local.json +17 -0
- package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +313 -0
- package/templates/vite-react-typescript/_claude/skills/tos-debugging/SKILL.md +299 -0
- package/templates/vite-react-typescript/_claude/skills/tos-media-api/SKILL.md +335 -0
- package/templates/vite-react-typescript/_claude/skills/tos-proxy-fetch/SKILL.md +319 -0
- package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +332 -0
- package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +252 -0
- package/templates/vite-react-typescript/_claude/skills/tos-settings-ui/SKILL.md +636 -0
- package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +359 -0
- package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +384 -0
- package/templates/vite-react-typescript/src/hooks/store.ts +3 -3
- package/templates/vite-react-typescript/src/index.css +0 -1
- package/templates/vite-react-typescript/src/views/Render.css +2 -1
- package/templates/vite-react-typescript/src/views/Render.tsx +2 -3
- package/templates/vite-react-typescript/src/views/Settings.tsx +2 -3
- package/templates/vite-react-typescript/AGENTS.md +0 -7
- /package/templates/vite-react-typescript/{gitignore → _gitignore} +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Skill(tos-architecture)",
|
|
5
|
+
"Skill(tos-debugging)",
|
|
6
|
+
"Skill(tos-media-api)",
|
|
7
|
+
"Skill(tos-proxy-fetch)",
|
|
8
|
+
"Skill(tos-render-design)",
|
|
9
|
+
"Skill(tos-requirements)",
|
|
10
|
+
"Skill(tos-settings-ui)",
|
|
11
|
+
"Skill(tos-store-sync)",
|
|
12
|
+
"Skill(tos-weather-api)",
|
|
13
|
+
"Bash(npm run build:*)",
|
|
14
|
+
"Bash(npm run dev:*)"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tos-architecture
|
|
3
|
+
description: Understand TelemetryOS two-mount-point architecture (Render vs Settings). Use when debugging mount-point issues or understanding how Settings and Render communicate.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TelemetryOS Architecture
|
|
7
|
+
|
|
8
|
+
TelemetryOS applications run on digital signage devices (TVs, kiosks, displays). Understanding the architecture is key to building effective apps.
|
|
9
|
+
|
|
10
|
+
## Two Mount Points
|
|
11
|
+
|
|
12
|
+
Every standard TOS app has two entry points:
|
|
13
|
+
|
|
14
|
+
### /render - Device Display
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────┐
|
|
18
|
+
│ Physical Device │
|
|
19
|
+
│ (TV, Kiosk, Digital Sign) │
|
|
20
|
+
│ │
|
|
21
|
+
│ ┌───────────────────────────┐ │
|
|
22
|
+
│ │ Chrome Browser │ │
|
|
23
|
+
│ │ ┌─────────────────────┐ │ │
|
|
24
|
+
│ │ │ Your App │ │ │
|
|
25
|
+
│ │ │ /render route │ │ │
|
|
26
|
+
│ │ │ │ │ │
|
|
27
|
+
│ │ │ Shows content to │ │ │
|
|
28
|
+
│ │ │ end users/viewers │ │ │
|
|
29
|
+
│ │ └─────────────────────┘ │ │
|
|
30
|
+
│ └───────────────────────────┘ │
|
|
31
|
+
└─────────────────────────────────┘
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Characteristics:**
|
|
35
|
+
- Runs on physical device (TelemetryOS player)
|
|
36
|
+
- Chrome browser in iframe sandbox
|
|
37
|
+
- Has access to `store().device` (device-local storage)
|
|
38
|
+
- Displays content to viewers/customers
|
|
39
|
+
- Full screen, optimized for viewing from distance
|
|
40
|
+
- Can access device hardware info
|
|
41
|
+
|
|
42
|
+
### /settings - Admin Configuration
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
┌─────────────────────────────────┐
|
|
46
|
+
│ Admin's Browser │
|
|
47
|
+
│ (Desktop/Laptop/Tablet) │
|
|
48
|
+
│ │
|
|
49
|
+
│ ┌───────────────────────────┐ │
|
|
50
|
+
│ │ TelemetryOS Studio │ │
|
|
51
|
+
│ │ ┌─────────────────────┐ │ │
|
|
52
|
+
│ │ │ Your App │ │ │
|
|
53
|
+
│ │ │ /settings route │ │ │
|
|
54
|
+
│ │ │ │ │ │
|
|
55
|
+
│ │ │ Configuration UI │ │ │
|
|
56
|
+
│ │ │ for administrators │ │ │
|
|
57
|
+
│ │ └─────────────────────┘ │ │
|
|
58
|
+
│ └───────────────────────────┘ │
|
|
59
|
+
└─────────────────────────────────┘
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Characteristics:**
|
|
63
|
+
- Runs in Studio admin portal (user's browser)
|
|
64
|
+
- NO access to `store().device` (throws error!)
|
|
65
|
+
- Admin-facing UI for configuration
|
|
66
|
+
- Side panel in Studio editor
|
|
67
|
+
- Form-based controls for settings
|
|
68
|
+
|
|
69
|
+
## Communication Flow
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
|
|
73
|
+
│ Settings │ ───▶ │ store(). │ ───▶ │ Render │
|
|
74
|
+
│ /settings │ WRITE │ instance │ READ │ /render │
|
|
75
|
+
│ │ │ │ +SUB │ │
|
|
76
|
+
│ Admin sets │ │ { city: 'NYC' │ │ Displays │
|
|
77
|
+
│ city='NYC' │ │ units: 'F' } │ │ NYC weather│
|
|
78
|
+
└─────────────┘ └─────────────────┘ └─────────────┘
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
1. **Settings writes** to instance store via hooks
|
|
82
|
+
2. **Render subscribes** to instance store via same hooks
|
|
83
|
+
3. Changes sync automatically (real-time)
|
|
84
|
+
4. Same hooks work in both mount points
|
|
85
|
+
|
|
86
|
+
## Project Structure
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
src/
|
|
90
|
+
├── index.tsx # Entry point - configure SDK here
|
|
91
|
+
├── App.tsx # Router - directs to correct view
|
|
92
|
+
├── views/
|
|
93
|
+
│ ├── Settings.tsx # /settings mount point
|
|
94
|
+
│ └── Render.tsx # /render mount point
|
|
95
|
+
├── hooks/
|
|
96
|
+
│ └── store.ts # Shared store hooks (used in both views)
|
|
97
|
+
├── components/ # Reusable UI components
|
|
98
|
+
├── types/ # TypeScript interfaces
|
|
99
|
+
└── utils/ # Helper functions
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Routing
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// App.tsx
|
|
106
|
+
import Settings from './views/Settings'
|
|
107
|
+
import Render from './views/Render'
|
|
108
|
+
|
|
109
|
+
export function App() {
|
|
110
|
+
const path = window.location.pathname
|
|
111
|
+
|
|
112
|
+
if (path === '/settings') return <Settings />
|
|
113
|
+
if (path === '/render') return <Render />
|
|
114
|
+
|
|
115
|
+
return <div>Invalid mount point: {path}</div>
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or with React Router:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { createBrowserRouter, RouterProvider } from 'react-router'
|
|
123
|
+
import Settings from './views/Settings'
|
|
124
|
+
import Render from './views/Render'
|
|
125
|
+
|
|
126
|
+
const router = createBrowserRouter([
|
|
127
|
+
{ path: '/render', element: <Render /> },
|
|
128
|
+
{ path: '/settings', element: <Settings /> },
|
|
129
|
+
])
|
|
130
|
+
|
|
131
|
+
export function App() {
|
|
132
|
+
return <RouterProvider router={router} />
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Runtime Environment
|
|
137
|
+
|
|
138
|
+
### Both Mount Points
|
|
139
|
+
|
|
140
|
+
- **Browser:** Modern Chrome (platform-controlled version)
|
|
141
|
+
- **Execution:** Iframe sandbox
|
|
142
|
+
- **Runtime:** Client-side only (no SSR, no Node.js)
|
|
143
|
+
- **APIs:** Full browser APIs (Fetch, WebSocket, Canvas, WebGL)
|
|
144
|
+
- **SDK:** `@telemetryos/sdk` for platform communication
|
|
145
|
+
|
|
146
|
+
### Render Only
|
|
147
|
+
|
|
148
|
+
- `store().device` - Device-local storage
|
|
149
|
+
- `devices().getInformation()` - Hardware info
|
|
150
|
+
- Physical display output
|
|
151
|
+
- Background worker communication
|
|
152
|
+
|
|
153
|
+
### Settings Only
|
|
154
|
+
|
|
155
|
+
- SettingsUI components (`@telemetryos/sdk/react`)
|
|
156
|
+
- Runs in Studio portal
|
|
157
|
+
- Form-based interaction
|
|
158
|
+
- Preview pane integration
|
|
159
|
+
|
|
160
|
+
## telemetry.config.json
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"name": "my-app",
|
|
165
|
+
"version": "1.0.0",
|
|
166
|
+
"mountPoints": {
|
|
167
|
+
"render": "/render",
|
|
168
|
+
"settings": "/settings"
|
|
169
|
+
},
|
|
170
|
+
"devServer": {
|
|
171
|
+
"runCommand": "vite --port 3000",
|
|
172
|
+
"url": "http://localhost:3000"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Mount Point Configuration
|
|
178
|
+
|
|
179
|
+
| Mount Point | Purpose | Route |
|
|
180
|
+
|-------------|---------|-------|
|
|
181
|
+
| render | Device display | /render |
|
|
182
|
+
| settings | Admin config UI | /settings |
|
|
183
|
+
|
|
184
|
+
### Optional: Workers
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"backgroundWorkers": {
|
|
189
|
+
"sync": "workers/sync.js"
|
|
190
|
+
},
|
|
191
|
+
"serverWorkers": {
|
|
192
|
+
"api": "workers/api.js"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Store Scopes
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
┌────────────────────────────────────────────────────────────────┐
|
|
201
|
+
│ Account │
|
|
202
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
203
|
+
│ │ store().application │ │
|
|
204
|
+
│ │ Shared across ALL instances of this app │ │
|
|
205
|
+
│ │ (API keys, account-wide settings) │ │
|
|
206
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
207
|
+
│ │
|
|
208
|
+
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
|
209
|
+
│ │ App Instance 1 │ │ App Instance 2 │ │
|
|
210
|
+
│ │ instance store │ │ instance store │ │
|
|
211
|
+
│ │ (Settings↔Render) │ │ (Settings↔Render) │ │
|
|
212
|
+
│ └─────────────────────┘ └─────────────────────┘ │
|
|
213
|
+
└────────────────────────────────────────────────────────────────┘
|
|
214
|
+
|
|
215
|
+
┌────────────────────────────────────────────────────────────────┐
|
|
216
|
+
│ Physical Device │
|
|
217
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
218
|
+
│ │ store().device │ │
|
|
219
|
+
│ │ Local to this device only (Render only!) │ │
|
|
220
|
+
│ │ (Cache, calibration, device-specific data) │ │
|
|
221
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
222
|
+
└────────────────────────────────────────────────────────────────┘
|
|
223
|
+
|
|
224
|
+
┌────────────────────────────────────────────────────────────────┐
|
|
225
|
+
│ store().shared('namespace') │
|
|
226
|
+
│ Inter-app communication (any app can read/write) │
|
|
227
|
+
│ (Weather data sharing, event broadcasting) │
|
|
228
|
+
└────────────────────────────────────────────────────────────────┘
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Development Workflow
|
|
232
|
+
|
|
233
|
+
### Local Development
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# Start dev server
|
|
237
|
+
tos serve
|
|
238
|
+
# Or: npm run dev
|
|
239
|
+
|
|
240
|
+
# Access locally:
|
|
241
|
+
# Settings: http://localhost:3000/settings
|
|
242
|
+
# Render: http://localhost:3000/render
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Build & Deploy
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# Build production
|
|
249
|
+
npm run build
|
|
250
|
+
|
|
251
|
+
# Deploy via Git
|
|
252
|
+
git add . && git commit -m "Update" && git push
|
|
253
|
+
# GitHub integration auto-deploys
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Common Patterns
|
|
257
|
+
|
|
258
|
+
### Check Mount Point
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const isSettings = window.location.pathname === '/settings'
|
|
262
|
+
const isRender = window.location.pathname === '/render'
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Conditional Features
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
// In a shared component
|
|
269
|
+
const isRender = window.location.pathname === '/render'
|
|
270
|
+
|
|
271
|
+
// Only fetch external data in Render
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
if (!isRender) return
|
|
274
|
+
fetchExternalData()
|
|
275
|
+
}, [isRender])
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Handle Missing Config
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// In Render.tsx
|
|
282
|
+
const [isLoading, city] = useCityStoreState()
|
|
283
|
+
|
|
284
|
+
if (isLoading) return <div>Loading...</div>
|
|
285
|
+
if (!city) return <div>Please configure city in Settings</div>
|
|
286
|
+
|
|
287
|
+
return <WeatherDisplay city={city} />
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Debugging
|
|
291
|
+
|
|
292
|
+
### Check Current Path
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
console.log('Current path:', window.location.pathname)
|
|
296
|
+
console.log('Is Settings:', window.location.pathname === '/settings')
|
|
297
|
+
console.log('Is Render:', window.location.pathname === '/render')
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Verify SDK Configuration
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// In index.tsx - name must match telemetry.config.json
|
|
304
|
+
configure('my-app')
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Check Store Values
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// Temporary debug
|
|
311
|
+
const value = await store().instance.get('myKey')
|
|
312
|
+
console.log('Store value:', value)
|
|
313
|
+
```
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tos-debugging
|
|
3
|
+
description: Debug common TelemetryOS application issues and errors. Use when encountering SDK errors, sync issues, or render problems.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TelemetryOS Debugging Guide
|
|
7
|
+
|
|
8
|
+
Common errors, their causes, and solutions.
|
|
9
|
+
|
|
10
|
+
## Error Reference
|
|
11
|
+
|
|
12
|
+
### "SDK not configured"
|
|
13
|
+
|
|
14
|
+
**Cause:** SDK methods called before `configure()`.
|
|
15
|
+
|
|
16
|
+
**Solution:**
|
|
17
|
+
```typescript
|
|
18
|
+
// index.tsx - MUST call configure before React renders
|
|
19
|
+
import { configure } from '@telemetryos/sdk'
|
|
20
|
+
|
|
21
|
+
configure('my-app') // Name must match telemetry.config.json
|
|
22
|
+
|
|
23
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Check:**
|
|
27
|
+
- Is `configure()` called in index.tsx?
|
|
28
|
+
- Is it called BEFORE ReactDOM.createRoot()?
|
|
29
|
+
- Does the name match `telemetry.config.json`?
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### "device storage not available"
|
|
34
|
+
|
|
35
|
+
**Cause:** Using `store().device` in Settings view.
|
|
36
|
+
|
|
37
|
+
**Why:** Settings runs in Studio (admin's browser), not on devices. Device storage is only available on physical TelemetryOS devices.
|
|
38
|
+
|
|
39
|
+
**Solution:**
|
|
40
|
+
```typescript
|
|
41
|
+
// WRONG - in Settings.tsx
|
|
42
|
+
const [, value, setValue] = useMyState() // using createUseDeviceStoreState
|
|
43
|
+
|
|
44
|
+
// CORRECT - use instance scope
|
|
45
|
+
const [, value, setValue] = useMyState() // using createUseInstanceStoreState
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Scope guide:**
|
|
49
|
+
- `createUseInstanceStoreState` - Settings ↔ Render communication
|
|
50
|
+
- `createUseApplicationStoreState` - Shared across all instances
|
|
51
|
+
- `createUseDeviceStoreState` - Render ONLY (device-local)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### CORS Error
|
|
56
|
+
|
|
57
|
+
**Symptom:** Browser console shows:
|
|
58
|
+
```
|
|
59
|
+
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000'
|
|
60
|
+
has been blocked by CORS policy
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Cause:** External API doesn't include CORS headers.
|
|
64
|
+
|
|
65
|
+
**Solution:**
|
|
66
|
+
```typescript
|
|
67
|
+
// WRONG - blocked by CORS
|
|
68
|
+
const response = await fetch('https://api.example.com/data')
|
|
69
|
+
|
|
70
|
+
// CORRECT - use proxy
|
|
71
|
+
import { proxy } from '@telemetryos/sdk'
|
|
72
|
+
const response = await proxy().fetch('https://api.example.com/data')
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### "Request timeout"
|
|
78
|
+
|
|
79
|
+
**Cause:** SDK operation exceeded 30-second timeout.
|
|
80
|
+
|
|
81
|
+
**Solution:**
|
|
82
|
+
```typescript
|
|
83
|
+
try {
|
|
84
|
+
const response = await proxy().fetch(url)
|
|
85
|
+
// ...
|
|
86
|
+
} catch (err) {
|
|
87
|
+
if (err instanceof Error && err.message.includes('timeout')) {
|
|
88
|
+
// Handle timeout - maybe retry or show error
|
|
89
|
+
console.error('Request timed out')
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Prevention:**
|
|
95
|
+
- Check if API endpoint is slow or unreachable
|
|
96
|
+
- Add loading states to prevent user confusion
|
|
97
|
+
- Consider retry logic for intermittent issues
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### Render Not Updating
|
|
102
|
+
|
|
103
|
+
**Symptoms:**
|
|
104
|
+
- Settings changes don't appear in Render
|
|
105
|
+
- Render shows stale data
|
|
106
|
+
- Preview in Studio doesn't update
|
|
107
|
+
|
|
108
|
+
**Causes & Solutions:**
|
|
109
|
+
|
|
110
|
+
**1. Missing store hook in Render:**
|
|
111
|
+
```typescript
|
|
112
|
+
// Render.tsx - MUST use same hook as Settings
|
|
113
|
+
import { useCityState } from '../hooks/store'
|
|
114
|
+
|
|
115
|
+
const [isLoading, city] = useCityState()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**2. Not using instance scope:**
|
|
119
|
+
```typescript
|
|
120
|
+
// WRONG - different scopes don't sync
|
|
121
|
+
// Settings: createUseApplicationStoreState
|
|
122
|
+
// Render: createUseInstanceStoreState
|
|
123
|
+
|
|
124
|
+
// CORRECT - same scope
|
|
125
|
+
// Both use: createUseInstanceStoreState
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**3. Missing subscription (low-level API):**
|
|
129
|
+
```typescript
|
|
130
|
+
// If using manual subscription
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
const handler = (value) => setValue(value)
|
|
133
|
+
store().instance.subscribe('key', handler)
|
|
134
|
+
|
|
135
|
+
return () => store().instance.unsubscribe('key', handler) // IMPORTANT!
|
|
136
|
+
}, [])
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### Memory Leak Warning
|
|
142
|
+
|
|
143
|
+
**Symptom:** React warning about memory leak or state update on unmounted component.
|
|
144
|
+
|
|
145
|
+
**Cause:** Not cleaning up store subscriptions.
|
|
146
|
+
|
|
147
|
+
**Solution - Use SDK hooks (recommended):**
|
|
148
|
+
```typescript
|
|
149
|
+
// Automatic cleanup
|
|
150
|
+
const [isLoading, value, setValue] = useMyState()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Solution - Manual subscription:**
|
|
154
|
+
```typescript
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
const handler = (value) => setValue(value)
|
|
157
|
+
store().instance.subscribe('key', handler)
|
|
158
|
+
|
|
159
|
+
// MUST return cleanup function
|
|
160
|
+
return () => {
|
|
161
|
+
store().instance.unsubscribe('key', handler)
|
|
162
|
+
}
|
|
163
|
+
}, [])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### Blank Screen
|
|
169
|
+
|
|
170
|
+
**Check these in order:**
|
|
171
|
+
|
|
172
|
+
1. **Browser console for errors**
|
|
173
|
+
- Open DevTools (F12)
|
|
174
|
+
- Check Console tab for red errors
|
|
175
|
+
|
|
176
|
+
2. **Correct route**
|
|
177
|
+
```
|
|
178
|
+
http://localhost:3000/render ← for Render view
|
|
179
|
+
http://localhost:3000/settings ← for Settings view
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
3. **App.tsx routing**
|
|
183
|
+
```typescript
|
|
184
|
+
if (path === '/settings') return <Settings />
|
|
185
|
+
if (path === '/render') return <Render />
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
4. **SDK configured**
|
|
189
|
+
```typescript
|
|
190
|
+
// index.tsx
|
|
191
|
+
configure('app-name') // Before React renders
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
5. **telemetry.config.json matches**
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"name": "app-name", // Must match configure()
|
|
198
|
+
"mountPoints": {
|
|
199
|
+
"render": "/render",
|
|
200
|
+
"settings": "/settings"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### TypeScript Errors
|
|
208
|
+
|
|
209
|
+
**"Cannot find module '@telemetryos/sdk'"**
|
|
210
|
+
```bash
|
|
211
|
+
npm install @telemetryos/sdk
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**"Property does not exist on type"**
|
|
215
|
+
```typescript
|
|
216
|
+
// Add type parameter to store operations
|
|
217
|
+
const value = await store().instance.get<MyType>('key')
|
|
218
|
+
|
|
219
|
+
// Or define interfaces
|
|
220
|
+
interface Config {
|
|
221
|
+
city: string
|
|
222
|
+
units: 'imperial' | 'metric'
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**"Type 'string | undefined' is not assignable"**
|
|
227
|
+
```typescript
|
|
228
|
+
// Handle undefined from store
|
|
229
|
+
const config = await store().instance.get<Config>('config')
|
|
230
|
+
if (config) {
|
|
231
|
+
// config is now Config, not Config | undefined
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Debugging Techniques
|
|
238
|
+
|
|
239
|
+
### Console Logging
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// Check current path
|
|
243
|
+
console.log('Path:', window.location.pathname)
|
|
244
|
+
|
|
245
|
+
// Check store values
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
store().instance.get('myKey').then(value => {
|
|
248
|
+
console.log('Store value:', value)
|
|
249
|
+
})
|
|
250
|
+
}, [])
|
|
251
|
+
|
|
252
|
+
// Check hook values
|
|
253
|
+
const [isLoading, value] = useMyState()
|
|
254
|
+
console.log('isLoading:', isLoading, 'value:', value)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Network Tab
|
|
258
|
+
|
|
259
|
+
1. Open DevTools → Network tab
|
|
260
|
+
2. Look for failed requests (red)
|
|
261
|
+
3. Check request/response details
|
|
262
|
+
4. Verify API endpoints and headers
|
|
263
|
+
|
|
264
|
+
### React DevTools
|
|
265
|
+
|
|
266
|
+
1. Install React DevTools browser extension
|
|
267
|
+
2. Inspect component props and state
|
|
268
|
+
3. Check hook values
|
|
269
|
+
4. Trace re-renders
|
|
270
|
+
|
|
271
|
+
### Local Development URLs
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
Settings: http://localhost:3000/settings
|
|
275
|
+
Render: http://localhost:3000/render
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Quick Fixes Checklist
|
|
281
|
+
|
|
282
|
+
- [ ] `configure()` called before React in index.tsx
|
|
283
|
+
- [ ] App name matches telemetry.config.json
|
|
284
|
+
- [ ] Using `createUseInstanceStoreState` (not device) in Settings
|
|
285
|
+
- [ ] Same store hooks in Settings and Render
|
|
286
|
+
- [ ] `disabled={isLoading}` on form inputs
|
|
287
|
+
- [ ] Error handling with try/catch
|
|
288
|
+
- [ ] Cleanup in useEffect return
|
|
289
|
+
- [ ] CORS issues → use `proxy().fetch()`
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Getting Help
|
|
294
|
+
|
|
295
|
+
1. **Check browser console** - Most errors show here first
|
|
296
|
+
2. **Check network tab** - API failures visible here
|
|
297
|
+
3. **Read error message** - SDK errors are descriptive
|
|
298
|
+
4. **Simplify** - Remove code until error disappears, then add back
|
|
299
|
+
5. **Compare to template** - Check working template code
|