@wcstack/wakelock 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -119,6 +119,57 @@ npm install @wcstack/wakelock
119
119
  | `request()` | ロックを望み状態にして取得する(可視かつ対応環境なら)。reject しない — `error` を参照。 |
120
120
  | `release()` | ロックを望まない状態にして、保持中の sentinel を解放する。 |
121
121
 
122
+ ## `:state()` による CSS スタイリング
123
+
124
+ `<wcs-wakelock>` は 2 つの boolean 出力ステートを
125
+ [`ElementInternals` の `CustomStateSet`](https://developer.mozilla.org/ja/docs/Web/API/CustomStateSet)
126
+ に反映します。そのため `data-wcs` バインディングやクラスの手動トグルなしに、CSS の
127
+ `:state()` 疑似クラスで直接スタイリングできます。
128
+
129
+ | ステート | on になる条件 |
130
+ |----------|----------------|
131
+ | `held` | `wcs-wakelock:held-changed` が `true` で発火(`false` でクリア) |
132
+ | `error` | `wcs-wakelock:error` が非 `null` の detail で発火(`null` でクリア) |
133
+
134
+ ```css
135
+ wcs-wakelock:state(held) ~ .awake-indicator { display: block; }
136
+ wcs-wakelock:state(held) ~ .awake-indicator { display: none; } /* デフォルト */
137
+
138
+ form:has(wcs-wakelock:state(error)) .banner { display: block; }
139
+ ```
140
+
141
+ 属性やクラスと異なり `:state()` は要素の外部から書き込めないため、この出力ステートが
142
+ 入力と混同される心配がありません。
143
+
144
+ **対応ブラウザ**(新構文 `:state(x)`): Chrome/Edge 125+、Safari 17.4+、Firefox 126+。
145
+ 非対応の環境ではステートが一切 set されないだけです — `:state()` セレクタがマッチしなく
146
+ なりますが、`<wcs-wakelock>` 自体は通常どおり動作し続けます(graceful degradation・never-throw)。
147
+
148
+ **SSR:** `:state()` は HTML にシリアライズできないため、サーバーレンダリングされた
149
+ マークアップの初期ペイントにはこれらのステートは乗りません(`@wcstack/server` は無改変)。
150
+ ハイドレーション前の見た目を制御したい場合は、代わりに `wcs-wakelock:not(:defined)` と組み合わせてください。
151
+
152
+ ### デバッグ
153
+
154
+ カスタムステートは DevTools の Elements パネルには表示されず、`attachInternals()`
155
+ は同一要素に 2 回呼べないため、コンソールから直接覗く手段がありません。そのための
156
+ デバッグ専用の補助を 2 つ用意しています:
157
+
158
+ - `el.debugStates` — 現在 on になっているステート名の**スナップショット**配列
159
+ (例: `["held"]`)。`wc-bindable` の一部ではなく(バインド対象ではない)、
160
+ 形状も契約として保証されません — デバッグ用途にのみ使ってください。
161
+ - `debug-states` 属性(opt-in・既定 OFF)は、ステート変化を要素の
162
+ `data-wcs-state-held` / `data-wcs-state-error` 属性にミラーします。
163
+ Elements パネルを開いておけば、トグルのたびにハイライトされます:
164
+
165
+ ```html
166
+ <wcs-wakelock data-wcs="active: isPlaying" debug-states></wcs-wakelock>
167
+ ```
168
+
169
+ **CSS は `data-wcs-state-*` ではなく `:state()` に書いてください。** ミラーされた
170
+ 属性は、DevTools を開いた状態でステート変化を可視化するためだけのものであり、
171
+ スタイリング用の正式なフックではありません。
172
+
122
173
  ## Binding Contract(`wcBindable`)
123
174
 
124
175
  Core も Shell も [wc-bindable](https://github.com/csbc-dev) プロトコルを宣言します。
package/README.md CHANGED
@@ -120,6 +120,59 @@ npm install @wcstack/wakelock
120
120
  | `request()` | Mark the lock desired and acquire it (if visible & supported). Never rejects — see `error`. |
121
121
  | `release()` | Mark the lock no longer desired and release any held sentinel. |
122
122
 
123
+ ## CSS styling with `:state()`
124
+
125
+ `<wcs-wakelock>` reflects two boolean output states onto its
126
+ [`ElementInternals` `CustomStateSet`](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet),
127
+ so you can style it directly from CSS with the `:state()` pseudo-class — no
128
+ `data-wcs` binding or extra class toggling required.
129
+
130
+ | State | On when |
131
+ |-------|---------|
132
+ | `held` | `wcs-wakelock:held-changed` fires with `true` (cleared on `false`) |
133
+ | `error` | `wcs-wakelock:error` fires with a non-`null` detail (cleared on `null`) |
134
+
135
+ ```css
136
+ wcs-wakelock:state(held) ~ .awake-indicator { display: block; }
137
+ wcs-wakelock:state(held) ~ .awake-indicator { display: none; } /* default */
138
+
139
+ form:has(wcs-wakelock:state(error)) .banner { display: block; }
140
+ ```
141
+
142
+ Unlike attributes or classes, `:state()` cannot be written from outside the
143
+ element, so there is no risk of confusing this output state with an input.
144
+
145
+ **Browser support** (`:state(x)` syntax): Chrome/Edge 125+, Safari 17.4+,
146
+ Firefox 126+. In older browsers the states are simply never set — `:state()`
147
+ selectors never match, but `<wcs-wakelock>` itself keeps working normally
148
+ (graceful degradation, never-throw).
149
+
150
+ **SSR**: `:state()` cannot be serialized into HTML, so server-rendered markup
151
+ never carries these states on first paint (`@wcstack/server` is unaffected).
152
+ If you need to style the pre-hydration gap, pair your rule with
153
+ `wcs-wakelock:not(:defined)` instead.
154
+
155
+ ### Debugging
156
+
157
+ Custom states are invisible in DevTools' Elements panel and `attachInternals()`
158
+ cannot be called twice, so there is no console way to inspect them directly.
159
+ Two debug-only aids are provided for that:
160
+
161
+ - `el.debugStates` — a **snapshot** array of the currently-on state names
162
+ (e.g. `["held"]`). It is not part of `wc-bindable` (not a bind target)
163
+ and its shape is not a guaranteed contract — use it for debugging only.
164
+ - The `debug-states` attribute (opt-in, default off) mirrors state changes
165
+ onto `data-wcs-state-held` / `data-wcs-state-error` attributes on the
166
+ element, so the Elements panel highlights them as they toggle:
167
+
168
+ ```html
169
+ <wcs-wakelock data-wcs="active: isPlaying" debug-states></wcs-wakelock>
170
+ ```
171
+
172
+ **Write your CSS against `:state()`, not `data-wcs-state-*`.** The mirrored
173
+ attributes exist purely to make state changes visible while debugging with
174
+ DevTools open; they are not a supported styling hook.
175
+
123
176
  ## Binding Contract (`wcBindable`)
124
177
 
125
178
  Both the Core and the Shell declare the [wc-bindable](https://github.com/csbc-dev) protocol.
package/dist/index.d.ts CHANGED
@@ -234,8 +234,12 @@ declare class WcsWakeLock extends HTMLElement {
234
234
  static wcBindable: IWcBindable;
235
235
  private _core;
236
236
  private _connectedCallbackPromise;
237
+ private _internals;
237
238
  constructor();
238
239
  get connectedCallbackPromise(): Promise<void>;
240
+ get debugStates(): string[];
241
+ private _initInternals;
242
+ private _wireStates;
239
243
  get active(): boolean;
240
244
  set active(value: boolean);
241
245
  get type(): WakeLockKind;
package/dist/index.esm.js CHANGED
@@ -435,15 +435,70 @@ class WcsWakeLock extends HTMLElement {
435
435
  };
436
436
  _core;
437
437
  _connectedCallbackPromise = Promise.resolve();
438
+ _internals = null;
438
439
  constructor() {
439
440
  super();
440
441
  this._core = new WakeLockCore(this);
442
+ this._internals = this._initInternals();
443
+ this._wireStates({
444
+ "wcs-wakelock:held-changed": (d) => ({ held: d === true }),
445
+ "wcs-wakelock:error": (d) => ({ error: d != null }),
446
+ });
441
447
  }
442
448
  // SSR (§4.1): the renderer awaits this before snapshotting. Backed by the Core's
443
449
  // observe() (a no-op resolving immediately for this command-driven sink).
444
450
  get connectedCallbackPromise() {
445
451
  return this._connectedCallbackPromise;
446
452
  }
453
+ // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of
454
+ // wc-bindable (not a bind target); see README "CSS styling with :state()".
455
+ // MUST NOT return the live CustomStateSet (that would let callers write
456
+ // states from outside, defeating the point of :state() being read-only).
457
+ get debugStates() {
458
+ return this._internals ? [...this._internals.states] : [];
459
+ }
460
+ _initInternals() {
461
+ // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent
462
+ // in happy-dom / older environments, and pre-125 Chromium rejects
463
+ // non-dashed state names from states.add() (probed and discarded here).
464
+ // Either case silently disables reflection — the component still works,
465
+ // it just doesn't expose :state() selectors.
466
+ try {
467
+ if (typeof this.attachInternals !== "function")
468
+ return null;
469
+ const internals = this.attachInternals();
470
+ internals.states.add("wcs-probe");
471
+ internals.states.delete("wcs-probe");
472
+ return internals;
473
+ }
474
+ catch {
475
+ return null;
476
+ }
477
+ }
478
+ _wireStates(map) {
479
+ if (this._internals === null)
480
+ return;
481
+ const states = this._internals.states;
482
+ for (const [event, toStates] of Object.entries(map)) {
483
+ this.addEventListener(event, (e) => {
484
+ const debug = this.hasAttribute("debug-states");
485
+ for (const [name, on] of Object.entries(toStates(e.detail))) {
486
+ try {
487
+ // The ternary expression-statement form trips ESLint no-unused-expressions.
488
+ if (on) {
489
+ states.add(name);
490
+ }
491
+ else {
492
+ states.delete(name);
493
+ }
494
+ }
495
+ catch { /* never-throw */ }
496
+ if (debug)
497
+ this.toggleAttribute(`data-wcs-state-${name}`, on);
498
+ }
499
+ });
500
+ }
501
+ }
447
502
  // --- Attribute accessors ---
448
503
  get active() {
449
504
  // Reflects the *attribute*, not the Core's desired intent (`_core.active`). These
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/WakeLockCore.ts","../src/components/WakeLock.ts","../src/registerComponents.ts","../src/bootstrapWakeLock.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n wakelock: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n wakelock: \"wcs-wakelock\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (a frozen snapshot) is\n// surfaced. `setConfig()` is applied internally via `bootstrapWakeLock()` and\n// is not re-exported from the package root, though a deep path import\n// (`.../src/config.js`) can still reach and mutate it. Accepted as-is for\n// cross-package consistency: every @wcstack package follows this same shape.\n// Use `getConfig()` for a frozen, safe read.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\n\n/**\n * Minimal structural views of the Screen Wake Lock API. Declared locally (rather\n * than relying on `lib.dom`'s experimental `WakeLock` types) so the package type-\n * checks the same across TypeScript lib versions, and so a runtime where\n * `navigator.wakeLock` is absent is just a value check, never a type error.\n */\ninterface WakeLockSentinelLike extends EventTarget {\n readonly released: boolean;\n readonly type: string;\n release(): Promise<void>;\n}\ninterface WakeLockLike {\n request(type?: string): Promise<WakeLockSentinelLike>;\n}\n\n/**\n * Headless screen-wake-lock primitive — a thin, framework-agnostic wrapper around\n * the Screen Wake Lock API exposed through the wc-bindable protocol.\n *\n * Unlike the other @wcstack sensors (geolocation / intersection), the wake lock is\n * a pure *sink*: nothing is read from the device. A bound state drives the desired\n * intent (`request()` / `release()`), and the only observable outputs are `held`\n * (whether a sentinel is actually held) and `error`.\n *\n * The OS releases the lock whenever the page stops being visible (tab hidden,\n * window minimized). To honor the declarative intent (\"keep awake *while* active\"),\n * the Core keeps the desired flag (`_active`) and re-acquires the lock on the next\n * `visibilitychange` back to visible. So `_active` (desired) and `held` (actual)\n * diverge across an auto-release — and only `held` is published, because desired\n * does not change when the OS drops the lock.\n *\n * Never-throw: `request()` never rejects (a failure surfaces via `error`), and an\n * unsupported environment is a silent no-op (`held` stays false), consistent with\n * the other @wcstack sensors.\n */\nexport class WakeLockCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"held\", event: \"wcs-wakelock:held-changed\" },\n { name: \"error\", event: \"wcs-wakelock:error\" },\n ],\n commands: [\n { name: \"request\", async: true },\n { name: \"release\" },\n ],\n };\n\n private _target: EventTarget;\n private _type: WakeLockKind;\n\n // `_active` is the desired intent (input); `_held` is whether a sentinel is\n // actually held right now (output). They diverge across an OS auto-release.\n private _active: boolean = false;\n private _held: boolean = false;\n private _error: Error | null = null;\n private _sentinel: WakeLockSentinelLike | null = null;\n\n // Bumped on every release()/new acquire so an in-flight async request() that\n // resolves late can detect it was superseded and drop its sentinel (mirrors the\n // generation guards in GeolocationCore).\n private _gen: number = 0;\n // True while an `_acquire()` is awaiting `navigator.wakeLock.request()`. The\n // `_held` flag is only set *after* that await resolves, so it cannot guard\n // against concurrent entry: two rapid visibilitychange events (or a Shell toggle\n // overlapping an in-flight request) would both pass `!this._held` and each call\n // `request()`. This in-flight flag closes that window — a re-entrant acquire is a\n // no-op. The `_gen` guard still ensures the *final* state is correct; this just\n // avoids the redundant `request()` call (and its duplicate error path on a denied\n // environment).\n private _acquiring: boolean = false;\n private _visibilityBound: boolean = false;\n\n // SSR (§3.8): a pure sink has no asynchronous probe to await — `request()` is\n // fire-and-forget and meaningless server-side — so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget, type: WakeLockKind = \"screen\") {\n super();\n this._target = target ?? this;\n this._type = type;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // Lifecycle (§3.5). The wake lock is command-driven (request / release) with no\n // ambient subscription to establish on connect, so observe() is an idempotent\n // no-op that resolves once ready; dispose() (below) tears down the visibility\n // listener, releases any held sentinel, and bumps _gen via release().\n observe(): Promise<void> {\n return this._ready;\n }\n\n get held(): boolean {\n return this._held;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n /** The desired intent. Read-only reflection; not a wc-bindable property (it does\n * not change on an OS auto-release, so there is nothing to observe). */\n get active(): boolean {\n return this._active;\n }\n\n get type(): WakeLockKind {\n return this._type;\n }\n\n set type(value: WakeLockKind) {\n // Currently effectively a no-op: \"screen\" is the only standardized lock type,\n // so `WakeLockKind` is a single value and this setter never observes a real\n // change. Kept as a forward-compatible seam for when the spec adds lock types.\n //\n // Takes effect on the next acquire. Changing the type mid-hold deliberately does\n // NOT re-acquire — the live sentinel is left as is, so a type change applies only\n // from the following acquire. If multiple lock types are ever added this becomes\n // an observable behavior gap (a held lock keeps its old type until release/re-\n // acquire) and must be re-examined — likely re-acquire here when held.\n this._type = value;\n }\n\n // --- State setters with event dispatch ---\n\n private _setHeld(held: boolean): void {\n if (this._held === held) return;\n this._held = held;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:held-changed\", {\n detail: held,\n bubbles: true,\n }));\n }\n\n private _setError(error: Error | null): void {\n // Value guard, not just reference: a denied request rejects with a *fresh*\n // Error on every visibility-driven retry, so a reference compare would let a\n // permanently-denied environment re-dispatch the same failure on each\n // hidden→visible toggle. Compare name+message too. Transitions through null (a\n // success clears the error) always re-fire, so a genuinely new failure is seen.\n if (this._sameError(this._error, error)) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _sameError(a: Error | null, b: Error | null): boolean {\n if (a === b) return true;\n if (a !== null && b !== null) return a.name === b.name && a.message === b.message;\n return false;\n }\n\n // --- Public API ---\n\n /**\n * Mark the lock as desired and acquire it. Idempotent while already held. If the\n * API is unavailable or the page is currently hidden, the desired flag is still\n * set (so the lock is acquired on the next return to visibility) but nothing is\n * acquired now. Never rejects — a request failure surfaces via `error`.\n */\n async request(): Promise<void> {\n this._active = true;\n this._ensureVisibilityListener();\n await this._acquire();\n }\n\n /** Mark the lock as no longer desired and release any held sentinel. */\n release(): void {\n this._active = false;\n // Invalidate any in-flight acquire so a late-resolving request() drops its\n // sentinel instead of leaving a lock held after release.\n this._gen++;\n const sentinel = this._sentinel;\n if (sentinel) {\n this._sentinel = null;\n sentinel.removeEventListener(\"release\", this._onRelease);\n void sentinel.release().catch(() => { /* never-throw */ });\n }\n this._setHeld(false);\n }\n\n /**\n * Full teardown: remove the visibility listener and release any held sentinel.\n * Call from the Shell's `disconnectedCallback`.\n *\n * Semantics: this is a terminal teardown, not a pause. After `dispose()` the Core\n * is meant to be discarded — there is no re-arm step, and the visibility listener\n * is gone, so an OS auto-release will no longer be followed by a re-acquire. A\n * later `request()` would still work in isolation (it re-attaches the listener via\n * `_ensureVisibilityListener`), but reusing a disposed Core is not an intended path;\n * the Shell always constructs a fresh Core per element instead.\n */\n dispose(): void {\n if (this._visibilityBound) {\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = false;\n }\n this.release();\n }\n\n // --- Internal ---\n\n private async _acquire(): Promise<void> {\n if (this._held) return; // idempotent: already holding a sentinel\n if (this._acquiring) return; // an acquire is already in flight — don't double-request\n const wakeLock = this._wakeLock();\n if (!wakeLock) return; // unsupported — stay active, never acquire (silent no-op)\n if (!this._isVisible()) return; // hidden — defer to the next visibilitychange\n const gen = ++this._gen;\n this._acquiring = true;\n // Flag management is centralized in `finally` and the coalesced retry is invoked\n // exactly once, AFTER the try/catch/finally settles. This keeps the reject and\n // resolve paths symmetric: neither calls `_retryIfStillDesired()` from inside the\n // try/catch (which would let `finally` re-clear the `_acquiring=true` the retry's\n // synchronous re-entry just set, reopening the double-request window). `superseded`\n // records that a newer release()/request() bumped `_gen` mid-flight so its still-\n // live intent — blocked by the in-flight guard at the time — gets one retry here.\n // NOTE: no early `return` inside the try/catch below — every branch must fall\n // through to the post-`finally` retry. A `return` from inside the try would run\n // `finally` and then exit the function, skipping the `if (superseded)` retry.\n let superseded = false;\n let sentinel: WakeLockSentinelLike | null = null;\n let failed: Error | null = null;\n try {\n sentinel = await wakeLock.request(this._type);\n } catch (e) {\n if (gen !== this._gen) {\n // Superseded while awaiting — drop this stale failure (do not clobber the\n // newer state) and let the post-finally retry honor the live intent.\n superseded = true;\n } else {\n failed = this._normalizeError(e);\n }\n } finally {\n // The sole owner of the flag clears it here — on every exit path. A concurrent\n // re-entrant `_acquire()` was a no-op at the `_acquiring` guard, so it never owns\n // the flag; a superseding release()/acquire only bumps `_gen` and does not start\n // its own in-flight cycle until this clears the flag. Because this runs before\n // the retry below, the retry's `_acquiring=true` is never clobbered.\n this._acquiring = false;\n }\n\n if (sentinel !== null && gen !== this._gen) {\n // release() (or a newer acquire) ran while we awaited — this sentinel is\n // unwanted; drop it so no lock lingers, and retry the newer intent below.\n void sentinel.release().catch(() => { /* never-throw */ });\n superseded = true;\n } else if (sentinel !== null) {\n this._sentinel = sentinel;\n sentinel.addEventListener(\"release\", this._onRelease);\n this._setError(null);\n this._setHeld(true);\n } else if (failed !== null) {\n // A live (non-superseded) failure: surface it. Never retried — the intent is\n // honored but the environment denied it, so looping would spin.\n this._setError(failed);\n this._setHeld(false);\n }\n\n // Coalesced retry: at most one re-attempt per supersession, after the flag is\n // clear. The `_acquiring` guard inside still protects any concurrent re-entry that\n // overlaps THIS retry's own in-flight window (reject- and resolve-retry alike).\n if (superseded) this._retryIfStillDesired();\n }\n\n /**\n * Re-attempt an acquire after an in-flight one was *superseded* (its generation no\n * longer matches), but only if the lock is still desired, not already held, and the\n * page is visible. This recovers a request() that was coalesced away by the\n * in-flight `_acquiring` guard: during a release()→request() overlap, the second\n * request() bumps `_gen` and is a no-op at the guard, so without this retry its\n * still-live intent would be lost until the next visibilitychange or manual call.\n *\n * Bounded — cannot loop forever: a retry runs ONLY on supersession, and a\n * supersession requires an external release()/request() to bump `_gen` mid-flight.\n * A retry's own `_acquire()`, if it is itself not superseded, terminates by either\n * acquiring (held=true) or recording the live failure (held=false, error set) —\n * neither path retries. So a denied environment that keeps rejecting does not\n * recurse; the retry chain length is bounded by the number of external overlaps.\n */\n private _retryIfStillDesired(): void {\n if (this._active && !this._held && this._isVisible()) {\n void this._acquire();\n }\n }\n\n // Fired for an OS release of a held sentinel — which the spec allows for several\n // reasons, NOT only a visibility change: tab hidden / window minimized, but also\n // battery-low, power-saver mode, etc. while the page stays visible. We reflect\n // held=false, then (lease renewal) re-acquire immediately IF the page is still\n // visible and the lock is still desired — because a visible-context release emits no\n // `visibilitychange`, so the visibilitychange listener (②) would never fire and the\n // lock would stay stuck at desired=true / held=false. The hidden case is the no-op\n // here: re-acquire is gated on `_isVisible()`, so a hide-driven release defers to ②\n // (re-acquire on the return to visibility), avoiding a release→acquire loop while\n // hidden.\n private _onRelease = (): void => {\n // The `if (this._sentinel)` false branch is defensive and unreachable in practice:\n // this listener is only ever attached to the live `_sentinel`, and the only paths\n // that null `_sentinel` (this handler itself, and release()) remove this listener\n // in the same step — so the listener and a non-null `_sentinel` are coupled and\n // this never fires with `_sentinel === null`. Guarded anyway in case a host\n // dispatches a spurious second \"release\". (c8 ignore the unhittable else.)\n /* c8 ignore next */\n if (this._sentinel) {\n this._sentinel.removeEventListener(\"release\", this._onRelease);\n this._sentinel = null;\n }\n this._setHeld(false);\n this._reacquireAfterRelease();\n };\n\n /**\n * Lease renewal after an OS release while the page is still visible. Honors the\n * \"keep awake *while* active\" promise for releases that do NOT coincide with a\n * visibility change (battery-low / power-saver), which otherwise leave the lock\n * stuck at desired=true / held=false until the next hide→show cycle.\n *\n * Bounded on failure: this only runs from `_onRelease`, which only fires when a\n * sentinel was genuinely acquired and then released. A re-acquire that FAILS takes\n * `_acquire()`'s live-failure path (error recorded, held=false) and attaches no\n * listener, so it cannot re-enter `_onRelease` — a denied environment records the\n * error once and stops. This is the dominant real path: per the Wake Lock spec a\n * re-request under battery-low / power-saver is rejected (`NotAllowedError`), so the\n * renewal terminates there.\n *\n * The one path NOT bounded by a counter is a pathological host that keeps GRANTING\n * the re-request and then immediately auto-releasing it (grant→release reflux). Each\n * iteration yields to the event loop and consumes a real OS grant, so it is not a\n * tight/synchronous loop, but it would churn request() calls. We deliberately do NOT\n * add a debounce or renewal cap: that reflux is not documented browser behavior\n * (real browsers reject, not grant-then-revoke), and the extra timing state would\n * complicate the pure-sink design to defend a case that does not occur in practice.\n *\n * The `_isVisible()` / `!_acquiring` guards (doubled by `_acquire()`'s own in-flight\n * and held guards) prevent re-entry during an in-flight acquire and while hidden.\n */\n private _reacquireAfterRelease(): void {\n if (this._active && this._isVisible() && !this._acquiring) {\n void this._acquire();\n }\n }\n\n // ② Re-acquire when the page becomes visible again while the lock is still\n // desired but was auto-released. This is what makes `active` a durable intent.\n private _onVisibilityChange = (): void => {\n if (this._isVisible() && this._active && !this._held) {\n void this._acquire();\n }\n };\n\n private _ensureVisibilityListener(): void {\n if (this._visibilityBound) return;\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = true;\n }\n\n private _wakeLock(): WakeLockLike | null {\n return (navigator as Navigator & { wakeLock?: WakeLockLike }).wakeLock ?? null;\n }\n\n private _isVisible(): boolean {\n // §4 deviation: document-scoped Web API; no element-free alternative —\n // `visibilityState` lives on `document`, not on any element.\n return document.visibilityState === \"visible\";\n }\n\n private _normalizeError(e: unknown): Error {\n return e instanceof Error ? e : new Error(String(e));\n }\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\nimport { WakeLockCore } from \"../core/WakeLockCore.js\";\n\n/**\n * `<wcs-wakelock>` — declarative Screen Wake Lock.\n *\n * The first @wcstack tag that is a pure *sink*: every other sensor is an\n * element→state producer, but the wake lock is state→element. The headline\n * binding is `active@isPlaying` — hold the screen awake while a bound boolean is\n * true. `active` is the single input knob (a mirrored attribute); `held` and\n * `error` are the observable outputs.\n *\n * The OS auto-releases the lock when the page is hidden; the Core re-acquires it\n * on the next return to visibility while `active` is still set, so the binding\n * means \"keep awake *while* active\", not just \"acquire once\".\n */\nexport class WcsWakeLock extends HTMLElement {\n // SSR contract (§4.1/@wcstack/server): the renderer awaits elements declaring\n // `hasConnectedCallbackPromise = true` before snapshotting. The wake lock has no\n // connect-time async probe to await (acquire is fire-and-forget and meaningless\n // server-side), so `connectedCallbackPromise` is backed by the Core's no-op\n // `observe()`, which resolves immediately. The flag is still declared `true` so\n // the renderer reads it via `ctor.hasConnectedCallbackPromise` and the Shell\n // participates uniformly in the SSR await protocol.\n static hasConnectedCallbackPromise = true;\n // `active` drives request/release; `type` propagates to the Core's next acquire.\n // `manual` is intentionally excluded: it is a connect-time policy (\"don't auto-\n // acquire on connect\"), not a live switch.\n static observedAttributes = [\"active\", \"type\"];\n\n static wcBindable: IWcBindable = {\n ...WakeLockCore.wcBindable,\n // Settable surface. `active` is the declarative intent; `type` selects the lock\n // kind; `manual` opts out of auto-acquire on connect. The request / release\n // commands are inherited from the Core via the spread above.\n inputs: [\n { name: \"active\", attribute: \"active\" },\n { name: \"type\", attribute: \"type\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Core の commands をそのまま継承(単一情報源)。<wcs-intersect>/<wcs-sse> と同型。\n // spread でも継承されるが、Core に command 追加時の追従漏れを防ぐため明示参照する。\n commands: WakeLockCore.wcBindable.commands,\n };\n\n private _core: WakeLockCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new WakeLockCore(this);\n }\n\n // SSR (§4.1): the renderer awaits this before snapshotting. Backed by the Core's\n // observe() (a no-op resolving immediately for this command-driven sink).\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get active(): boolean {\n // Reflects the *attribute*, not the Core's desired intent (`_core.active`). These\n // can diverge: invoking the `request` / `release` commands directly (e.g. via a\n // command-token binding) flips the Core's desired flag without touching the\n // attribute, so `el.active` may read false while `el.held` is true (or vice\n // versa). The attribute is the declarative input surface; the commands are an\n // imperative side door. Bind via `active@...` for a single source of truth.\n return this.hasAttribute(\"active\");\n }\n\n set active(value: boolean) {\n if (value) {\n this.setAttribute(\"active\", \"\");\n } else {\n this.removeAttribute(\"active\");\n }\n }\n\n get type(): WakeLockKind {\n // Only \"screen\" is standardized; an absent/empty attribute defaults to it.\n return (this.getAttribute(\"type\") as WakeLockKind) || \"screen\";\n }\n\n set type(value: WakeLockKind) {\n this.setAttribute(\"type\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get held(): boolean {\n return this._core.held;\n }\n\n get error(): Error | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n /** Acquire (and keep) the wake lock. Never rejects — see the `error` property. */\n request(): Promise<void> {\n return this._core.request();\n }\n\n /** Release the wake lock and stop re-acquiring it. */\n release(): void {\n this._core.release();\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n // Headless resource: no layout box (mirrors the @wcstack sensor convention).\n this.style.display = \"none\";\n // Propagate the requested lock type to the Core. Currently a no-op in effect:\n // \"screen\" is the only standardized type, so `type` is always \"screen\". Wired\n // up as a forward-compatible seam (observedAttributes + setter + this line) for\n // when the spec adds lock types; until then it carries a constant.\n this._core.type = this.type;\n // Establish monitoring (§3.5) and expose readiness for SSR (§4.1). observe()\n // is a no-op that resolves immediately for this command-driven sink.\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual && this.active) {\n void this._core.request();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n // Ignore changes applied before connect (e.g. createElement + setAttribute);\n // connectedCallback applies the initial state. Acquiring a lock for a detached\n // element would be wrong.\n if (!this.isConnected) return;\n if (name === \"type\") {\n this._core.type = this.type;\n return;\n }\n // name === \"active\": a live toggle always drives request/release. `manual` only\n // gates the connect-time auto-acquire, not an explicit author toggle.\n if (this.active) {\n void this._core.request();\n } else {\n this._core.release();\n }\n }\n}\n","import { WcsWakeLock } from \"./components/WakeLock.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.wakelock)) {\n customElements.define(config.tagNames.wakelock, WcsWakeLock);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapWakeLock(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/WakeLockCore.ts","../src/components/WakeLock.ts","../src/registerComponents.ts","../src/bootstrapWakeLock.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n wakelock: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n wakelock: \"wcs-wakelock\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (a frozen snapshot) is\n// surfaced. `setConfig()` is applied internally via `bootstrapWakeLock()` and\n// is not re-exported from the package root, though a deep path import\n// (`.../src/config.js`) can still reach and mutate it. Accepted as-is for\n// cross-package consistency: every @wcstack package follows this same shape.\n// Use `getConfig()` for a frozen, safe read.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\n\n/**\n * Minimal structural views of the Screen Wake Lock API. Declared locally (rather\n * than relying on `lib.dom`'s experimental `WakeLock` types) so the package type-\n * checks the same across TypeScript lib versions, and so a runtime where\n * `navigator.wakeLock` is absent is just a value check, never a type error.\n */\ninterface WakeLockSentinelLike extends EventTarget {\n readonly released: boolean;\n readonly type: string;\n release(): Promise<void>;\n}\ninterface WakeLockLike {\n request(type?: string): Promise<WakeLockSentinelLike>;\n}\n\n/**\n * Headless screen-wake-lock primitive — a thin, framework-agnostic wrapper around\n * the Screen Wake Lock API exposed through the wc-bindable protocol.\n *\n * Unlike the other @wcstack sensors (geolocation / intersection), the wake lock is\n * a pure *sink*: nothing is read from the device. A bound state drives the desired\n * intent (`request()` / `release()`), and the only observable outputs are `held`\n * (whether a sentinel is actually held) and `error`.\n *\n * The OS releases the lock whenever the page stops being visible (tab hidden,\n * window minimized). To honor the declarative intent (\"keep awake *while* active\"),\n * the Core keeps the desired flag (`_active`) and re-acquires the lock on the next\n * `visibilitychange` back to visible. So `_active` (desired) and `held` (actual)\n * diverge across an auto-release — and only `held` is published, because desired\n * does not change when the OS drops the lock.\n *\n * Never-throw: `request()` never rejects (a failure surfaces via `error`), and an\n * unsupported environment is a silent no-op (`held` stays false), consistent with\n * the other @wcstack sensors.\n */\nexport class WakeLockCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"held\", event: \"wcs-wakelock:held-changed\" },\n { name: \"error\", event: \"wcs-wakelock:error\" },\n ],\n commands: [\n { name: \"request\", async: true },\n { name: \"release\" },\n ],\n };\n\n private _target: EventTarget;\n private _type: WakeLockKind;\n\n // `_active` is the desired intent (input); `_held` is whether a sentinel is\n // actually held right now (output). They diverge across an OS auto-release.\n private _active: boolean = false;\n private _held: boolean = false;\n private _error: Error | null = null;\n private _sentinel: WakeLockSentinelLike | null = null;\n\n // Bumped on every release()/new acquire so an in-flight async request() that\n // resolves late can detect it was superseded and drop its sentinel (mirrors the\n // generation guards in GeolocationCore).\n private _gen: number = 0;\n // True while an `_acquire()` is awaiting `navigator.wakeLock.request()`. The\n // `_held` flag is only set *after* that await resolves, so it cannot guard\n // against concurrent entry: two rapid visibilitychange events (or a Shell toggle\n // overlapping an in-flight request) would both pass `!this._held` and each call\n // `request()`. This in-flight flag closes that window — a re-entrant acquire is a\n // no-op. The `_gen` guard still ensures the *final* state is correct; this just\n // avoids the redundant `request()` call (and its duplicate error path on a denied\n // environment).\n private _acquiring: boolean = false;\n private _visibilityBound: boolean = false;\n\n // SSR (§3.8): a pure sink has no asynchronous probe to await — `request()` is\n // fire-and-forget and meaningless server-side — so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget, type: WakeLockKind = \"screen\") {\n super();\n this._target = target ?? this;\n this._type = type;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // Lifecycle (§3.5). The wake lock is command-driven (request / release) with no\n // ambient subscription to establish on connect, so observe() is an idempotent\n // no-op that resolves once ready; dispose() (below) tears down the visibility\n // listener, releases any held sentinel, and bumps _gen via release().\n observe(): Promise<void> {\n return this._ready;\n }\n\n get held(): boolean {\n return this._held;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n /** The desired intent. Read-only reflection; not a wc-bindable property (it does\n * not change on an OS auto-release, so there is nothing to observe). */\n get active(): boolean {\n return this._active;\n }\n\n get type(): WakeLockKind {\n return this._type;\n }\n\n set type(value: WakeLockKind) {\n // Currently effectively a no-op: \"screen\" is the only standardized lock type,\n // so `WakeLockKind` is a single value and this setter never observes a real\n // change. Kept as a forward-compatible seam for when the spec adds lock types.\n //\n // Takes effect on the next acquire. Changing the type mid-hold deliberately does\n // NOT re-acquire — the live sentinel is left as is, so a type change applies only\n // from the following acquire. If multiple lock types are ever added this becomes\n // an observable behavior gap (a held lock keeps its old type until release/re-\n // acquire) and must be re-examined — likely re-acquire here when held.\n this._type = value;\n }\n\n // --- State setters with event dispatch ---\n\n private _setHeld(held: boolean): void {\n if (this._held === held) return;\n this._held = held;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:held-changed\", {\n detail: held,\n bubbles: true,\n }));\n }\n\n private _setError(error: Error | null): void {\n // Value guard, not just reference: a denied request rejects with a *fresh*\n // Error on every visibility-driven retry, so a reference compare would let a\n // permanently-denied environment re-dispatch the same failure on each\n // hidden→visible toggle. Compare name+message too. Transitions through null (a\n // success clears the error) always re-fire, so a genuinely new failure is seen.\n if (this._sameError(this._error, error)) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _sameError(a: Error | null, b: Error | null): boolean {\n if (a === b) return true;\n if (a !== null && b !== null) return a.name === b.name && a.message === b.message;\n return false;\n }\n\n // --- Public API ---\n\n /**\n * Mark the lock as desired and acquire it. Idempotent while already held. If the\n * API is unavailable or the page is currently hidden, the desired flag is still\n * set (so the lock is acquired on the next return to visibility) but nothing is\n * acquired now. Never rejects — a request failure surfaces via `error`.\n */\n async request(): Promise<void> {\n this._active = true;\n this._ensureVisibilityListener();\n await this._acquire();\n }\n\n /** Mark the lock as no longer desired and release any held sentinel. */\n release(): void {\n this._active = false;\n // Invalidate any in-flight acquire so a late-resolving request() drops its\n // sentinel instead of leaving a lock held after release.\n this._gen++;\n const sentinel = this._sentinel;\n if (sentinel) {\n this._sentinel = null;\n sentinel.removeEventListener(\"release\", this._onRelease);\n void sentinel.release().catch(() => { /* never-throw */ });\n }\n this._setHeld(false);\n }\n\n /**\n * Full teardown: remove the visibility listener and release any held sentinel.\n * Call from the Shell's `disconnectedCallback`.\n *\n * Semantics: this is a terminal teardown, not a pause. After `dispose()` the Core\n * is meant to be discarded — there is no re-arm step, and the visibility listener\n * is gone, so an OS auto-release will no longer be followed by a re-acquire. A\n * later `request()` would still work in isolation (it re-attaches the listener via\n * `_ensureVisibilityListener`), but reusing a disposed Core is not an intended path;\n * the Shell always constructs a fresh Core per element instead.\n */\n dispose(): void {\n if (this._visibilityBound) {\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = false;\n }\n this.release();\n }\n\n // --- Internal ---\n\n private async _acquire(): Promise<void> {\n if (this._held) return; // idempotent: already holding a sentinel\n if (this._acquiring) return; // an acquire is already in flight — don't double-request\n const wakeLock = this._wakeLock();\n if (!wakeLock) return; // unsupported — stay active, never acquire (silent no-op)\n if (!this._isVisible()) return; // hidden — defer to the next visibilitychange\n const gen = ++this._gen;\n this._acquiring = true;\n // Flag management is centralized in `finally` and the coalesced retry is invoked\n // exactly once, AFTER the try/catch/finally settles. This keeps the reject and\n // resolve paths symmetric: neither calls `_retryIfStillDesired()` from inside the\n // try/catch (which would let `finally` re-clear the `_acquiring=true` the retry's\n // synchronous re-entry just set, reopening the double-request window). `superseded`\n // records that a newer release()/request() bumped `_gen` mid-flight so its still-\n // live intent — blocked by the in-flight guard at the time — gets one retry here.\n // NOTE: no early `return` inside the try/catch below — every branch must fall\n // through to the post-`finally` retry. A `return` from inside the try would run\n // `finally` and then exit the function, skipping the `if (superseded)` retry.\n let superseded = false;\n let sentinel: WakeLockSentinelLike | null = null;\n let failed: Error | null = null;\n try {\n sentinel = await wakeLock.request(this._type);\n } catch (e) {\n if (gen !== this._gen) {\n // Superseded while awaiting — drop this stale failure (do not clobber the\n // newer state) and let the post-finally retry honor the live intent.\n superseded = true;\n } else {\n failed = this._normalizeError(e);\n }\n } finally {\n // The sole owner of the flag clears it here — on every exit path. A concurrent\n // re-entrant `_acquire()` was a no-op at the `_acquiring` guard, so it never owns\n // the flag; a superseding release()/acquire only bumps `_gen` and does not start\n // its own in-flight cycle until this clears the flag. Because this runs before\n // the retry below, the retry's `_acquiring=true` is never clobbered.\n this._acquiring = false;\n }\n\n if (sentinel !== null && gen !== this._gen) {\n // release() (or a newer acquire) ran while we awaited — this sentinel is\n // unwanted; drop it so no lock lingers, and retry the newer intent below.\n void sentinel.release().catch(() => { /* never-throw */ });\n superseded = true;\n } else if (sentinel !== null) {\n this._sentinel = sentinel;\n sentinel.addEventListener(\"release\", this._onRelease);\n this._setError(null);\n this._setHeld(true);\n } else if (failed !== null) {\n // A live (non-superseded) failure: surface it. Never retried — the intent is\n // honored but the environment denied it, so looping would spin.\n this._setError(failed);\n this._setHeld(false);\n }\n\n // Coalesced retry: at most one re-attempt per supersession, after the flag is\n // clear. The `_acquiring` guard inside still protects any concurrent re-entry that\n // overlaps THIS retry's own in-flight window (reject- and resolve-retry alike).\n if (superseded) this._retryIfStillDesired();\n }\n\n /**\n * Re-attempt an acquire after an in-flight one was *superseded* (its generation no\n * longer matches), but only if the lock is still desired, not already held, and the\n * page is visible. This recovers a request() that was coalesced away by the\n * in-flight `_acquiring` guard: during a release()→request() overlap, the second\n * request() bumps `_gen` and is a no-op at the guard, so without this retry its\n * still-live intent would be lost until the next visibilitychange or manual call.\n *\n * Bounded — cannot loop forever: a retry runs ONLY on supersession, and a\n * supersession requires an external release()/request() to bump `_gen` mid-flight.\n * A retry's own `_acquire()`, if it is itself not superseded, terminates by either\n * acquiring (held=true) or recording the live failure (held=false, error set) —\n * neither path retries. So a denied environment that keeps rejecting does not\n * recurse; the retry chain length is bounded by the number of external overlaps.\n */\n private _retryIfStillDesired(): void {\n if (this._active && !this._held && this._isVisible()) {\n void this._acquire();\n }\n }\n\n // Fired for an OS release of a held sentinel — which the spec allows for several\n // reasons, NOT only a visibility change: tab hidden / window minimized, but also\n // battery-low, power-saver mode, etc. while the page stays visible. We reflect\n // held=false, then (lease renewal) re-acquire immediately IF the page is still\n // visible and the lock is still desired — because a visible-context release emits no\n // `visibilitychange`, so the visibilitychange listener (②) would never fire and the\n // lock would stay stuck at desired=true / held=false. The hidden case is the no-op\n // here: re-acquire is gated on `_isVisible()`, so a hide-driven release defers to ②\n // (re-acquire on the return to visibility), avoiding a release→acquire loop while\n // hidden.\n private _onRelease = (): void => {\n // The `if (this._sentinel)` false branch is defensive and unreachable in practice:\n // this listener is only ever attached to the live `_sentinel`, and the only paths\n // that null `_sentinel` (this handler itself, and release()) remove this listener\n // in the same step — so the listener and a non-null `_sentinel` are coupled and\n // this never fires with `_sentinel === null`. Guarded anyway in case a host\n // dispatches a spurious second \"release\". (c8 ignore the unhittable else.)\n /* c8 ignore next */\n if (this._sentinel) {\n this._sentinel.removeEventListener(\"release\", this._onRelease);\n this._sentinel = null;\n }\n this._setHeld(false);\n this._reacquireAfterRelease();\n };\n\n /**\n * Lease renewal after an OS release while the page is still visible. Honors the\n * \"keep awake *while* active\" promise for releases that do NOT coincide with a\n * visibility change (battery-low / power-saver), which otherwise leave the lock\n * stuck at desired=true / held=false until the next hide→show cycle.\n *\n * Bounded on failure: this only runs from `_onRelease`, which only fires when a\n * sentinel was genuinely acquired and then released. A re-acquire that FAILS takes\n * `_acquire()`'s live-failure path (error recorded, held=false) and attaches no\n * listener, so it cannot re-enter `_onRelease` — a denied environment records the\n * error once and stops. This is the dominant real path: per the Wake Lock spec a\n * re-request under battery-low / power-saver is rejected (`NotAllowedError`), so the\n * renewal terminates there.\n *\n * The one path NOT bounded by a counter is a pathological host that keeps GRANTING\n * the re-request and then immediately auto-releasing it (grant→release reflux). Each\n * iteration yields to the event loop and consumes a real OS grant, so it is not a\n * tight/synchronous loop, but it would churn request() calls. We deliberately do NOT\n * add a debounce or renewal cap: that reflux is not documented browser behavior\n * (real browsers reject, not grant-then-revoke), and the extra timing state would\n * complicate the pure-sink design to defend a case that does not occur in practice.\n *\n * The `_isVisible()` / `!_acquiring` guards (doubled by `_acquire()`'s own in-flight\n * and held guards) prevent re-entry during an in-flight acquire and while hidden.\n */\n private _reacquireAfterRelease(): void {\n if (this._active && this._isVisible() && !this._acquiring) {\n void this._acquire();\n }\n }\n\n // ② Re-acquire when the page becomes visible again while the lock is still\n // desired but was auto-released. This is what makes `active` a durable intent.\n private _onVisibilityChange = (): void => {\n if (this._isVisible() && this._active && !this._held) {\n void this._acquire();\n }\n };\n\n private _ensureVisibilityListener(): void {\n if (this._visibilityBound) return;\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = true;\n }\n\n private _wakeLock(): WakeLockLike | null {\n return (navigator as Navigator & { wakeLock?: WakeLockLike }).wakeLock ?? null;\n }\n\n private _isVisible(): boolean {\n // §4 deviation: document-scoped Web API; no element-free alternative —\n // `visibilityState` lives on `document`, not on any element.\n return document.visibilityState === \"visible\";\n }\n\n private _normalizeError(e: unknown): Error {\n return e instanceof Error ? e : new Error(String(e));\n }\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\nimport { WakeLockCore } from \"../core/WakeLockCore.js\";\n\n/**\n * `<wcs-wakelock>` — declarative Screen Wake Lock.\n *\n * The first @wcstack tag that is a pure *sink*: every other sensor is an\n * element→state producer, but the wake lock is state→element. The headline\n * binding is `active@isPlaying` — hold the screen awake while a bound boolean is\n * true. `active` is the single input knob (a mirrored attribute); `held` and\n * `error` are the observable outputs.\n *\n * The OS auto-releases the lock when the page is hidden; the Core re-acquires it\n * on the next return to visibility while `active` is still set, so the binding\n * means \"keep awake *while* active\", not just \"acquire once\".\n */\nexport class WcsWakeLock extends HTMLElement {\n // SSR contract (§4.1/@wcstack/server): the renderer awaits elements declaring\n // `hasConnectedCallbackPromise = true` before snapshotting. The wake lock has no\n // connect-time async probe to await (acquire is fire-and-forget and meaningless\n // server-side), so `connectedCallbackPromise` is backed by the Core's no-op\n // `observe()`, which resolves immediately. The flag is still declared `true` so\n // the renderer reads it via `ctor.hasConnectedCallbackPromise` and the Shell\n // participates uniformly in the SSR await protocol.\n static hasConnectedCallbackPromise = true;\n // `active` drives request/release; `type` propagates to the Core's next acquire.\n // `manual` is intentionally excluded: it is a connect-time policy (\"don't auto-\n // acquire on connect\"), not a live switch.\n static observedAttributes = [\"active\", \"type\"];\n\n static wcBindable: IWcBindable = {\n ...WakeLockCore.wcBindable,\n // Settable surface. `active` is the declarative intent; `type` selects the lock\n // kind; `manual` opts out of auto-acquire on connect. The request / release\n // commands are inherited from the Core via the spread above.\n inputs: [\n { name: \"active\", attribute: \"active\" },\n { name: \"type\", attribute: \"type\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Core の commands をそのまま継承(単一情報源)。<wcs-intersect>/<wcs-sse> と同型。\n // spread でも継承されるが、Core に command 追加時の追従漏れを防ぐため明示参照する。\n commands: WakeLockCore.wcBindable.commands,\n };\n\n private _core: WakeLockCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new WakeLockCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-wakelock:held-changed\": (d) => ({ held: d === true }),\n \"wcs-wakelock:error\": (d) => ({ error: d != null }),\n });\n }\n\n // SSR (§4.1): the renderer awaits this before snapshotting. Backed by the Core's\n // observe() (a no-op resolving immediately for this command-driven sink).\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of\n // wc-bindable (not a bind target); see README \"CSS styling with :state()\".\n // MUST NOT return the live CustomStateSet (that would let callers write\n // states from outside, defeating the point of :state() being read-only).\n get debugStates(): string[] {\n return this._internals ? [...this._internals.states] : [];\n }\n\n private _initInternals(): ElementInternals | null {\n // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent\n // in happy-dom / older environments, and pre-125 Chromium rejects\n // non-dashed state names from states.add() (probed and discarded here).\n // Either case silently disables reflection — the component still works,\n // it just doesn't expose :state() selectors.\n try {\n if (typeof this.attachInternals !== \"function\") return null;\n const internals = this.attachInternals();\n internals.states.add(\"wcs-probe\");\n internals.states.delete(\"wcs-probe\");\n return internals;\n } catch {\n return null;\n }\n }\n\n private _wireStates(map: Record<string, (detail: any) => Record<string, boolean>>): void {\n if (this._internals === null) return;\n const states = this._internals.states;\n for (const [event, toStates] of Object.entries(map)) {\n this.addEventListener(event, (e) => {\n const debug = this.hasAttribute(\"debug-states\");\n for (const [name, on] of Object.entries(toStates((e as CustomEvent).detail))) {\n try {\n // The ternary expression-statement form trips ESLint no-unused-expressions.\n if (on) { states.add(name); } else { states.delete(name); }\n } catch { /* never-throw */ }\n if (debug) this.toggleAttribute(`data-wcs-state-${name}`, on);\n }\n });\n }\n }\n\n // --- Attribute accessors ---\n\n get active(): boolean {\n // Reflects the *attribute*, not the Core's desired intent (`_core.active`). These\n // can diverge: invoking the `request` / `release` commands directly (e.g. via a\n // command-token binding) flips the Core's desired flag without touching the\n // attribute, so `el.active` may read false while `el.held` is true (or vice\n // versa). The attribute is the declarative input surface; the commands are an\n // imperative side door. Bind via `active@...` for a single source of truth.\n return this.hasAttribute(\"active\");\n }\n\n set active(value: boolean) {\n if (value) {\n this.setAttribute(\"active\", \"\");\n } else {\n this.removeAttribute(\"active\");\n }\n }\n\n get type(): WakeLockKind {\n // Only \"screen\" is standardized; an absent/empty attribute defaults to it.\n return (this.getAttribute(\"type\") as WakeLockKind) || \"screen\";\n }\n\n set type(value: WakeLockKind) {\n this.setAttribute(\"type\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get held(): boolean {\n return this._core.held;\n }\n\n get error(): Error | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n /** Acquire (and keep) the wake lock. Never rejects — see the `error` property. */\n request(): Promise<void> {\n return this._core.request();\n }\n\n /** Release the wake lock and stop re-acquiring it. */\n release(): void {\n this._core.release();\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n // Headless resource: no layout box (mirrors the @wcstack sensor convention).\n this.style.display = \"none\";\n // Propagate the requested lock type to the Core. Currently a no-op in effect:\n // \"screen\" is the only standardized type, so `type` is always \"screen\". Wired\n // up as a forward-compatible seam (observedAttributes + setter + this line) for\n // when the spec adds lock types; until then it carries a constant.\n this._core.type = this.type;\n // Establish monitoring (§3.5) and expose readiness for SSR (§4.1). observe()\n // is a no-op that resolves immediately for this command-driven sink.\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual && this.active) {\n void this._core.request();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n // Ignore changes applied before connect (e.g. createElement + setAttribute);\n // connectedCallback applies the initial state. Acquiring a lock for a detached\n // element would be wrong.\n if (!this.isConnected) return;\n if (name === \"type\") {\n this._core.type = this.type;\n return;\n }\n // name === \"active\": a live toggle always drives request/release. `manual` only\n // gates the connect-time auto-acquire, not an explicit author toggle.\n if (this.active) {\n void this._core.request();\n } else {\n this._core.release();\n }\n }\n}\n","import { WcsWakeLock } from \"./components/WakeLock.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.wakelock)) {\n customElements.define(config.tagNames.wakelock, WcsWakeLock);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapWakeLock(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"AAQA,MAAM,OAAO,GAAoB;AAC/B,IAAA,QAAQ,EAAE;AACR,QAAA,QAAQ,EAAE,cAAc;AACzB,KAAA;CACF;AAED,SAAS,UAAU,CAAI,GAAM,EAAA;AAC3B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;AACvD,IAAA,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAClC,QAAA,UAAU,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IACnD;AACA,IAAA,OAAO,GAAG;AACZ;AAEA,SAAS,SAAS,CAAI,GAAM,EAAA;AAC1B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;IACvD,MAAM,KAAK,GAA4B,EAAE;IACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAClC,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IAC/D;AACA,IAAA,OAAO,KAAU;AACnB;AAEA,IAAI,YAAY,GAAmB,IAAI;AAEvC;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,MAAM,GAAY,OAAkB;SAEjC,SAAS,GAAA;IACvB,IAAI,CAAC,YAAY,EAAE;QACjB,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC/C;AACA,IAAA,OAAO,YAAY;AACrB;AAEM,SAAU,SAAS,CAAC,aAA8B,EAAA;AACtD,IAAA,IAAI,aAAa,CAAC,QAAQ,EAAE;QAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IACzD;IACA,YAAY,GAAG,IAAI;AACrB;;ACtCA;;;;;;;;;;;;;;;;;;;AAmBG;AACG,MAAO,YAAa,SAAQ,WAAW,CAAA;IAC3C,OAAO,UAAU,GAAgB;AAC/B,QAAA,QAAQ,EAAE,aAAa;AACvB,QAAA,OAAO,EAAE,CAAC;AACV,QAAA,UAAU,EAAE;AACV,YAAA,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,2BAA2B,EAAE;AACpD,YAAA,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE;AAC/C,SAAA;AACD,QAAA,QAAQ,EAAE;AACR,YAAA,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE;YAChC,EAAE,IAAI,EAAE,SAAS,EAAE;AACpB,SAAA;KACF;AAEO,IAAA,OAAO;AACP,IAAA,KAAK;;;IAIL,OAAO,GAAY,KAAK;IACxB,KAAK,GAAY,KAAK;IACtB,MAAM,GAAiB,IAAI;IAC3B,SAAS,GAAgC,IAAI;;;;IAK7C,IAAI,GAAW,CAAC;;;;;;;;;IAShB,UAAU,GAAY,KAAK;IAC3B,gBAAgB,GAAY,KAAK;;;AAIjC,IAAA,MAAM,GAAkB,OAAO,CAAC,OAAO,EAAE;IAEjD,WAAA,CAAY,MAAoB,EAAE,IAAA,GAAqB,QAAQ,EAAA;AAC7D,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,IAAI;AAC7B,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;IACnB;AAEA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;;;;;IAMA,OAAO,GAAA;QACL,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA,IAAA,IAAI,IAAI,GAAA;QACN,OAAO,IAAI,CAAC,KAAK;IACnB;AAEA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA;AACwE;AACxE,IAAA,IAAI,MAAM,GAAA;QACR,OAAO,IAAI,CAAC,OAAO;IACrB;AAEA,IAAA,IAAI,IAAI,GAAA;QACN,OAAO,IAAI,CAAC,KAAK;IACnB;IAEA,IAAI,IAAI,CAAC,KAAmB,EAAA;;;;;;;;;;AAU1B,QAAA,IAAI,CAAC,KAAK,GAAG,KAAK;IACpB;;AAIQ,IAAA,QAAQ,CAAC,IAAa,EAAA;AAC5B,QAAA,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE;AACzB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;QACjB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,2BAA2B,EAAE;AACtE,YAAA,MAAM,EAAE,IAAI;AACZ,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;AAEQ,IAAA,SAAS,CAAC,KAAmB,EAAA;;;;;;QAMnC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;YAAE;AACzC,QAAA,IAAI,CAAC,MAAM,GAAG,KAAK;QACnB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,oBAAoB,EAAE;AAC/D,YAAA,MAAM,EAAE,KAAK;AACb,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;IAEQ,UAAU,CAAC,CAAe,EAAE,CAAe,EAAA;QACjD,IAAI,CAAC,KAAK,CAAC;AAAE,YAAA,OAAO,IAAI;AACxB,QAAA,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;AAAE,YAAA,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO;AACjF,QAAA,OAAO,KAAK;IACd;;AAIA;;;;;AAKG;AACH,IAAA,MAAM,OAAO,GAAA;AACX,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI;QACnB,IAAI,CAAC,yBAAyB,EAAE;AAChC,QAAA,MAAM,IAAI,CAAC,QAAQ,EAAE;IACvB;;IAGA,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,OAAO,GAAG,KAAK;;;QAGpB,IAAI,CAAC,IAAI,EAAE;AACX,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS;QAC/B,IAAI,QAAQ,EAAE;AACZ,YAAA,IAAI,CAAC,SAAS,GAAG,IAAI;YACrB,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;AACxD,YAAA,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,MAAK,EAAqB,CAAC,CAAC;QAC5D;AACA,QAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;IACtB;AAEA;;;;;;;;;;AAUG;IACH,OAAO,GAAA;AACL,QAAA,IAAI,IAAI,CAAC,gBAAgB,EAAE;;;YAGzB,QAAQ,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,IAAI,CAAC,mBAAmB,CAAC;AAC1E,YAAA,IAAI,CAAC,gBAAgB,GAAG,KAAK;QAC/B;QACA,IAAI,CAAC,OAAO,EAAE;IAChB;;AAIQ,IAAA,MAAM,QAAQ,GAAA;QACpB,IAAI,IAAI,CAAC,KAAK;AAAE,YAAA,OAAO;QACvB,IAAI,IAAI,CAAC,UAAU;AAAE,YAAA,OAAO;AAC5B,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE;AACjC,QAAA,IAAI,CAAC,QAAQ;AAAE,YAAA,OAAO;AACtB,QAAA,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;AAAE,YAAA,OAAO;AAC/B,QAAA,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,IAAI;AACvB,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI;;;;;;;;;;;QAWtB,IAAI,UAAU,GAAG,KAAK;QACtB,IAAI,QAAQ,GAAgC,IAAI;QAChD,IAAI,MAAM,GAAiB,IAAI;AAC/B,QAAA,IAAI;YACF,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/C;QAAE,OAAO,CAAC,EAAE;AACV,YAAA,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,EAAE;;;gBAGrB,UAAU,GAAG,IAAI;YACnB;iBAAO;AACL,gBAAA,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;YAClC;QACF;gBAAU;;;;;;AAMR,YAAA,IAAI,CAAC,UAAU,GAAG,KAAK;QACzB;QAEA,IAAI,QAAQ,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,EAAE;;;AAG1C,YAAA,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,MAAK,EAAqB,CAAC,CAAC;YAC1D,UAAU,GAAG,IAAI;QACnB;AAAO,aAAA,IAAI,QAAQ,KAAK,IAAI,EAAE;AAC5B,YAAA,IAAI,CAAC,SAAS,GAAG,QAAQ;YACzB,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;AACrD,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;AACpB,YAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QACrB;AAAO,aAAA,IAAI,MAAM,KAAK,IAAI,EAAE;;;AAG1B,YAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;AACtB,YAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtB;;;;AAKA,QAAA,IAAI,UAAU;YAAE,IAAI,CAAC,oBAAoB,EAAE;IAC7C;AAEA;;;;;;;;;;;;;;AAcG;IACK,oBAAoB,GAAA;AAC1B,QAAA,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE;AACpD,YAAA,KAAK,IAAI,CAAC,QAAQ,EAAE;QACtB;IACF;;;;;;;;;;;IAYQ,UAAU,GAAG,MAAW;;;;;;;;AAQ9B,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;AAC9D,YAAA,IAAI,CAAC,SAAS,GAAG,IAAI;QACvB;AACA,QAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpB,IAAI,CAAC,sBAAsB,EAAE;AAC/B,IAAA,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;IACK,sBAAsB,GAAA;AAC5B,QAAA,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;AACzD,YAAA,KAAK,IAAI,CAAC,QAAQ,EAAE;QACtB;IACF;;;IAIQ,mBAAmB,GAAG,MAAW;AACvC,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;AACpD,YAAA,KAAK,IAAI,CAAC,QAAQ,EAAE;QACtB;AACF,IAAA,CAAC;IAEO,yBAAyB,GAAA;QAC/B,IAAI,IAAI,CAAC,gBAAgB;YAAE;;;QAG3B,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,IAAI,CAAC,mBAAmB,CAAC;AACvE,QAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI;IAC9B;IAEQ,SAAS,GAAA;AACf,QAAA,OAAQ,SAAqD,CAAC,QAAQ,IAAI,IAAI;IAChF;IAEQ,UAAU,GAAA;;;AAGhB,QAAA,OAAO,QAAQ,CAAC,eAAe,KAAK,SAAS;IAC/C;AAEQ,IAAA,eAAe,CAAC,CAAU,EAAA;AAChC,QAAA,OAAO,CAAC,YAAY,KAAK,GAAG,CAAC,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtD;;;AC1XF;;;;;;;;;;;;AAYG;AACG,MAAO,WAAY,SAAQ,WAAW,CAAA;;;;;;;;AAQ1C,IAAA,OAAO,2BAA2B,GAAG,IAAI;;;;IAIzC,OAAO,kBAAkB,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE9C,OAAO,UAAU,GAAgB;QAC/B,GAAG,YAAY,CAAC,UAAU;;;;AAI1B,QAAA,MAAM,EAAE;AACN,YAAA,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE;AACvC,YAAA,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE;AACnC,YAAA,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE;AACxC,SAAA;;;AAGD,QAAA,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,QAAQ;KAC3C;AAEO,IAAA,KAAK;AACL,IAAA,yBAAyB,GAAkB,OAAO,CAAC,OAAO,EAAE;IAC5D,UAAU,GAA4B,IAAI;AAElD,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;QACP,IAAI,CAAC,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC;AACnC,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE;QACvC,IAAI,CAAC,WAAW,CAAC;AACf,YAAA,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;AAC1D,YAAA,oBAAoB,EAAS,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;AAC3D,SAAA,CAAC;IACJ;;;AAIA,IAAA,IAAI,wBAAwB,GAAA;QAC1B,OAAO,IAAI,CAAC,yBAAyB;IACvC;;;;;AAMA,IAAA,IAAI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE;IAC3D;IAEQ,cAAc,GAAA;;;;;;AAMpB,QAAA,IAAI;AACF,YAAA,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,UAAU;AAAE,gBAAA,OAAO,IAAI;AAC3D,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,EAAE;AACxC,YAAA,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;AACjC,YAAA,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;AACpC,YAAA,OAAO,SAAS;QAClB;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,IAAI;QACb;IACF;AAEQ,IAAA,WAAW,CAAC,GAA6D,EAAA;AAC/E,QAAA,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI;YAAE;AAC9B,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM;AACrC,QAAA,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACnD,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,KAAI;gBACjC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC;AAC/C,gBAAA,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAE,CAAiB,CAAC,MAAM,CAAC,CAAC,EAAE;AAC5E,oBAAA,IAAI;;wBAEF,IAAI,EAAE,EAAE;AAAE,4BAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;wBAAE;6BAAO;AAAE,4BAAA,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;wBAAE;oBAC5D;AAAE,oBAAA,MAAM,oBAAoB;AAC5B,oBAAA,IAAI,KAAK;wBAAE,IAAI,CAAC,eAAe,CAAC,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAE,EAAE,EAAE,CAAC;gBAC/D;AACF,YAAA,CAAC,CAAC;QACJ;IACF;;AAIA,IAAA,IAAI,MAAM,GAAA;;;;;;;AAOR,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;IACpC;IAEA,IAAI,MAAM,CAAC,KAAc,EAAA;QACvB,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;QACjC;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;QAChC;IACF;AAEA,IAAA,IAAI,IAAI,GAAA;;QAEN,OAAQ,IAAI,CAAC,YAAY,CAAC,MAAM,CAAkB,IAAI,QAAQ;IAChE;IAEA,IAAI,IAAI,CAAC,KAAmB,EAAA;AAC1B,QAAA,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAClC;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;IACpC;IAEA,IAAI,MAAM,CAAC,KAAc,EAAA;QACvB,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;QACjC;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;QAChC;IACF;;AAIA,IAAA,IAAI,IAAI,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI;IACxB;AAEA,IAAA,IAAI,KAAK,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK;IACzB;;;IAKA,OAAO,GAAA;AACL,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IAC7B;;IAGA,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IACtB;;IAIA,iBAAiB,GAAA;;AAEf,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;;;;;QAK3B,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;;;QAG3B,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;QACrD,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE;AAC/B,YAAA,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;QAC3B;IACF;IAEA,oBAAoB,GAAA;AAClB,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IACtB;AAEA,IAAA,wBAAwB,CAAC,IAAY,EAAE,QAAuB,EAAE,QAAuB,EAAA;QACrF,IAAI,QAAQ,KAAK,QAAQ;YAAE;;;;QAI3B,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE;AACvB,QAAA,IAAI,IAAI,KAAK,MAAM,EAAE;YACnB,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;YAC3B;QACF;;;AAGA,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACf,YAAA,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;QAC3B;aAAO;AACL,YAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;QACtB;IACF;;;SC9Mc,kBAAkB,GAAA;AAChC,IAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QACjD,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC9D;AACF;;ACHM,SAAU,iBAAiB,CAAC,UAA4B,EAAA;IAC5D,IAAI,UAAU,EAAE;QACd,SAAS,CAAC,UAAU,CAAC;IACvB;AACA,IAAA,kBAAkB,EAAE;AACtB;;;;"}
@@ -1,2 +1,2 @@
1
- const e={tagNames:{wakelock:"wcs-wakelock"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const i of Object.keys(e))t(e[i]);return e}function i(e){if(null===e||"object"!=typeof e)return e;const t={};for(const s of Object.keys(e))t[s]=i(e[s]);return t}let s=null;const r=e;function n(){return s||(s=t(i(e))),s}class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"held",event:"wcs-wakelock:held-changed"},{name:"error",event:"wcs-wakelock:error"}],commands:[{name:"request",async:!0},{name:"release"}]};_target;_type;_active=!1;_held=!1;_error=null;_sentinel=null;_gen=0;_acquiring=!1;_visibilityBound=!1;_ready=Promise.resolve();constructor(e,t="screen"){super(),this._target=e??this,this._type=t}get ready(){return this._ready}observe(){return this._ready}get held(){return this._held}get error(){return this._error}get active(){return this._active}get type(){return this._type}set type(e){this._type=e}_setHeld(e){this._held!==e&&(this._held=e,this._target.dispatchEvent(new CustomEvent("wcs-wakelock:held-changed",{detail:e,bubbles:!0})))}_setError(e){this._sameError(this._error,e)||(this._error=e,this._target.dispatchEvent(new CustomEvent("wcs-wakelock:error",{detail:e,bubbles:!0})))}_sameError(e,t){return e===t||null!==e&&null!==t&&(e.name===t.name&&e.message===t.message)}async request(){this._active=!0,this._ensureVisibilityListener(),await this._acquire()}release(){this._active=!1,this._gen++;const e=this._sentinel;e&&(this._sentinel=null,e.removeEventListener("release",this._onRelease),e.release().catch(()=>{})),this._setHeld(!1)}dispose(){this._visibilityBound&&(document.removeEventListener("visibilitychange",this._onVisibilityChange),this._visibilityBound=!1),this.release()}async _acquire(){if(this._held)return;if(this._acquiring)return;const e=this._wakeLock();if(!e)return;if(!this._isVisible())return;const t=++this._gen;this._acquiring=!0;let i=!1,s=null,r=null;try{s=await e.request(this._type)}catch(e){t!==this._gen?i=!0:r=this._normalizeError(e)}finally{this._acquiring=!1}null!==s&&t!==this._gen?(s.release().catch(()=>{}),i=!0):null!==s?(this._sentinel=s,s.addEventListener("release",this._onRelease),this._setError(null),this._setHeld(!0)):null!==r&&(this._setError(r),this._setHeld(!1)),i&&this._retryIfStillDesired()}_retryIfStillDesired(){this._active&&!this._held&&this._isVisible()&&this._acquire()}_onRelease=()=>{this._sentinel&&(this._sentinel.removeEventListener("release",this._onRelease),this._sentinel=null),this._setHeld(!1),this._reacquireAfterRelease()};_reacquireAfterRelease(){this._active&&this._isVisible()&&!this._acquiring&&this._acquire()}_onVisibilityChange=()=>{this._isVisible()&&this._active&&!this._held&&this._acquire()};_ensureVisibilityListener(){this._visibilityBound||(document.addEventListener("visibilitychange",this._onVisibilityChange),this._visibilityBound=!0)}_wakeLock(){return navigator.wakeLock??null}_isVisible(){return"visible"===document.visibilityState}_normalizeError(e){return e instanceof Error?e:new Error(String(e))}}class l extends HTMLElement{static hasConnectedCallbackPromise=!0;static observedAttributes=["active","type"];static wcBindable={...a.wcBindable,inputs:[{name:"active",attribute:"active"},{name:"type",attribute:"type"},{name:"manual",attribute:"manual"}],commands:a.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new a(this)}get connectedCallbackPromise(){return this._connectedCallbackPromise}get active(){return this.hasAttribute("active")}set active(e){e?this.setAttribute("active",""):this.removeAttribute("active")}get type(){return this.getAttribute("type")||"screen"}set type(e){this.setAttribute("type",e)}get manual(){return this.hasAttribute("manual")}set manual(e){e?this.setAttribute("manual",""):this.removeAttribute("manual")}get held(){return this._core.held}get error(){return this._core.error}request(){return this._core.request()}release(){this._core.release()}connectedCallback(){this.style.display="none",this._core.type=this.type,this._connectedCallbackPromise=this._core.observe(),!this.manual&&this.active&&this._core.request()}disconnectedCallback(){this._core.dispose()}attributeChangedCallback(e,t,i){t!==i&&this.isConnected&&("type"!==e?this.active?this._core.request():this._core.release():this._core.type=this.type)}}function c(t){var i;t&&((i=t).tagNames&&Object.assign(e.tagNames,i.tagNames),s=null),customElements.get(r.tagNames.wakelock)||customElements.define(r.tagNames.wakelock,l)}export{a as WakeLockCore,l as WcsWakeLock,c as bootstrapWakeLock,n as getConfig};
1
+ const e={tagNames:{wakelock:"wcs-wakelock"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const s of Object.keys(e))t(e[s]);return e}function s(e){if(null===e||"object"!=typeof e)return e;const t={};for(const i of Object.keys(e))t[i]=s(e[i]);return t}let i=null;const r=e;function n(){return i||(i=t(s(e))),i}class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"held",event:"wcs-wakelock:held-changed"},{name:"error",event:"wcs-wakelock:error"}],commands:[{name:"request",async:!0},{name:"release"}]};_target;_type;_active=!1;_held=!1;_error=null;_sentinel=null;_gen=0;_acquiring=!1;_visibilityBound=!1;_ready=Promise.resolve();constructor(e,t="screen"){super(),this._target=e??this,this._type=t}get ready(){return this._ready}observe(){return this._ready}get held(){return this._held}get error(){return this._error}get active(){return this._active}get type(){return this._type}set type(e){this._type=e}_setHeld(e){this._held!==e&&(this._held=e,this._target.dispatchEvent(new CustomEvent("wcs-wakelock:held-changed",{detail:e,bubbles:!0})))}_setError(e){this._sameError(this._error,e)||(this._error=e,this._target.dispatchEvent(new CustomEvent("wcs-wakelock:error",{detail:e,bubbles:!0})))}_sameError(e,t){return e===t||null!==e&&null!==t&&(e.name===t.name&&e.message===t.message)}async request(){this._active=!0,this._ensureVisibilityListener(),await this._acquire()}release(){this._active=!1,this._gen++;const e=this._sentinel;e&&(this._sentinel=null,e.removeEventListener("release",this._onRelease),e.release().catch(()=>{})),this._setHeld(!1)}dispose(){this._visibilityBound&&(document.removeEventListener("visibilitychange",this._onVisibilityChange),this._visibilityBound=!1),this.release()}async _acquire(){if(this._held)return;if(this._acquiring)return;const e=this._wakeLock();if(!e)return;if(!this._isVisible())return;const t=++this._gen;this._acquiring=!0;let s=!1,i=null,r=null;try{i=await e.request(this._type)}catch(e){t!==this._gen?s=!0:r=this._normalizeError(e)}finally{this._acquiring=!1}null!==i&&t!==this._gen?(i.release().catch(()=>{}),s=!0):null!==i?(this._sentinel=i,i.addEventListener("release",this._onRelease),this._setError(null),this._setHeld(!0)):null!==r&&(this._setError(r),this._setHeld(!1)),s&&this._retryIfStillDesired()}_retryIfStillDesired(){this._active&&!this._held&&this._isVisible()&&this._acquire()}_onRelease=()=>{this._sentinel&&(this._sentinel.removeEventListener("release",this._onRelease),this._sentinel=null),this._setHeld(!1),this._reacquireAfterRelease()};_reacquireAfterRelease(){this._active&&this._isVisible()&&!this._acquiring&&this._acquire()}_onVisibilityChange=()=>{this._isVisible()&&this._active&&!this._held&&this._acquire()};_ensureVisibilityListener(){this._visibilityBound||(document.addEventListener("visibilitychange",this._onVisibilityChange),this._visibilityBound=!0)}_wakeLock(){return navigator.wakeLock??null}_isVisible(){return"visible"===document.visibilityState}_normalizeError(e){return e instanceof Error?e:new Error(String(e))}}class l extends HTMLElement{static hasConnectedCallbackPromise=!0;static observedAttributes=["active","type"];static wcBindable={...a.wcBindable,inputs:[{name:"active",attribute:"active"},{name:"type",attribute:"type"},{name:"manual",attribute:"manual"}],commands:a.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new a(this),this._internals=this._initInternals(),this._wireStates({"wcs-wakelock:held-changed":e=>({held:!0===e}),"wcs-wakelock:error":e=>({error:null!=e})})}get connectedCallbackPromise(){return this._connectedCallbackPromise}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[s,i]of Object.entries(e))this.addEventListener(s,e=>{const s=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(i(e.detail))){try{n?t.add(r):t.delete(r)}catch{}s&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get active(){return this.hasAttribute("active")}set active(e){e?this.setAttribute("active",""):this.removeAttribute("active")}get type(){return this.getAttribute("type")||"screen"}set type(e){this.setAttribute("type",e)}get manual(){return this.hasAttribute("manual")}set manual(e){e?this.setAttribute("manual",""):this.removeAttribute("manual")}get held(){return this._core.held}get error(){return this._core.error}request(){return this._core.request()}release(){this._core.release()}connectedCallback(){this.style.display="none",this._core.type=this.type,this._connectedCallbackPromise=this._core.observe(),!this.manual&&this.active&&this._core.request()}disconnectedCallback(){this._core.dispose()}attributeChangedCallback(e,t,s){t!==s&&this.isConnected&&("type"!==e?this.active?this._core.request():this._core.release():this._core.type=this.type)}}function c(t){var s;t&&((s=t).tagNames&&Object.assign(e.tagNames,s.tagNames),i=null),customElements.get(r.tagNames.wakelock)||customElements.define(r.tagNames.wakelock,l)}export{a as WakeLockCore,l as WcsWakeLock,c as bootstrapWakeLock,n as getConfig};
2
2
  //# sourceMappingURL=index.esm.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/WakeLockCore.ts","../src/components/WakeLock.ts","../src/bootstrapWakeLock.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n wakelock: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n wakelock: \"wcs-wakelock\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (a frozen snapshot) is\n// surfaced. `setConfig()` is applied internally via `bootstrapWakeLock()` and\n// is not re-exported from the package root, though a deep path import\n// (`.../src/config.js`) can still reach and mutate it. Accepted as-is for\n// cross-package consistency: every @wcstack package follows this same shape.\n// Use `getConfig()` for a frozen, safe read.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\n\n/**\n * Minimal structural views of the Screen Wake Lock API. Declared locally (rather\n * than relying on `lib.dom`'s experimental `WakeLock` types) so the package type-\n * checks the same across TypeScript lib versions, and so a runtime where\n * `navigator.wakeLock` is absent is just a value check, never a type error.\n */\ninterface WakeLockSentinelLike extends EventTarget {\n readonly released: boolean;\n readonly type: string;\n release(): Promise<void>;\n}\ninterface WakeLockLike {\n request(type?: string): Promise<WakeLockSentinelLike>;\n}\n\n/**\n * Headless screen-wake-lock primitive — a thin, framework-agnostic wrapper around\n * the Screen Wake Lock API exposed through the wc-bindable protocol.\n *\n * Unlike the other @wcstack sensors (geolocation / intersection), the wake lock is\n * a pure *sink*: nothing is read from the device. A bound state drives the desired\n * intent (`request()` / `release()`), and the only observable outputs are `held`\n * (whether a sentinel is actually held) and `error`.\n *\n * The OS releases the lock whenever the page stops being visible (tab hidden,\n * window minimized). To honor the declarative intent (\"keep awake *while* active\"),\n * the Core keeps the desired flag (`_active`) and re-acquires the lock on the next\n * `visibilitychange` back to visible. So `_active` (desired) and `held` (actual)\n * diverge across an auto-release — and only `held` is published, because desired\n * does not change when the OS drops the lock.\n *\n * Never-throw: `request()` never rejects (a failure surfaces via `error`), and an\n * unsupported environment is a silent no-op (`held` stays false), consistent with\n * the other @wcstack sensors.\n */\nexport class WakeLockCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"held\", event: \"wcs-wakelock:held-changed\" },\n { name: \"error\", event: \"wcs-wakelock:error\" },\n ],\n commands: [\n { name: \"request\", async: true },\n { name: \"release\" },\n ],\n };\n\n private _target: EventTarget;\n private _type: WakeLockKind;\n\n // `_active` is the desired intent (input); `_held` is whether a sentinel is\n // actually held right now (output). They diverge across an OS auto-release.\n private _active: boolean = false;\n private _held: boolean = false;\n private _error: Error | null = null;\n private _sentinel: WakeLockSentinelLike | null = null;\n\n // Bumped on every release()/new acquire so an in-flight async request() that\n // resolves late can detect it was superseded and drop its sentinel (mirrors the\n // generation guards in GeolocationCore).\n private _gen: number = 0;\n // True while an `_acquire()` is awaiting `navigator.wakeLock.request()`. The\n // `_held` flag is only set *after* that await resolves, so it cannot guard\n // against concurrent entry: two rapid visibilitychange events (or a Shell toggle\n // overlapping an in-flight request) would both pass `!this._held` and each call\n // `request()`. This in-flight flag closes that window — a re-entrant acquire is a\n // no-op. The `_gen` guard still ensures the *final* state is correct; this just\n // avoids the redundant `request()` call (and its duplicate error path on a denied\n // environment).\n private _acquiring: boolean = false;\n private _visibilityBound: boolean = false;\n\n // SSR (§3.8): a pure sink has no asynchronous probe to await — `request()` is\n // fire-and-forget and meaningless server-side — so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget, type: WakeLockKind = \"screen\") {\n super();\n this._target = target ?? this;\n this._type = type;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // Lifecycle (§3.5). The wake lock is command-driven (request / release) with no\n // ambient subscription to establish on connect, so observe() is an idempotent\n // no-op that resolves once ready; dispose() (below) tears down the visibility\n // listener, releases any held sentinel, and bumps _gen via release().\n observe(): Promise<void> {\n return this._ready;\n }\n\n get held(): boolean {\n return this._held;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n /** The desired intent. Read-only reflection; not a wc-bindable property (it does\n * not change on an OS auto-release, so there is nothing to observe). */\n get active(): boolean {\n return this._active;\n }\n\n get type(): WakeLockKind {\n return this._type;\n }\n\n set type(value: WakeLockKind) {\n // Currently effectively a no-op: \"screen\" is the only standardized lock type,\n // so `WakeLockKind` is a single value and this setter never observes a real\n // change. Kept as a forward-compatible seam for when the spec adds lock types.\n //\n // Takes effect on the next acquire. Changing the type mid-hold deliberately does\n // NOT re-acquire — the live sentinel is left as is, so a type change applies only\n // from the following acquire. If multiple lock types are ever added this becomes\n // an observable behavior gap (a held lock keeps its old type until release/re-\n // acquire) and must be re-examined — likely re-acquire here when held.\n this._type = value;\n }\n\n // --- State setters with event dispatch ---\n\n private _setHeld(held: boolean): void {\n if (this._held === held) return;\n this._held = held;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:held-changed\", {\n detail: held,\n bubbles: true,\n }));\n }\n\n private _setError(error: Error | null): void {\n // Value guard, not just reference: a denied request rejects with a *fresh*\n // Error on every visibility-driven retry, so a reference compare would let a\n // permanently-denied environment re-dispatch the same failure on each\n // hidden→visible toggle. Compare name+message too. Transitions through null (a\n // success clears the error) always re-fire, so a genuinely new failure is seen.\n if (this._sameError(this._error, error)) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _sameError(a: Error | null, b: Error | null): boolean {\n if (a === b) return true;\n if (a !== null && b !== null) return a.name === b.name && a.message === b.message;\n return false;\n }\n\n // --- Public API ---\n\n /**\n * Mark the lock as desired and acquire it. Idempotent while already held. If the\n * API is unavailable or the page is currently hidden, the desired flag is still\n * set (so the lock is acquired on the next return to visibility) but nothing is\n * acquired now. Never rejects — a request failure surfaces via `error`.\n */\n async request(): Promise<void> {\n this._active = true;\n this._ensureVisibilityListener();\n await this._acquire();\n }\n\n /** Mark the lock as no longer desired and release any held sentinel. */\n release(): void {\n this._active = false;\n // Invalidate any in-flight acquire so a late-resolving request() drops its\n // sentinel instead of leaving a lock held after release.\n this._gen++;\n const sentinel = this._sentinel;\n if (sentinel) {\n this._sentinel = null;\n sentinel.removeEventListener(\"release\", this._onRelease);\n void sentinel.release().catch(() => { /* never-throw */ });\n }\n this._setHeld(false);\n }\n\n /**\n * Full teardown: remove the visibility listener and release any held sentinel.\n * Call from the Shell's `disconnectedCallback`.\n *\n * Semantics: this is a terminal teardown, not a pause. After `dispose()` the Core\n * is meant to be discarded — there is no re-arm step, and the visibility listener\n * is gone, so an OS auto-release will no longer be followed by a re-acquire. A\n * later `request()` would still work in isolation (it re-attaches the listener via\n * `_ensureVisibilityListener`), but reusing a disposed Core is not an intended path;\n * the Shell always constructs a fresh Core per element instead.\n */\n dispose(): void {\n if (this._visibilityBound) {\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = false;\n }\n this.release();\n }\n\n // --- Internal ---\n\n private async _acquire(): Promise<void> {\n if (this._held) return; // idempotent: already holding a sentinel\n if (this._acquiring) return; // an acquire is already in flight — don't double-request\n const wakeLock = this._wakeLock();\n if (!wakeLock) return; // unsupported — stay active, never acquire (silent no-op)\n if (!this._isVisible()) return; // hidden — defer to the next visibilitychange\n const gen = ++this._gen;\n this._acquiring = true;\n // Flag management is centralized in `finally` and the coalesced retry is invoked\n // exactly once, AFTER the try/catch/finally settles. This keeps the reject and\n // resolve paths symmetric: neither calls `_retryIfStillDesired()` from inside the\n // try/catch (which would let `finally` re-clear the `_acquiring=true` the retry's\n // synchronous re-entry just set, reopening the double-request window). `superseded`\n // records that a newer release()/request() bumped `_gen` mid-flight so its still-\n // live intent — blocked by the in-flight guard at the time — gets one retry here.\n // NOTE: no early `return` inside the try/catch below — every branch must fall\n // through to the post-`finally` retry. A `return` from inside the try would run\n // `finally` and then exit the function, skipping the `if (superseded)` retry.\n let superseded = false;\n let sentinel: WakeLockSentinelLike | null = null;\n let failed: Error | null = null;\n try {\n sentinel = await wakeLock.request(this._type);\n } catch (e) {\n if (gen !== this._gen) {\n // Superseded while awaiting — drop this stale failure (do not clobber the\n // newer state) and let the post-finally retry honor the live intent.\n superseded = true;\n } else {\n failed = this._normalizeError(e);\n }\n } finally {\n // The sole owner of the flag clears it here — on every exit path. A concurrent\n // re-entrant `_acquire()` was a no-op at the `_acquiring` guard, so it never owns\n // the flag; a superseding release()/acquire only bumps `_gen` and does not start\n // its own in-flight cycle until this clears the flag. Because this runs before\n // the retry below, the retry's `_acquiring=true` is never clobbered.\n this._acquiring = false;\n }\n\n if (sentinel !== null && gen !== this._gen) {\n // release() (or a newer acquire) ran while we awaited — this sentinel is\n // unwanted; drop it so no lock lingers, and retry the newer intent below.\n void sentinel.release().catch(() => { /* never-throw */ });\n superseded = true;\n } else if (sentinel !== null) {\n this._sentinel = sentinel;\n sentinel.addEventListener(\"release\", this._onRelease);\n this._setError(null);\n this._setHeld(true);\n } else if (failed !== null) {\n // A live (non-superseded) failure: surface it. Never retried — the intent is\n // honored but the environment denied it, so looping would spin.\n this._setError(failed);\n this._setHeld(false);\n }\n\n // Coalesced retry: at most one re-attempt per supersession, after the flag is\n // clear. The `_acquiring` guard inside still protects any concurrent re-entry that\n // overlaps THIS retry's own in-flight window (reject- and resolve-retry alike).\n if (superseded) this._retryIfStillDesired();\n }\n\n /**\n * Re-attempt an acquire after an in-flight one was *superseded* (its generation no\n * longer matches), but only if the lock is still desired, not already held, and the\n * page is visible. This recovers a request() that was coalesced away by the\n * in-flight `_acquiring` guard: during a release()→request() overlap, the second\n * request() bumps `_gen` and is a no-op at the guard, so without this retry its\n * still-live intent would be lost until the next visibilitychange or manual call.\n *\n * Bounded — cannot loop forever: a retry runs ONLY on supersession, and a\n * supersession requires an external release()/request() to bump `_gen` mid-flight.\n * A retry's own `_acquire()`, if it is itself not superseded, terminates by either\n * acquiring (held=true) or recording the live failure (held=false, error set) —\n * neither path retries. So a denied environment that keeps rejecting does not\n * recurse; the retry chain length is bounded by the number of external overlaps.\n */\n private _retryIfStillDesired(): void {\n if (this._active && !this._held && this._isVisible()) {\n void this._acquire();\n }\n }\n\n // Fired for an OS release of a held sentinel — which the spec allows for several\n // reasons, NOT only a visibility change: tab hidden / window minimized, but also\n // battery-low, power-saver mode, etc. while the page stays visible. We reflect\n // held=false, then (lease renewal) re-acquire immediately IF the page is still\n // visible and the lock is still desired — because a visible-context release emits no\n // `visibilitychange`, so the visibilitychange listener (②) would never fire and the\n // lock would stay stuck at desired=true / held=false. The hidden case is the no-op\n // here: re-acquire is gated on `_isVisible()`, so a hide-driven release defers to ②\n // (re-acquire on the return to visibility), avoiding a release→acquire loop while\n // hidden.\n private _onRelease = (): void => {\n // The `if (this._sentinel)` false branch is defensive and unreachable in practice:\n // this listener is only ever attached to the live `_sentinel`, and the only paths\n // that null `_sentinel` (this handler itself, and release()) remove this listener\n // in the same step — so the listener and a non-null `_sentinel` are coupled and\n // this never fires with `_sentinel === null`. Guarded anyway in case a host\n // dispatches a spurious second \"release\". (c8 ignore the unhittable else.)\n /* c8 ignore next */\n if (this._sentinel) {\n this._sentinel.removeEventListener(\"release\", this._onRelease);\n this._sentinel = null;\n }\n this._setHeld(false);\n this._reacquireAfterRelease();\n };\n\n /**\n * Lease renewal after an OS release while the page is still visible. Honors the\n * \"keep awake *while* active\" promise for releases that do NOT coincide with a\n * visibility change (battery-low / power-saver), which otherwise leave the lock\n * stuck at desired=true / held=false until the next hide→show cycle.\n *\n * Bounded on failure: this only runs from `_onRelease`, which only fires when a\n * sentinel was genuinely acquired and then released. A re-acquire that FAILS takes\n * `_acquire()`'s live-failure path (error recorded, held=false) and attaches no\n * listener, so it cannot re-enter `_onRelease` — a denied environment records the\n * error once and stops. This is the dominant real path: per the Wake Lock spec a\n * re-request under battery-low / power-saver is rejected (`NotAllowedError`), so the\n * renewal terminates there.\n *\n * The one path NOT bounded by a counter is a pathological host that keeps GRANTING\n * the re-request and then immediately auto-releasing it (grant→release reflux). Each\n * iteration yields to the event loop and consumes a real OS grant, so it is not a\n * tight/synchronous loop, but it would churn request() calls. We deliberately do NOT\n * add a debounce or renewal cap: that reflux is not documented browser behavior\n * (real browsers reject, not grant-then-revoke), and the extra timing state would\n * complicate the pure-sink design to defend a case that does not occur in practice.\n *\n * The `_isVisible()` / `!_acquiring` guards (doubled by `_acquire()`'s own in-flight\n * and held guards) prevent re-entry during an in-flight acquire and while hidden.\n */\n private _reacquireAfterRelease(): void {\n if (this._active && this._isVisible() && !this._acquiring) {\n void this._acquire();\n }\n }\n\n // ② Re-acquire when the page becomes visible again while the lock is still\n // desired but was auto-released. This is what makes `active` a durable intent.\n private _onVisibilityChange = (): void => {\n if (this._isVisible() && this._active && !this._held) {\n void this._acquire();\n }\n };\n\n private _ensureVisibilityListener(): void {\n if (this._visibilityBound) return;\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = true;\n }\n\n private _wakeLock(): WakeLockLike | null {\n return (navigator as Navigator & { wakeLock?: WakeLockLike }).wakeLock ?? null;\n }\n\n private _isVisible(): boolean {\n // §4 deviation: document-scoped Web API; no element-free alternative —\n // `visibilityState` lives on `document`, not on any element.\n return document.visibilityState === \"visible\";\n }\n\n private _normalizeError(e: unknown): Error {\n return e instanceof Error ? e : new Error(String(e));\n }\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\nimport { WakeLockCore } from \"../core/WakeLockCore.js\";\n\n/**\n * `<wcs-wakelock>` — declarative Screen Wake Lock.\n *\n * The first @wcstack tag that is a pure *sink*: every other sensor is an\n * element→state producer, but the wake lock is state→element. The headline\n * binding is `active@isPlaying` — hold the screen awake while a bound boolean is\n * true. `active` is the single input knob (a mirrored attribute); `held` and\n * `error` are the observable outputs.\n *\n * The OS auto-releases the lock when the page is hidden; the Core re-acquires it\n * on the next return to visibility while `active` is still set, so the binding\n * means \"keep awake *while* active\", not just \"acquire once\".\n */\nexport class WcsWakeLock extends HTMLElement {\n // SSR contract (§4.1/@wcstack/server): the renderer awaits elements declaring\n // `hasConnectedCallbackPromise = true` before snapshotting. The wake lock has no\n // connect-time async probe to await (acquire is fire-and-forget and meaningless\n // server-side), so `connectedCallbackPromise` is backed by the Core's no-op\n // `observe()`, which resolves immediately. The flag is still declared `true` so\n // the renderer reads it via `ctor.hasConnectedCallbackPromise` and the Shell\n // participates uniformly in the SSR await protocol.\n static hasConnectedCallbackPromise = true;\n // `active` drives request/release; `type` propagates to the Core's next acquire.\n // `manual` is intentionally excluded: it is a connect-time policy (\"don't auto-\n // acquire on connect\"), not a live switch.\n static observedAttributes = [\"active\", \"type\"];\n\n static wcBindable: IWcBindable = {\n ...WakeLockCore.wcBindable,\n // Settable surface. `active` is the declarative intent; `type` selects the lock\n // kind; `manual` opts out of auto-acquire on connect. The request / release\n // commands are inherited from the Core via the spread above.\n inputs: [\n { name: \"active\", attribute: \"active\" },\n { name: \"type\", attribute: \"type\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Core の commands をそのまま継承(単一情報源)。<wcs-intersect>/<wcs-sse> と同型。\n // spread でも継承されるが、Core に command 追加時の追従漏れを防ぐため明示参照する。\n commands: WakeLockCore.wcBindable.commands,\n };\n\n private _core: WakeLockCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new WakeLockCore(this);\n }\n\n // SSR (§4.1): the renderer awaits this before snapshotting. Backed by the Core's\n // observe() (a no-op resolving immediately for this command-driven sink).\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get active(): boolean {\n // Reflects the *attribute*, not the Core's desired intent (`_core.active`). These\n // can diverge: invoking the `request` / `release` commands directly (e.g. via a\n // command-token binding) flips the Core's desired flag without touching the\n // attribute, so `el.active` may read false while `el.held` is true (or vice\n // versa). The attribute is the declarative input surface; the commands are an\n // imperative side door. Bind via `active@...` for a single source of truth.\n return this.hasAttribute(\"active\");\n }\n\n set active(value: boolean) {\n if (value) {\n this.setAttribute(\"active\", \"\");\n } else {\n this.removeAttribute(\"active\");\n }\n }\n\n get type(): WakeLockKind {\n // Only \"screen\" is standardized; an absent/empty attribute defaults to it.\n return (this.getAttribute(\"type\") as WakeLockKind) || \"screen\";\n }\n\n set type(value: WakeLockKind) {\n this.setAttribute(\"type\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get held(): boolean {\n return this._core.held;\n }\n\n get error(): Error | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n /** Acquire (and keep) the wake lock. Never rejects — see the `error` property. */\n request(): Promise<void> {\n return this._core.request();\n }\n\n /** Release the wake lock and stop re-acquiring it. */\n release(): void {\n this._core.release();\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n // Headless resource: no layout box (mirrors the @wcstack sensor convention).\n this.style.display = \"none\";\n // Propagate the requested lock type to the Core. Currently a no-op in effect:\n // \"screen\" is the only standardized type, so `type` is always \"screen\". Wired\n // up as a forward-compatible seam (observedAttributes + setter + this line) for\n // when the spec adds lock types; until then it carries a constant.\n this._core.type = this.type;\n // Establish monitoring (§3.5) and expose readiness for SSR (§4.1). observe()\n // is a no-op that resolves immediately for this command-driven sink.\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual && this.active) {\n void this._core.request();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n // Ignore changes applied before connect (e.g. createElement + setAttribute);\n // connectedCallback applies the initial state. Acquiring a lock for a detached\n // element would be wrong.\n if (!this.isConnected) return;\n if (name === \"type\") {\n this._core.type = this.type;\n return;\n }\n // name === \"active\": a live toggle always drives request/release. `manual` only\n // gates the connect-time auto-acquire, not an explicit author toggle.\n if (this.active) {\n void this._core.request();\n } else {\n this._core.release();\n }\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapWakeLock(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsWakeLock } from \"./components/WakeLock.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.wakelock)) {\n customElements.define(config.tagNames.wakelock, WcsWakeLock);\n }\n}\n"],"names":["_config","tagNames","wakelock","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","WakeLockCore","EventTarget","static","protocol","version","properties","name","event","commands","async","_target","_type","_active","_held","_error","_sentinel","_gen","_acquiring","_visibilityBound","_ready","Promise","resolve","constructor","target","type","super","this","ready","observe","held","error","active","value","_setHeld","dispatchEvent","CustomEvent","detail","bubbles","_setError","_sameError","a","b","message","request","_ensureVisibilityListener","_acquire","release","sentinel","removeEventListener","_onRelease","catch","dispose","document","_onVisibilityChange","wakeLock","_wakeLock","_isVisible","gen","superseded","failed","e","_normalizeError","addEventListener","_retryIfStillDesired","_reacquireAfterRelease","navigator","visibilityState","Error","String","WcsWakeLock","HTMLElement","wcBindable","inputs","attribute","_core","_connectedCallbackPromise","connectedCallbackPromise","hasAttribute","setAttribute","removeAttribute","getAttribute","manual","connectedCallback","style","display","disconnectedCallback","attributeChangedCallback","oldValue","newValue","isConnected","bootstrapWakeLock","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/WakeLockCore.ts","../src/components/WakeLock.ts","../src/bootstrapWakeLock.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n wakelock: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n wakelock: \"wcs-wakelock\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (a frozen snapshot) is\n// surfaced. `setConfig()` is applied internally via `bootstrapWakeLock()` and\n// is not re-exported from the package root, though a deep path import\n// (`.../src/config.js`) can still reach and mutate it. Accepted as-is for\n// cross-package consistency: every @wcstack package follows this same shape.\n// Use `getConfig()` for a frozen, safe read.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\n\n/**\n * Minimal structural views of the Screen Wake Lock API. Declared locally (rather\n * than relying on `lib.dom`'s experimental `WakeLock` types) so the package type-\n * checks the same across TypeScript lib versions, and so a runtime where\n * `navigator.wakeLock` is absent is just a value check, never a type error.\n */\ninterface WakeLockSentinelLike extends EventTarget {\n readonly released: boolean;\n readonly type: string;\n release(): Promise<void>;\n}\ninterface WakeLockLike {\n request(type?: string): Promise<WakeLockSentinelLike>;\n}\n\n/**\n * Headless screen-wake-lock primitive — a thin, framework-agnostic wrapper around\n * the Screen Wake Lock API exposed through the wc-bindable protocol.\n *\n * Unlike the other @wcstack sensors (geolocation / intersection), the wake lock is\n * a pure *sink*: nothing is read from the device. A bound state drives the desired\n * intent (`request()` / `release()`), and the only observable outputs are `held`\n * (whether a sentinel is actually held) and `error`.\n *\n * The OS releases the lock whenever the page stops being visible (tab hidden,\n * window minimized). To honor the declarative intent (\"keep awake *while* active\"),\n * the Core keeps the desired flag (`_active`) and re-acquires the lock on the next\n * `visibilitychange` back to visible. So `_active` (desired) and `held` (actual)\n * diverge across an auto-release — and only `held` is published, because desired\n * does not change when the OS drops the lock.\n *\n * Never-throw: `request()` never rejects (a failure surfaces via `error`), and an\n * unsupported environment is a silent no-op (`held` stays false), consistent with\n * the other @wcstack sensors.\n */\nexport class WakeLockCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"held\", event: \"wcs-wakelock:held-changed\" },\n { name: \"error\", event: \"wcs-wakelock:error\" },\n ],\n commands: [\n { name: \"request\", async: true },\n { name: \"release\" },\n ],\n };\n\n private _target: EventTarget;\n private _type: WakeLockKind;\n\n // `_active` is the desired intent (input); `_held` is whether a sentinel is\n // actually held right now (output). They diverge across an OS auto-release.\n private _active: boolean = false;\n private _held: boolean = false;\n private _error: Error | null = null;\n private _sentinel: WakeLockSentinelLike | null = null;\n\n // Bumped on every release()/new acquire so an in-flight async request() that\n // resolves late can detect it was superseded and drop its sentinel (mirrors the\n // generation guards in GeolocationCore).\n private _gen: number = 0;\n // True while an `_acquire()` is awaiting `navigator.wakeLock.request()`. The\n // `_held` flag is only set *after* that await resolves, so it cannot guard\n // against concurrent entry: two rapid visibilitychange events (or a Shell toggle\n // overlapping an in-flight request) would both pass `!this._held` and each call\n // `request()`. This in-flight flag closes that window — a re-entrant acquire is a\n // no-op. The `_gen` guard still ensures the *final* state is correct; this just\n // avoids the redundant `request()` call (and its duplicate error path on a denied\n // environment).\n private _acquiring: boolean = false;\n private _visibilityBound: boolean = false;\n\n // SSR (§3.8): a pure sink has no asynchronous probe to await — `request()` is\n // fire-and-forget and meaningless server-side — so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget, type: WakeLockKind = \"screen\") {\n super();\n this._target = target ?? this;\n this._type = type;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // Lifecycle (§3.5). The wake lock is command-driven (request / release) with no\n // ambient subscription to establish on connect, so observe() is an idempotent\n // no-op that resolves once ready; dispose() (below) tears down the visibility\n // listener, releases any held sentinel, and bumps _gen via release().\n observe(): Promise<void> {\n return this._ready;\n }\n\n get held(): boolean {\n return this._held;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n /** The desired intent. Read-only reflection; not a wc-bindable property (it does\n * not change on an OS auto-release, so there is nothing to observe). */\n get active(): boolean {\n return this._active;\n }\n\n get type(): WakeLockKind {\n return this._type;\n }\n\n set type(value: WakeLockKind) {\n // Currently effectively a no-op: \"screen\" is the only standardized lock type,\n // so `WakeLockKind` is a single value and this setter never observes a real\n // change. Kept as a forward-compatible seam for when the spec adds lock types.\n //\n // Takes effect on the next acquire. Changing the type mid-hold deliberately does\n // NOT re-acquire — the live sentinel is left as is, so a type change applies only\n // from the following acquire. If multiple lock types are ever added this becomes\n // an observable behavior gap (a held lock keeps its old type until release/re-\n // acquire) and must be re-examined — likely re-acquire here when held.\n this._type = value;\n }\n\n // --- State setters with event dispatch ---\n\n private _setHeld(held: boolean): void {\n if (this._held === held) return;\n this._held = held;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:held-changed\", {\n detail: held,\n bubbles: true,\n }));\n }\n\n private _setError(error: Error | null): void {\n // Value guard, not just reference: a denied request rejects with a *fresh*\n // Error on every visibility-driven retry, so a reference compare would let a\n // permanently-denied environment re-dispatch the same failure on each\n // hidden→visible toggle. Compare name+message too. Transitions through null (a\n // success clears the error) always re-fire, so a genuinely new failure is seen.\n if (this._sameError(this._error, error)) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-wakelock:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _sameError(a: Error | null, b: Error | null): boolean {\n if (a === b) return true;\n if (a !== null && b !== null) return a.name === b.name && a.message === b.message;\n return false;\n }\n\n // --- Public API ---\n\n /**\n * Mark the lock as desired and acquire it. Idempotent while already held. If the\n * API is unavailable or the page is currently hidden, the desired flag is still\n * set (so the lock is acquired on the next return to visibility) but nothing is\n * acquired now. Never rejects — a request failure surfaces via `error`.\n */\n async request(): Promise<void> {\n this._active = true;\n this._ensureVisibilityListener();\n await this._acquire();\n }\n\n /** Mark the lock as no longer desired and release any held sentinel. */\n release(): void {\n this._active = false;\n // Invalidate any in-flight acquire so a late-resolving request() drops its\n // sentinel instead of leaving a lock held after release.\n this._gen++;\n const sentinel = this._sentinel;\n if (sentinel) {\n this._sentinel = null;\n sentinel.removeEventListener(\"release\", this._onRelease);\n void sentinel.release().catch(() => { /* never-throw */ });\n }\n this._setHeld(false);\n }\n\n /**\n * Full teardown: remove the visibility listener and release any held sentinel.\n * Call from the Shell's `disconnectedCallback`.\n *\n * Semantics: this is a terminal teardown, not a pause. After `dispose()` the Core\n * is meant to be discarded — there is no re-arm step, and the visibility listener\n * is gone, so an OS auto-release will no longer be followed by a re-acquire. A\n * later `request()` would still work in isolation (it re-attaches the listener via\n * `_ensureVisibilityListener`), but reusing a disposed Core is not an intended path;\n * the Shell always constructs a fresh Core per element instead.\n */\n dispose(): void {\n if (this._visibilityBound) {\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = false;\n }\n this.release();\n }\n\n // --- Internal ---\n\n private async _acquire(): Promise<void> {\n if (this._held) return; // idempotent: already holding a sentinel\n if (this._acquiring) return; // an acquire is already in flight — don't double-request\n const wakeLock = this._wakeLock();\n if (!wakeLock) return; // unsupported — stay active, never acquire (silent no-op)\n if (!this._isVisible()) return; // hidden — defer to the next visibilitychange\n const gen = ++this._gen;\n this._acquiring = true;\n // Flag management is centralized in `finally` and the coalesced retry is invoked\n // exactly once, AFTER the try/catch/finally settles. This keeps the reject and\n // resolve paths symmetric: neither calls `_retryIfStillDesired()` from inside the\n // try/catch (which would let `finally` re-clear the `_acquiring=true` the retry's\n // synchronous re-entry just set, reopening the double-request window). `superseded`\n // records that a newer release()/request() bumped `_gen` mid-flight so its still-\n // live intent — blocked by the in-flight guard at the time — gets one retry here.\n // NOTE: no early `return` inside the try/catch below — every branch must fall\n // through to the post-`finally` retry. A `return` from inside the try would run\n // `finally` and then exit the function, skipping the `if (superseded)` retry.\n let superseded = false;\n let sentinel: WakeLockSentinelLike | null = null;\n let failed: Error | null = null;\n try {\n sentinel = await wakeLock.request(this._type);\n } catch (e) {\n if (gen !== this._gen) {\n // Superseded while awaiting — drop this stale failure (do not clobber the\n // newer state) and let the post-finally retry honor the live intent.\n superseded = true;\n } else {\n failed = this._normalizeError(e);\n }\n } finally {\n // The sole owner of the flag clears it here — on every exit path. A concurrent\n // re-entrant `_acquire()` was a no-op at the `_acquiring` guard, so it never owns\n // the flag; a superseding release()/acquire only bumps `_gen` and does not start\n // its own in-flight cycle until this clears the flag. Because this runs before\n // the retry below, the retry's `_acquiring=true` is never clobbered.\n this._acquiring = false;\n }\n\n if (sentinel !== null && gen !== this._gen) {\n // release() (or a newer acquire) ran while we awaited — this sentinel is\n // unwanted; drop it so no lock lingers, and retry the newer intent below.\n void sentinel.release().catch(() => { /* never-throw */ });\n superseded = true;\n } else if (sentinel !== null) {\n this._sentinel = sentinel;\n sentinel.addEventListener(\"release\", this._onRelease);\n this._setError(null);\n this._setHeld(true);\n } else if (failed !== null) {\n // A live (non-superseded) failure: surface it. Never retried — the intent is\n // honored but the environment denied it, so looping would spin.\n this._setError(failed);\n this._setHeld(false);\n }\n\n // Coalesced retry: at most one re-attempt per supersession, after the flag is\n // clear. The `_acquiring` guard inside still protects any concurrent re-entry that\n // overlaps THIS retry's own in-flight window (reject- and resolve-retry alike).\n if (superseded) this._retryIfStillDesired();\n }\n\n /**\n * Re-attempt an acquire after an in-flight one was *superseded* (its generation no\n * longer matches), but only if the lock is still desired, not already held, and the\n * page is visible. This recovers a request() that was coalesced away by the\n * in-flight `_acquiring` guard: during a release()→request() overlap, the second\n * request() bumps `_gen` and is a no-op at the guard, so without this retry its\n * still-live intent would be lost until the next visibilitychange or manual call.\n *\n * Bounded — cannot loop forever: a retry runs ONLY on supersession, and a\n * supersession requires an external release()/request() to bump `_gen` mid-flight.\n * A retry's own `_acquire()`, if it is itself not superseded, terminates by either\n * acquiring (held=true) or recording the live failure (held=false, error set) —\n * neither path retries. So a denied environment that keeps rejecting does not\n * recurse; the retry chain length is bounded by the number of external overlaps.\n */\n private _retryIfStillDesired(): void {\n if (this._active && !this._held && this._isVisible()) {\n void this._acquire();\n }\n }\n\n // Fired for an OS release of a held sentinel — which the spec allows for several\n // reasons, NOT only a visibility change: tab hidden / window minimized, but also\n // battery-low, power-saver mode, etc. while the page stays visible. We reflect\n // held=false, then (lease renewal) re-acquire immediately IF the page is still\n // visible and the lock is still desired — because a visible-context release emits no\n // `visibilitychange`, so the visibilitychange listener (②) would never fire and the\n // lock would stay stuck at desired=true / held=false. The hidden case is the no-op\n // here: re-acquire is gated on `_isVisible()`, so a hide-driven release defers to ②\n // (re-acquire on the return to visibility), avoiding a release→acquire loop while\n // hidden.\n private _onRelease = (): void => {\n // The `if (this._sentinel)` false branch is defensive and unreachable in practice:\n // this listener is only ever attached to the live `_sentinel`, and the only paths\n // that null `_sentinel` (this handler itself, and release()) remove this listener\n // in the same step — so the listener and a non-null `_sentinel` are coupled and\n // this never fires with `_sentinel === null`. Guarded anyway in case a host\n // dispatches a spurious second \"release\". (c8 ignore the unhittable else.)\n /* c8 ignore next */\n if (this._sentinel) {\n this._sentinel.removeEventListener(\"release\", this._onRelease);\n this._sentinel = null;\n }\n this._setHeld(false);\n this._reacquireAfterRelease();\n };\n\n /**\n * Lease renewal after an OS release while the page is still visible. Honors the\n * \"keep awake *while* active\" promise for releases that do NOT coincide with a\n * visibility change (battery-low / power-saver), which otherwise leave the lock\n * stuck at desired=true / held=false until the next hide→show cycle.\n *\n * Bounded on failure: this only runs from `_onRelease`, which only fires when a\n * sentinel was genuinely acquired and then released. A re-acquire that FAILS takes\n * `_acquire()`'s live-failure path (error recorded, held=false) and attaches no\n * listener, so it cannot re-enter `_onRelease` — a denied environment records the\n * error once and stops. This is the dominant real path: per the Wake Lock spec a\n * re-request under battery-low / power-saver is rejected (`NotAllowedError`), so the\n * renewal terminates there.\n *\n * The one path NOT bounded by a counter is a pathological host that keeps GRANTING\n * the re-request and then immediately auto-releasing it (grant→release reflux). Each\n * iteration yields to the event loop and consumes a real OS grant, so it is not a\n * tight/synchronous loop, but it would churn request() calls. We deliberately do NOT\n * add a debounce or renewal cap: that reflux is not documented browser behavior\n * (real browsers reject, not grant-then-revoke), and the extra timing state would\n * complicate the pure-sink design to defend a case that does not occur in practice.\n *\n * The `_isVisible()` / `!_acquiring` guards (doubled by `_acquire()`'s own in-flight\n * and held guards) prevent re-entry during an in-flight acquire and while hidden.\n */\n private _reacquireAfterRelease(): void {\n if (this._active && this._isVisible() && !this._acquiring) {\n void this._acquire();\n }\n }\n\n // ② Re-acquire when the page becomes visible again while the lock is still\n // desired but was auto-released. This is what makes `active` a durable intent.\n private _onVisibilityChange = (): void => {\n if (this._isVisible() && this._active && !this._held) {\n void this._acquire();\n }\n };\n\n private _ensureVisibilityListener(): void {\n if (this._visibilityBound) return;\n // §4 deviation: document-scoped Web API; no element-free alternative — the\n // Page Visibility `visibilitychange` event is only dispatched on `document`.\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._visibilityBound = true;\n }\n\n private _wakeLock(): WakeLockLike | null {\n return (navigator as Navigator & { wakeLock?: WakeLockLike }).wakeLock ?? null;\n }\n\n private _isVisible(): boolean {\n // §4 deviation: document-scoped Web API; no element-free alternative —\n // `visibilityState` lives on `document`, not on any element.\n return document.visibilityState === \"visible\";\n }\n\n private _normalizeError(e: unknown): Error {\n return e instanceof Error ? e : new Error(String(e));\n }\n}\n","import { IWcBindable, WakeLockKind } from \"../types.js\";\nimport { WakeLockCore } from \"../core/WakeLockCore.js\";\n\n/**\n * `<wcs-wakelock>` — declarative Screen Wake Lock.\n *\n * The first @wcstack tag that is a pure *sink*: every other sensor is an\n * element→state producer, but the wake lock is state→element. The headline\n * binding is `active@isPlaying` — hold the screen awake while a bound boolean is\n * true. `active` is the single input knob (a mirrored attribute); `held` and\n * `error` are the observable outputs.\n *\n * The OS auto-releases the lock when the page is hidden; the Core re-acquires it\n * on the next return to visibility while `active` is still set, so the binding\n * means \"keep awake *while* active\", not just \"acquire once\".\n */\nexport class WcsWakeLock extends HTMLElement {\n // SSR contract (§4.1/@wcstack/server): the renderer awaits elements declaring\n // `hasConnectedCallbackPromise = true` before snapshotting. The wake lock has no\n // connect-time async probe to await (acquire is fire-and-forget and meaningless\n // server-side), so `connectedCallbackPromise` is backed by the Core's no-op\n // `observe()`, which resolves immediately. The flag is still declared `true` so\n // the renderer reads it via `ctor.hasConnectedCallbackPromise` and the Shell\n // participates uniformly in the SSR await protocol.\n static hasConnectedCallbackPromise = true;\n // `active` drives request/release; `type` propagates to the Core's next acquire.\n // `manual` is intentionally excluded: it is a connect-time policy (\"don't auto-\n // acquire on connect\"), not a live switch.\n static observedAttributes = [\"active\", \"type\"];\n\n static wcBindable: IWcBindable = {\n ...WakeLockCore.wcBindable,\n // Settable surface. `active` is the declarative intent; `type` selects the lock\n // kind; `manual` opts out of auto-acquire on connect. The request / release\n // commands are inherited from the Core via the spread above.\n inputs: [\n { name: \"active\", attribute: \"active\" },\n { name: \"type\", attribute: \"type\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Core の commands をそのまま継承(単一情報源)。<wcs-intersect>/<wcs-sse> と同型。\n // spread でも継承されるが、Core に command 追加時の追従漏れを防ぐため明示参照する。\n commands: WakeLockCore.wcBindable.commands,\n };\n\n private _core: WakeLockCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new WakeLockCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-wakelock:held-changed\": (d) => ({ held: d === true }),\n \"wcs-wakelock:error\": (d) => ({ error: d != null }),\n });\n }\n\n // SSR (§4.1): the renderer awaits this before snapshotting. Backed by the Core's\n // observe() (a no-op resolving immediately for this command-driven sink).\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of\n // wc-bindable (not a bind target); see README \"CSS styling with :state()\".\n // MUST NOT return the live CustomStateSet (that would let callers write\n // states from outside, defeating the point of :state() being read-only).\n get debugStates(): string[] {\n return this._internals ? [...this._internals.states] : [];\n }\n\n private _initInternals(): ElementInternals | null {\n // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent\n // in happy-dom / older environments, and pre-125 Chromium rejects\n // non-dashed state names from states.add() (probed and discarded here).\n // Either case silently disables reflection — the component still works,\n // it just doesn't expose :state() selectors.\n try {\n if (typeof this.attachInternals !== \"function\") return null;\n const internals = this.attachInternals();\n internals.states.add(\"wcs-probe\");\n internals.states.delete(\"wcs-probe\");\n return internals;\n } catch {\n return null;\n }\n }\n\n private _wireStates(map: Record<string, (detail: any) => Record<string, boolean>>): void {\n if (this._internals === null) return;\n const states = this._internals.states;\n for (const [event, toStates] of Object.entries(map)) {\n this.addEventListener(event, (e) => {\n const debug = this.hasAttribute(\"debug-states\");\n for (const [name, on] of Object.entries(toStates((e as CustomEvent).detail))) {\n try {\n // The ternary expression-statement form trips ESLint no-unused-expressions.\n if (on) { states.add(name); } else { states.delete(name); }\n } catch { /* never-throw */ }\n if (debug) this.toggleAttribute(`data-wcs-state-${name}`, on);\n }\n });\n }\n }\n\n // --- Attribute accessors ---\n\n get active(): boolean {\n // Reflects the *attribute*, not the Core's desired intent (`_core.active`). These\n // can diverge: invoking the `request` / `release` commands directly (e.g. via a\n // command-token binding) flips the Core's desired flag without touching the\n // attribute, so `el.active` may read false while `el.held` is true (or vice\n // versa). The attribute is the declarative input surface; the commands are an\n // imperative side door. Bind via `active@...` for a single source of truth.\n return this.hasAttribute(\"active\");\n }\n\n set active(value: boolean) {\n if (value) {\n this.setAttribute(\"active\", \"\");\n } else {\n this.removeAttribute(\"active\");\n }\n }\n\n get type(): WakeLockKind {\n // Only \"screen\" is standardized; an absent/empty attribute defaults to it.\n return (this.getAttribute(\"type\") as WakeLockKind) || \"screen\";\n }\n\n set type(value: WakeLockKind) {\n this.setAttribute(\"type\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get held(): boolean {\n return this._core.held;\n }\n\n get error(): Error | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n /** Acquire (and keep) the wake lock. Never rejects — see the `error` property. */\n request(): Promise<void> {\n return this._core.request();\n }\n\n /** Release the wake lock and stop re-acquiring it. */\n release(): void {\n this._core.release();\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n // Headless resource: no layout box (mirrors the @wcstack sensor convention).\n this.style.display = \"none\";\n // Propagate the requested lock type to the Core. Currently a no-op in effect:\n // \"screen\" is the only standardized type, so `type` is always \"screen\". Wired\n // up as a forward-compatible seam (observedAttributes + setter + this line) for\n // when the spec adds lock types; until then it carries a constant.\n this._core.type = this.type;\n // Establish monitoring (§3.5) and expose readiness for SSR (§4.1). observe()\n // is a no-op that resolves immediately for this command-driven sink.\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual && this.active) {\n void this._core.request();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n // Ignore changes applied before connect (e.g. createElement + setAttribute);\n // connectedCallback applies the initial state. Acquiring a lock for a detached\n // element would be wrong.\n if (!this.isConnected) return;\n if (name === \"type\") {\n this._core.type = this.type;\n return;\n }\n // name === \"active\": a live toggle always drives request/release. `manual` only\n // gates the connect-time auto-acquire, not an explicit author toggle.\n if (this.active) {\n void this._core.request();\n } else {\n this._core.release();\n }\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapWakeLock(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsWakeLock } from \"./components/WakeLock.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.wakelock)) {\n customElements.define(config.tagNames.wakelock, WcsWakeLock);\n }\n}\n"],"names":["_config","tagNames","wakelock","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","WakeLockCore","EventTarget","static","protocol","version","properties","name","event","commands","async","_target","_type","_active","_held","_error","_sentinel","_gen","_acquiring","_visibilityBound","_ready","Promise","resolve","constructor","target","type","super","this","ready","observe","held","error","active","value","_setHeld","dispatchEvent","CustomEvent","detail","bubbles","_setError","_sameError","a","b","message","request","_ensureVisibilityListener","_acquire","release","sentinel","removeEventListener","_onRelease","catch","dispose","document","_onVisibilityChange","wakeLock","_wakeLock","_isVisible","gen","superseded","failed","e","_normalizeError","addEventListener","_retryIfStillDesired","_reacquireAfterRelease","navigator","visibilityState","Error","String","WcsWakeLock","HTMLElement","wcBindable","inputs","attribute","_core","_connectedCallbackPromise","_internals","_initInternals","_wireStates","d","connectedCallbackPromise","debugStates","states","attachInternals","internals","add","delete","map","toStates","entries","debug","hasAttribute","on","toggleAttribute","setAttribute","removeAttribute","getAttribute","manual","connectedCallback","style","display","disconnectedCallback","attributeChangedCallback","oldValue","newValue","isConnected","bootstrapWakeLock","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/wakelock",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "Declarative Screen Wake Lock component for Web Components. Framework-agnostic, holds the screen awake while a bound state is true via wc-bindable-protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",