@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.
- package/dist/cjs/implementations/retry/backoff.cjs +58 -0
- package/dist/cjs/implementations/retry/backoff.d.cts +19 -0
- package/dist/cjs/implementations/retry/retry-errors.cjs +28 -0
- package/dist/cjs/implementations/retry/retry-errors.d.cts +18 -0
- package/dist/cjs/implementations/retry/retry-manager.cjs +182 -0
- package/dist/cjs/implementations/retry/retry-manager.d.cts +97 -0
- package/dist/cjs/index.cjs +6 -0
- package/dist/cjs/index.d.cts +6 -0
- package/dist/cjs/packages/energy-app-onboarding.d.cts +17 -1
- package/dist/cjs/types/enyo-appliance.cjs +6 -3
- package/dist/cjs/types/enyo-appliance.d.cts +25 -7
- package/dist/cjs/types/enyo-authentication.d.cts +10 -0
- package/dist/cjs/types/enyo-charge.d.cts +5 -0
- package/dist/cjs/types/enyo-data-bus-value.d.cts +6 -4
- package/dist/cjs/types/enyo-onboarding.cjs +15 -1
- package/dist/cjs/types/enyo-onboarding.d.cts +19 -0
- package/dist/cjs/types/enyo-retry-manager.cjs +19 -0
- package/dist/cjs/types/enyo-retry-manager.d.cts +72 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/implementations/retry/backoff.d.ts +19 -0
- package/dist/implementations/retry/backoff.js +55 -0
- package/dist/implementations/retry/retry-errors.d.ts +18 -0
- package/dist/implementations/retry/retry-errors.js +24 -0
- package/dist/implementations/retry/retry-manager.d.ts +97 -0
- package/dist/implementations/retry/retry-manager.js +178 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/packages/energy-app-onboarding.d.ts +17 -1
- package/dist/types/enyo-appliance.d.ts +25 -7
- package/dist/types/enyo-appliance.js +6 -3
- package/dist/types/enyo-authentication.d.ts +10 -0
- package/dist/types/enyo-charge.d.ts +5 -0
- package/dist/types/enyo-data-bus-value.d.ts +6 -4
- package/dist/types/enyo-onboarding.d.ts +19 -0
- package/dist/types/enyo-onboarding.js +14 -0
- package/dist/types/enyo-retry-manager.d.ts +72 -0
- package/dist/types/enyo-retry-manager.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeBackoffDelay = computeBackoffDelay;
|
|
4
|
+
/**
|
|
5
|
+
* Computes the delay (ms) the {@link RetryManager} should wait before retry attempt `attempt`.
|
|
6
|
+
*
|
|
7
|
+
* Pure function — no timers, no global state — so it can be called directly by
|
|
8
|
+
* callers that drive their own retry loops.
|
|
9
|
+
*
|
|
10
|
+
* @param config Backoff curve and bounds.
|
|
11
|
+
* @param attempt 1-based attempt number; `1` is the first retry (the call right after the first failure).
|
|
12
|
+
* @param rng Source of randomness for jitter, in `[0, 1)`. Injectable for deterministic tests.
|
|
13
|
+
* @returns Non-negative delay in milliseconds.
|
|
14
|
+
* @throws RangeError on invalid configuration or non-positive `attempt`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* computeBackoffDelay({ type: 'exponential', initialMs: 100, maxMs: 5000 }, 4); // 800
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function computeBackoffDelay(config, attempt, rng = Math.random) {
|
|
22
|
+
if (!Number.isFinite(attempt) || attempt < 1) {
|
|
23
|
+
throw new RangeError(`attempt must be a positive integer, got ${attempt}`);
|
|
24
|
+
}
|
|
25
|
+
if (config.initialMs < 0) {
|
|
26
|
+
throw new RangeError(`initialMs must be >= 0, got ${config.initialMs}`);
|
|
27
|
+
}
|
|
28
|
+
if (config.maxMs !== undefined && config.maxMs < 0) {
|
|
29
|
+
throw new RangeError(`maxMs must be >= 0, got ${config.maxMs}`);
|
|
30
|
+
}
|
|
31
|
+
const factor = config.factor ?? 2;
|
|
32
|
+
if (config.type === 'exponential' && factor < 1) {
|
|
33
|
+
throw new RangeError(`factor must be >= 1, got ${factor}`);
|
|
34
|
+
}
|
|
35
|
+
const jitter = config.jitter ?? 0;
|
|
36
|
+
if (jitter < 0 || jitter > 1) {
|
|
37
|
+
throw new RangeError(`jitter must be in [0, 1], got ${jitter}`);
|
|
38
|
+
}
|
|
39
|
+
let delay;
|
|
40
|
+
switch (config.type) {
|
|
41
|
+
case 'fixed':
|
|
42
|
+
delay = config.initialMs;
|
|
43
|
+
break;
|
|
44
|
+
case 'linear':
|
|
45
|
+
delay = config.initialMs * attempt;
|
|
46
|
+
break;
|
|
47
|
+
case 'exponential':
|
|
48
|
+
delay = config.initialMs * factor ** (attempt - 1);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
if (config.maxMs !== undefined && delay > config.maxMs) {
|
|
52
|
+
delay = config.maxMs;
|
|
53
|
+
}
|
|
54
|
+
if (jitter > 0) {
|
|
55
|
+
delay = delay * (1 + (rng() * 2 - 1) * jitter);
|
|
56
|
+
}
|
|
57
|
+
return delay < 0 ? 0 : delay;
|
|
58
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BackoffConfig } from '../../types/enyo-retry-manager.cjs';
|
|
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,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetryExhaustedError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Thrown by {@link RetryManager.execute} when all attempts have been used or
|
|
6
|
+
* the policy's `isRetryable` predicate rejected the last error.
|
|
7
|
+
*
|
|
8
|
+
* The original failure is preserved on {@link RetryExhaustedError.cause} so
|
|
9
|
+
* callers can inspect or rethrow it.
|
|
10
|
+
*/
|
|
11
|
+
class RetryExhaustedError extends Error {
|
|
12
|
+
id;
|
|
13
|
+
attempts;
|
|
14
|
+
cause;
|
|
15
|
+
/**
|
|
16
|
+
* @param id Identifier of the retry instance that gave up.
|
|
17
|
+
* @param attempts Number of attempts that ran before giving up.
|
|
18
|
+
* @param cause The error from the final failing attempt.
|
|
19
|
+
*/
|
|
20
|
+
constructor(id, attempts, cause) {
|
|
21
|
+
super(`Retry exhausted for "${id}" after ${attempts} attempt(s)`);
|
|
22
|
+
this.id = id;
|
|
23
|
+
this.attempts = attempts;
|
|
24
|
+
this.cause = cause;
|
|
25
|
+
this.name = 'RetryExhaustedError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.RetryExhaustedError = RetryExhaustedError;
|
|
@@ -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,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetryManager = void 0;
|
|
4
|
+
const enyo_retry_manager_js_1 = require("../../types/enyo-retry-manager.cjs");
|
|
5
|
+
const backoff_js_1 = require("./backoff.cjs");
|
|
6
|
+
const retry_errors_js_1 = require("./retry-errors.cjs");
|
|
7
|
+
const defaultSleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
8
|
+
/**
|
|
9
|
+
* Protocol-agnostic retry coordinator.
|
|
10
|
+
*
|
|
11
|
+
* Each *instance* (a Modbus connection, an MQTT client, a REST endpoint, …) is
|
|
12
|
+
* registered under a string `id` together with its own {@link RetryPolicy}.
|
|
13
|
+
* Callers then run an operation through {@link RetryManager.execute}; the
|
|
14
|
+
* manager handles attempt counting, backoff, and state transitions and reports
|
|
15
|
+
* progress through {@link RetryManager.onStateChange}.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const retries = new RetryManager();
|
|
20
|
+
* retries.register('modbus-inverter-1', {
|
|
21
|
+
* maxAttempts: 5,
|
|
22
|
+
* backoff: { type: 'exponential', initialMs: 200, maxMs: 5000, jitter: 0.2 },
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* await retries.execute('modbus-inverter-1', () => client.connect());
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
class RetryManager {
|
|
29
|
+
instances = new Map();
|
|
30
|
+
listeners = new Set();
|
|
31
|
+
sleep;
|
|
32
|
+
rng;
|
|
33
|
+
/**
|
|
34
|
+
* @param options Test hooks for sleep and randomness. Production callers can omit this argument.
|
|
35
|
+
*/
|
|
36
|
+
constructor(options = {}) {
|
|
37
|
+
this.sleep = options.sleep ?? defaultSleep;
|
|
38
|
+
this.rng = options.rng ?? Math.random;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Registers a new retry instance.
|
|
42
|
+
*
|
|
43
|
+
* @param id Identifier used for all subsequent operations and reported in {@link RetryStatus}.
|
|
44
|
+
* @param policy Retry policy applied to this instance.
|
|
45
|
+
* @throws Error if `id` is already registered.
|
|
46
|
+
*/
|
|
47
|
+
register(id, policy) {
|
|
48
|
+
if (this.instances.has(id)) {
|
|
49
|
+
throw new Error(`Retry instance "${id}" is already registered`);
|
|
50
|
+
}
|
|
51
|
+
if (policy.maxAttempts < 1) {
|
|
52
|
+
throw new RangeError(`maxAttempts must be >= 1, got ${policy.maxAttempts}`);
|
|
53
|
+
}
|
|
54
|
+
this.instances.set(id, { id, policy, state: enyo_retry_manager_js_1.RetryState.Idle, attempts: 0 });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Removes the instance from the manager. No-op if `id` is not registered.
|
|
58
|
+
*/
|
|
59
|
+
unregister(id) {
|
|
60
|
+
this.instances.delete(id);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns whether `id` is registered.
|
|
64
|
+
*/
|
|
65
|
+
has(id) {
|
|
66
|
+
return this.instances.has(id);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns a snapshot of `id`'s current state, or `undefined` if not registered.
|
|
70
|
+
*/
|
|
71
|
+
status(id) {
|
|
72
|
+
const instance = this.instances.get(id);
|
|
73
|
+
return instance ? toStatus(instance) : undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns snapshots for every registered instance.
|
|
77
|
+
*/
|
|
78
|
+
statuses() {
|
|
79
|
+
return Array.from(this.instances.values(), toStatus);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resets `id` to {@link RetryState.Idle} with zero attempts. The policy is preserved.
|
|
83
|
+
*
|
|
84
|
+
* @throws Error if `id` is not registered.
|
|
85
|
+
*/
|
|
86
|
+
reset(id) {
|
|
87
|
+
const instance = this.requireInstance(id);
|
|
88
|
+
instance.state = enyo_retry_manager_js_1.RetryState.Idle;
|
|
89
|
+
instance.attempts = 0;
|
|
90
|
+
instance.lastError = undefined;
|
|
91
|
+
instance.nextDelayMs = undefined;
|
|
92
|
+
this.emit(instance);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Subscribes to state-change events for every registered instance.
|
|
96
|
+
*
|
|
97
|
+
* @param listener Called once per transition with the post-transition snapshot.
|
|
98
|
+
* @returns Unsubscribe function.
|
|
99
|
+
*/
|
|
100
|
+
onStateChange(listener) {
|
|
101
|
+
this.listeners.add(listener);
|
|
102
|
+
return () => {
|
|
103
|
+
this.listeners.delete(listener);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Runs `fn` under the policy registered for `id`.
|
|
108
|
+
*
|
|
109
|
+
* Each invocation starts a fresh attempt cycle: counters and `lastError` are
|
|
110
|
+
* cleared before the first attempt so the same instance can be re-used after
|
|
111
|
+
* a previous success or exhaustion.
|
|
112
|
+
*
|
|
113
|
+
* @param id Identifier registered via {@link RetryManager.register}.
|
|
114
|
+
* @param fn Operation to execute. Rejecting causes a retry (subject to the policy).
|
|
115
|
+
* @returns Whatever `fn` resolves to.
|
|
116
|
+
* @throws RetryExhaustedError when all attempts have been used or `isRetryable` returns `false`.
|
|
117
|
+
* @throws Error when `id` is not registered.
|
|
118
|
+
*/
|
|
119
|
+
async execute(id, fn) {
|
|
120
|
+
const instance = this.requireInstance(id);
|
|
121
|
+
instance.attempts = 0;
|
|
122
|
+
instance.lastError = undefined;
|
|
123
|
+
instance.nextDelayMs = undefined;
|
|
124
|
+
const { maxAttempts, backoff, isRetryable } = instance.policy;
|
|
125
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
126
|
+
instance.attempts = attempt;
|
|
127
|
+
instance.nextDelayMs = undefined;
|
|
128
|
+
this.transition(instance, enyo_retry_manager_js_1.RetryState.Running);
|
|
129
|
+
try {
|
|
130
|
+
const result = await fn();
|
|
131
|
+
instance.lastError = undefined;
|
|
132
|
+
this.transition(instance, enyo_retry_manager_js_1.RetryState.Succeeded);
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
instance.lastError = error;
|
|
137
|
+
const retryable = isRetryable ? isRetryable(error) : true;
|
|
138
|
+
const hasMore = attempt < maxAttempts;
|
|
139
|
+
if (!retryable || !hasMore) {
|
|
140
|
+
this.transition(instance, enyo_retry_manager_js_1.RetryState.Exhausted);
|
|
141
|
+
throw new retry_errors_js_1.RetryExhaustedError(id, attempt, error);
|
|
142
|
+
}
|
|
143
|
+
const delay = (0, backoff_js_1.computeBackoffDelay)(backoff, attempt, this.rng);
|
|
144
|
+
instance.nextDelayMs = delay;
|
|
145
|
+
this.transition(instance, enyo_retry_manager_js_1.RetryState.Waiting);
|
|
146
|
+
await this.sleep(delay);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Unreachable: the loop either returns, throws, or completes by entering
|
|
150
|
+
// the catch branch on the final attempt (which throws Exhausted above).
|
|
151
|
+
throw new retry_errors_js_1.RetryExhaustedError(id, instance.attempts, instance.lastError);
|
|
152
|
+
}
|
|
153
|
+
requireInstance(id) {
|
|
154
|
+
const instance = this.instances.get(id);
|
|
155
|
+
if (!instance) {
|
|
156
|
+
throw new Error(`Retry instance "${id}" is not registered`);
|
|
157
|
+
}
|
|
158
|
+
return instance;
|
|
159
|
+
}
|
|
160
|
+
transition(instance, state) {
|
|
161
|
+
instance.state = state;
|
|
162
|
+
this.emit(instance);
|
|
163
|
+
}
|
|
164
|
+
emit(instance) {
|
|
165
|
+
if (this.listeners.size === 0)
|
|
166
|
+
return;
|
|
167
|
+
const snapshot = toStatus(instance);
|
|
168
|
+
for (const listener of this.listeners) {
|
|
169
|
+
listener(snapshot);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.RetryManager = RetryManager;
|
|
174
|
+
function toStatus(instance) {
|
|
175
|
+
return {
|
|
176
|
+
id: instance.id,
|
|
177
|
+
state: instance.state,
|
|
178
|
+
attempts: instance.attempts,
|
|
179
|
+
lastError: instance.lastError,
|
|
180
|
+
nextDelayMs: instance.nextDelayMs,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type RetryPolicy, type RetryStateListener, type RetryStatus } from '../../types/enyo-retry-manager.cjs';
|
|
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
|
+
}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -56,6 +56,12 @@ __exportStar(require("./packages/energy-app-diagnostics.cjs"), exports);
|
|
|
56
56
|
__exportStar(require("./types/enyo-learning-phase.cjs"), exports);
|
|
57
57
|
__exportStar(require("./packages/energy-app-learning-phase.cjs"), exports);
|
|
58
58
|
__exportStar(require("./types/enyo-air-conditioning-appliance.cjs"), exports);
|
|
59
|
+
__exportStar(require("./types/enyo-onboarding.cjs"), exports);
|
|
60
|
+
__exportStar(require("./packages/energy-app-onboarding.cjs"), exports);
|
|
61
|
+
__exportStar(require("./types/enyo-retry-manager.cjs"), exports);
|
|
62
|
+
__exportStar(require("./implementations/retry/backoff.cjs"), exports);
|
|
63
|
+
__exportStar(require("./implementations/retry/retry-manager.cjs"), exports);
|
|
64
|
+
__exportStar(require("./implementations/retry/retry-errors.cjs"), exports);
|
|
59
65
|
__exportStar(require("./integrations/integration-types.cjs"), exports);
|
|
60
66
|
__exportStar(require("./integrations/integration-energy-app.cjs"), exports);
|
|
61
67
|
__exportStar(require("./integrations/heatpump-integration-energy-app.cjs"), exports);
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -40,6 +40,12 @@ export * from './packages/energy-app-diagnostics.cjs';
|
|
|
40
40
|
export * from './types/enyo-learning-phase.cjs';
|
|
41
41
|
export * from './packages/energy-app-learning-phase.cjs';
|
|
42
42
|
export * from './types/enyo-air-conditioning-appliance.cjs';
|
|
43
|
+
export * from './types/enyo-onboarding.cjs';
|
|
44
|
+
export * from './packages/energy-app-onboarding.cjs';
|
|
45
|
+
export * from './types/enyo-retry-manager.cjs';
|
|
46
|
+
export * from './implementations/retry/backoff.cjs';
|
|
47
|
+
export * from './implementations/retry/retry-manager.cjs';
|
|
48
|
+
export * from './implementations/retry/retry-errors.cjs';
|
|
43
49
|
export * from './integrations/integration-types.cjs';
|
|
44
50
|
export * from './integrations/integration-energy-app.cjs';
|
|
45
51
|
export * from './integrations/heatpump-integration-energy-app.cjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnyoOnboardingGuide, EnyoOnboardingStep, EnyoOnboardingStepListener, EnyoOnboardingStepResponse } from "../types/enyo-onboarding.cjs";
|
|
1
|
+
import { EnyoOnboardingGuide, EnyoOnboardingGuideCategory, EnyoOnboardingStep, EnyoOnboardingStepListener, EnyoOnboardingStepResponse } from "../types/enyo-onboarding.cjs";
|
|
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.
|
|
@@ -21,14 +21,17 @@ var EnyoApplianceStateEnum;
|
|
|
21
21
|
/**
|
|
22
22
|
* Health status of an appliance. Orthogonal to {@link EnyoApplianceStateEnum},
|
|
23
23
|
* which describes connectivity. `Healthy` means the appliance is operating
|
|
24
|
-
* normally; `
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* normally; `Warning` means a non-blocking issue has been reported (the
|
|
25
|
+
* appliance is still functional but should be inspected); `Faulted` means it
|
|
26
|
+
* has reported an internal error and may need attention. Vendor- or
|
|
27
|
+
* protocol-specific details should be conveyed via accompanying error codes.
|
|
27
28
|
*/
|
|
28
29
|
var EnyoApplianceStatusEnum;
|
|
29
30
|
(function (EnyoApplianceStatusEnum) {
|
|
30
31
|
/** Appliance is operating normally */
|
|
31
32
|
EnyoApplianceStatusEnum["Healthy"] = "healthy";
|
|
33
|
+
/** Appliance is operating but has reported a non-blocking issue that should be inspected */
|
|
34
|
+
EnyoApplianceStatusEnum["Warning"] = "warning";
|
|
32
35
|
/** Appliance has reported an internal fault */
|
|
33
36
|
EnyoApplianceStatusEnum["Faulted"] = "faulted";
|
|
34
37
|
})(EnyoApplianceStatusEnum || (exports.EnyoApplianceStatusEnum = EnyoApplianceStatusEnum = {}));
|
|
@@ -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; `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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,
|
|
53
|
-
* accompanied by translated human-readable messages. The `code` is
|
|
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*/
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { EnyoPackageConfigurationTranslatedValue } from "./enyo-settings.cjs";
|
|
2
2
|
export type EnyoAuthenticationType = 'apiKey' | 'oauth' | 'usernamePassword';
|
|
3
|
+
/**
|
|
4
|
+
* Indicates whether the authentication request should be presented to the user
|
|
5
|
+
* before or after the onboarding guide. When omitted, the host decides the default ordering.
|
|
6
|
+
*/
|
|
7
|
+
export type EnyoAuthenticationOnboardingOrder = 'before-onboarding' | 'after-onboarding';
|
|
3
8
|
/**
|
|
4
9
|
* Represents an additional custom field for authentication forms.
|
|
5
10
|
* Allows developers to add extra input fields beyond the standard username/password or API key.
|
|
@@ -66,6 +71,11 @@ export interface EnyoAuthentication {
|
|
|
66
71
|
oneTimeAuthentication: boolean;
|
|
67
72
|
/** Optional appliance ID. If provided, authentication is for specific appliance. If omitted, authentication is for the whole package */
|
|
68
73
|
applianceId?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Optional ordering hint indicating whether this authentication request should be
|
|
76
|
+
* presented before or after the onboarding guide. When omitted, the host decides the default.
|
|
77
|
+
*/
|
|
78
|
+
onboardingOrder?: EnyoAuthenticationOnboardingOrder;
|
|
69
79
|
}
|
|
70
80
|
export interface EnyoApiKeyAuthenticationResponse {
|
|
71
81
|
apiKey: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EnyoChargeModeEnum } from "./enyo-data-bus-value.cjs";
|
|
1
2
|
/**
|
|
2
3
|
* Status of a charging session
|
|
3
4
|
*/
|
|
@@ -65,6 +66,10 @@ export interface EnyoCharge {
|
|
|
65
66
|
numberOfPhases: number;
|
|
66
67
|
/** Active charging schedule entries, if smart charging is in use */
|
|
67
68
|
schedule?: EnyoChargeScheduleEntry[];
|
|
69
|
+
/** Charging mode applied to this session (e.g. immediate, cost-optimized, price-limit) */
|
|
70
|
+
chargeMode?: EnyoChargeModeEnum;
|
|
71
|
+
/** Target completion time for the charging session as an ISO 8601 timestamp */
|
|
72
|
+
completeAtIsoTimestamp?: string;
|
|
68
73
|
}
|
|
69
74
|
/**
|
|
70
75
|
* Represents a single entry in a charging schedule.
|
|
@@ -330,7 +330,8 @@ export interface EnyoDataBusApplianceFlexibilityAnnouncementV1 extends EnyoDataB
|
|
|
330
330
|
* changes. At least one of `state` or `status` should be set; both may be
|
|
331
331
|
* provided together if they change in the same event. `errorCodes` carries
|
|
332
332
|
* vendor- or protocol-specific codes that explain a transition into a
|
|
333
|
-
* `faulted` status
|
|
333
|
+
* `warning` or `faulted` status; each entry's `severity` field indicates
|
|
334
|
+
* which.
|
|
334
335
|
*/
|
|
335
336
|
export interface EnyoDataBusApplianceStateUpdateV1 extends EnyoDataBusMessage {
|
|
336
337
|
type: 'message';
|
|
@@ -345,9 +346,10 @@ export interface EnyoDataBusApplianceStateUpdateV1 extends EnyoDataBusMessage {
|
|
|
345
346
|
/**
|
|
346
347
|
* Optional vendor- or protocol-specific error codes that explain the
|
|
347
348
|
* current status. Each entry carries the raw code and may include
|
|
348
|
-
* pre-translated messages for UI display
|
|
349
|
-
*
|
|
350
|
-
*
|
|
349
|
+
* pre-translated messages for UI display, plus an optional `severity`
|
|
350
|
+
* (`'error'` or `'warning'`). Typically populated when transitioning
|
|
351
|
+
* into `warning` or `faulted`; omitted or empty when there is nothing
|
|
352
|
+
* to report.
|
|
351
353
|
*/
|
|
352
354
|
errorCodes?: EnyoApplianceErrorCode[];
|
|
353
355
|
};
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EnyoOnboardingSectionType = void 0;
|
|
3
|
+
exports.EnyoOnboardingSectionType = exports.EnyoOnboardingGuideCategory = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Enum representing the lifecycle category of an onboarding guide.
|
|
6
|
+
* Used to distinguish guides that perform different roles, e.g. initial
|
|
7
|
+
* configuration of a package vs. adding or reconnecting a single device.
|
|
8
|
+
*/
|
|
9
|
+
var EnyoOnboardingGuideCategory;
|
|
10
|
+
(function (EnyoOnboardingGuideCategory) {
|
|
11
|
+
/** Initial package configuration — shown when EnergyAppStateEnum is 'configuration-required' */
|
|
12
|
+
EnyoOnboardingGuideCategory["InitialSetup"] = "initial-setup";
|
|
13
|
+
/** Guide for adding a new device to an already-configured package */
|
|
14
|
+
EnyoOnboardingGuideCategory["AddNewDevice"] = "add-new-device";
|
|
15
|
+
/** Guide for reconnecting an existing device that has lost its connection */
|
|
16
|
+
EnyoOnboardingGuideCategory["ReconnectDevice"] = "reconnect-device";
|
|
17
|
+
})(EnyoOnboardingGuideCategory || (exports.EnyoOnboardingGuideCategory = EnyoOnboardingGuideCategory = {}));
|
|
4
18
|
/**
|
|
5
19
|
* Enum representing the type of content an onboarding section displays.
|
|
6
20
|
*/
|
|
@@ -9,6 +9,19 @@ export interface EnyoOnboardingTranslatedContent {
|
|
|
9
9
|
/** The translated text value */
|
|
10
10
|
value: string;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Enum representing the lifecycle category of an onboarding guide.
|
|
14
|
+
* Used to distinguish guides that perform different roles, e.g. initial
|
|
15
|
+
* configuration of a package vs. adding or reconnecting a single device.
|
|
16
|
+
*/
|
|
17
|
+
export declare enum EnyoOnboardingGuideCategory {
|
|
18
|
+
/** Initial package configuration — shown when EnergyAppStateEnum is 'configuration-required' */
|
|
19
|
+
InitialSetup = "initial-setup",
|
|
20
|
+
/** Guide for adding a new device to an already-configured package */
|
|
21
|
+
AddNewDevice = "add-new-device",
|
|
22
|
+
/** Guide for reconnecting an existing device that has lost its connection */
|
|
23
|
+
ReconnectDevice = "reconnect-device"
|
|
24
|
+
}
|
|
12
25
|
/**
|
|
13
26
|
* Enum representing the type of content an onboarding section displays.
|
|
14
27
|
*/
|
|
@@ -125,6 +138,12 @@ export interface EnyoOnboardingGuide {
|
|
|
125
138
|
description?: EnyoOnboardingTranslatedContent[];
|
|
126
139
|
/** Optional appliance ID if this guide is associated with an appliance */
|
|
127
140
|
applianceId?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Optional lifecycle category for this guide.
|
|
143
|
+
* Allows hosts to render category-specific entry points (e.g. "Add new device" buttons).
|
|
144
|
+
* Defaults to InitialSetup semantics when omitted, for backwards compatibility.
|
|
145
|
+
*/
|
|
146
|
+
category?: EnyoOnboardingGuideCategory;
|
|
128
147
|
/** Ordered array of steps in the onboarding flow */
|
|
129
148
|
steps: EnyoOnboardingStep[];
|
|
130
149
|
}
|