@telemetryos/cli 1.9.0 → 1.11.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 (41) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/commands/auth.js +8 -15
  3. package/dist/commands/init.js +131 -68
  4. package/dist/commands/publish.d.ts +22 -0
  5. package/dist/commands/publish.js +238 -0
  6. package/dist/index.js +2 -0
  7. package/dist/plugins/math-tools.d.ts +2 -0
  8. package/dist/plugins/math-tools.js +18 -0
  9. package/dist/services/api-client.d.ts +18 -0
  10. package/dist/services/api-client.js +70 -0
  11. package/dist/services/archiver.d.ts +4 -0
  12. package/dist/services/archiver.js +65 -0
  13. package/dist/services/build-poller.d.ts +10 -0
  14. package/dist/services/build-poller.js +63 -0
  15. package/dist/services/cli-config.d.ts +10 -0
  16. package/dist/services/cli-config.js +45 -0
  17. package/dist/services/generate-application.d.ts +2 -1
  18. package/dist/services/generate-application.js +31 -32
  19. package/dist/services/project-config.d.ts +24 -0
  20. package/dist/services/project-config.js +51 -0
  21. package/dist/services/run-server.js +29 -73
  22. package/dist/types/api.d.ts +44 -0
  23. package/dist/types/api.js +1 -0
  24. package/dist/types/applications.d.ts +44 -0
  25. package/dist/types/applications.js +1 -0
  26. package/dist/utils/ansi.d.ts +10 -0
  27. package/dist/utils/ansi.js +10 -0
  28. package/dist/utils/path-utils.d.ts +55 -0
  29. package/dist/utils/path-utils.js +99 -0
  30. package/package.json +4 -2
  31. package/templates/vite-react-typescript/CLAUDE.md +14 -6
  32. package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +4 -28
  33. package/templates/vite-react-typescript/_claude/skills/tos-multi-mode/SKILL.md +359 -0
  34. package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +304 -12
  35. package/templates/vite-react-typescript/_claude/skills/tos-render-kiosk-design/SKILL.md +384 -0
  36. package/templates/vite-react-typescript/_claude/skills/tos-render-signage-design/SKILL.md +515 -0
  37. package/templates/vite-react-typescript/_claude/skills/tos-render-ui-design/SKILL.md +325 -0
  38. package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +405 -125
  39. package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +96 -5
  40. package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +443 -269
  41. package/templates/vite-react-typescript/index.html +1 -1
@@ -26,6 +26,7 @@ import {
26
26
  createUseInstanceStoreState,
27
27
  createUseApplicationStoreState,
28
28
  createUseDeviceStoreState,
29
+ createUseDynamicNamespaceStoreState,
29
30
  } from '@telemetryos/sdk/react'
30
31
 
31
32
  // Localization (instance scope)
@@ -39,16 +40,33 @@ export const useApiKeyStoreState = createUseApplicationStoreState<string>('apiKe
39
40
  export const useBrightnessStoreState = createUseDeviceStoreState<number>('brightness', 100)
40
41
  ```
41
42
 
42
- **Usage:**
43
+ **Usage (Always Handle isLoading):**
44
+
45
+ ⚠️ **Important:** The `isLoading` boolean is `true` until the store returns the first value. During this time, the `value` is either the `initialValue` or potentially `undefined`. Always check `isLoading` before relying on the value.
46
+
43
47
  ```typescript
44
48
  // Instance-scoped (Settings + Render) - 250ms debounce for text input
45
- const [, city, setCity] = useCityStoreState(250)
49
+ const [isLoading, city, setCity] = useCityStoreState(250)
46
50
 
47
- // Application-scoped (shared across all instances) - 250ms debounce for text input
48
- const [, apiKey, setApiKey] = useApiKeyStoreState(250)
51
+ // BAD: Ignoring isLoading can cause bugs - value may not be from store yet!
52
+ // const [, city, setCity] = useCityStoreState(250)
53
+ // return <div>{city}</div> // May show default value instead of real data
54
+
55
+ // ✅ GOOD Pattern 1: Check isLoading before rendering
56
+ if (isLoading) return <Spinner />
57
+ return <div>Weather for {city}</div>
58
+
59
+ // ✅ GOOD Pattern 2: Use fallback value with nullish coalescing
60
+ return <div>Weather for {city ?? "Unknown City"}</div>
61
+
62
+ // Application-scoped (shared across all instances) - 250ms debounce
63
+ const [isLoading, apiKey, setApiKey] = useApiKeyStoreState(250)
64
+ if (isLoading) return <LoadingState />
65
+ return <APIClient apiKey={apiKey} />
49
66
 
50
67
  // Device-scoped (Render only - NOT available in Settings) - 5ms for slider
51
- const [, brightness, setBrightness] = useBrightnessStoreState(5)
68
+ const [isLoading, brightness, setBrightness] = useBrightnessStoreState(5)
69
+ const safeValue = brightness ?? 100 // Fallback to default if still loading
52
70
  ```
53
71
 
54
72
  ## Quick Pattern
@@ -179,6 +197,73 @@ const [isLoading, temp] = useTempStoreState()
179
197
  - Event broadcasting
180
198
  - Coordinated updates
181
199
 
200
+ ### createUseDynamicNamespaceStoreState
201
+
202
+ Like shared store, but the namespace is passed at **call time** instead of definition time. Use when the namespace is dynamic — derived from other data at runtime (e.g., a user-selected location, a route parameter, or any runtime key).
203
+
204
+ > **Building a multi-mode app?** For the complete architecture pattern using dynamic namespaces with mode switching and entity management, see the `tos-multi-mode` skill.
205
+
206
+ ```typescript
207
+ import { createUseDynamicNamespaceStoreState } from '@telemetryos/sdk/react'
208
+
209
+ // Define with key + default only (no namespace yet)
210
+ export const useItemsStoreState = createUseDynamicNamespaceStoreState<Item[]>('items', [])
211
+
212
+ // Usage - pass namespace AND debounceDelay at call time
213
+ const [isLoading, items, setItems] = useItemsStoreState(namespace, 250)
214
+ ```
215
+
216
+ **Key difference from `createUseSharedStoreState`:**
217
+
218
+ | | `createUseSharedStoreState` | `createUseDynamicNamespaceStoreState` |
219
+ |---|---|---|
220
+ | **Namespace** | Fixed at hook definition | Passed at call time |
221
+ | **Definition** | `(key, default, namespace)` | `(key, default)` |
222
+ | **Call signature** | `(debounceDelay?)` | `(namespace, debounceDelay)` |
223
+ | **Use when** | Namespace is always the same | Namespace depends on runtime data |
224
+
225
+ **Use cases:**
226
+ - Multi-location apps (same data structure per location)
227
+ - User-selected scopes or categories
228
+ - Data partitioned by a runtime key (route param, selected item, etc.)
229
+
230
+ **Pattern: Namespace helper + instance-scoped selector**
231
+
232
+ A common pattern is combining an instance-scoped hook (to select which namespace) with dynamic namespace hooks (to access that namespace's data):
233
+
234
+ ```typescript
235
+ // hooks/store.ts
236
+ import {
237
+ createUseInstanceStoreState,
238
+ createUseDynamicNamespaceStoreState,
239
+ } from '@telemetryos/sdk/react'
240
+
241
+ // Instance-scoped: which location this device is assigned to
242
+ export const useSelectedLocationStoreState = createUseInstanceStoreState<string>('selected-location', 'Location A')
243
+
244
+ // Dynamic namespace: per-location data
245
+ export const useQueuesStoreState = createUseDynamicNamespaceStoreState<Queue[]>('queues', [])
246
+ export const useCountersStoreState = createUseDynamicNamespaceStoreState<Counter[]>('counters', [])
247
+
248
+ // Helper to build namespace from location
249
+ export function locationNamespace(location: string): string {
250
+ return `my-app-${location}`
251
+ }
252
+ ```
253
+
254
+ ```typescript
255
+ // In a component
256
+ const [isLoadingLocation, selectedLocation] = useSelectedLocationStoreState()
257
+ const ns = locationNamespace(selectedLocation)
258
+
259
+ const [isLoadingQueues, queues, setQueues] = useQueuesStoreState(ns, 250)
260
+ const [isLoadingCounters, counters, setCounters] = useCountersStoreState(ns, 250)
261
+
262
+ if (isLoadingLocation || isLoadingQueues || isLoadingCounters) return <Spinner />
263
+ ```
264
+
265
+ When `selectedLocation` changes, the namespace changes, and the hooks automatically re-subscribe to the new namespace's data.
266
+
182
267
  ## Return Value
183
268
 
184
269
  All hooks return a tuple:
@@ -199,6 +284,12 @@ The default debounce delay is 0ms (immediate updates). Pass a value for debounce
199
284
  const [isLoading, city, setCity] = useCityStoreState(250) // 250ms debounce for text inputs
200
285
  ```
201
286
 
287
+ **Note:** `createUseDynamicNamespaceStoreState` hooks take `(namespace, debounceDelay)` — namespace is required at call time:
288
+
289
+ ```typescript
290
+ const [isLoading, items, setItems] = useItemsStoreState(namespace, 250)
291
+ ```
292
+
202
293
  ## TypeScript Types
203
294
 
204
295
  ### Primitive Types