@telemetryos/root-sdk 1.14.0 → 1.16.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 CHANGED
@@ -1,5 +1,43 @@
1
1
  # @telemetryos/root-sdk
2
2
 
3
+ ## 1.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 15a06b5: Iframe management, heartbeat logic, and SDK fixes
8
+ - Added iframe lifecycle management and heartbeat logic for monitoring application health
9
+ - Introduced dependency store for tracking application dependencies
10
+ - Added subscription registry for managing real-time subscriptions in the API coordinator
11
+ - Re-exported missing root-sdk types and utilities from the sdk package (Navigation class, media types, bridge utilities, client types)
12
+ - Fixed application store namespace from incorrect value to "application"
13
+ - Removed unused media fields from root-sdk
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [15a06b5]
18
+ - @telemetryos/sdk-bridge@1.16.0
19
+
20
+ ## 1.15.0
21
+
22
+ ### Minor Changes
23
+
24
+ - ### New Features
25
+ - **Theme system** — Templates now ship with 8 built-in themes (`telemetryos`, `neon-pulse`, `solar-flare`, `arctic-aurora`, `emerald-matrix`, `the-matrix`, `plain-light`, `plain-dark`), each with a complete color palette including gradients, text colors, accents, and status indicators.
26
+ - **FlickeringGrid component** — New canvas-based animated background component with configurable square size, grid gap, flicker chance, opacity, and color.
27
+ - **SettingsSection component** — New collapsible accordion component exported from `@telemetryos/sdk` for organizing settings into expandable sections. Includes `aria-expanded`/`aria-controls` and `inert` support.
28
+ - **Settings form styles** — New shared CSS (`@telemetryos/sdk/react/styles.css`) with light/dark theme variables for text inputs, selects, sliders, color pickers, toggles, and checkboxes.
29
+ - **Expanded entrance animations** — Added `flip`, `fade`, `scale`, `slide`, `drop`, `fade-in`, `blur`, `zoom`, `unfold`, `rise`, `glitch`, and `breathe-glow` animations; removed `swing`.
30
+ - **Dark wordmark** — Added `telemetryos-wordmark-dark.svg` to template assets.
31
+ - **Root-SDK bridge re-exports** — `@telemetryos/root-sdk` now re-exports `BridgeMessage`, `ClientMessage`, and their validators/formatters from `@telemetryos/sdk-bridge`, so consumers no longer need a direct `sdk-bridge` import.
32
+
33
+ ### Infrastructure
34
+ - Moved template assets from `assets/` to `public/assets/` for automatic Vite build inclusion (both `vite-react-typescript` and `vite-react-typescript-web` templates).
35
+
36
+ ### Patch Changes
37
+
38
+ - Updated dependencies
39
+ - @telemetryos/sdk-bridge@1.15.0
40
+
3
41
  ## 1.14.0
4
42
 
5
43
  ### Minor Changes
@@ -1,22 +1,108 @@
1
1
  import { Client, MessageInterceptor } from './client.js';
2
+ /** The source control origin of an application. */
3
+ export type ApplicationSourceKind = 'github' | 'git' | 'uploaded';
4
+ /** A named entry point that maps a surface (e.g. render, settings, web) to a file path. */
2
5
  export type MountPoint = {
3
- [key: string]: any;
6
+ name: string;
4
7
  path: string;
5
8
  };
9
+ /** A background worker script that runs alongside the application. */
10
+ export type BackgroundWorker = {
11
+ name: string;
12
+ path: string;
13
+ };
14
+ /** A Docker container that runs alongside the application on devices. */
15
+ export type Container = unknown;
16
+ /** Full application record including source, build, and metadata fields. */
6
17
  export type Application = {
18
+ kind: ApplicationSourceKind;
19
+ gitUrl?: string;
20
+ gitRef?: string;
21
+ hasGitSshPrivateKey?: boolean;
22
+ hasHttpCredentials?: boolean;
23
+ hasGithubToken?: boolean;
24
+ githubAccount?: string;
25
+ githubRepository?: string;
26
+ baseImage: string;
27
+ buildWorkingPath?: string;
28
+ buildScript?: string;
29
+ buildOutputPath?: string;
30
+ buildEnvironmentVariables?: Record<string, string>;
31
+ id: string;
32
+ title: string;
33
+ description: string;
34
+ isEnabled: boolean;
35
+ isProtected: boolean;
36
+ isGlobal: boolean;
37
+ isBlock: boolean;
38
+ useLatest: boolean;
39
+ versions: ApplicationVersion[];
40
+ createdAt: string;
41
+ updatedAt: string;
42
+ };
43
+ /** A specific published version of an application. */
44
+ export type ApplicationVersion = {
7
45
  name: string;
8
- mountPoints: Record<string, MountPoint>;
46
+ version: string;
47
+ logoPath: string;
48
+ thumbnailPath: string;
49
+ mountPoints: MountPoint[];
50
+ backgroundWorkers: BackgroundWorker[];
51
+ containers: Container[];
52
+ applicationId: string;
53
+ buildId: string;
54
+ baseUrl: string;
55
+ /** 40-character lowercase hex string uniquely identifying this version. */
56
+ applicationSpecifier: string;
57
+ filePaths: string[];
58
+ publishedAt: string;
9
59
  };
60
+ /** Response from {@link Applications.getById}. */
61
+ export type GetByIdResult = {
62
+ application: Application;
63
+ };
64
+ /** Response from {@link Applications.getBySpecifier}. */
65
+ export type GetBySpecifierResult = {
66
+ application: Application;
67
+ };
68
+ /** Response from {@link Applications.getAllByMountPoint}. */
69
+ export type GetByMountPointResult = {
70
+ applications: Application[];
71
+ };
72
+ /** Response containing a resolved URL for an application asset. */
10
73
  export type GetUrlResult = {
11
74
  url: string;
12
75
  };
13
- export type ApplicationsState = {
14
- ready: string[];
15
- unavailable: string[];
76
+ /** The download/readiness status of a dependency application on the device. */
77
+ export type ApplicationStatus = 'unloaded' | 'downloading' | 'ready';
78
+ /** A real-time status update for a single dependency application. */
79
+ export type DependencyResult = {
80
+ status: ApplicationStatus;
81
+ application: Application | null;
16
82
  };
83
+ /**
84
+ * Provides methods for discovering, querying, and managing application
85
+ * dependencies within the TelemetryOS platform.
86
+ *
87
+ * Accessed via the `applications()` convenience function exported from the SDK.
88
+ */
17
89
  export declare class Applications {
18
90
  _client: Client;
19
91
  constructor(client: Client);
92
+ /**
93
+ * Retrieve a single application by its id.
94
+ *
95
+ * @param applicationId The unique identifier of the application to retrieve
96
+ * @returns A promise that resolves to the application data
97
+ */
98
+ getById(applicationId: string): Promise<Application>;
99
+ /**
100
+ * Retrieves a single application by specifier
101
+ *
102
+ * @param applicationSpecifier The unique specifier of the application to retrieve
103
+ * @returns A promise that resolves to the application data
104
+ */
105
+ getBySpecifier(applicationSpecifier: string): Promise<Application>;
20
106
  /**
21
107
  * Retrieves all applications with a specific mount point within the current account.
22
108
  *
@@ -31,17 +117,6 @@ export declare class Applications {
31
117
  * @returns A promise that resolves to an array of applications having the specified mount point
32
118
  */
33
119
  getAllByMountPoint(mountPoint: string): Promise<Application[]>;
34
- /**
35
- * Retrieves an application by its name.
36
- *
37
- * This method allows finding a specific application when you know its name. It's useful
38
- * when you need to check if a particular application is available or get its details
39
- * before attempting to embed it.
40
- *
41
- * @param name The name of the application to query for
42
- * @returns A promise that resolves to the application object if found, or null if not found
43
- */
44
- getByName(name: string): Promise<Application | null>;
45
120
  /**
46
121
  * Sets the dependencies for the current application.
47
122
  *
@@ -49,19 +124,23 @@ export declare class Applications {
49
124
  * The player will download and prepare these dependencies before they can be loaded.
50
125
  *
51
126
  * IMPORTANT: This method must be called and awaited before loading any sub-applications
52
- * in iframes. Only applications that return as 'ready' should be loaded.
127
+ * in iframes.
53
128
  *
54
- * @param applicationSpecifiers An array of application specifiers that this application depends on
55
- * @returns A promise that resolves with arrays of ready and unavailable application specifiers
129
+ * @param applicationSpecifiers A map of application specifier to an array of instance IDs
130
+ * @returns A function that returns a {@link DependencyHandle} for a given specifier and instance ID
56
131
  *
57
132
  * @example
58
133
  * ```typescript
59
- * const result = await client.applications.setDependencies(['app1-hash', 'app2-hash'])
60
- * // result.ready: ['app1-hash'] - these can be loaded in iframes
61
- * // result.unavailable: ['app2-hash'] - these failed to load
134
+ * const getHandle = await applications().setDependencies({
135
+ * 'app1-specifier-hash': ['instance-1', 'instance-2'],
136
+ * 'app2-specifier-hash': ['instance-1'],
137
+ * })
138
+ * const handle = getHandle('app1-specifier-hash', 'instance-1')
139
+ * handle.onStatus((status) => console.log(status))
140
+ * const iframe = await handle.frame()
62
141
  * ```
63
142
  */
64
- setDependencies(applicationSpecifiers: Record<string, string[]>): Promise<ApplicationsState>;
143
+ setDependencies(applicationSpecifiers: Record<string, string[]>): Promise<(applicationSpecifier: string, instanceId: string) => import("./dependency-store.js").DependencyHandle>;
65
144
  /**
66
145
  * Registers a message interceptor for client messages from sub-applications.
67
146
  *
package/dist/client.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Applications } from './applications.js';
2
2
  import { Media } from './media.js';
3
- import type { BridgeMessage } from '@telemetryos/sdk-bridge';
3
+ import type { BridgeMessage, ClientMessage } from '@telemetryos/sdk-bridge';
4
4
  import { Store } from './store.js';
5
5
  import { RootSettingsNavigation } from './root-settings-navigation.js';
6
6
  import { Accounts } from './accounts.js';
@@ -12,6 +12,7 @@ import { Currency } from './currency.js';
12
12
  import { Environment } from './environment.js';
13
13
  import { Mqtt } from './mqtt.js';
14
14
  import { Navigation } from './navigation.js';
15
+ import { DependencyStore } from './dependency-store.js';
15
16
  /**
16
17
  * The maximum time in milliseconds to wait for a response to a request call.
17
18
  *
@@ -23,6 +24,12 @@ import { Navigation } from './navigation.js';
23
24
  * platform doesn't respond or if network connectivity is lost.
24
25
  */
25
26
  export declare const requestResponseTimeout: number;
27
+ /**
28
+ * The interval the client will send it's heartbeat signature to the TelemetryOS platform.
29
+ * If the platform stops receiving the heartbeat, it will consider the client disconnected,
30
+ * and clean it's resources as well as try to reload the client application.
31
+ */
32
+ export declare const heartbeatInterval: number;
26
33
  /**
27
34
  * A callback function type for handling messages received from the TelemetryOS platform.
28
35
  *
@@ -61,7 +68,7 @@ export type SubscriptionResult<D = void> = {
61
68
  * }
62
69
  * ```
63
70
  */
64
- export type MessageInterceptor = (data: any) => BridgeMessage;
71
+ export type MessageInterceptor = (data: ClientMessage) => BridgeMessage | undefined | void;
65
72
  /**
66
73
  * Client is the core class that powers communication with the TelemetryOS platform.
67
74
  *
@@ -79,6 +86,9 @@ export declare class Client {
79
86
  _applicationInstance: string;
80
87
  _applicationSpecifier: string;
81
88
  _deviceId: string;
89
+ _heartbeatInterval: NodeJS.Timeout | null;
90
+ _heartbeatSignature: string;
91
+ _dependencyStore: DependencyStore;
82
92
  _navigation: Navigation;
83
93
  _messageInterceptors: Map<string, MessageInterceptor>;
84
94
  _onHandlers: Map<string, MessageHandler<any>[]>;
@@ -0,0 +1,103 @@
1
+ import { Application, ApplicationStatus, DependencyResult } from './applications.js';
2
+ import { Client } from './client.js';
3
+ /**
4
+ * Manages the set of declared dependency applications and their handles.
5
+ *
6
+ * A single DependencyStore is created per {@link Client} and maintains one
7
+ * {@link DependencyHandle} per specifier+instanceId pair. When dependencies
8
+ * are updated via {@link setDependencies}, handles for removed dependencies
9
+ * are destroyed automatically.
10
+ *
11
+ * The store also intercepts heartbeat messages from child iframes. If a
12
+ * dependency stops sending heartbeats, its iframe is automatically reloaded.
13
+ */
14
+ export declare class DependencyStore {
15
+ _client: Client;
16
+ _dependencies: Map<string, DependencyHandle>;
17
+ _heartbeatTimeouts: Map<string, NodeJS.Timeout>;
18
+ constructor(client: Client);
19
+ /**
20
+ * Sends the full set of dependency specifiers to the platform and reconciles
21
+ * local handles. Any previously tracked dependency that is no longer present
22
+ * in the new specifiers map is destroyed.
23
+ *
24
+ * @param applicationSpecifiers A map of application specifier to an array of instance IDs
25
+ */
26
+ setDependencies(applicationSpecifiers: Record<string, string[]>): Promise<void>;
27
+ /**
28
+ * Returns the {@link DependencyHandle} for a given specifier and instance ID,
29
+ * creating one if it does not already exist.
30
+ *
31
+ * @param specifier The application specifier (40-char hex)
32
+ * @param instanceId The unique instance identifier
33
+ * @returns The existing or newly created DependencyHandle
34
+ */
35
+ handleFor(specifier: string, instanceId: string): DependencyHandle;
36
+ /**
37
+ * Resets the heartbeat timeout for the given dependency. If no heartbeat
38
+ * arrives within {@link missedHeartbeatsThreshold} intervals the
39
+ * dependency's iframe is reloaded.
40
+ */
41
+ _handleHeartbeat(applicationSpecifier: string, applicationInstance: string): void;
42
+ }
43
+ /**
44
+ * A reactive handle to a single dependency application.
45
+ *
46
+ * Created by {@link DependencyStore.handleFor}, a DependencyHandle subscribes to
47
+ * real-time status updates from the platform for its specifier+instanceId pair.
48
+ * Consumers can observe status changes via {@link onStatus} and obtain an iframe
49
+ * element for embedding via {@link frame}.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const handle = getHandle('specifier', 'instance-1')
54
+ * handle.onStatus((status) => console.log('status:', status))
55
+ * const iframe = await handle.frame()
56
+ * if (iframe) document.body.appendChild(iframe)
57
+ * ```
58
+ */
59
+ export declare class DependencyHandle {
60
+ _store: DependencyStore;
61
+ _specifier: string;
62
+ _instanceId: string;
63
+ _status: ApplicationStatus;
64
+ _application: Application | null;
65
+ _iframe: HTMLIFrameElement | null;
66
+ _onStatusHandlers: Set<(status: ApplicationStatus) => void>;
67
+ _subscriptionHandler: (message: DependencyResult) => void;
68
+ /** The current download/readiness status of the dependency. */
69
+ get status(): ApplicationStatus;
70
+ /** A deep clone of the application record, or null if not yet available. */
71
+ get application(): Application | null;
72
+ constructor(store: DependencyStore, specifier: string, instanceId: string);
73
+ /**
74
+ * Returns a promise that resolves with an iframe element once the dependency
75
+ * is ready, or null if the dependency becomes unloaded before it is ready.
76
+ *
77
+ * If the dependency is already ready, the promise resolves immediately with
78
+ * the cached iframe. The iframe's `src` is pre-configured for the dependency.
79
+ *
80
+ * @returns A promise resolving to the iframe element or null
81
+ */
82
+ frame(): Promise<HTMLIFrameElement | null>;
83
+ /**
84
+ * Registers a callback that fires whenever the dependency status changes.
85
+ *
86
+ * @param handler Callback receiving the new {@link ApplicationStatus}
87
+ * @returns An unsubscribe function that removes the handler
88
+ */
89
+ onStatus(handler: (status: ApplicationStatus) => void): () => void;
90
+ _buildIframe(): HTMLIFrameElement | null;
91
+ /**
92
+ * Resets the iframe by clearing it, requesting the host to clean up
93
+ * subscriptions and state for this instance, then reloading it if the
94
+ * iframe is still in the DOM.
95
+ */
96
+ _resetIframe(): Promise<void>;
97
+ /**
98
+ * Tears down this handle: resets status to 'unloaded', blanks the iframe,
99
+ * requests the host to clean up all subscriptions (window + worker),
100
+ * notifies status handlers, and unsubscribes from platform updates.
101
+ */
102
+ _destroy(): Promise<void>;
103
+ }