@wcstack/wakelock 1.12.1

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.ja.md ADDED
@@ -0,0 +1,173 @@
1
+ # @wcstack/wakelock
2
+
3
+ `@wcstack/wakelock` は wcstack エコシステムのためのヘッドレスな Screen Wake Lock コンポーネントです。
4
+
5
+ 視覚的な UI ウィジェットではありません。
6
+ **非同期プリミティブノード** ですが、他のすべての @wcstack センサーとは *逆向き* に動作します。`@wcstack/geolocation` や `@wcstack/intersection` などは **プロデューサー**(`element → state`)で、デバイスのシグナルをリアクティブな状態に変換します。`@wcstack/wakelock` は **純粋なシンク**(`state → element`)で、バインドされた真偽値が画面を起こし続けるかどうかを駆動します。
7
+
8
+ `@wcstack/state` と組み合わせると、`<wcs-wakelock>` は宣言的な1行として読めます:
9
+
10
+ ```html
11
+ <wcs-wakelock data-wcs="active: isPlaying"></wcs-wakelock>
12
+ ```
13
+
14
+ *「`isPlaying` が true である **間** 画面を起こし続ける」*。`navigator.wakeLock.request()` も、`visibilitychange` での再取得のグルーコードも、後始末も不要です。
15
+
16
+ - **入力サーフェス**: `active`, `type`, `manual`
17
+ - **出力ステートサーフェス**: `held`, `error`
18
+ - **コマンド**: `request()`, `release()`
19
+
20
+ `@wcstack/wakelock` は [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md)(Core / Shell / Binding Contract)アーキテクチャに従います:
21
+
22
+ - **Core** (`WakeLockCore`) が sentinel、desired/actual の分離、自動解放後の再取得ループ、never-throw な失敗処理を所有
23
+ - **Shell** (`<wcs-wakelock>`) が `active` 属性を `request()` / `release()` にマップし、ライフサイクルを管理
24
+ - **Binding Contract** (`static wcBindable`) が観測可能な `properties`、書き込み可能な `inputs`、呼び出し可能な `commands` を宣言
25
+
26
+ ## なぜ存在するのか
27
+
28
+ Screen Wake Lock API には扱いにくい性質があります。ページが可視でなくなった瞬間(タブが非表示、ウィンドウが最小化)に、OS が **自動的にロックを解放** してしまうのです。実際に「再生中は画面を起こし続ける」を実現するには、望み(desired intent)を自分で保持し、ページが再び可視になるたびにロックを再取得しなければなりません。
29
+
30
+ `@wcstack/wakelock` はそのリース管理をコンポーネントの内側に閉じ込めます。真偽値をバインドすれば、その値が true である限り、コンポーネントが可視性変化をまたいでロックを生かし続けます。
31
+
32
+ ## 望み(`active`)と実状態(`held`)
33
+
34
+ 自動解放があるため、*望んでいること* と *いま実際に保持していること* は乖離します。そこで両者は別々のサーフェスになっています:
35
+
36
+ | サーフェス | 向き | 意味 |
37
+ |----------|---------|------|
38
+ | `active` | 入力 | **望み(desired intent)。** `true` の間ロックを保持。OS の自動解放を越えて維持される。 |
39
+ | `held` | 出力 | **実状態。** *いま* sentinel を保持しているか。自動解放で `false` になり、ページが再び可視になると `true` に戻る。 |
40
+
41
+ `active` は意図的に観測可能な出力 **ではありません**。OS がロックを落としても変化せず、変化するのは `held` だけだからです。UI でライブな「画面が起き続けている」状態を反映したい場合は `held` をバインドしてください。
42
+
43
+ ## インストール
44
+
45
+ ```bash
46
+ npm install @wcstack/wakelock
47
+ ```
48
+
49
+ ## クイックスタート
50
+
51
+ ### 動画再生中に画面を起こし続ける
52
+
53
+ ```html
54
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
55
+ <script type="module" src="https://esm.run/@wcstack/wakelock/auto"></script>
56
+
57
+ <wcs-state>
58
+ <script type="module">
59
+ export default {
60
+ playing: false,
61
+ startPlaying() { this.playing = true; },
62
+ stopPlaying() { this.playing = false; },
63
+ };
64
+ </script>
65
+
66
+ <video
67
+ src="/movie.mp4"
68
+ data-wcs="onplay: startPlaying; onpause: stopPlaying"
69
+ ></video>
70
+
71
+ <!-- ロックは `playing` が true の間だけ保持され、タブ切り替えを越えて生き残る。 -->
72
+ <wcs-wakelock data-wcs="active: playing"></wcs-wakelock>
73
+ </wcs-state>
74
+ ```
75
+
76
+ ### 実際のロック状態を UI に反映する
77
+
78
+ ```html
79
+ <wcs-wakelock data-wcs="active: keepAwake; held: screenLocked"></wcs-wakelock>
80
+
81
+ <span data-wcs="textContent: screenLocked"></span>
82
+ ```
83
+
84
+ ### コマンド駆動(命令的)
85
+
86
+ ```html
87
+ <wcs-wakelock data-wcs="command.request: $command.stayAwake"></wcs-wakelock>
88
+ ```
89
+
90
+ > **コマンドは `active` 属性をミラーしません。** `request` / `release` コマンドは
91
+ > `active` 属性に触れずに Core の望み(desired intent)を直接切り替えるため、コマンド後は
92
+ > 要素の `active` プロパティ(属性のミラー)が `false` のまま `held` が `true`(またはその逆)
93
+ > になりえます。同一要素でコマンド駆動と `active` 属性バインドを混在させないでください
94
+ > — どちらか一方を選ぶこと。単一情報源には `active: ...` でバインドします。
95
+
96
+ ## 属性
97
+
98
+ | 属性 | 型 | 既定値 | 説明 |
99
+ |-----------|---------|----------|------|
100
+ | `active` | boolean | `false` | 望み: 存在する間、画面を起こし続ける。看板バインディング(`active: isPlaying`)。 |
101
+ | `type` | string | `screen` | ロックの種類。標準化されているのは `screen` のみ。属性は将来互換のために存在。 |
102
+ | `manual` | boolean | `false` | `active` が付いていても接続時に自動取得しない。代わりに `request()` / `release()` で駆動する。 |
103
+
104
+ > **`manual` は接続時のポリシーであり、ライブなスイッチではありません。** 接続 *後* に `manual` 属性を外しても自動取得はしません — `active` をトグルするか `request()` を呼んでください。(ライブな `active` のトグルは `manual` に関係なく常に request/release を駆動します。)
105
+
106
+ ## 出力ステート
107
+
108
+ | プロパティ | 型 | 説明 |
109
+ |----------|------------------|------|
110
+ | `held` | `boolean` | wake lock の sentinel をいま保持しているか。OS の自動解放と再取得を反映する。 |
111
+ | `error` | `Error \| null` | 直近のリクエスト失敗(拒否、未対応など)、または `null`。 |
112
+
113
+ > **`wcs-wakelock:error` は単なる失敗シグナルではなく、プロパティ変更通知**(wc-bindable モデル)です: 失敗時には `detail` = エラーで発火し、その後のリクエストが成功して `error` プロパティがクリアされると `detail = null` で再度発火します。`error == null` は「過去に一度もエラーがなかった」ではなく「いまエラーがない」と解釈してください。
114
+
115
+ ## コマンド
116
+
117
+ | コマンド | 説明 |
118
+ |-------------|------|
119
+ | `request()` | ロックを望み状態にして取得する(可視かつ対応環境なら)。reject しない — `error` を参照。 |
120
+ | `release()` | ロックを望まない状態にして、保持中の sentinel を解放する。 |
121
+
122
+ ## Binding Contract(`wcBindable`)
123
+
124
+ Core も Shell も [wc-bindable](https://github.com/csbc-dev) プロトコルを宣言します。
125
+
126
+ ```js
127
+ // WakeLockCore (ヘッドレス)
128
+ WakeLockCore.wcBindable = {
129
+ protocol: "wc-bindable",
130
+ version: 1,
131
+ properties: [
132
+ { name: "held", event: "wcs-wakelock:held-changed" },
133
+ { name: "error", event: "wcs-wakelock:error" },
134
+ ],
135
+ commands: [
136
+ { name: "request", async: true }, { name: "release" },
137
+ ],
138
+ };
139
+ ```
140
+
141
+ Shell(`<wcs-wakelock>`)は Core の `properties` / `commands` を継承し、DOM 駆動の `inputs`(`active`, `type`, `manual`)を宣言します。
142
+
143
+ ## Core を単体で使う
144
+
145
+ `WakeLockCore` はフレームワーク非依存で、カスタム要素なしでも使えます:
146
+
147
+ ```js
148
+ import { WakeLockCore } from "@wcstack/wakelock";
149
+
150
+ const core = new WakeLockCore();
151
+ core.addEventListener("wcs-wakelock:held-changed", (e) => {
152
+ console.log("screen awake:", e.detail);
153
+ });
154
+
155
+ await core.request(); // 取得(そして可視性変化をまたいで維持)
156
+ // 後で
157
+ core.release(); // 起こし続けるのを停止
158
+ core.dispose(); // visibilitychange リスナを除去
159
+ ```
160
+
161
+ Core を直接駆動する場合は、終わったら `dispose()` を呼んで `visibilitychange` リスナを除去してください。
162
+
163
+ ## ノートと制限
164
+
165
+ - **プロデューサーではなくシンク。** 他の @wcstack センサーとは違い、wake lock は状態に *よって* 駆動されます(`state → element`)。それが返すのは `held` だけです。
166
+ - **自動解放は肩代わりされます。** OS は複数の理由でロックを落とします — ページの非表示だけでなく、ページが可視のままでもバッテリー低下や省電力モードで落ちることがあります。コンポーネントはどちらの場合もリースを更新します: *可視*中の解放は即座に再取得し、*非表示*中の解放は次に可視へ戻ったときに再取得します(`active` が立っている限り)。このバインディングは「一度取得する」ではなく「active の **間** 起こし続ける」を意味します。再取得が**拒否**された場合はループせず、`error` で surface して静止します(支配的なケース: 仕様上バッテリー低下/省電力下の再リクエストは拒否されます)。再取得が**許可**された場合はリースを更新するため、OS が許可と解放を繰り返す限りコンポーネントも更新を続けます(解放1回につき request 1回、OS 駆動であり同期的なスピンではありません)。
167
+ - **セキュアコンテキスト(HTTPS)。** Screen Wake Lock API はセキュアコンテキスト(HTTPS、または `localhost`)でのみ動作します。
168
+ - **決して throw しない。** 未対応環境はサイレントな no-op(`held` は `false` のまま)。拒否されたリクエストは throw せず `error` プロパティで surface します。`request()` は決して reject しません。
169
+ - **パーミッションゲートなし。** 別個のパーミッションプロンプトはありません(ページが可視でない場合はリクエストが拒否されることがあり、それは `error` として surface します)。
170
+
171
+ ## ライセンス
172
+
173
+ MIT
package/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # @wcstack/wakelock
2
+
3
+ `@wcstack/wakelock` is a headless Screen Wake Lock component for the wcstack ecosystem.
4
+
5
+ It is not a visual UI widget.
6
+ It is an **async primitive node** — but, unlike every other @wcstack sensor, it runs *the other way*. `@wcstack/geolocation`, `@wcstack/intersection`, and friends are **producers** (`element → state`): they turn a device signal into reactive state. `@wcstack/wakelock` is a **pure sink** (`state → element`): a bound boolean drives whether the screen is kept awake.
7
+
8
+ With `@wcstack/state`, `<wcs-wakelock>` reads as one declarative line:
9
+
10
+ ```html
11
+ <wcs-wakelock data-wcs="active: isPlaying"></wcs-wakelock>
12
+ ```
13
+
14
+ *"Keep the screen awake **while** `isPlaying` is true."* No `navigator.wakeLock.request()`, no `visibilitychange` re-acquire glue, no teardown.
15
+
16
+ - **input surface**: `active`, `type`, `manual`
17
+ - **output state surface**: `held`, `error`
18
+ - **commands**: `request()`, `release()`
19
+
20
+ `@wcstack/wakelock` follows the [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md) (Core / Shell / Binding Contract) architecture:
21
+
22
+ - **Core** (`WakeLockCore`) owns the sentinel, the desired/actual split, the auto-release re-acquire loop, and never-throw failure handling
23
+ - **Shell** (`<wcs-wakelock>`) maps the `active` attribute to `request()` / `release()` and manages lifecycle
24
+ - **Binding Contract** (`static wcBindable`) declares observable `properties`, writable `inputs`, and callable `commands`
25
+
26
+ ## Why this exists
27
+
28
+ The Screen Wake Lock API has an awkward edge: the OS **automatically releases** the lock the moment the page stops being visible (tab hidden, window minimized). To actually "keep the screen awake while playing", you must hold the desired intent yourself and re-acquire the lock every time the page becomes visible again.
29
+
30
+ `@wcstack/wakelock` moves that lease management into the component. You bind a boolean; the component keeps the lock alive across visibility changes for as long as the boolean is true.
31
+
32
+ ## Desired (`active`) vs actual (`held`)
33
+
34
+ Because of auto-release, *what you want* and *what is currently held* diverge — so they are two separate surfaces:
35
+
36
+ | Surface | Direction | Meaning |
37
+ |----------|------------------|---------|
38
+ | `active` | input | **Desired intent.** Hold the lock while `true`. Survives an OS auto-release. |
39
+ | `held` | output | **Actual state.** Whether a sentinel is held *right now*. Flips to `false` on auto-release, back to `true` when the page is visible again. |
40
+
41
+ `active` is deliberately **not** an observable output: it does not change when the OS drops the lock — only `held` does. Bind `held` if your UI needs to reflect the live "screen is being kept awake" state.
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ npm install @wcstack/wakelock
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ### Keep the screen awake during video playback
52
+
53
+ ```html
54
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
55
+ <script type="module" src="https://esm.run/@wcstack/wakelock/auto"></script>
56
+
57
+ <wcs-state>
58
+ <script type="module">
59
+ export default {
60
+ playing: false,
61
+ startPlaying() { this.playing = true; },
62
+ stopPlaying() { this.playing = false; },
63
+ };
64
+ </script>
65
+
66
+ <video
67
+ src="/movie.mp4"
68
+ data-wcs="onplay: startPlaying; onpause: stopPlaying"
69
+ ></video>
70
+
71
+ <!-- Lock is held only while `playing` is true, and survives tab switches. -->
72
+ <wcs-wakelock data-wcs="active: playing"></wcs-wakelock>
73
+ </wcs-state>
74
+ ```
75
+
76
+ ### Reflect the actual lock state in the UI
77
+
78
+ ```html
79
+ <wcs-wakelock data-wcs="active: keepAwake; held: screenLocked"></wcs-wakelock>
80
+
81
+ <span data-wcs="textContent: screenLocked"></span>
82
+ ```
83
+
84
+ ### Command-driven (imperative)
85
+
86
+ ```html
87
+ <wcs-wakelock data-wcs="command.request: $command.stayAwake"></wcs-wakelock>
88
+ ```
89
+
90
+ > **Commands do not mirror the `active` attribute.** The `request` / `release`
91
+ > commands flip the desired intent on the Core directly without touching the
92
+ > `active` attribute, so the element's `active` property (an attribute mirror) can
93
+ > read `false` while `held` is `true` (or vice versa) after a command. Don't mix
94
+ > command-driven control with `active` attribute binding on the same element —
95
+ > pick one. Bind via `active: ...` for a single source of truth.
96
+
97
+ ## Attributes
98
+
99
+ | Attribute | Type | Default | Description |
100
+ |-----------|---------|----------|-------------|
101
+ | `active` | boolean | `false` | Desired intent: hold the screen awake while present. The headline binding (`active: isPlaying`). |
102
+ | `type` | string | `screen` | Lock type. Only `screen` is standardized; the attribute exists for forward compatibility. |
103
+ | `manual` | boolean | `false` | Do not auto-acquire on connect even if `active` is present; drive via `request()` / `release()` instead. |
104
+
105
+ > **`manual` is a connect-time policy, not a live switch.** Removing the `manual` attribute *after* connect does not auto-acquire — toggle `active` or call `request()`. (A live `active` toggle always drives request/release regardless of `manual`.)
106
+
107
+ ## Output state
108
+
109
+ | Property | Type | Description |
110
+ |----------|------------------|-------------|
111
+ | `held` | `boolean` | Whether a wake lock sentinel is currently held. Reflects OS auto-release and re-acquisition. |
112
+ | `error` | `Error \| null` | The last request failure (e.g. denied, unsupported), or `null`. |
113
+
114
+ > **`wcs-wakelock:error` is a property-change notification** (wc-bindable model), not just a failure signal: it fires with `detail` = the Error on a failed request, and fires again with `detail = null` when a later request succeeds and the `error` property is cleared. Read `error == null` as "no error *right now*", not "an error never happened".
115
+
116
+ ## Commands
117
+
118
+ | Command | Description |
119
+ |-------------|-------------|
120
+ | `request()` | Mark the lock desired and acquire it (if visible & supported). Never rejects — see `error`. |
121
+ | `release()` | Mark the lock no longer desired and release any held sentinel. |
122
+
123
+ ## Binding Contract (`wcBindable`)
124
+
125
+ Both the Core and the Shell declare the [wc-bindable](https://github.com/csbc-dev) protocol.
126
+
127
+ ```js
128
+ // WakeLockCore (headless)
129
+ WakeLockCore.wcBindable = {
130
+ protocol: "wc-bindable",
131
+ version: 1,
132
+ properties: [
133
+ { name: "held", event: "wcs-wakelock:held-changed" },
134
+ { name: "error", event: "wcs-wakelock:error" },
135
+ ],
136
+ commands: [
137
+ { name: "request", async: true }, { name: "release" },
138
+ ],
139
+ };
140
+ ```
141
+
142
+ The Shell (`<wcs-wakelock>`) inherits the Core's `properties` / `commands` and declares the DOM-driven `inputs` (`active`, `type`, `manual`).
143
+
144
+ ## Using the Core standalone
145
+
146
+ `WakeLockCore` is framework-agnostic and can be used without the custom element:
147
+
148
+ ```js
149
+ import { WakeLockCore } from "@wcstack/wakelock";
150
+
151
+ const core = new WakeLockCore();
152
+ core.addEventListener("wcs-wakelock:held-changed", (e) => {
153
+ console.log("screen awake:", e.detail);
154
+ });
155
+
156
+ await core.request(); // acquire (and keep across visibility changes)
157
+ // later
158
+ core.release(); // stop keeping awake
159
+ core.dispose(); // remove the visibilitychange listener
160
+ ```
161
+
162
+ When you drive the Core directly, call `dispose()` when you are done so its
163
+ `visibilitychange` listener is removed.
164
+
165
+ ## Notes & limitations
166
+
167
+ - **Sink, not producer.** Unlike the other @wcstack sensors, the wake lock is driven *by* state (`state → element`). `held` is the only thing it produces back.
168
+ - **Auto-release is handled for you.** The OS may drop the lock for several reasons — the page being hidden, but also battery-low or power-saver mode while the page stays visible. The component renews the lease in both cases: a release while *visible* is re-acquired immediately, and a release while *hidden* is re-acquired on the next return to visibility — as long as `active` is still set. The binding means "keep awake *while* active", not "acquire once". A **denied** re-acquire does not loop — it surfaces via `error` and stays quiet (the dominant case: the spec rejects re-requests under battery-low / power-saver). A **granted** re-acquire renews the lease, so if the OS keeps granting and releasing, the component keeps renewing (one request per release, driven by the OS, not a synchronous spin).
169
+ - **Secure context (HTTPS).** The Screen Wake Lock API only works in a secure context (HTTPS, or `localhost`).
170
+ - **Never throws.** An unsupported environment is a silent no-op (`held` stays `false`); a rejected request surfaces via the `error` property rather than throwing. `request()` never rejects.
171
+ - **No permission gate.** There is no separate permission prompt (a request may still be denied if the page is not visible — that surfaces as `error`).
172
+
173
+ ## License
174
+
175
+ MIT
package/dist/auto.js ADDED
@@ -0,0 +1,3 @@
1
+ import { bootstrapWakeLock } from "./index.esm.js";
2
+
3
+ bootstrapWakeLock();
@@ -0,0 +1 @@
1
+ import{bootstrapWakeLock as o}from"./index.esm.min.js";o();
@@ -0,0 +1,250 @@
1
+ interface ITagNames {
2
+ readonly wakelock: string;
3
+ }
4
+ interface IWritableTagNames {
5
+ wakelock?: string;
6
+ }
7
+ interface IConfig {
8
+ readonly tagNames: ITagNames;
9
+ }
10
+ interface IWritableConfig {
11
+ tagNames?: IWritableTagNames;
12
+ }
13
+ interface IWcBindableProperty {
14
+ readonly name: string;
15
+ readonly event: string;
16
+ readonly getter?: (event: Event) => any;
17
+ }
18
+ interface IWcBindableInput {
19
+ readonly name: string;
20
+ readonly attribute?: string;
21
+ }
22
+ interface IWcBindableCommand {
23
+ readonly name: string;
24
+ readonly async?: boolean;
25
+ }
26
+ interface IWcBindable {
27
+ readonly protocol: "wc-bindable";
28
+ readonly version: number;
29
+ readonly properties: IWcBindableProperty[];
30
+ readonly inputs?: IWcBindableInput[];
31
+ readonly commands?: IWcBindableCommand[];
32
+ }
33
+ /**
34
+ * Wake lock type. The spec currently standardizes only `"screen"`; the field
35
+ * exists for forward compatibility with future lock types.
36
+ */
37
+ type WakeLockKind = "screen";
38
+ /**
39
+ * Value types for WakeLockCore (headless) — the observable state properties.
40
+ * Use with `bind()` from `@wc-bindable/core` for compile-time type checking.
41
+ *
42
+ * Unlike the @wcstack sensor tags (geolocation / intersection), the wake lock is
43
+ * a pure *sink*: a bound state drives whether the lock is held (`active`, an
44
+ * input), and the only outputs are `held` — whether a sentinel is actually held
45
+ * right now — and `error`. `active` (the desired intent) is deliberately not an
46
+ * observable output: it does not change when the OS auto-releases the lock, only
47
+ * `held` does.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const core = new WakeLockCore();
52
+ * bind(core, (name: keyof WcsWakeLockCoreValues, value) => { ... });
53
+ * await core.request();
54
+ * ```
55
+ */
56
+ interface WcsWakeLockCoreValues {
57
+ /** Whether a wake lock sentinel is currently held (actual state). */
58
+ held: boolean;
59
+ /** The last request failure, or `null` while none. */
60
+ error: Error | null;
61
+ }
62
+ /**
63
+ * Value types for the Shell (`<wcs-wakelock>`) — identical observable surface to
64
+ * the Core (`held` / `error`).
65
+ */
66
+ type WcsWakeLockValues = WcsWakeLockCoreValues;
67
+ interface WcsWakeLockInputs {
68
+ /**
69
+ * Desired intent: hold the screen awake while `true`. The headline declarative
70
+ * binding (`active@isPlaying`). Mirrored to the `active` boolean attribute.
71
+ * Setting it `false` releases the lock. It stays `true` across an OS auto-release
72
+ * (tab hidden) so the lock is re-acquired when the page becomes visible again —
73
+ * read `held` for the actual current state.
74
+ */
75
+ active: boolean;
76
+ /** Lock type. Only `"screen"` is standardized; defaults to `"screen"`. */
77
+ type: WakeLockKind;
78
+ /** Do not auto-acquire on connect even if `active` is present; drive via commands. */
79
+ manual: boolean;
80
+ }
81
+ interface WcsWakeLockCoreCommands {
82
+ /**
83
+ * Mark the lock as desired and acquire it (if the page is visible and the API
84
+ * is supported). Never rejects — a failure surfaces via the `error` property.
85
+ */
86
+ request(): Promise<void>;
87
+ /** Mark the lock as no longer desired and release any held sentinel. */
88
+ release(): void;
89
+ }
90
+ interface WcsWakeLockCommands {
91
+ request(): Promise<void>;
92
+ release(): void;
93
+ }
94
+
95
+ declare function bootstrapWakeLock(userConfig?: IWritableConfig): void;
96
+
97
+ declare function getConfig(): IConfig;
98
+
99
+ /**
100
+ * Headless screen-wake-lock primitive — a thin, framework-agnostic wrapper around
101
+ * the Screen Wake Lock API exposed through the wc-bindable protocol.
102
+ *
103
+ * Unlike the other @wcstack sensors (geolocation / intersection), the wake lock is
104
+ * a pure *sink*: nothing is read from the device. A bound state drives the desired
105
+ * intent (`request()` / `release()`), and the only observable outputs are `held`
106
+ * (whether a sentinel is actually held) and `error`.
107
+ *
108
+ * The OS releases the lock whenever the page stops being visible (tab hidden,
109
+ * window minimized). To honor the declarative intent ("keep awake *while* active"),
110
+ * the Core keeps the desired flag (`_active`) and re-acquires the lock on the next
111
+ * `visibilitychange` back to visible. So `_active` (desired) and `held` (actual)
112
+ * diverge across an auto-release — and only `held` is published, because desired
113
+ * does not change when the OS drops the lock.
114
+ *
115
+ * Never-throw: `request()` never rejects (a failure surfaces via `error`), and an
116
+ * unsupported environment is a silent no-op (`held` stays false), consistent with
117
+ * the other @wcstack sensors.
118
+ */
119
+ declare class WakeLockCore extends EventTarget {
120
+ static wcBindable: IWcBindable;
121
+ private _target;
122
+ private _type;
123
+ private _active;
124
+ private _held;
125
+ private _error;
126
+ private _sentinel;
127
+ private _gen;
128
+ private _acquiring;
129
+ private _visibilityBound;
130
+ constructor(target?: EventTarget, type?: WakeLockKind);
131
+ get held(): boolean;
132
+ get error(): Error | null;
133
+ /** The desired intent. Read-only reflection; not a wc-bindable property (it does
134
+ * not change on an OS auto-release, so there is nothing to observe). */
135
+ get active(): boolean;
136
+ get type(): WakeLockKind;
137
+ set type(value: WakeLockKind);
138
+ private _setHeld;
139
+ private _setError;
140
+ private _sameError;
141
+ /**
142
+ * Mark the lock as desired and acquire it. Idempotent while already held. If the
143
+ * API is unavailable or the page is currently hidden, the desired flag is still
144
+ * set (so the lock is acquired on the next return to visibility) but nothing is
145
+ * acquired now. Never rejects — a request failure surfaces via `error`.
146
+ */
147
+ request(): Promise<void>;
148
+ /** Mark the lock as no longer desired and release any held sentinel. */
149
+ release(): void;
150
+ /**
151
+ * Full teardown: remove the visibility listener and release any held sentinel.
152
+ * Call from the Shell's `disconnectedCallback`.
153
+ *
154
+ * Semantics: this is a terminal teardown, not a pause. After `dispose()` the Core
155
+ * is meant to be discarded — there is no re-arm step, and the visibility listener
156
+ * is gone, so an OS auto-release will no longer be followed by a re-acquire. A
157
+ * later `request()` would still work in isolation (it re-attaches the listener via
158
+ * `_ensureVisibilityListener`), but reusing a disposed Core is not an intended path;
159
+ * the Shell always constructs a fresh Core per element instead.
160
+ */
161
+ dispose(): void;
162
+ private _acquire;
163
+ /**
164
+ * Re-attempt an acquire after an in-flight one was *superseded* (its generation no
165
+ * longer matches), but only if the lock is still desired, not already held, and the
166
+ * page is visible. This recovers a request() that was coalesced away by the
167
+ * in-flight `_acquiring` guard: during a release()→request() overlap, the second
168
+ * request() bumps `_gen` and is a no-op at the guard, so without this retry its
169
+ * still-live intent would be lost until the next visibilitychange or manual call.
170
+ *
171
+ * Bounded — cannot loop forever: a retry runs ONLY on supersession, and a
172
+ * supersession requires an external release()/request() to bump `_gen` mid-flight.
173
+ * A retry's own `_acquire()`, if it is itself not superseded, terminates by either
174
+ * acquiring (held=true) or recording the live failure (held=false, error set) —
175
+ * neither path retries. So a denied environment that keeps rejecting does not
176
+ * recurse; the retry chain length is bounded by the number of external overlaps.
177
+ */
178
+ private _retryIfStillDesired;
179
+ private _onRelease;
180
+ /**
181
+ * Lease renewal after an OS release while the page is still visible. Honors the
182
+ * "keep awake *while* active" promise for releases that do NOT coincide with a
183
+ * visibility change (battery-low / power-saver), which otherwise leave the lock
184
+ * stuck at desired=true / held=false until the next hide→show cycle.
185
+ *
186
+ * Bounded on failure: this only runs from `_onRelease`, which only fires when a
187
+ * sentinel was genuinely acquired and then released. A re-acquire that FAILS takes
188
+ * `_acquire()`'s live-failure path (error recorded, held=false) and attaches no
189
+ * listener, so it cannot re-enter `_onRelease` — a denied environment records the
190
+ * error once and stops. This is the dominant real path: per the Wake Lock spec a
191
+ * re-request under battery-low / power-saver is rejected (`NotAllowedError`), so the
192
+ * renewal terminates there.
193
+ *
194
+ * The one path NOT bounded by a counter is a pathological host that keeps GRANTING
195
+ * the re-request and then immediately auto-releasing it (grant→release reflux). Each
196
+ * iteration yields to the event loop and consumes a real OS grant, so it is not a
197
+ * tight/synchronous loop, but it would churn request() calls. We deliberately do NOT
198
+ * add a debounce or renewal cap: that reflux is not documented browser behavior
199
+ * (real browsers reject, not grant-then-revoke), and the extra timing state would
200
+ * complicate the pure-sink design to defend a case that does not occur in practice.
201
+ *
202
+ * The `_isVisible()` / `!_acquiring` guards (doubled by `_acquire()`'s own in-flight
203
+ * and held guards) prevent re-entry during an in-flight acquire and while hidden.
204
+ */
205
+ private _reacquireAfterRelease;
206
+ private _onVisibilityChange;
207
+ private _ensureVisibilityListener;
208
+ private _wakeLock;
209
+ private _isVisible;
210
+ private _normalizeError;
211
+ }
212
+
213
+ /**
214
+ * `<wcs-wakelock>` — declarative Screen Wake Lock.
215
+ *
216
+ * The first @wcstack tag that is a pure *sink*: every other sensor is an
217
+ * element→state producer, but the wake lock is state→element. The headline
218
+ * binding is `active@isPlaying` — hold the screen awake while a bound boolean is
219
+ * true. `active` is the single input knob (a mirrored attribute); `held` and
220
+ * `error` are the observable outputs.
221
+ *
222
+ * The OS auto-releases the lock when the page is hidden; the Core re-acquires it
223
+ * on the next return to visibility while `active` is still set, so the binding
224
+ * means "keep awake *while* active", not just "acquire once".
225
+ */
226
+ declare class WcsWakeLock extends HTMLElement {
227
+ static hasConnectedCallbackPromise: boolean;
228
+ static observedAttributes: string[];
229
+ static wcBindable: IWcBindable;
230
+ private _core;
231
+ constructor();
232
+ get active(): boolean;
233
+ set active(value: boolean);
234
+ get type(): WakeLockKind;
235
+ set type(value: WakeLockKind);
236
+ get manual(): boolean;
237
+ set manual(value: boolean);
238
+ get held(): boolean;
239
+ get error(): Error | null;
240
+ /** Acquire (and keep) the wake lock. Never rejects — see the `error` property. */
241
+ request(): Promise<void>;
242
+ /** Release the wake lock and stop re-acquiring it. */
243
+ release(): void;
244
+ connectedCallback(): void;
245
+ disconnectedCallback(): void;
246
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
247
+ }
248
+
249
+ export { WakeLockCore, WcsWakeLock, bootstrapWakeLock, getConfig };
250
+ export type { IWritableConfig, IWritableTagNames, WakeLockKind, WcsWakeLockCommands, WcsWakeLockCoreCommands, WcsWakeLockCoreValues, WcsWakeLockInputs, WcsWakeLockValues };