@enyo-energy/energy-app-sdk 0.0.113 → 0.0.115

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/dist/cjs/implementations/retry/backoff.cjs +58 -0
  2. package/dist/cjs/implementations/retry/backoff.d.cts +19 -0
  3. package/dist/cjs/implementations/retry/retry-errors.cjs +28 -0
  4. package/dist/cjs/implementations/retry/retry-errors.d.cts +18 -0
  5. package/dist/cjs/implementations/retry/retry-manager.cjs +182 -0
  6. package/dist/cjs/implementations/retry/retry-manager.d.cts +97 -0
  7. package/dist/cjs/index.cjs +6 -0
  8. package/dist/cjs/index.d.cts +6 -0
  9. package/dist/cjs/packages/energy-app-onboarding.d.cts +17 -1
  10. package/dist/cjs/types/enyo-appliance.cjs +6 -3
  11. package/dist/cjs/types/enyo-appliance.d.cts +25 -7
  12. package/dist/cjs/types/enyo-authentication.d.cts +10 -0
  13. package/dist/cjs/types/enyo-charge.d.cts +5 -0
  14. package/dist/cjs/types/enyo-data-bus-value.d.cts +6 -4
  15. package/dist/cjs/types/enyo-onboarding.cjs +15 -1
  16. package/dist/cjs/types/enyo-onboarding.d.cts +19 -0
  17. package/dist/cjs/types/enyo-retry-manager.cjs +19 -0
  18. package/dist/cjs/types/enyo-retry-manager.d.cts +72 -0
  19. package/dist/cjs/version.cjs +1 -1
  20. package/dist/cjs/version.d.cts +1 -1
  21. package/dist/implementations/retry/backoff.d.ts +19 -0
  22. package/dist/implementations/retry/backoff.js +55 -0
  23. package/dist/implementations/retry/retry-errors.d.ts +18 -0
  24. package/dist/implementations/retry/retry-errors.js +24 -0
  25. package/dist/implementations/retry/retry-manager.d.ts +97 -0
  26. package/dist/implementations/retry/retry-manager.js +178 -0
  27. package/dist/index.d.ts +6 -0
  28. package/dist/index.js +6 -0
  29. package/dist/packages/energy-app-onboarding.d.ts +17 -1
  30. package/dist/types/enyo-appliance.d.ts +25 -7
  31. package/dist/types/enyo-appliance.js +6 -3
  32. package/dist/types/enyo-authentication.d.ts +10 -0
  33. package/dist/types/enyo-charge.d.ts +5 -0
  34. package/dist/types/enyo-data-bus-value.d.ts +6 -4
  35. package/dist/types/enyo-onboarding.d.ts +19 -0
  36. package/dist/types/enyo-onboarding.js +14 -0
  37. package/dist/types/enyo-retry-manager.d.ts +72 -0
  38. package/dist/types/enyo-retry-manager.js +16 -0
  39. package/dist/version.d.ts +1 -1
  40. package/dist/version.js +1 -1
  41. package/package.json +6 -3
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetryState = void 0;
4
+ /**
5
+ * Lifecycle state of a single retryable instance tracked by {@link RetryManager}.
6
+ */
7
+ var RetryState;
8
+ (function (RetryState) {
9
+ /** Registered but no attempt has run yet (or the instance was reset). */
10
+ RetryState["Idle"] = "idle";
11
+ /** An attempt is currently executing. */
12
+ RetryState["Running"] = "running";
13
+ /** The previous attempt failed and the manager is sleeping before the next one. */
14
+ RetryState["Waiting"] = "waiting";
15
+ /** The most recent attempt succeeded. */
16
+ RetryState["Succeeded"] = "succeeded";
17
+ /** All attempts have been used (or the error was non-retryable). */
18
+ RetryState["Exhausted"] = "exhausted";
19
+ })(RetryState || (exports.RetryState = RetryState = {}));
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Lifecycle state of a single retryable instance tracked by {@link RetryManager}.
3
+ */
4
+ export declare enum RetryState {
5
+ /** Registered but no attempt has run yet (or the instance was reset). */
6
+ Idle = "idle",
7
+ /** An attempt is currently executing. */
8
+ Running = "running",
9
+ /** The previous attempt failed and the manager is sleeping before the next one. */
10
+ Waiting = "waiting",
11
+ /** The most recent attempt succeeded. */
12
+ Succeeded = "succeeded",
13
+ /** All attempts have been used (or the error was non-retryable). */
14
+ Exhausted = "exhausted"
15
+ }
16
+ /**
17
+ * Backoff curve used to compute the delay between retry attempts.
18
+ *
19
+ * The same shape covers fixed, linear and exponential strategies so callers
20
+ * configure backoff with plain data instead of constructing strategy classes.
21
+ */
22
+ export interface BackoffConfig {
23
+ /** Backoff curve. `exponential` is the typical default for network I/O. */
24
+ type: 'fixed' | 'linear' | 'exponential';
25
+ /** Base delay for the first retry, in milliseconds. Must be `>= 0`. */
26
+ initialMs: number;
27
+ /** Optional upper bound (ms) applied after the curve and before jitter. */
28
+ maxMs?: number;
29
+ /** Growth factor for `exponential`. Defaults to `2`. Must be `>= 1`. */
30
+ factor?: number;
31
+ /**
32
+ * Random jitter ratio in `[0, 1]`. `0.2` means the final delay is uniformly
33
+ * distributed in `[delay * 0.8, delay * 1.2]`. Defaults to `0`.
34
+ */
35
+ jitter?: number;
36
+ }
37
+ /**
38
+ * Per-instance retry policy. Registered with {@link RetryManager.register}.
39
+ */
40
+ export interface RetryPolicy {
41
+ /** Total attempts allowed including the first call. Use `Infinity` for endless retry. */
42
+ maxAttempts: number;
43
+ /** Backoff curve applied between attempts. */
44
+ backoff: BackoffConfig;
45
+ /**
46
+ * Predicate consulted after each failure. Returning `false` short-circuits
47
+ * to {@link RetryState.Exhausted} without further retries (e.g. for auth or 4xx errors).
48
+ * Defaults to "always retryable".
49
+ */
50
+ isRetryable?: (error: unknown) => boolean;
51
+ }
52
+ /**
53
+ * Snapshot of a single instance's current retry state.
54
+ */
55
+ export interface RetryStatus {
56
+ /** Identifier the instance was registered under. */
57
+ id: string;
58
+ /** Current lifecycle state. */
59
+ state: RetryState;
60
+ /** Number of attempts that have started so far in the current cycle. */
61
+ attempts: number;
62
+ /** Error from the most recent failed attempt, if any. */
63
+ lastError?: unknown;
64
+ /** Delay (ms) the manager is sleeping for, set only while `state === Waiting`. */
65
+ nextDelayMs?: number;
66
+ }
67
+ /**
68
+ * Listener invoked on every state transition for any registered instance.
69
+ *
70
+ * @param status Snapshot of the instance immediately after the transition.
71
+ */
72
+ export type RetryStateListener = (status: RetryStatus) => void;
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
9
9
  /**
10
10
  * Current version of the enyo Energy App SDK.
11
11
  */
12
- exports.SDK_VERSION = '0.0.113';
12
+ exports.SDK_VERSION = '0.0.115';
13
13
  /**
14
14
  * Gets the current SDK version.
15
15
  * @returns The semantic version string of the SDK
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Current version of the enyo Energy App SDK.
7
7
  */
8
- export declare const SDK_VERSION = "0.0.113";
8
+ export declare const SDK_VERSION = "0.0.115";
9
9
  /**
10
10
  * Gets the current SDK version.
11
11
  * @returns The semantic version string of the SDK
@@ -0,0 +1,19 @@
1
+ import type { BackoffConfig } from '../../types/enyo-retry-manager.js';
2
+ /**
3
+ * Computes the delay (ms) the {@link RetryManager} should wait before retry attempt `attempt`.
4
+ *
5
+ * Pure function — no timers, no global state — so it can be called directly by
6
+ * callers that drive their own retry loops.
7
+ *
8
+ * @param config Backoff curve and bounds.
9
+ * @param attempt 1-based attempt number; `1` is the first retry (the call right after the first failure).
10
+ * @param rng Source of randomness for jitter, in `[0, 1)`. Injectable for deterministic tests.
11
+ * @returns Non-negative delay in milliseconds.
12
+ * @throws RangeError on invalid configuration or non-positive `attempt`.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * computeBackoffDelay({ type: 'exponential', initialMs: 100, maxMs: 5000 }, 4); // 800
17
+ * ```
18
+ */
19
+ export declare function computeBackoffDelay(config: BackoffConfig, attempt: number, rng?: () => number): number;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Computes the delay (ms) the {@link RetryManager} should wait before retry attempt `attempt`.
3
+ *
4
+ * Pure function — no timers, no global state — so it can be called directly by
5
+ * callers that drive their own retry loops.
6
+ *
7
+ * @param config Backoff curve and bounds.
8
+ * @param attempt 1-based attempt number; `1` is the first retry (the call right after the first failure).
9
+ * @param rng Source of randomness for jitter, in `[0, 1)`. Injectable for deterministic tests.
10
+ * @returns Non-negative delay in milliseconds.
11
+ * @throws RangeError on invalid configuration or non-positive `attempt`.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * computeBackoffDelay({ type: 'exponential', initialMs: 100, maxMs: 5000 }, 4); // 800
16
+ * ```
17
+ */
18
+ export function computeBackoffDelay(config, attempt, rng = Math.random) {
19
+ if (!Number.isFinite(attempt) || attempt < 1) {
20
+ throw new RangeError(`attempt must be a positive integer, got ${attempt}`);
21
+ }
22
+ if (config.initialMs < 0) {
23
+ throw new RangeError(`initialMs must be >= 0, got ${config.initialMs}`);
24
+ }
25
+ if (config.maxMs !== undefined && config.maxMs < 0) {
26
+ throw new RangeError(`maxMs must be >= 0, got ${config.maxMs}`);
27
+ }
28
+ const factor = config.factor ?? 2;
29
+ if (config.type === 'exponential' && factor < 1) {
30
+ throw new RangeError(`factor must be >= 1, got ${factor}`);
31
+ }
32
+ const jitter = config.jitter ?? 0;
33
+ if (jitter < 0 || jitter > 1) {
34
+ throw new RangeError(`jitter must be in [0, 1], got ${jitter}`);
35
+ }
36
+ let delay;
37
+ switch (config.type) {
38
+ case 'fixed':
39
+ delay = config.initialMs;
40
+ break;
41
+ case 'linear':
42
+ delay = config.initialMs * attempt;
43
+ break;
44
+ case 'exponential':
45
+ delay = config.initialMs * factor ** (attempt - 1);
46
+ break;
47
+ }
48
+ if (config.maxMs !== undefined && delay > config.maxMs) {
49
+ delay = config.maxMs;
50
+ }
51
+ if (jitter > 0) {
52
+ delay = delay * (1 + (rng() * 2 - 1) * jitter);
53
+ }
54
+ return delay < 0 ? 0 : delay;
55
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Thrown by {@link RetryManager.execute} when all attempts have been used or
3
+ * the policy's `isRetryable` predicate rejected the last error.
4
+ *
5
+ * The original failure is preserved on {@link RetryExhaustedError.cause} so
6
+ * callers can inspect or rethrow it.
7
+ */
8
+ export declare class RetryExhaustedError extends Error {
9
+ readonly id: string;
10
+ readonly attempts: number;
11
+ readonly cause: unknown;
12
+ /**
13
+ * @param id Identifier of the retry instance that gave up.
14
+ * @param attempts Number of attempts that ran before giving up.
15
+ * @param cause The error from the final failing attempt.
16
+ */
17
+ constructor(id: string, attempts: number, cause: unknown);
18
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Thrown by {@link RetryManager.execute} when all attempts have been used or
3
+ * the policy's `isRetryable` predicate rejected the last error.
4
+ *
5
+ * The original failure is preserved on {@link RetryExhaustedError.cause} so
6
+ * callers can inspect or rethrow it.
7
+ */
8
+ export class RetryExhaustedError extends Error {
9
+ id;
10
+ attempts;
11
+ cause;
12
+ /**
13
+ * @param id Identifier of the retry instance that gave up.
14
+ * @param attempts Number of attempts that ran before giving up.
15
+ * @param cause The error from the final failing attempt.
16
+ */
17
+ constructor(id, attempts, cause) {
18
+ super(`Retry exhausted for "${id}" after ${attempts} attempt(s)`);
19
+ this.id = id;
20
+ this.attempts = attempts;
21
+ this.cause = cause;
22
+ this.name = 'RetryExhaustedError';
23
+ }
24
+ }
@@ -0,0 +1,97 @@
1
+ import { type RetryPolicy, type RetryStateListener, type RetryStatus } from '../../types/enyo-retry-manager.js';
2
+ /**
3
+ * Options controlling how a {@link RetryManager} interacts with the outside world.
4
+ *
5
+ * Both fields exist purely so unit tests can inject deterministic substitutes;
6
+ * production code should use the defaults.
7
+ */
8
+ export interface RetryManagerOptions {
9
+ /** Replacement for `setTimeout`-based sleeping. Defaults to a `setTimeout` wrapper. */
10
+ sleep?: (ms: number) => Promise<void>;
11
+ /** Source of randomness for backoff jitter. Defaults to `Math.random`. */
12
+ rng?: () => number;
13
+ }
14
+ /**
15
+ * Protocol-agnostic retry coordinator.
16
+ *
17
+ * Each *instance* (a Modbus connection, an MQTT client, a REST endpoint, …) is
18
+ * registered under a string `id` together with its own {@link RetryPolicy}.
19
+ * Callers then run an operation through {@link RetryManager.execute}; the
20
+ * manager handles attempt counting, backoff, and state transitions and reports
21
+ * progress through {@link RetryManager.onStateChange}.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const retries = new RetryManager();
26
+ * retries.register('modbus-inverter-1', {
27
+ * maxAttempts: 5,
28
+ * backoff: { type: 'exponential', initialMs: 200, maxMs: 5000, jitter: 0.2 },
29
+ * });
30
+ *
31
+ * await retries.execute('modbus-inverter-1', () => client.connect());
32
+ * ```
33
+ */
34
+ export declare class RetryManager {
35
+ private readonly instances;
36
+ private readonly listeners;
37
+ private readonly sleep;
38
+ private readonly rng;
39
+ /**
40
+ * @param options Test hooks for sleep and randomness. Production callers can omit this argument.
41
+ */
42
+ constructor(options?: RetryManagerOptions);
43
+ /**
44
+ * Registers a new retry instance.
45
+ *
46
+ * @param id Identifier used for all subsequent operations and reported in {@link RetryStatus}.
47
+ * @param policy Retry policy applied to this instance.
48
+ * @throws Error if `id` is already registered.
49
+ */
50
+ register(id: string, policy: RetryPolicy): void;
51
+ /**
52
+ * Removes the instance from the manager. No-op if `id` is not registered.
53
+ */
54
+ unregister(id: string): void;
55
+ /**
56
+ * Returns whether `id` is registered.
57
+ */
58
+ has(id: string): boolean;
59
+ /**
60
+ * Returns a snapshot of `id`'s current state, or `undefined` if not registered.
61
+ */
62
+ status(id: string): RetryStatus | undefined;
63
+ /**
64
+ * Returns snapshots for every registered instance.
65
+ */
66
+ statuses(): RetryStatus[];
67
+ /**
68
+ * Resets `id` to {@link RetryState.Idle} with zero attempts. The policy is preserved.
69
+ *
70
+ * @throws Error if `id` is not registered.
71
+ */
72
+ reset(id: string): void;
73
+ /**
74
+ * Subscribes to state-change events for every registered instance.
75
+ *
76
+ * @param listener Called once per transition with the post-transition snapshot.
77
+ * @returns Unsubscribe function.
78
+ */
79
+ onStateChange(listener: RetryStateListener): () => void;
80
+ /**
81
+ * Runs `fn` under the policy registered for `id`.
82
+ *
83
+ * Each invocation starts a fresh attempt cycle: counters and `lastError` are
84
+ * cleared before the first attempt so the same instance can be re-used after
85
+ * a previous success or exhaustion.
86
+ *
87
+ * @param id Identifier registered via {@link RetryManager.register}.
88
+ * @param fn Operation to execute. Rejecting causes a retry (subject to the policy).
89
+ * @returns Whatever `fn` resolves to.
90
+ * @throws RetryExhaustedError when all attempts have been used or `isRetryable` returns `false`.
91
+ * @throws Error when `id` is not registered.
92
+ */
93
+ execute<T>(id: string, fn: () => Promise<T>): Promise<T>;
94
+ private requireInstance;
95
+ private transition;
96
+ private emit;
97
+ }
@@ -0,0 +1,178 @@
1
+ import { RetryState, } from '../../types/enyo-retry-manager.js';
2
+ import { computeBackoffDelay } from './backoff.js';
3
+ import { RetryExhaustedError } from './retry-errors.js';
4
+ const defaultSleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
5
+ /**
6
+ * Protocol-agnostic retry coordinator.
7
+ *
8
+ * Each *instance* (a Modbus connection, an MQTT client, a REST endpoint, …) is
9
+ * registered under a string `id` together with its own {@link RetryPolicy}.
10
+ * Callers then run an operation through {@link RetryManager.execute}; the
11
+ * manager handles attempt counting, backoff, and state transitions and reports
12
+ * progress through {@link RetryManager.onStateChange}.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const retries = new RetryManager();
17
+ * retries.register('modbus-inverter-1', {
18
+ * maxAttempts: 5,
19
+ * backoff: { type: 'exponential', initialMs: 200, maxMs: 5000, jitter: 0.2 },
20
+ * });
21
+ *
22
+ * await retries.execute('modbus-inverter-1', () => client.connect());
23
+ * ```
24
+ */
25
+ export class RetryManager {
26
+ instances = new Map();
27
+ listeners = new Set();
28
+ sleep;
29
+ rng;
30
+ /**
31
+ * @param options Test hooks for sleep and randomness. Production callers can omit this argument.
32
+ */
33
+ constructor(options = {}) {
34
+ this.sleep = options.sleep ?? defaultSleep;
35
+ this.rng = options.rng ?? Math.random;
36
+ }
37
+ /**
38
+ * Registers a new retry instance.
39
+ *
40
+ * @param id Identifier used for all subsequent operations and reported in {@link RetryStatus}.
41
+ * @param policy Retry policy applied to this instance.
42
+ * @throws Error if `id` is already registered.
43
+ */
44
+ register(id, policy) {
45
+ if (this.instances.has(id)) {
46
+ throw new Error(`Retry instance "${id}" is already registered`);
47
+ }
48
+ if (policy.maxAttempts < 1) {
49
+ throw new RangeError(`maxAttempts must be >= 1, got ${policy.maxAttempts}`);
50
+ }
51
+ this.instances.set(id, { id, policy, state: RetryState.Idle, attempts: 0 });
52
+ }
53
+ /**
54
+ * Removes the instance from the manager. No-op if `id` is not registered.
55
+ */
56
+ unregister(id) {
57
+ this.instances.delete(id);
58
+ }
59
+ /**
60
+ * Returns whether `id` is registered.
61
+ */
62
+ has(id) {
63
+ return this.instances.has(id);
64
+ }
65
+ /**
66
+ * Returns a snapshot of `id`'s current state, or `undefined` if not registered.
67
+ */
68
+ status(id) {
69
+ const instance = this.instances.get(id);
70
+ return instance ? toStatus(instance) : undefined;
71
+ }
72
+ /**
73
+ * Returns snapshots for every registered instance.
74
+ */
75
+ statuses() {
76
+ return Array.from(this.instances.values(), toStatus);
77
+ }
78
+ /**
79
+ * Resets `id` to {@link RetryState.Idle} with zero attempts. The policy is preserved.
80
+ *
81
+ * @throws Error if `id` is not registered.
82
+ */
83
+ reset(id) {
84
+ const instance = this.requireInstance(id);
85
+ instance.state = RetryState.Idle;
86
+ instance.attempts = 0;
87
+ instance.lastError = undefined;
88
+ instance.nextDelayMs = undefined;
89
+ this.emit(instance);
90
+ }
91
+ /**
92
+ * Subscribes to state-change events for every registered instance.
93
+ *
94
+ * @param listener Called once per transition with the post-transition snapshot.
95
+ * @returns Unsubscribe function.
96
+ */
97
+ onStateChange(listener) {
98
+ this.listeners.add(listener);
99
+ return () => {
100
+ this.listeners.delete(listener);
101
+ };
102
+ }
103
+ /**
104
+ * Runs `fn` under the policy registered for `id`.
105
+ *
106
+ * Each invocation starts a fresh attempt cycle: counters and `lastError` are
107
+ * cleared before the first attempt so the same instance can be re-used after
108
+ * a previous success or exhaustion.
109
+ *
110
+ * @param id Identifier registered via {@link RetryManager.register}.
111
+ * @param fn Operation to execute. Rejecting causes a retry (subject to the policy).
112
+ * @returns Whatever `fn` resolves to.
113
+ * @throws RetryExhaustedError when all attempts have been used or `isRetryable` returns `false`.
114
+ * @throws Error when `id` is not registered.
115
+ */
116
+ async execute(id, fn) {
117
+ const instance = this.requireInstance(id);
118
+ instance.attempts = 0;
119
+ instance.lastError = undefined;
120
+ instance.nextDelayMs = undefined;
121
+ const { maxAttempts, backoff, isRetryable } = instance.policy;
122
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
123
+ instance.attempts = attempt;
124
+ instance.nextDelayMs = undefined;
125
+ this.transition(instance, RetryState.Running);
126
+ try {
127
+ const result = await fn();
128
+ instance.lastError = undefined;
129
+ this.transition(instance, RetryState.Succeeded);
130
+ return result;
131
+ }
132
+ catch (error) {
133
+ instance.lastError = error;
134
+ const retryable = isRetryable ? isRetryable(error) : true;
135
+ const hasMore = attempt < maxAttempts;
136
+ if (!retryable || !hasMore) {
137
+ this.transition(instance, RetryState.Exhausted);
138
+ throw new RetryExhaustedError(id, attempt, error);
139
+ }
140
+ const delay = computeBackoffDelay(backoff, attempt, this.rng);
141
+ instance.nextDelayMs = delay;
142
+ this.transition(instance, RetryState.Waiting);
143
+ await this.sleep(delay);
144
+ }
145
+ }
146
+ // Unreachable: the loop either returns, throws, or completes by entering
147
+ // the catch branch on the final attempt (which throws Exhausted above).
148
+ throw new RetryExhaustedError(id, instance.attempts, instance.lastError);
149
+ }
150
+ requireInstance(id) {
151
+ const instance = this.instances.get(id);
152
+ if (!instance) {
153
+ throw new Error(`Retry instance "${id}" is not registered`);
154
+ }
155
+ return instance;
156
+ }
157
+ transition(instance, state) {
158
+ instance.state = state;
159
+ this.emit(instance);
160
+ }
161
+ emit(instance) {
162
+ if (this.listeners.size === 0)
163
+ return;
164
+ const snapshot = toStatus(instance);
165
+ for (const listener of this.listeners) {
166
+ listener(snapshot);
167
+ }
168
+ }
169
+ }
170
+ function toStatus(instance) {
171
+ return {
172
+ id: instance.id,
173
+ state: instance.state,
174
+ attempts: instance.attempts,
175
+ lastError: instance.lastError,
176
+ nextDelayMs: instance.nextDelayMs,
177
+ };
178
+ }
package/dist/index.d.ts CHANGED
@@ -40,6 +40,12 @@ export * from './packages/energy-app-diagnostics.js';
40
40
  export * from './types/enyo-learning-phase.js';
41
41
  export * from './packages/energy-app-learning-phase.js';
42
42
  export * from './types/enyo-air-conditioning-appliance.js';
43
+ export * from './types/enyo-onboarding.js';
44
+ export * from './packages/energy-app-onboarding.js';
45
+ export * from './types/enyo-retry-manager.js';
46
+ export * from './implementations/retry/backoff.js';
47
+ export * from './implementations/retry/retry-manager.js';
48
+ export * from './implementations/retry/retry-errors.js';
43
49
  export * from './integrations/integration-types.js';
44
50
  export * from './integrations/integration-energy-app.js';
45
51
  export * from './integrations/heatpump-integration-energy-app.js';
package/dist/index.js CHANGED
@@ -40,6 +40,12 @@ export * from './packages/energy-app-diagnostics.js';
40
40
  export * from './types/enyo-learning-phase.js';
41
41
  export * from './packages/energy-app-learning-phase.js';
42
42
  export * from './types/enyo-air-conditioning-appliance.js';
43
+ export * from './types/enyo-onboarding.js';
44
+ export * from './packages/energy-app-onboarding.js';
45
+ export * from './types/enyo-retry-manager.js';
46
+ export * from './implementations/retry/backoff.js';
47
+ export * from './implementations/retry/retry-manager.js';
48
+ export * from './implementations/retry/retry-errors.js';
43
49
  export * from './integrations/integration-types.js';
44
50
  export * from './integrations/integration-energy-app.js';
45
51
  export * from './integrations/heatpump-integration-energy-app.js';
@@ -1,4 +1,4 @@
1
- import { EnyoOnboardingGuide, EnyoOnboardingStep, EnyoOnboardingStepListener, EnyoOnboardingStepResponse } from "../types/enyo-onboarding.js";
1
+ import { EnyoOnboardingGuide, EnyoOnboardingGuideCategory, EnyoOnboardingStep, EnyoOnboardingStepListener, EnyoOnboardingStepResponse } from "../types/enyo-onboarding.js";
2
2
  /**
3
3
  * Interface for managing onboarding guides within Energy App packages.
4
4
  * Provides methods to create, manage, and navigate through onboarding flows
@@ -40,6 +40,22 @@ export interface EnergyAppOnboarding {
40
40
  * @returns Promise that resolves to an array of all active onboarding guides
41
41
  */
42
42
  getAllOnboardingGuides(): Promise<EnyoOnboardingGuide[]>;
43
+ /**
44
+ * Gets all currently active onboarding guides that belong to the given category.
45
+ * Guides without an explicit category are excluded — use {@link getAllOnboardingGuides}
46
+ * if you need uncategorized guides as well.
47
+ *
48
+ * @param category - The lifecycle category to filter guides by
49
+ * @returns Promise resolving to all guides whose `category` matches
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const reconnectGuides = await onboarding.getGuidesByCategory(
54
+ * EnyoOnboardingGuideCategory.ReconnectDevice
55
+ * );
56
+ * ```
57
+ */
58
+ getGuidesByCategory(category: EnyoOnboardingGuideCategory): Promise<EnyoOnboardingGuide[]>;
43
59
  /**
44
60
  * Gets the current step being displayed in the onboarding flow for a specific guide.
45
61
  * Returns null if no onboarding is active or if the guide is complete.
@@ -28,16 +28,26 @@ export declare enum EnyoApplianceStateEnum {
28
28
  /**
29
29
  * Health status of an appliance. Orthogonal to {@link EnyoApplianceStateEnum},
30
30
  * which describes connectivity. `Healthy` means the appliance is operating
31
- * normally; `Faulted` means it has reported an internal error and may need
32
- * attention. Vendor- or protocol-specific details should be conveyed via
33
- * accompanying error codes.
31
+ * normally; `Warning` means a non-blocking issue has been reported (the
32
+ * appliance is still functional but should be inspected); `Faulted` means it
33
+ * has reported an internal error and may need attention. Vendor- or
34
+ * protocol-specific details should be conveyed via accompanying error codes.
34
35
  */
35
36
  export declare enum EnyoApplianceStatusEnum {
36
37
  /** Appliance is operating normally */
37
38
  Healthy = "healthy",
39
+ /** Appliance is operating but has reported a non-blocking issue that should be inspected */
40
+ Warning = "warning",
38
41
  /** Appliance has reported an internal fault */
39
42
  Faulted = "faulted"
40
43
  }
44
+ /**
45
+ * Severity classification for an {@link EnyoApplianceErrorCode}.
46
+ * Producers should mark non-blocking issues as `'warning'` and blocking
47
+ * faults as `'error'`. When omitted on an error code, consumers should
48
+ * treat it as `'error'` for backwards compatibility.
49
+ */
50
+ export type EnyoApplianceErrorSeverity = 'error' | 'warning';
41
51
  /**
42
52
  * Translated, human-readable message for an appliance error code.
43
53
  * Producers should emit at most one entry per supported language.
@@ -49,18 +59,26 @@ export interface EnyoApplianceErrorMessage {
49
59
  message: string;
50
60
  }
51
61
  /**
52
- * Vendor- or protocol-specific error reported by an appliance, optionally
53
- * accompanied by translated human-readable messages. The `code` is the
54
- * machine-readable identifier (stable, non-localized); `messages` is an
62
+ * Vendor- or protocol-specific error or warning reported by an appliance,
63
+ * optionally accompanied by translated human-readable messages. The `code` is
64
+ * the machine-readable identifier (stable, non-localized); `messages` is an
55
65
  * optional set of pre-translated descriptions intended for UI display.
56
66
  * Consumers should fall back to rendering `code` when no `messages` entry
57
- * matches their locale.
67
+ * matches their locale. Use `severity` to distinguish a blocking error from
68
+ * a non-blocking warning; when omitted, consumers should treat the entry as
69
+ * an error for backwards compatibility.
58
70
  */
59
71
  export interface EnyoApplianceErrorCode {
60
72
  /** Machine-readable, vendor- or protocol-specific error code */
61
73
  code: string;
62
74
  /** Optional translated messages explaining the error */
63
75
  messages?: EnyoApplianceErrorMessage[];
76
+ /**
77
+ * Optional severity of this entry. Defaults to `'error'` semantics when
78
+ * omitted. Use `'warning'` to indicate a non-blocking issue that should
79
+ * be surfaced but does not put the appliance into a faulted state.
80
+ */
81
+ severity?: EnyoApplianceErrorSeverity;
64
82
  }
65
83
  export interface EnyoApplianceNetworkMetadata {
66
84
  /** If the appliance is connected via cellular network, you can put the imsi here*/
@@ -18,14 +18,17 @@ export var EnyoApplianceStateEnum;
18
18
  /**
19
19
  * Health status of an appliance. Orthogonal to {@link EnyoApplianceStateEnum},
20
20
  * which describes connectivity. `Healthy` means the appliance is operating
21
- * normally; `Faulted` means it has reported an internal error and may need
22
- * attention. Vendor- or protocol-specific details should be conveyed via
23
- * accompanying error codes.
21
+ * normally; `Warning` means a non-blocking issue has been reported (the
22
+ * appliance is still functional but should be inspected); `Faulted` means it
23
+ * has reported an internal error and may need attention. Vendor- or
24
+ * protocol-specific details should be conveyed via accompanying error codes.
24
25
  */
25
26
  export var EnyoApplianceStatusEnum;
26
27
  (function (EnyoApplianceStatusEnum) {
27
28
  /** Appliance is operating normally */
28
29
  EnyoApplianceStatusEnum["Healthy"] = "healthy";
30
+ /** Appliance is operating but has reported a non-blocking issue that should be inspected */
31
+ EnyoApplianceStatusEnum["Warning"] = "warning";
29
32
  /** Appliance has reported an internal fault */
30
33
  EnyoApplianceStatusEnum["Faulted"] = "faulted";
31
34
  })(EnyoApplianceStatusEnum || (EnyoApplianceStatusEnum = {}));