@flighthq/power 0.5.0-next.1944.cd6b6e7 → 0.5.1-edge.638.d4dbd4e

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/README.md CHANGED
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/power`. The `@flight
13
13
  This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
14
14
 
15
15
  - [Flight project](https://github.com/flighthq/flight)
16
- - [Source for @flighthq/power@0.5.0-next.1944.cd6b6e7](https://github.com/flighthq/flight/tree/cd6b6e71eb472b3a4249830a7743333f019fa7b7/packages/power)
17
- - [License](https://github.com/flighthq/flight/blob/cd6b6e71eb472b3a4249830a7743333f019fa7b7/LICENSE.md)
16
+ - [Source for @flighthq/power@0.5.1-edge.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/power)
17
+ - [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { attachPower, createPower, detachPower, disposePower, enablePowerSignals, explainPowerBackend, getPowerBatteryHealth, getPowerIdlePollingIntervalMs, getPowerStatus, getPowerSystemIdleState, getPowerSystemIdleTime, getPowerThermalState, hasPowerKeepAwake, setPowerIdlePollingIntervalMs, setPowerKeepAwake, } from './contract';
1
+ export { acquirePowerKeepAwake, attachPower, createPower, destroyPowerKeepAwake, detachPower, disposePower, enablePowerSignals, getPowerBatteryHealth, getPowerIdlePollingIntervalMs, getPowerStatus, getPowerSystemIdleState, getPowerSystemIdleTime, getPowerThermalState, isPowerKeepAwakeActive, makePowerBatteryHealth, makePowerStatus, releasePowerKeepAwake, setPowerIdlePollingIntervalMs, } from './contract';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,6BAA6B,EAC7B,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { attachPower, createPower, detachPower, disposePower, enablePowerSignals, explainPowerBackend, getPowerBatteryHealth, getPowerIdlePollingIntervalMs, getPowerStatus, getPowerSystemIdleState, getPowerSystemIdleTime, getPowerThermalState, hasPowerKeepAwake, setPowerIdlePollingIntervalMs, setPowerKeepAwake, } from './contract';
1
+ export { acquirePowerKeepAwake, attachPower, createPower, destroyPowerKeepAwake, detachPower, disposePower, enablePowerSignals, getPowerBatteryHealth, getPowerIdlePollingIntervalMs, getPowerStatus, getPowerSystemIdleState, getPowerSystemIdleTime, getPowerThermalState, isPowerKeepAwakeActive, makePowerBatteryHealth, makePowerStatus, releasePowerKeepAwake, setPowerIdlePollingIntervalMs, } from './contract';
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,6BAA6B,EAC7B,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
package/dist/power.d.ts CHANGED
@@ -1,25 +1,26 @@
1
- import type { BackendExplanation } from '@flighthq/types/contract';
2
- import type { Power, PowerBackend, PowerBatteryHealth, PowerIdleState, PowerKeepAwakeMode, PowerStatus, PowerThermalState } from '@flighthq/types/contract';
3
- export declare function attachPower(power: Power, idleThresholdSeconds?: number): void;
1
+ import type { HasPowerIdle, HasPowerKeepAwake, HasPowerStatus, HasPowerThermal, PowerAttachHost, Power, PowerBatteryHealth, PowerIdleState, PowerKeepAwakeAcquireResult, PowerKeepAwakeMode, PowerKeepAwakeReleaseResult, PowerStatus, PowerThermalState } from '@flighthq/types/contract';
2
+ export declare function acquirePowerKeepAwake(host: HasPowerKeepAwake, mode?: PowerKeepAwakeMode): Promise<PowerKeepAwakeAcquireResult>;
3
+ export declare function attachPower(host: PowerAttachHost, power: Power, idleThresholdSeconds?: number): void;
4
4
  export declare function createPower(): Power;
5
- export declare function createPowerBatteryHealth(): PowerBatteryHealth;
6
- export declare function createPowerStatus(): PowerStatus;
5
+ export declare function destroyPowerKeepAwake(...hosts: readonly HasPowerKeepAwake[]): void;
7
6
  export declare function detachPower(power: Power): void;
8
7
  export declare function disposePower(power: Power): void;
9
8
  export declare function enablePowerSignals(power: Power): void;
10
- export declare function explainPowerBackend(): BackendExplanation;
11
- export declare function getPowerBackend(): PowerBackend;
12
- export declare function getPowerBatteryHealth(out: PowerBatteryHealth): PowerBatteryHealth | null;
9
+ export declare function getPowerBatteryHealth(host: {
10
+ readonly power: {
11
+ readonly batteryHealth: {
12
+ getBatteryHealth(out: PowerBatteryHealth): PowerBatteryHealth;
13
+ };
14
+ };
15
+ }, out: PowerBatteryHealth): PowerBatteryHealth;
13
16
  export declare function getPowerIdlePollingIntervalMs(): number;
14
- export declare function getPowerStatus(out: PowerStatus): PowerStatus;
15
- export declare function getPowerSystemIdleState(thresholdSeconds: number): PowerIdleState;
16
- export declare function getPowerSystemIdleTime(): number;
17
- export declare function getPowerThermalState(): PowerThermalState;
18
- export declare function hasPowerKeepAwake(): boolean;
19
- export declare function installPowerHostBackend(backend: PowerBackend): void;
20
- export declare function observePowerHostResult(operation: string, succeeded: boolean): void;
21
- export declare function resetPowerBackendForTest(): void;
22
- export declare function setPowerBackend(backend: PowerBackend | null): void;
17
+ export declare function getPowerStatus(host: HasPowerStatus, out: PowerStatus): PowerStatus;
18
+ export declare function getPowerSystemIdleState(host: HasPowerIdle, thresholdSeconds: number): PowerIdleState;
19
+ export declare function getPowerSystemIdleTime(host: HasPowerIdle): number;
20
+ export declare function getPowerThermalState(host: HasPowerThermal): PowerThermalState;
21
+ export declare function isPowerKeepAwakeActive(host: HasPowerKeepAwake): boolean;
22
+ export declare function makePowerBatteryHealth(): PowerBatteryHealth;
23
+ export declare function makePowerStatus(): PowerStatus;
24
+ export declare function releasePowerKeepAwake(host: HasPowerKeepAwake): Promise<PowerKeepAwakeReleaseResult>;
23
25
  export declare function setPowerIdlePollingIntervalMs(intervalMs: number): void;
24
- export declare function setPowerKeepAwake(enabled: boolean, mode?: PowerKeepAwakeMode): boolean;
25
26
  //# sourceMappingURL=power.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"power.d.ts","sourceRoot":"","sources":["../src/power.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EACV,KAAK,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AASlC,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,oBAAoB,SAAK,GAAG,IAAI,CAyDzE;AAID,wBAAgB,WAAW,IAAI,KAAK,CAanC;AAGD,wBAAgB,wBAAwB,IAAI,kBAAkB,CAQ7D;AAGD,wBAAgB,iBAAiB,IAAI,WAAW,CAW/C;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAM9C;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAE/C;AAID,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAWrD;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAaxD;AAED,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAID,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,IAAI,CAExF;AAGD,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD;AAGD,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,CAE5D;AAGD,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,GAAG,cAAc,CAEhF;AAGD,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAGD,wBAAgB,oBAAoB,IAAI,iBAAiB,CAExD;AAGD,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAMnE;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAKlF;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAK/C;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAElE;AAKD,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEtE;AAID,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAEtF"}
1
+ {"version":3,"file":"power.d.ts","sourceRoot":"","sources":["../src/power.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EAEjB,cAAc,EACd,eAAe,EACf,eAAe,EACf,KAAK,EACL,kBAAkB,EAClB,cAAc,EACd,2BAA2B,EAC3B,kBAAkB,EAClB,2BAA2B,EAC3B,WAAW,EACX,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AASlC,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,iBAAiB,EACvB,IAAI,GAAE,kBAA0C,GAC/C,OAAO,CAAC,2BAA2B,CAAC,CAEtC;AAQD,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,SAAK,GAAG,IAAI,CA+DhG;AAID,wBAAgB,WAAW,IAAI,KAAK,CAYnC;AAYD,wBAAgB,qBAAqB,CAAC,GAAG,KAAK,EAAE,SAAS,iBAAiB,EAAE,GAAG,IAAI,CAiBlF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAa9C;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAW/C;AAGD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAUrD;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE;IACJ,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE;YAAE,gBAAgB,CAAC,GAAG,EAAE,kBAAkB,GAAG,kBAAkB,CAAA;SAAE,CAAA;KAAE,CAAC;CAC/G,EACD,GAAG,EAAE,kBAAkB,GACtB,kBAAkB,CAEpB;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,WAAW,GAAG,WAAW,CAElF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,GAAG,cAAc,CAEpG;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAE7E;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAEvE;AAKD,wBAAgB,sBAAsB,IAAI,kBAAkB,CAQ3D;AAID,wBAAgB,eAAe,IAAI,WAAW,CAW7C;AAID,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAEnG;AAED,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEtE"}
package/dist/power.js CHANGED
@@ -1,286 +1,243 @@
1
- import { createSignal, emitSignal, hasSignalSlots } from '@flighthq/signals/contract';
2
- // Begins delivering power changes to `power`'s signals by subscribing to the active backend. On each
3
- // change it reads a fresh status and emits onChange plus onCharging/onDischarging on charging
4
- // transitions. For onIdleStateChange, a polling interval is started at the rate set by
5
- // setPowerIdlePollingIntervalMs (default 5000ms); the poll is guarded so it only emits when at least
6
- // one listener is connected to onIdleStateChange. idleThresholdSeconds (default 60) controls what
7
- // idle state the backend reports as 'Idle' vs 'Active'. Idempotent: a prior subscription is torn
8
- // down first. Pair with detachPower/disposePower.
9
- export function attachPower(power, idleThresholdSeconds = 60) {
1
+ import { createEntity } from '@flighthq/entity/contract';
2
+ import { clearSignal, createSignal, emitSignal, hasSignalSlots } from '@flighthq/signals/contract';
3
+ // AN ATTEMPT IS NEVER AN OUTCOME. The previous seam recorded that an operation had been STARTED and
4
+ // reported that as availability: a denied Wake Lock request was observed as 'available' because the
5
+ // observation ran on the synchronous path before the promise settled. Every result here is produced
6
+ // after the underlying operation has actually completed.
7
+ // Acquires a keep-awake lock, resolving only once the provider has really taken it. `ok` means the
8
+ // provider acquired its mechanism — not that the hardware can never sleep for other policy reasons.
9
+ export function acquirePowerKeepAwake(host, mode = 'PreventDisplaySleep') {
10
+ return host.power.keepAwake.acquire(mode);
11
+ }
12
+ // Starts delivering the host's power events into `power`'s signals. Re-attaching detaches first, so one
13
+ // entity never holds two live subscriptions.
14
+ //
15
+ // Each unsubscribe is ORIGIN-PINNED: it is kept beside the entity that opened it, so detaching one
16
+ // entity ends exactly its own subscriptions. Teardown ATTEMPTS ALL of them even if one throws, because
17
+ // abandoning the rest would leak every subscription after the first failure.
18
+ export function attachPower(host, power, idleThresholdSeconds = 60) {
10
19
  detachPower(power);
11
- const backend = getPowerBackend();
12
- let wasCharging = backend.getStatus(_scratch).isCharging;
13
- const unsubscribeChange = backend.subscribe(() => {
14
- const status = backend.getStatus(_scratch);
15
- if (power.onChange !== null)
16
- emitSignal(power.onChange, status);
17
- if (status.isCharging !== wasCharging) {
18
- wasCharging = status.isCharging;
19
- const transition = status.isCharging ? power.onCharging : power.onDischarging;
20
- if (transition !== null)
21
- emitSignal(transition);
22
- }
23
- });
24
- const unsubscribeLockScreen = backend.subscribeLockScreen(() => {
25
- if (power.onLockScreen !== null)
26
- emitSignal(power.onLockScreen);
27
- });
28
- const unsubscribeLowPowerModeChange = backend.subscribeLowPowerModeChange(() => {
29
- if (power.onLowPowerModeChange !== null)
30
- emitSignal(power.onLowPowerModeChange);
31
- });
32
- const unsubscribeResume = backend.subscribeResume(() => {
33
- if (power.onResume !== null)
34
- emitSignal(power.onResume);
35
- });
36
- const unsubscribeSuspend = backend.subscribeSuspend(() => {
37
- if (power.onSuspend !== null)
38
- emitSignal(power.onSuspend);
39
- });
40
- const unsubscribeThermalStateChange = backend.subscribeThermalStateChange(() => {
41
- if (power.onThermalStateChange !== null)
42
- emitSignal(power.onThermalStateChange);
43
- });
44
- const unsubscribeUnlockScreen = backend.subscribeUnlockScreen(() => {
45
- if (power.onUnlockScreen !== null)
46
- emitSignal(power.onUnlockScreen);
47
- });
48
- // Idle state polling: poll at the configured interval and emit onIdleStateChange on transitions.
49
- // The poll emits only when at least one slot is connected, avoiding spurious allocations when
50
- // nobody is listening. The interval still runs (cheaply) so state transitions are never missed
51
- // when a listener connects after attach.
52
- let lastIdleState = backend.getSystemIdleState(idleThresholdSeconds);
53
- const idleIntervalId = setInterval(() => {
54
- const idleSignal = power.onIdleStateChange;
55
- if (idleSignal === null || !hasSignalSlots(idleSignal))
56
- return;
57
- const current = backend.getSystemIdleState(idleThresholdSeconds);
58
- if (current !== lastIdleState) {
59
- lastIdleState = current;
60
- emitSignal(idleSignal);
61
- }
62
- }, _idlePollingIntervalMs);
63
- _subscriptions.set(power, () => {
64
- unsubscribeChange();
65
- unsubscribeLockScreen();
66
- unsubscribeLowPowerModeChange();
67
- unsubscribeResume();
68
- unsubscribeSuspend();
69
- unsubscribeThermalStateChange();
70
- unsubscribeUnlockScreen();
71
- clearInterval(idleIntervalId);
72
- });
20
+ const teardowns = [];
21
+ const status = host.power.status;
22
+ const change = host.power.change;
23
+ if (change !== undefined) {
24
+ let wasCharging = status !== undefined ? status.getStatus(makePowerStatus()).isCharging : false;
25
+ teardowns.push(change.subscribe(() => {
26
+ // A FRESH value per event, never a shared reusable buffer: a listener may retain this payload
27
+ // and it must not mutate underneath on the next power event.
28
+ const current = status !== undefined ? status.getStatus(makePowerStatus()) : null;
29
+ if (current !== null && power.onChange !== null)
30
+ emitSignal(power.onChange, current);
31
+ if (current !== null && current.isCharging !== wasCharging) {
32
+ wasCharging = current.isCharging;
33
+ const transition = current.isCharging ? power.onCharging : power.onDischarging;
34
+ if (transition !== null)
35
+ emitSignal(transition);
36
+ }
37
+ }));
38
+ }
39
+ const sessionLock = host.power.sessionLock;
40
+ if (sessionLock !== undefined) {
41
+ teardowns.push(sessionLock.subscribeLock(() => emitSignalWhenPresent(power.onLockScreen)));
42
+ teardowns.push(sessionLock.subscribeUnlock(() => emitSignalWhenPresent(power.onUnlockScreen)));
43
+ }
44
+ const suspension = host.power.suspension;
45
+ if (suspension !== undefined) {
46
+ teardowns.push(suspension.subscribeSuspend(() => emitSignalWhenPresent(power.onSuspend)));
47
+ teardowns.push(suspension.subscribeResume(() => emitSignalWhenPresent(power.onResume)));
48
+ }
49
+ const thermal = host.power.thermal;
50
+ if (thermal !== undefined) {
51
+ teardowns.push(thermal.subscribeThermalStateChange((state) => {
52
+ if (power.onThermalStateChange !== null)
53
+ emitSignal(power.onThermalStateChange, state);
54
+ }));
55
+ }
56
+ // Idle is polled because no host pushes idle transitions. The interval exists only when the host
57
+ // actually offers the idle slot: a host that would answer a constant 'Unknown' omits the slot, so
58
+ // nothing polls a value that provably cannot change.
59
+ const idle = host.power.idle;
60
+ if (idle !== undefined) {
61
+ let lastIdleState = idle.getIdleState(idleThresholdSeconds);
62
+ const idleIntervalId = setInterval(() => {
63
+ const idleSignal = power.onIdleStateChange;
64
+ if (idleSignal === null || !hasSignalSlots(idleSignal))
65
+ return;
66
+ const current = idle.getIdleState(idleThresholdSeconds);
67
+ if (current !== lastIdleState) {
68
+ lastIdleState = current;
69
+ emitSignal(idleSignal);
70
+ }
71
+ }, _idlePollingIntervalMs);
72
+ teardowns.push(() => clearInterval(idleIntervalId));
73
+ }
74
+ _subscriptions.set(power, teardowns);
73
75
  }
74
76
  // Allocates a Power event entity with its signals left null. Call enablePowerSignals to allocate the
75
- // signals to connect to, and attachPower to start delivering backend changes into them.
77
+ // signals to connect to, and attachPower to start delivering host events into them.
76
78
  export function createPower() {
77
- return {
79
+ return createEntity({
78
80
  onChange: null,
79
81
  onCharging: null,
80
82
  onDischarging: null,
81
83
  onIdleStateChange: null,
82
84
  onLockScreen: null,
83
- onLowPowerModeChange: null,
84
85
  onResume: null,
85
86
  onSuspend: null,
86
87
  onThermalStateChange: null,
87
88
  onUnlockScreen: null,
88
- };
89
- }
90
- // Allocates a zeroed PowerBatteryHealth, suitable as the `out` for getPowerBatteryHealth.
91
- export function createPowerBatteryHealth() {
92
- return {
93
- capacityWearLevel: -1,
94
- cycleCount: -1,
95
- healthState: 'Unknown',
96
- temperatureCelsius: -1,
97
- voltage: -1,
98
- };
89
+ });
99
90
  }
100
- // Allocates a zeroed PowerStatus, suitable as the `out` for getPowerStatus.
101
- export function createPowerStatus() {
102
- return {
103
- batteryLevel: -1,
104
- chargingTime: -1,
105
- dischargingTime: -1,
106
- isBatteryLow: false,
107
- isCharging: false,
108
- isLowPower: false,
109
- isOnBattery: false,
110
- thermalState: 'Unknown',
111
- };
91
+ // Stops delivery without discarding the entity. Runs every teardown this entity opened, ATTEMPTING ALL
92
+ // of them: one throwing unsubscribe must not strand the rest. Does not touch the provider — that is
93
+ // `destroy` on the host's slot, a separate lifecycle.
94
+ // FINAL RELEASE for the one power slot that owns a whole-provider resource. Destroys every DISTINCT
95
+ // keep-awake provider exactly once — alias-safe, because two hosts may share one provider object and
96
+ // destroying it twice would double-release an OS lock.
97
+ //
98
+ // Attempt-all: every obligation is tried even after one throws, and the first error is rethrown once the
99
+ // siblings have run. A provider whose destroy threw is RETAINED, so a later call retries only the
100
+ // failures; the ones that succeeded are forgotten and never destroyed twice.
101
+ export function destroyPowerKeepAwake(...hosts) {
102
+ const pending = new Set();
103
+ for (const host of hosts) {
104
+ const provider = host.power.keepAwake;
105
+ if (!_destroyedKeepAwake.has(provider))
106
+ pending.add(provider);
107
+ }
108
+ let failure = null;
109
+ for (const provider of pending) {
110
+ try {
111
+ if (provider.destroy !== undefined)
112
+ assertSyncVoid(provider.destroy());
113
+ }
114
+ catch (error) {
115
+ failure ??= error;
116
+ continue;
117
+ }
118
+ _destroyedKeepAwake.add(provider);
119
+ }
120
+ if (failure !== null)
121
+ throw failure;
112
122
  }
113
- // Stops delivery to `power` and forgets its subscription. Safe to call when not attached.
114
123
  export function detachPower(power) {
115
- const unsubscribe = _subscriptions.get(power);
116
- if (unsubscribe !== undefined) {
117
- unsubscribe();
118
- _subscriptions.delete(power);
124
+ const teardowns = _subscriptions.get(power);
125
+ if (teardowns === undefined)
126
+ return;
127
+ _subscriptions.delete(power);
128
+ let failure = null;
129
+ for (const teardown of teardowns) {
130
+ try {
131
+ teardown();
132
+ }
133
+ catch (error) {
134
+ failure ??= error;
135
+ }
119
136
  }
137
+ if (failure !== null)
138
+ throw failure;
120
139
  }
121
- // Releases `power` for garbage collection by detaching its backend subscription. The signals remain
122
- // plain GC-managed memory afterward.
140
+ // Terminal disposal: detaches and then clears the entity's signals, so it becomes GC-eligible.
123
141
  export function disposePower(power) {
124
142
  detachPower(power);
125
- }
126
- // Allocates any not-yet-allocated Power signals so callers can connect to them. Idempotent: signals
127
- // already allocated are left untouched. Pair with attachPower to begin delivery.
143
+ if (power.onChange !== null)
144
+ clearSignal(power.onChange);
145
+ if (power.onCharging !== null)
146
+ clearSignal(power.onCharging);
147
+ if (power.onDischarging !== null)
148
+ clearSignal(power.onDischarging);
149
+ if (power.onIdleStateChange !== null)
150
+ clearSignal(power.onIdleStateChange);
151
+ if (power.onLockScreen !== null)
152
+ clearSignal(power.onLockScreen);
153
+ if (power.onResume !== null)
154
+ clearSignal(power.onResume);
155
+ if (power.onSuspend !== null)
156
+ clearSignal(power.onSuspend);
157
+ if (power.onThermalStateChange !== null)
158
+ clearSignal(power.onThermalStateChange);
159
+ if (power.onUnlockScreen !== null)
160
+ clearSignal(power.onUnlockScreen);
161
+ }
162
+ // Allocates any not-yet-allocated Power signals so callers can connect to them. Idempotent.
128
163
  export function enablePowerSignals(power) {
129
- if (power.onChange === null)
130
- power.onChange = createSignal();
131
- if (power.onCharging === null)
132
- power.onCharging = createSignal();
133
- if (power.onDischarging === null)
134
- power.onDischarging = createSignal();
135
- if (power.onIdleStateChange === null)
136
- power.onIdleStateChange = createSignal();
137
- if (power.onLockScreen === null)
138
- power.onLockScreen = createSignal();
139
- if (power.onLowPowerModeChange === null)
140
- power.onLowPowerModeChange = createSignal();
141
- if (power.onResume === null)
142
- power.onResume = createSignal();
143
- if (power.onSuspend === null)
144
- power.onSuspend = createSignal();
145
- if (power.onThermalStateChange === null)
146
- power.onThermalStateChange = createSignal();
147
- if (power.onUnlockScreen === null)
148
- power.onUnlockScreen = createSignal();
164
+ power.onChange ??= createSignal();
165
+ power.onCharging ??= createSignal();
166
+ power.onDischarging ??= createSignal();
167
+ power.onIdleStateChange ??= createSignal();
168
+ power.onLockScreen ??= createSignal();
169
+ power.onResume ??= createSignal();
170
+ power.onSuspend ??= createSignal();
171
+ power.onThermalStateChange ??= createSignal();
172
+ power.onUnlockScreen ??= createSignal();
173
+ }
174
+ export function getPowerBatteryHealth(host, out) {
175
+ return host.power.batteryHealth.getBatteryHealth(out);
149
176
  }
150
- export function explainPowerBackend() {
151
- if (_custom !== null) {
152
- return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
153
- }
154
- if (_host !== null) {
155
- return {
156
- conflict: _hostConflict,
157
- layer: 'host',
158
- operation: _hostObservation !== null ? _hostObservation.operation : null,
159
- viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
160
- };
161
- }
162
- return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
163
- }
164
- export function getPowerBackend() {
165
- return _custom ?? _host ?? _sentinel;
166
- }
167
- // Returns the battery health detail from the active backend, writing into `out` and returning it,
168
- // or returns null when the active backend does not support battery health reporting.
169
- export function getPowerBatteryHealth(out) {
170
- return getPowerBackend().getBatteryHealth(out);
171
- }
172
- // Returns the current idle-state polling interval in milliseconds (default 5000).
173
177
  export function getPowerIdlePollingIntervalMs() {
174
178
  return _idlePollingIntervalMs;
175
179
  }
176
- // Fills `out` with the current power snapshot and returns it.
177
- export function getPowerStatus(out) {
178
- return getPowerBackend().getStatus(out);
179
- }
180
- // Returns the current system idle state at the given threshold in seconds.
181
- export function getPowerSystemIdleState(thresholdSeconds) {
182
- return getPowerBackend().getSystemIdleState(thresholdSeconds);
180
+ export function getPowerStatus(host, out) {
181
+ return host.power.status.getStatus(out);
183
182
  }
184
- // Returns the elapsed seconds since the last user input event, or -1 when unsupported.
185
- export function getPowerSystemIdleTime() {
186
- return getPowerBackend().getSystemIdleTime();
183
+ export function getPowerSystemIdleState(host, thresholdSeconds) {
184
+ return host.power.idle.getIdleState(thresholdSeconds);
187
185
  }
188
- // Returns the current thermal state from the active backend.
189
- export function getPowerThermalState() {
190
- return getPowerBackend().getStatus(_scratch).thermalState;
186
+ export function getPowerSystemIdleTime(host) {
187
+ return host.power.idle.getIdleTimeSeconds();
191
188
  }
192
- // Returns true when a keep-awake lock is currently held by the active backend.
193
- export function hasPowerKeepAwake() {
194
- return getPowerBackend().isKeepAwakeActive();
189
+ export function getPowerThermalState(host) {
190
+ return host.power.thermal.getThermalState();
195
191
  }
196
- export function installPowerHostBackend(backend) {
197
- if (_host !== null) {
198
- if (_host !== backend)
199
- _hostConflict = true;
200
- return;
201
- }
202
- _host = backend;
192
+ export function isPowerKeepAwakeActive(host) {
193
+ return host.power.keepAwake.isActive();
203
194
  }
204
- export function observePowerHostResult(operation, succeeded) {
205
- _hostObservation = {
206
- operation,
207
- viability: succeeded ? 'available' : 'runtime-api-unavailable',
195
+ // Allocates a PowerBatteryHealth value with the domain's complete unknown encoding. Not a create*: this
196
+ // is a plain value the queries fill, with no identity — but it earns a function because the -1/'Unknown'
197
+ // defaults are load-bearing and a literal `{}` would read as undefined where the domain expects -1.
198
+ export function makePowerBatteryHealth() {
199
+ return {
200
+ capacityWearLevel: -1,
201
+ cycleCount: -1,
202
+ healthState: 'Unknown',
203
+ temperatureCelsius: -1,
204
+ voltage: -1,
208
205
  };
209
206
  }
210
- export function resetPowerBackendForTest() {
211
- _custom = null;
212
- _host = null;
213
- _hostConflict = false;
214
- _hostObservation = null;
207
+ // Allocates a PowerStatus value with the domain's complete unknown encoding. Same reasoning as
208
+ // makePowerBatteryHealth: a value, not an entity, but the defaults are load-bearing.
209
+ export function makePowerStatus() {
210
+ return {
211
+ batteryLevel: -1,
212
+ chargingTime: -1,
213
+ dischargingTime: -1,
214
+ isBatteryLow: false,
215
+ isCharging: false,
216
+ isLowPower: false,
217
+ isOnBattery: false,
218
+ thermalState: 'Unknown',
219
+ };
215
220
  }
216
- export function setPowerBackend(backend) {
217
- _custom = backend;
221
+ // Releases a keep-awake lock, resolving only once the provider has really let it go. State is never
222
+ // published before the awaited release succeeds.
223
+ export function releasePowerKeepAwake(host) {
224
+ return host.power.keepAwake.release();
218
225
  }
219
- // Sets the interval at which attachPower polls the backend for idle state changes. The default is
220
- // 5000ms (5 seconds); set lower for more responsive idle detection at the cost of more frequent
221
- // backend calls. Only affects Power entities attached after this call.
222
226
  export function setPowerIdlePollingIntervalMs(intervalMs) {
223
- _idlePollingIntervalMs = intervalMs;
227
+ _idlePollingIntervalMs = intervalMs > 0 ? intervalMs : 1;
224
228
  }
225
- // Requests or releases a keep-awake lock for the given mode; returns whether honored.
226
- // mode defaults to 'PreventDisplaySleep'.
227
- export function setPowerKeepAwake(enabled, mode) {
228
- return getPowerBackend().setKeepAwake(enabled, mode);
229
+ function emitSignalWhenPresent(signal) {
230
+ if (signal !== null)
231
+ emitSignal(signal);
229
232
  }
230
- let _custom = null;
231
- let _host = null;
232
- let _hostConflict = false;
233
- let _hostObservation = null;
234
233
  let _idlePollingIntervalMs = 5000;
235
- const _scratch = createPowerStatus();
236
- const _sentinel = {
237
- getBatteryHealth() {
238
- return null;
239
- },
240
- getStatus(out) {
241
- out.batteryLevel = -1;
242
- out.chargingTime = -1;
243
- out.dischargingTime = -1;
244
- out.isBatteryLow = false;
245
- out.isCharging = false;
246
- out.isLowPower = false;
247
- out.isOnBattery = false;
248
- out.thermalState = 'Unknown';
249
- return out;
250
- },
251
- getSystemIdleState() {
252
- return 'Unknown';
253
- },
254
- getSystemIdleTime() {
255
- return -1;
256
- },
257
- isKeepAwakeActive() {
258
- return false;
259
- },
260
- setKeepAwake() {
261
- return false;
262
- },
263
- subscribe() {
264
- return () => { };
265
- },
266
- subscribeLockScreen() {
267
- return () => { };
268
- },
269
- subscribeLowPowerModeChange() {
270
- return () => { };
271
- },
272
- subscribeResume() {
273
- return () => { };
274
- },
275
- subscribeSuspend() {
276
- return () => { };
277
- },
278
- subscribeThermalStateChange() {
279
- return () => { };
280
- },
281
- subscribeUnlockScreen() {
282
- return () => { };
283
- },
284
- };
234
+ // Origin-pinned teardown bookkeeping: each entity's own unsubscribes, held beside the entity rather
235
+ // than in a shared slot, so detaching one never ends another's subscriptions.
285
236
  const _subscriptions = new WeakMap();
237
+ // Providers already finally-released. A destroy that THREW is deliberately absent, so the next call
238
+ // retries exactly the failed obligations and never re-destroys a successful one.
239
+ const _destroyedKeepAwake = new WeakSet();
240
+ function assertSyncVoid(value) {
241
+ void value;
242
+ }
286
243
  //# sourceMappingURL=power.js.map