@wcstack/ambient-light-sensor 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 +50 -0
- package/README.md +52 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +53 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -110,6 +110,56 @@ npm install @wcstack/ambient-light-sensor
|
|
|
110
110
|
| `start` | いいえ | センサーを構築(never-throw: コンストラクタの同期例外はキャッチし`error`へ)し読み取りを開始する。 |
|
|
111
111
|
| `stop` | いいえ | センサーを停止しリスナーを解除する。未開始でも安全に呼べる。 |
|
|
112
112
|
|
|
113
|
+
## `:state()` による CSS スタイリング
|
|
114
|
+
|
|
115
|
+
`<wcs-ambient-light-sensor>` は 1 つの boolean 出力ステートを
|
|
116
|
+
[`ElementInternals` の `CustomStateSet`](https://developer.mozilla.org/ja/docs/Web/API/CustomStateSet)
|
|
117
|
+
に反映します。そのため `data-wcs` バインディングやクラスの手動トグルなしに、CSS の
|
|
118
|
+
`:state()` 疑似クラスで直接スタイリングできます。
|
|
119
|
+
|
|
120
|
+
| ステート | on になる条件 |
|
|
121
|
+
|----------|----------------|
|
|
122
|
+
| `error` | `wcs-ambient-light-sensor:error` が非 `null` の detail で発火(`null` でクリア) |
|
|
123
|
+
|
|
124
|
+
`illuminance` は反映されません — 連続的・高頻度な読み取り値であり、`:state()`
|
|
125
|
+
反映の対象外です(設計ドキュメントの除外リストを参照)。
|
|
126
|
+
|
|
127
|
+
```css
|
|
128
|
+
wcs-ambient-light-sensor:state(error) ~ .fallback { display: block; }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
属性やクラスと異なり `:state()` は要素の外部から書き込めないため、この出力ステートが
|
|
132
|
+
入力と混同される心配がありません。
|
|
133
|
+
|
|
134
|
+
**対応ブラウザ**(新構文 `:state(x)`): Chrome/Edge 125+、Safari 17.4+、Firefox 126+。
|
|
135
|
+
非対応の環境ではステートが一切 set されないだけです — `:state()` セレクタがマッチしなく
|
|
136
|
+
なりますが、`<wcs-ambient-light-sensor>` 自体は通常どおり動作し続けます(graceful degradation・never-throw)。
|
|
137
|
+
|
|
138
|
+
**SSR:** `:state()` は HTML にシリアライズできないため、サーバーレンダリングされた
|
|
139
|
+
マークアップの初期ペイントにはこれらのステートは乗りません(`@wcstack/server` は無改変)。
|
|
140
|
+
ハイドレーション前の見た目を制御したい場合は、代わりに `wcs-ambient-light-sensor:not(:defined)` と組み合わせてください。
|
|
141
|
+
|
|
142
|
+
### デバッグ
|
|
143
|
+
|
|
144
|
+
カスタムステートは DevTools の Elements パネルには表示されず、`attachInternals()`
|
|
145
|
+
は同一要素に 2 回呼べないため、コンソールから直接覗く手段がありません。そのための
|
|
146
|
+
デバッグ専用の補助を 2 つ用意しています:
|
|
147
|
+
|
|
148
|
+
- `el.debugStates` — 現在 on になっているステート名の**スナップショット**配列
|
|
149
|
+
(例: `["error"]`)。`wc-bindable` の一部ではなく(バインド対象ではない)、
|
|
150
|
+
形状も契約として保証されません — デバッグ用途にのみ使ってください。
|
|
151
|
+
- `debug-states` 属性(opt-in・既定 OFF)は、ステート変化を要素の
|
|
152
|
+
`data-wcs-state-error` 属性にミラーします。
|
|
153
|
+
Elements パネルを開いておけば、トグルのたびにハイライトされます:
|
|
154
|
+
|
|
155
|
+
```html
|
|
156
|
+
<wcs-ambient-light-sensor debug-states></wcs-ambient-light-sensor>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**CSS は `data-wcs-state-*` ではなく `:state()` に書いてください。** ミラーされた
|
|
160
|
+
属性は、DevTools を開いた状態でステート変化を可視化するためだけのものであり、
|
|
161
|
+
スタイリング用の正式なフックではありません。
|
|
162
|
+
|
|
113
163
|
## 注意・制限
|
|
114
164
|
|
|
115
165
|
- **`_gen`世代ガードは無し。** `start()`/`stop()`は同期的な購読/購読解除のトグルであり、`dispose()`とレースしうる非同期probeが存在しません(`docs/sensor-tag-design.md` §1.5)。
|
package/README.md
CHANGED
|
@@ -111,6 +111,58 @@ Every bound state path must be declared up front — binding an undeclared path
|
|
|
111
111
|
| `start` | no | Construct the sensor (never-throw: a synchronous constructor exception is caught and surfaced via `error`) and begin reading. |
|
|
112
112
|
| `stop` | no | Stop the sensor and detach its listeners. Safe to call when not started. |
|
|
113
113
|
|
|
114
|
+
## CSS styling with `:state()`
|
|
115
|
+
|
|
116
|
+
`<wcs-ambient-light-sensor>` reflects one boolean output state onto its
|
|
117
|
+
[`ElementInternals` `CustomStateSet`](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet),
|
|
118
|
+
so you can style it directly from CSS with the `:state()` pseudo-class — no
|
|
119
|
+
`data-wcs` binding or extra class toggling required.
|
|
120
|
+
|
|
121
|
+
| State | On when |
|
|
122
|
+
|-------|---------|
|
|
123
|
+
| `error` | `wcs-ambient-light-sensor:error` fires with a non-`null` detail (cleared on `null`) |
|
|
124
|
+
|
|
125
|
+
`illuminance` is not reflected — it is a continuous/high-frequency reading, out
|
|
126
|
+
of scope for `:state()` reflection (see the design doc's excluded-values list).
|
|
127
|
+
|
|
128
|
+
```css
|
|
129
|
+
wcs-ambient-light-sensor:state(error) ~ .fallback { display: block; }
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Unlike attributes or classes, `:state()` cannot be written from outside the
|
|
133
|
+
element, so there is no risk of confusing this output state with an input.
|
|
134
|
+
|
|
135
|
+
**Browser support** (`:state(x)` syntax): Chrome/Edge 125+, Safari 17.4+,
|
|
136
|
+
Firefox 126+. In older browsers the states are simply never set — `:state()`
|
|
137
|
+
selectors never match, but `<wcs-ambient-light-sensor>` itself keeps working normally
|
|
138
|
+
(graceful degradation, never-throw).
|
|
139
|
+
|
|
140
|
+
**SSR**: `:state()` cannot be serialized into HTML, so server-rendered markup
|
|
141
|
+
never carries these states on first paint (`@wcstack/server` is unaffected).
|
|
142
|
+
If you need to style the pre-hydration gap, pair your rule with
|
|
143
|
+
`wcs-ambient-light-sensor:not(:defined)` instead.
|
|
144
|
+
|
|
145
|
+
### Debugging
|
|
146
|
+
|
|
147
|
+
Custom states are invisible in DevTools' Elements panel and `attachInternals()`
|
|
148
|
+
cannot be called twice, so there is no console way to inspect them directly.
|
|
149
|
+
Two debug-only aids are provided for that:
|
|
150
|
+
|
|
151
|
+
- `el.debugStates` — a **snapshot** array of the currently-on state names
|
|
152
|
+
(e.g. `["error"]`). It is not part of `wc-bindable` (not a bind target)
|
|
153
|
+
and its shape is not a guaranteed contract — use it for debugging only.
|
|
154
|
+
- The `debug-states` attribute (opt-in, default off) mirrors state changes
|
|
155
|
+
onto `data-wcs-state-error` attribute on the element, so the Elements
|
|
156
|
+
panel highlights it as it toggles:
|
|
157
|
+
|
|
158
|
+
```html
|
|
159
|
+
<wcs-ambient-light-sensor debug-states></wcs-ambient-light-sensor>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Write your CSS against `:state()`, not `data-wcs-state-*`.** The mirrored
|
|
163
|
+
attribute exists purely to make state changes visible while debugging with
|
|
164
|
+
DevTools open; it is not a supported styling hook.
|
|
165
|
+
|
|
114
166
|
## Notes & limitations
|
|
115
167
|
|
|
116
168
|
- **No `_gen` generation guard.** `start()`/`stop()` are a synchronous subscribe/unsubscribe toggle with no asynchronous probe to race against a `dispose()` — see `docs/sensor-tag-design.md` §1.5.
|
package/dist/index.d.ts
CHANGED
|
@@ -183,7 +183,11 @@ declare class WcsAmbientLightSensor extends HTMLElement {
|
|
|
183
183
|
static wcBindable: IWcBindable;
|
|
184
184
|
private _core;
|
|
185
185
|
private _connectedCallbackPromise;
|
|
186
|
+
private _internals;
|
|
186
187
|
constructor();
|
|
188
|
+
get debugStates(): string[];
|
|
189
|
+
private _initInternals;
|
|
190
|
+
private _wireStates;
|
|
187
191
|
/**
|
|
188
192
|
* Sampling frequency in Hz. Reads back `null` when unset, blank, or when the
|
|
189
193
|
* attribute does not parse to a positive finite number (NaN, `"0"`, negative)
|
package/dist/index.esm.js
CHANGED
|
@@ -266,9 +266,62 @@ class WcsAmbientLightSensor extends HTMLElement {
|
|
|
266
266
|
};
|
|
267
267
|
_core;
|
|
268
268
|
_connectedCallbackPromise = Promise.resolve();
|
|
269
|
+
_internals = null;
|
|
269
270
|
constructor() {
|
|
270
271
|
super();
|
|
271
272
|
this._core = new AmbientLightSensorCore(this);
|
|
273
|
+
this._internals = this._initInternals();
|
|
274
|
+
this._wireStates({
|
|
275
|
+
"wcs-ambient-light-sensor:error": (d) => ({ error: d != null }),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
// CSS state reflection (:state()) — debug-only snapshot getter. NOT part of
|
|
279
|
+
// wc-bindable (not a bind target); see README "CSS styling with :state()".
|
|
280
|
+
// MUST NOT return the live CustomStateSet (that would let callers write
|
|
281
|
+
// states from outside, defeating the point of :state() being read-only).
|
|
282
|
+
get debugStates() {
|
|
283
|
+
return this._internals ? [...this._internals.states] : [];
|
|
284
|
+
}
|
|
285
|
+
_initInternals() {
|
|
286
|
+
// never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent
|
|
287
|
+
// in happy-dom / older environments, and pre-125 Chromium rejects
|
|
288
|
+
// non-dashed state names from states.add() (probed and discarded here).
|
|
289
|
+
// Either case silently disables reflection — the component still works,
|
|
290
|
+
// it just doesn't expose :state() selectors.
|
|
291
|
+
try {
|
|
292
|
+
if (typeof this.attachInternals !== "function")
|
|
293
|
+
return null;
|
|
294
|
+
const internals = this.attachInternals();
|
|
295
|
+
internals.states.add("wcs-probe");
|
|
296
|
+
internals.states.delete("wcs-probe");
|
|
297
|
+
return internals;
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
_wireStates(map) {
|
|
304
|
+
if (this._internals === null)
|
|
305
|
+
return;
|
|
306
|
+
const states = this._internals.states;
|
|
307
|
+
for (const [event, toStates] of Object.entries(map)) {
|
|
308
|
+
this.addEventListener(event, (e) => {
|
|
309
|
+
const debug = this.hasAttribute("debug-states");
|
|
310
|
+
for (const [name, on] of Object.entries(toStates(e.detail))) {
|
|
311
|
+
try {
|
|
312
|
+
if (on) {
|
|
313
|
+
states.add(name);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
states.delete(name);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch { /* never-throw */ }
|
|
320
|
+
if (debug)
|
|
321
|
+
this.toggleAttribute(`data-wcs-state-${name}`, on);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
272
325
|
}
|
|
273
326
|
// --- Attribute accessors ---
|
|
274
327
|
/**
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/AmbientLightSensorCore.ts","../src/components/AmbientLightSensor.ts","../src/registerComponents.ts","../src/bootstrapAmbientLightSensor.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n ambientLightSensor: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n ambientLightSensor: \"wcs-ambient-light-sensor\",\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\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, WcsAmbientLightSensorReading, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsAmbientLightSensorReading = Object.freeze({ illuminance: null });\n\n/**\n * Headless AmbientLightSensor primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `AmbientLightSensor` class exposed through the\n * wc-bindable protocol.\n *\n * The platform `Sensor` base class (shared by `Accelerometer` / `Gyroscope` /\n * `Magnetometer` / `AmbientLightSensor`) reports failure through an `'error'`\n * event rather than a rejected promise, so this Core can satisfy never-throw\n * (docs/async-io-node-guidelines.md §3.6) by simply forwarding that event —\n * see docs/sensor-tag-design.md §0. The one place a synchronous\n * exception *can* still escape the platform API is the `AmbientLightSensor`\n * constructor itself (e.g. `SecurityError` on permission denial or a\n * feature-policy block); `_createSensor()` wraps that single call in\n * try/catch, mirroring FetchCore's `_doFetch` try/catch around\n * `globalThis.fetch` (packages/fetch/src/core/FetchCore.ts).\n *\n * `illuminance` is a single getter derived from the `wcs-ambient-light-sensor:reading`\n * event (unlike Accelerometer/Gyroscope/Magnetometer's x/y/z, this sensor\n * reports one scalar — docs/sensor-tag-design.md §2). `reading`\n * is an event-like signal (a fresh sample every time, not a settled state) and\n * is therefore deliberately NOT same-value guarded — every sample dispatches.\n * `error` is state-like (denial / unsupported does not change from tick to\n * tick) and IS same-value guarded, and is published on its own\n * `wcs-ambient-light-sensor:error` event, independent of `reading`.\n *\n * No `_gen` generation guard: start()/stop() are a synchronous\n * subscribe/unsubscribe toggle with no asynchronous probe whose stale\n * resolution could race a dispose() — see docs/sensor-tag-design.md §1.5\n * (the same reasoning as NetworkCore, docs/network-tag-design.md §5).\n *\n * Permissions: this Core does not query `navigator.permissions` itself.\n * Compose with `<wcs-permission name=\"ambient-light-sensor\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class AmbientLightSensorCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"illuminance\", event: \"wcs-ambient-light-sensor:reading\", getter: (e: Event) => (e as CustomEvent).detail.illuminance },\n { name: \"error\", event: \"wcs-ambient-light-sensor:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsAmbientLightSensorReading = NULL_READING;\n private _error: WcsAmbientLightSensorErrorDetail | null = null;\n\n // The live sensor instance while started (null otherwise), kept so stop()\n // can remove its listeners precisely and so start() can detect \"already\n // started\" without a separate boolean (docs/async-io-node-guidelines.md §3.5\n // idempotency).\n private _sensor: (EventTarget & { start(): void; stop(): void }) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get illuminance(): number | null {\n return this._reading.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._error;\n }\n\n /** No asynchronous probe to await: start()/stop() are synchronous\n * (docs/async-io-node-guidelines.md §3.8 is satisfied trivially, mirroring\n * NetworkCore). */\n get ready(): Promise<void> {\n return Promise.resolve();\n }\n\n // --- State setters ---\n\n // Deliberately NOT same-value guarded: a `reading` is a fresh sample, not a\n // settled state, so it must dispatch every time even when the values happen\n // to repeat (docs/sensor-tag-design.md §1.1).\n private _setReading(reading: WcsAmbientLightSensorReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsAmbientLightSensorErrorDetail | null): void {\n // Same-value guard (by error name + message): error is state-like, unlike\n // reading — a repeated identical error (same name and message) must not\n // redispatch. Note `error` is also STICKY: nothing calls _setError(null),\n // so a successful (re)start does not clear a prior failure — the monitoring\n // sensor family deliberately keeps the last observed error (docs/sensor-tag-design.md §1.5).\n if (this._error?.error === error?.error && this._error?.message === error?.message) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Start the sensor at the given `frequency` (Hz), or the platform default\n * when omitted. Idempotent while already started: a redundant start() does\n * not construct a second sensor instance (which would leak the first).\n * Restart with a different frequency via stop() + start().\n *\n * Synchronous, mirroring the native `Sensor.start()` — never throws\n * (docs/async-io-node-guidelines.md §3.6): both \"unsupported\" and a\n * synchronous constructor exception (permission denial, feature-policy\n * block) are converted to the `error` property instead of propagating.\n */\n start(frequency?: number): void {\n if (this._sensor) return;\n const sensor = this._createSensor(frequency);\n if (!sensor) return;\n sensor.addEventListener(\"reading\", this._onReading as EventListener);\n sensor.addEventListener(\"error\", this._onError as EventListener);\n this._sensor = sensor;\n try {\n sensor.start();\n } catch (e: any) {\n // Defensive: the platform contract says start()/stop() do not throw\n // (failures surface via the 'error' event), but never-throw is a hard\n // requirement here, so guard against a non-conformant implementation\n // too.\n this._teardownSensor();\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n }\n }\n\n /** Stop the sensor and detach its listeners. Safe to call when not started. */\n stop(): void {\n if (!this._sensor) return;\n try {\n this._sensor.stop();\n } catch {\n // Never-throw defensive guard, symmetric with start(). Teardown below\n // still runs so listeners are detached regardless.\n }\n this._teardownSensor();\n }\n\n /** Lifecycle alias for start(), so the Shell's connectedCallback can drive\n * this Core the same way as other IO nodes' observe()/dispose() pair. No\n * asynchronous probe, so the returned promise always resolves immediately. */\n observe(frequency?: number): Promise<void> {\n this.start(frequency);\n return this.ready;\n }\n\n /** Lifecycle alias for stop(), invoked from the Shell's disconnectedCallback. */\n dispose(): void {\n this.stop();\n }\n\n // --- Internal ---\n\n // Both call sites (start()'s catch, stop()) only ever invoke this once\n // `this._sensor` is already known non-null, so there is no null-guard here\n // (nothing to defend against).\n private _teardownSensor(): void {\n this._sensor!.removeEventListener(\"reading\", this._onReading as EventListener);\n this._sensor!.removeEventListener(\"error\", this._onError as EventListener);\n this._sensor = null;\n }\n\n /**\n * Construct the platform `AmbientLightSensor`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new AmbientLightSensor(...)`\n * anywhere else in this class — see docs/sensor-tag-design.md §1.5.\n *\n * API resolution is call-time (docs/async-io-node-guidelines.md §3.7):\n * re-checked on every start(), never cached, so tests can install/remove the\n * global freely and an unsupported environment is always reported correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).AmbientLightSensor;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"AmbientLightSensor is not supported\" });\n return null;\n }\n try {\n return new Ctor(frequency !== undefined ? { frequency } : undefined);\n } catch (e: any) {\n // SecurityError (permission denial, feature-policy block) or any other\n // synchronous construction failure. Mirrors the FetchCore._doFetch\n // try/catch structure (packages/fetch/src/core/FetchCore.ts) — a\n // synchronous constructor call here instead of an awaited fetch().\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n return null;\n }\n }\n\n private _onReading = (event: Event): void => {\n const sensor = event.target as unknown as { illuminance: number | null };\n this._setReading({ illuminance: sensor.illuminance });\n };\n\n private _onError = (event: Event): void => {\n const err = (event as any).error as { name?: string; message?: string } | undefined;\n // Fallback is a meaningful constant, NOT String(err): a SensorErrorEvent\n // without an `error` field would otherwise stringify `undefined` into the\n // literal message \"undefined\" (aligned across the sensor family).\n this._setError({ error: err?.name ?? \"error\", message: err?.message ?? \"Sensor error\" });\n };\n}\n","import { IWcBindable, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\nimport { AmbientLightSensorCore } from \"../core/AmbientLightSensorCore.js\";\n\n/**\n * `<wcs-ambient-light-sensor>` — declarative Generic Sensor API (`AmbientLightSensor`)\n * monitor + start/stop control.\n *\n * Unlike `<wcs-network>` / `<wcs-permission>` (pure monitors), this Shell is a\n * bidirectional node: `start`/`stop` commands (command-token: state → element)\n * alongside the `illuminance`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `AmbientLightSensor` constructor's `{ frequency }` option\n * (docs/sensor-tag-design.md §1.2). The getter normalizes it: a non-finite or\n * non-positive value (NaN, 0, negative) reads back as `null` — meaning \"no\n * frequency specified\" — so start() falls back to the platform default rather\n * than forwarding a value the sensor would reject. Any positive finite value is\n * passed through verbatim (no upper-bound clamping — an out-of-range-but-positive\n * rate is still left to the browser/sensor to reject via `error`).\n *\n * Permission handling is intentionally NOT implemented here. Compose with\n * `<wcs-permission name=\"ambient-light-sensor\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsAmbientLightSensor extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...AmbientLightSensorCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: AmbientLightSensorCore.wcBindable.commands,\n };\n\n private _core: AmbientLightSensorCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new AmbientLightSensorCore(this);\n }\n\n // --- Attribute accessors ---\n\n /**\n * Sampling frequency in Hz. Reads back `null` when unset, blank, or when the\n * attribute does not parse to a positive finite number (NaN, `\"0\"`, negative)\n * — in every such \"no usable value\" case the platform default applies.\n *\n * Note the deliberate set/get asymmetry: `set frequency(0)` (or any\n * non-positive/non-finite value) still writes the attribute verbatim for\n * transparency/inspectability, but the getter normalizes it back to `null`.\n * A round-trip through a non-positive value therefore does NOT preserve it —\n * that value carries no valid sampling meaning, so it is treated as \"unset\"\n * on read. Only positive finite frequencies survive a set→get round-trip.\n */\n get frequency(): number | null {\n const attr = this.getAttribute(\"frequency\");\n if (attr === null || attr.trim() === \"\") return null;\n const parsed = Number(attr);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n }\n\n set frequency(value: number | null | undefined) {\n if (value === null || value === undefined) {\n this.removeAttribute(\"frequency\");\n } else {\n this.setAttribute(\"frequency\", String(value));\n }\n }\n\n // --- Core delegated getters ---\n\n get illuminance(): number | null {\n return this._core.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this.frequency ?? undefined);\n }\n\n stop(): void {\n this._core.stop();\n }\n\n // --- Lifecycle ---\n\n // Deliberately does NOT auto-start the sensor on connect. Unlike\n // Geolocation (whose default phase acquires a fix immediately unless\n // `manual` is set), AmbientLightSensor has no such \"connect implies observing\"\n // precedent in the design doc (docs/sensor-tag-design.md §1.3):\n // start/stop are the only commands, so connecting the element merely makes\n // it inert until a command-token `start` (or the `start()` method) is\n // invoked. This also keeps behavior predictable when composed with\n // `<wcs-permission name=\"ambient-light-sensor\">`: the caller decides when to start,\n // typically gated on `granted`.\n connectedCallback(): void {\n this.style.display = \"none\";\n // No asynchronous probe to await (§3.8); kept for SSR uniformity with\n // other IO nodes.\n this._connectedCallbackPromise = this._core.ready;\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsAmbientLightSensor } from \"./components/AmbientLightSensor.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.ambientLightSensor)) {\n customElements.define(config.tagNames.ambientLightSensor, WcsAmbientLightSensor);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapAmbientLightSensor(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/AmbientLightSensorCore.ts","../src/components/AmbientLightSensor.ts","../src/registerComponents.ts","../src/bootstrapAmbientLightSensor.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n ambientLightSensor: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n ambientLightSensor: \"wcs-ambient-light-sensor\",\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\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, WcsAmbientLightSensorReading, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsAmbientLightSensorReading = Object.freeze({ illuminance: null });\n\n/**\n * Headless AmbientLightSensor primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `AmbientLightSensor` class exposed through the\n * wc-bindable protocol.\n *\n * The platform `Sensor` base class (shared by `Accelerometer` / `Gyroscope` /\n * `Magnetometer` / `AmbientLightSensor`) reports failure through an `'error'`\n * event rather than a rejected promise, so this Core can satisfy never-throw\n * (docs/async-io-node-guidelines.md §3.6) by simply forwarding that event —\n * see docs/sensor-tag-design.md §0. The one place a synchronous\n * exception *can* still escape the platform API is the `AmbientLightSensor`\n * constructor itself (e.g. `SecurityError` on permission denial or a\n * feature-policy block); `_createSensor()` wraps that single call in\n * try/catch, mirroring FetchCore's `_doFetch` try/catch around\n * `globalThis.fetch` (packages/fetch/src/core/FetchCore.ts).\n *\n * `illuminance` is a single getter derived from the `wcs-ambient-light-sensor:reading`\n * event (unlike Accelerometer/Gyroscope/Magnetometer's x/y/z, this sensor\n * reports one scalar — docs/sensor-tag-design.md §2). `reading`\n * is an event-like signal (a fresh sample every time, not a settled state) and\n * is therefore deliberately NOT same-value guarded — every sample dispatches.\n * `error` is state-like (denial / unsupported does not change from tick to\n * tick) and IS same-value guarded, and is published on its own\n * `wcs-ambient-light-sensor:error` event, independent of `reading`.\n *\n * No `_gen` generation guard: start()/stop() are a synchronous\n * subscribe/unsubscribe toggle with no asynchronous probe whose stale\n * resolution could race a dispose() — see docs/sensor-tag-design.md §1.5\n * (the same reasoning as NetworkCore, docs/network-tag-design.md §5).\n *\n * Permissions: this Core does not query `navigator.permissions` itself.\n * Compose with `<wcs-permission name=\"ambient-light-sensor\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class AmbientLightSensorCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"illuminance\", event: \"wcs-ambient-light-sensor:reading\", getter: (e: Event) => (e as CustomEvent).detail.illuminance },\n { name: \"error\", event: \"wcs-ambient-light-sensor:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsAmbientLightSensorReading = NULL_READING;\n private _error: WcsAmbientLightSensorErrorDetail | null = null;\n\n // The live sensor instance while started (null otherwise), kept so stop()\n // can remove its listeners precisely and so start() can detect \"already\n // started\" without a separate boolean (docs/async-io-node-guidelines.md §3.5\n // idempotency).\n private _sensor: (EventTarget & { start(): void; stop(): void }) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get illuminance(): number | null {\n return this._reading.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._error;\n }\n\n /** No asynchronous probe to await: start()/stop() are synchronous\n * (docs/async-io-node-guidelines.md §3.8 is satisfied trivially, mirroring\n * NetworkCore). */\n get ready(): Promise<void> {\n return Promise.resolve();\n }\n\n // --- State setters ---\n\n // Deliberately NOT same-value guarded: a `reading` is a fresh sample, not a\n // settled state, so it must dispatch every time even when the values happen\n // to repeat (docs/sensor-tag-design.md §1.1).\n private _setReading(reading: WcsAmbientLightSensorReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsAmbientLightSensorErrorDetail | null): void {\n // Same-value guard (by error name + message): error is state-like, unlike\n // reading — a repeated identical error (same name and message) must not\n // redispatch. Note `error` is also STICKY: nothing calls _setError(null),\n // so a successful (re)start does not clear a prior failure — the monitoring\n // sensor family deliberately keeps the last observed error (docs/sensor-tag-design.md §1.5).\n if (this._error?.error === error?.error && this._error?.message === error?.message) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Start the sensor at the given `frequency` (Hz), or the platform default\n * when omitted. Idempotent while already started: a redundant start() does\n * not construct a second sensor instance (which would leak the first).\n * Restart with a different frequency via stop() + start().\n *\n * Synchronous, mirroring the native `Sensor.start()` — never throws\n * (docs/async-io-node-guidelines.md §3.6): both \"unsupported\" and a\n * synchronous constructor exception (permission denial, feature-policy\n * block) are converted to the `error` property instead of propagating.\n */\n start(frequency?: number): void {\n if (this._sensor) return;\n const sensor = this._createSensor(frequency);\n if (!sensor) return;\n sensor.addEventListener(\"reading\", this._onReading as EventListener);\n sensor.addEventListener(\"error\", this._onError as EventListener);\n this._sensor = sensor;\n try {\n sensor.start();\n } catch (e: any) {\n // Defensive: the platform contract says start()/stop() do not throw\n // (failures surface via the 'error' event), but never-throw is a hard\n // requirement here, so guard against a non-conformant implementation\n // too.\n this._teardownSensor();\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n }\n }\n\n /** Stop the sensor and detach its listeners. Safe to call when not started. */\n stop(): void {\n if (!this._sensor) return;\n try {\n this._sensor.stop();\n } catch {\n // Never-throw defensive guard, symmetric with start(). Teardown below\n // still runs so listeners are detached regardless.\n }\n this._teardownSensor();\n }\n\n /** Lifecycle alias for start(), so the Shell's connectedCallback can drive\n * this Core the same way as other IO nodes' observe()/dispose() pair. No\n * asynchronous probe, so the returned promise always resolves immediately. */\n observe(frequency?: number): Promise<void> {\n this.start(frequency);\n return this.ready;\n }\n\n /** Lifecycle alias for stop(), invoked from the Shell's disconnectedCallback. */\n dispose(): void {\n this.stop();\n }\n\n // --- Internal ---\n\n // Both call sites (start()'s catch, stop()) only ever invoke this once\n // `this._sensor` is already known non-null, so there is no null-guard here\n // (nothing to defend against).\n private _teardownSensor(): void {\n this._sensor!.removeEventListener(\"reading\", this._onReading as EventListener);\n this._sensor!.removeEventListener(\"error\", this._onError as EventListener);\n this._sensor = null;\n }\n\n /**\n * Construct the platform `AmbientLightSensor`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new AmbientLightSensor(...)`\n * anywhere else in this class — see docs/sensor-tag-design.md §1.5.\n *\n * API resolution is call-time (docs/async-io-node-guidelines.md §3.7):\n * re-checked on every start(), never cached, so tests can install/remove the\n * global freely and an unsupported environment is always reported correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).AmbientLightSensor;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"AmbientLightSensor is not supported\" });\n return null;\n }\n try {\n return new Ctor(frequency !== undefined ? { frequency } : undefined);\n } catch (e: any) {\n // SecurityError (permission denial, feature-policy block) or any other\n // synchronous construction failure. Mirrors the FetchCore._doFetch\n // try/catch structure (packages/fetch/src/core/FetchCore.ts) — a\n // synchronous constructor call here instead of an awaited fetch().\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n return null;\n }\n }\n\n private _onReading = (event: Event): void => {\n const sensor = event.target as unknown as { illuminance: number | null };\n this._setReading({ illuminance: sensor.illuminance });\n };\n\n private _onError = (event: Event): void => {\n const err = (event as any).error as { name?: string; message?: string } | undefined;\n // Fallback is a meaningful constant, NOT String(err): a SensorErrorEvent\n // without an `error` field would otherwise stringify `undefined` into the\n // literal message \"undefined\" (aligned across the sensor family).\n this._setError({ error: err?.name ?? \"error\", message: err?.message ?? \"Sensor error\" });\n };\n}\n","import { IWcBindable, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\nimport { AmbientLightSensorCore } from \"../core/AmbientLightSensorCore.js\";\n\n/**\n * `<wcs-ambient-light-sensor>` — declarative Generic Sensor API (`AmbientLightSensor`)\n * monitor + start/stop control.\n *\n * Unlike `<wcs-network>` / `<wcs-permission>` (pure monitors), this Shell is a\n * bidirectional node: `start`/`stop` commands (command-token: state → element)\n * alongside the `illuminance`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `AmbientLightSensor` constructor's `{ frequency }` option\n * (docs/sensor-tag-design.md §1.2). The getter normalizes it: a non-finite or\n * non-positive value (NaN, 0, negative) reads back as `null` — meaning \"no\n * frequency specified\" — so start() falls back to the platform default rather\n * than forwarding a value the sensor would reject. Any positive finite value is\n * passed through verbatim (no upper-bound clamping — an out-of-range-but-positive\n * rate is still left to the browser/sensor to reject via `error`).\n *\n * Permission handling is intentionally NOT implemented here. Compose with\n * `<wcs-permission name=\"ambient-light-sensor\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsAmbientLightSensor extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...AmbientLightSensorCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: AmbientLightSensorCore.wcBindable.commands,\n };\n\n private _core: AmbientLightSensorCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new AmbientLightSensorCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-ambient-light-sensor:error\": (d) => ({ error: d != null }),\n });\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 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 /**\n * Sampling frequency in Hz. Reads back `null` when unset, blank, or when the\n * attribute does not parse to a positive finite number (NaN, `\"0\"`, negative)\n * — in every such \"no usable value\" case the platform default applies.\n *\n * Note the deliberate set/get asymmetry: `set frequency(0)` (or any\n * non-positive/non-finite value) still writes the attribute verbatim for\n * transparency/inspectability, but the getter normalizes it back to `null`.\n * A round-trip through a non-positive value therefore does NOT preserve it —\n * that value carries no valid sampling meaning, so it is treated as \"unset\"\n * on read. Only positive finite frequencies survive a set→get round-trip.\n */\n get frequency(): number | null {\n const attr = this.getAttribute(\"frequency\");\n if (attr === null || attr.trim() === \"\") return null;\n const parsed = Number(attr);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n }\n\n set frequency(value: number | null | undefined) {\n if (value === null || value === undefined) {\n this.removeAttribute(\"frequency\");\n } else {\n this.setAttribute(\"frequency\", String(value));\n }\n }\n\n // --- Core delegated getters ---\n\n get illuminance(): number | null {\n return this._core.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this.frequency ?? undefined);\n }\n\n stop(): void {\n this._core.stop();\n }\n\n // --- Lifecycle ---\n\n // Deliberately does NOT auto-start the sensor on connect. Unlike\n // Geolocation (whose default phase acquires a fix immediately unless\n // `manual` is set), AmbientLightSensor has no such \"connect implies observing\"\n // precedent in the design doc (docs/sensor-tag-design.md §1.3):\n // start/stop are the only commands, so connecting the element merely makes\n // it inert until a command-token `start` (or the `start()` method) is\n // invoked. This also keeps behavior predictable when composed with\n // `<wcs-permission name=\"ambient-light-sensor\">`: the caller decides when to start,\n // typically gated on `granted`.\n connectedCallback(): void {\n this.style.display = \"none\";\n // No asynchronous probe to await (§3.8); kept for SSR uniformity with\n // other IO nodes.\n this._connectedCallbackPromise = this._core.ready;\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsAmbientLightSensor } from \"./components/AmbientLightSensor.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.ambientLightSensor)) {\n customElements.define(config.tagNames.ambientLightSensor, WcsAmbientLightSensor);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapAmbientLightSensor(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.esm.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e={tagNames:{ambientLightSensor:"wcs-ambient-light-sensor"}};function
|
|
1
|
+
const e={tagNames:{ambientLightSensor:"wcs-ambient-light-sensor"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const r of Object.keys(e))t(e[r]);return e}function r(e){if(null===e||"object"!=typeof e)return e;const t={};for(const s of Object.keys(e))t[s]=r(e[s]);return t}let s=null;const n=e;function i(){return s||(s=t(r(e))),s}const o=Object.freeze({illuminance:null});class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"illuminance",event:"wcs-ambient-light-sensor:reading",getter:e=>e.detail.illuminance},{name:"error",event:"wcs-ambient-light-sensor:error"}],commands:[{name:"start"},{name:"stop"}]};_target;_reading=o;_error=null;_sensor=null;constructor(e){super(),this._target=e??this}get illuminance(){return this._reading.illuminance}get error(){return this._error}get ready(){return Promise.resolve()}_setReading(e){this._reading=e,this._target.dispatchEvent(new CustomEvent("wcs-ambient-light-sensor:reading",{detail:e,bubbles:!0}))}_setError(e){this._error?.error===e?.error&&this._error?.message===e?.message||(this._error=e,this._target.dispatchEvent(new CustomEvent("wcs-ambient-light-sensor:error",{detail:e,bubbles:!0})))}start(e){if(this._sensor)return;const t=this._createSensor(e);if(t){t.addEventListener("reading",this._onReading),t.addEventListener("error",this._onError),this._sensor=t;try{t.start()}catch(e){this._teardownSensor(),this._setError({error:e?.name??"error",message:e?.message??String(e)})}}}stop(){if(this._sensor){try{this._sensor.stop()}catch{}this._teardownSensor()}}observe(e){return this.start(e),this.ready}dispose(){this.stop()}_teardownSensor(){this._sensor.removeEventListener("reading",this._onReading),this._sensor.removeEventListener("error",this._onError),this._sensor=null}_createSensor(e){const t=globalThis.AmbientLightSensor;if("function"!=typeof t)return this._setError({error:"unsupported",message:"AmbientLightSensor is not supported"}),null;try{return new t(void 0!==e?{frequency:e}:void 0)}catch(e){return this._setError({error:e?.name??"error",message:e?.message??String(e)}),null}}_onReading=e=>{const t=e.target;this._setReading({illuminance:t.illuminance})};_onError=e=>{const t=e.error;this._setError({error:t?.name??"error",message:t?.message??"Sensor error"})}}class c extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...a.wcBindable,inputs:[{name:"frequency"}],commands:a.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new a(this),this._internals=this._initInternals(),this._wireStates({"wcs-ambient-light-sensor:error":e=>({error:null!=e})})}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[r,s]of Object.entries(e))this.addEventListener(r,e=>{const r=this.hasAttribute("debug-states");for(const[n,i]of Object.entries(s(e.detail))){try{i?t.add(n):t.delete(n)}catch{}r&&this.toggleAttribute(`data-wcs-state-${n}`,i)}})}get frequency(){const e=this.getAttribute("frequency");if(null===e||""===e.trim())return null;const t=Number(e);return Number.isFinite(t)&&t>0?t:null}set frequency(e){null==e?this.removeAttribute("frequency"):this.setAttribute("frequency",String(e))}get illuminance(){return this._core.illuminance}get error(){return this._core.error}get connectedCallbackPromise(){return this._connectedCallbackPromise}start(){this._core.start(this.frequency??void 0)}stop(){this._core.stop()}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.ready}disconnectedCallback(){this._core.dispose()}}function l(t){var r;t&&((r=t).tagNames&&Object.assign(e.tagNames,r.tagNames),s=null),customElements.get(n.tagNames.ambientLightSensor)||customElements.define(n.tagNames.ambientLightSensor,c)}export{a as AmbientLightSensorCore,c as WcsAmbientLightSensor,l as bootstrapAmbientLightSensor,i 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/AmbientLightSensorCore.ts","../src/components/AmbientLightSensor.ts","../src/bootstrapAmbientLightSensor.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n ambientLightSensor: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n ambientLightSensor: \"wcs-ambient-light-sensor\",\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\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, WcsAmbientLightSensorReading, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsAmbientLightSensorReading = Object.freeze({ illuminance: null });\n\n/**\n * Headless AmbientLightSensor primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `AmbientLightSensor` class exposed through the\n * wc-bindable protocol.\n *\n * The platform `Sensor` base class (shared by `Accelerometer` / `Gyroscope` /\n * `Magnetometer` / `AmbientLightSensor`) reports failure through an `'error'`\n * event rather than a rejected promise, so this Core can satisfy never-throw\n * (docs/async-io-node-guidelines.md §3.6) by simply forwarding that event —\n * see docs/sensor-tag-design.md §0. The one place a synchronous\n * exception *can* still escape the platform API is the `AmbientLightSensor`\n * constructor itself (e.g. `SecurityError` on permission denial or a\n * feature-policy block); `_createSensor()` wraps that single call in\n * try/catch, mirroring FetchCore's `_doFetch` try/catch around\n * `globalThis.fetch` (packages/fetch/src/core/FetchCore.ts).\n *\n * `illuminance` is a single getter derived from the `wcs-ambient-light-sensor:reading`\n * event (unlike Accelerometer/Gyroscope/Magnetometer's x/y/z, this sensor\n * reports one scalar — docs/sensor-tag-design.md §2). `reading`\n * is an event-like signal (a fresh sample every time, not a settled state) and\n * is therefore deliberately NOT same-value guarded — every sample dispatches.\n * `error` is state-like (denial / unsupported does not change from tick to\n * tick) and IS same-value guarded, and is published on its own\n * `wcs-ambient-light-sensor:error` event, independent of `reading`.\n *\n * No `_gen` generation guard: start()/stop() are a synchronous\n * subscribe/unsubscribe toggle with no asynchronous probe whose stale\n * resolution could race a dispose() — see docs/sensor-tag-design.md §1.5\n * (the same reasoning as NetworkCore, docs/network-tag-design.md §5).\n *\n * Permissions: this Core does not query `navigator.permissions` itself.\n * Compose with `<wcs-permission name=\"ambient-light-sensor\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class AmbientLightSensorCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"illuminance\", event: \"wcs-ambient-light-sensor:reading\", getter: (e: Event) => (e as CustomEvent).detail.illuminance },\n { name: \"error\", event: \"wcs-ambient-light-sensor:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsAmbientLightSensorReading = NULL_READING;\n private _error: WcsAmbientLightSensorErrorDetail | null = null;\n\n // The live sensor instance while started (null otherwise), kept so stop()\n // can remove its listeners precisely and so start() can detect \"already\n // started\" without a separate boolean (docs/async-io-node-guidelines.md §3.5\n // idempotency).\n private _sensor: (EventTarget & { start(): void; stop(): void }) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get illuminance(): number | null {\n return this._reading.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._error;\n }\n\n /** No asynchronous probe to await: start()/stop() are synchronous\n * (docs/async-io-node-guidelines.md §3.8 is satisfied trivially, mirroring\n * NetworkCore). */\n get ready(): Promise<void> {\n return Promise.resolve();\n }\n\n // --- State setters ---\n\n // Deliberately NOT same-value guarded: a `reading` is a fresh sample, not a\n // settled state, so it must dispatch every time even when the values happen\n // to repeat (docs/sensor-tag-design.md §1.1).\n private _setReading(reading: WcsAmbientLightSensorReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsAmbientLightSensorErrorDetail | null): void {\n // Same-value guard (by error name + message): error is state-like, unlike\n // reading — a repeated identical error (same name and message) must not\n // redispatch. Note `error` is also STICKY: nothing calls _setError(null),\n // so a successful (re)start does not clear a prior failure — the monitoring\n // sensor family deliberately keeps the last observed error (docs/sensor-tag-design.md §1.5).\n if (this._error?.error === error?.error && this._error?.message === error?.message) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Start the sensor at the given `frequency` (Hz), or the platform default\n * when omitted. Idempotent while already started: a redundant start() does\n * not construct a second sensor instance (which would leak the first).\n * Restart with a different frequency via stop() + start().\n *\n * Synchronous, mirroring the native `Sensor.start()` — never throws\n * (docs/async-io-node-guidelines.md §3.6): both \"unsupported\" and a\n * synchronous constructor exception (permission denial, feature-policy\n * block) are converted to the `error` property instead of propagating.\n */\n start(frequency?: number): void {\n if (this._sensor) return;\n const sensor = this._createSensor(frequency);\n if (!sensor) return;\n sensor.addEventListener(\"reading\", this._onReading as EventListener);\n sensor.addEventListener(\"error\", this._onError as EventListener);\n this._sensor = sensor;\n try {\n sensor.start();\n } catch (e: any) {\n // Defensive: the platform contract says start()/stop() do not throw\n // (failures surface via the 'error' event), but never-throw is a hard\n // requirement here, so guard against a non-conformant implementation\n // too.\n this._teardownSensor();\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n }\n }\n\n /** Stop the sensor and detach its listeners. Safe to call when not started. */\n stop(): void {\n if (!this._sensor) return;\n try {\n this._sensor.stop();\n } catch {\n // Never-throw defensive guard, symmetric with start(). Teardown below\n // still runs so listeners are detached regardless.\n }\n this._teardownSensor();\n }\n\n /** Lifecycle alias for start(), so the Shell's connectedCallback can drive\n * this Core the same way as other IO nodes' observe()/dispose() pair. No\n * asynchronous probe, so the returned promise always resolves immediately. */\n observe(frequency?: number): Promise<void> {\n this.start(frequency);\n return this.ready;\n }\n\n /** Lifecycle alias for stop(), invoked from the Shell's disconnectedCallback. */\n dispose(): void {\n this.stop();\n }\n\n // --- Internal ---\n\n // Both call sites (start()'s catch, stop()) only ever invoke this once\n // `this._sensor` is already known non-null, so there is no null-guard here\n // (nothing to defend against).\n private _teardownSensor(): void {\n this._sensor!.removeEventListener(\"reading\", this._onReading as EventListener);\n this._sensor!.removeEventListener(\"error\", this._onError as EventListener);\n this._sensor = null;\n }\n\n /**\n * Construct the platform `AmbientLightSensor`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new AmbientLightSensor(...)`\n * anywhere else in this class — see docs/sensor-tag-design.md §1.5.\n *\n * API resolution is call-time (docs/async-io-node-guidelines.md §3.7):\n * re-checked on every start(), never cached, so tests can install/remove the\n * global freely and an unsupported environment is always reported correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).AmbientLightSensor;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"AmbientLightSensor is not supported\" });\n return null;\n }\n try {\n return new Ctor(frequency !== undefined ? { frequency } : undefined);\n } catch (e: any) {\n // SecurityError (permission denial, feature-policy block) or any other\n // synchronous construction failure. Mirrors the FetchCore._doFetch\n // try/catch structure (packages/fetch/src/core/FetchCore.ts) — a\n // synchronous constructor call here instead of an awaited fetch().\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n return null;\n }\n }\n\n private _onReading = (event: Event): void => {\n const sensor = event.target as unknown as { illuminance: number | null };\n this._setReading({ illuminance: sensor.illuminance });\n };\n\n private _onError = (event: Event): void => {\n const err = (event as any).error as { name?: string; message?: string } | undefined;\n // Fallback is a meaningful constant, NOT String(err): a SensorErrorEvent\n // without an `error` field would otherwise stringify `undefined` into the\n // literal message \"undefined\" (aligned across the sensor family).\n this._setError({ error: err?.name ?? \"error\", message: err?.message ?? \"Sensor error\" });\n };\n}\n","import { IWcBindable, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\nimport { AmbientLightSensorCore } from \"../core/AmbientLightSensorCore.js\";\n\n/**\n * `<wcs-ambient-light-sensor>` — declarative Generic Sensor API (`AmbientLightSensor`)\n * monitor + start/stop control.\n *\n * Unlike `<wcs-network>` / `<wcs-permission>` (pure monitors), this Shell is a\n * bidirectional node: `start`/`stop` commands (command-token: state → element)\n * alongside the `illuminance`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `AmbientLightSensor` constructor's `{ frequency }` option\n * (docs/sensor-tag-design.md §1.2). The getter normalizes it: a non-finite or\n * non-positive value (NaN, 0, negative) reads back as `null` — meaning \"no\n * frequency specified\" — so start() falls back to the platform default rather\n * than forwarding a value the sensor would reject. Any positive finite value is\n * passed through verbatim (no upper-bound clamping — an out-of-range-but-positive\n * rate is still left to the browser/sensor to reject via `error`).\n *\n * Permission handling is intentionally NOT implemented here. Compose with\n * `<wcs-permission name=\"ambient-light-sensor\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsAmbientLightSensor extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...AmbientLightSensorCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: AmbientLightSensorCore.wcBindable.commands,\n };\n\n private _core: AmbientLightSensorCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new AmbientLightSensorCore(this);\n }\n\n // --- Attribute accessors ---\n\n /**\n * Sampling frequency in Hz. Reads back `null` when unset, blank, or when the\n * attribute does not parse to a positive finite number (NaN, `\"0\"`, negative)\n * — in every such \"no usable value\" case the platform default applies.\n *\n * Note the deliberate set/get asymmetry: `set frequency(0)` (or any\n * non-positive/non-finite value) still writes the attribute verbatim for\n * transparency/inspectability, but the getter normalizes it back to `null`.\n * A round-trip through a non-positive value therefore does NOT preserve it —\n * that value carries no valid sampling meaning, so it is treated as \"unset\"\n * on read. Only positive finite frequencies survive a set→get round-trip.\n */\n get frequency(): number | null {\n const attr = this.getAttribute(\"frequency\");\n if (attr === null || attr.trim() === \"\") return null;\n const parsed = Number(attr);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n }\n\n set frequency(value: number | null | undefined) {\n if (value === null || value === undefined) {\n this.removeAttribute(\"frequency\");\n } else {\n this.setAttribute(\"frequency\", String(value));\n }\n }\n\n // --- Core delegated getters ---\n\n get illuminance(): number | null {\n return this._core.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this.frequency ?? undefined);\n }\n\n stop(): void {\n this._core.stop();\n }\n\n // --- Lifecycle ---\n\n // Deliberately does NOT auto-start the sensor on connect. Unlike\n // Geolocation (whose default phase acquires a fix immediately unless\n // `manual` is set), AmbientLightSensor has no such \"connect implies observing\"\n // precedent in the design doc (docs/sensor-tag-design.md §1.3):\n // start/stop are the only commands, so connecting the element merely makes\n // it inert until a command-token `start` (or the `start()` method) is\n // invoked. This also keeps behavior predictable when composed with\n // `<wcs-permission name=\"ambient-light-sensor\">`: the caller decides when to start,\n // typically gated on `granted`.\n connectedCallback(): void {\n this.style.display = \"none\";\n // No asynchronous probe to await (§3.8); kept for SSR uniformity with\n // other IO nodes.\n this._connectedCallbackPromise = this._core.ready;\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapAmbientLightSensor(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsAmbientLightSensor } from \"./components/AmbientLightSensor.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.ambientLightSensor)) {\n customElements.define(config.tagNames.ambientLightSensor, WcsAmbientLightSensor);\n }\n}\n"],"names":["_config","tagNames","ambientLightSensor","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","NULL_READING","illuminance","AmbientLightSensorCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","commands","_target","_reading","_error","_sensor","constructor","target","super","this","error","ready","Promise","resolve","_setReading","reading","dispatchEvent","CustomEvent","bubbles","_setError","message","start","frequency","sensor","_createSensor","addEventListener","_onReading","_onError","_teardownSensor","String","stop","observe","dispose","removeEventListener","Ctor","globalThis","AmbientLightSensor","undefined","err","WcsAmbientLightSensor","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","attr","getAttribute","trim","parsed","Number","isFinite","value","removeAttribute","setAttribute","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapAmbientLightSensor","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/AmbientLightSensorCore.ts","../src/components/AmbientLightSensor.ts","../src/bootstrapAmbientLightSensor.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n ambientLightSensor: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n ambientLightSensor: \"wcs-ambient-light-sensor\",\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\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, WcsAmbientLightSensorReading, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsAmbientLightSensorReading = Object.freeze({ illuminance: null });\n\n/**\n * Headless AmbientLightSensor primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `AmbientLightSensor` class exposed through the\n * wc-bindable protocol.\n *\n * The platform `Sensor` base class (shared by `Accelerometer` / `Gyroscope` /\n * `Magnetometer` / `AmbientLightSensor`) reports failure through an `'error'`\n * event rather than a rejected promise, so this Core can satisfy never-throw\n * (docs/async-io-node-guidelines.md §3.6) by simply forwarding that event —\n * see docs/sensor-tag-design.md §0. The one place a synchronous\n * exception *can* still escape the platform API is the `AmbientLightSensor`\n * constructor itself (e.g. `SecurityError` on permission denial or a\n * feature-policy block); `_createSensor()` wraps that single call in\n * try/catch, mirroring FetchCore's `_doFetch` try/catch around\n * `globalThis.fetch` (packages/fetch/src/core/FetchCore.ts).\n *\n * `illuminance` is a single getter derived from the `wcs-ambient-light-sensor:reading`\n * event (unlike Accelerometer/Gyroscope/Magnetometer's x/y/z, this sensor\n * reports one scalar — docs/sensor-tag-design.md §2). `reading`\n * is an event-like signal (a fresh sample every time, not a settled state) and\n * is therefore deliberately NOT same-value guarded — every sample dispatches.\n * `error` is state-like (denial / unsupported does not change from tick to\n * tick) and IS same-value guarded, and is published on its own\n * `wcs-ambient-light-sensor:error` event, independent of `reading`.\n *\n * No `_gen` generation guard: start()/stop() are a synchronous\n * subscribe/unsubscribe toggle with no asynchronous probe whose stale\n * resolution could race a dispose() — see docs/sensor-tag-design.md §1.5\n * (the same reasoning as NetworkCore, docs/network-tag-design.md §5).\n *\n * Permissions: this Core does not query `navigator.permissions` itself.\n * Compose with `<wcs-permission name=\"ambient-light-sensor\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class AmbientLightSensorCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"illuminance\", event: \"wcs-ambient-light-sensor:reading\", getter: (e: Event) => (e as CustomEvent).detail.illuminance },\n { name: \"error\", event: \"wcs-ambient-light-sensor:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsAmbientLightSensorReading = NULL_READING;\n private _error: WcsAmbientLightSensorErrorDetail | null = null;\n\n // The live sensor instance while started (null otherwise), kept so stop()\n // can remove its listeners precisely and so start() can detect \"already\n // started\" without a separate boolean (docs/async-io-node-guidelines.md §3.5\n // idempotency).\n private _sensor: (EventTarget & { start(): void; stop(): void }) | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get illuminance(): number | null {\n return this._reading.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._error;\n }\n\n /** No asynchronous probe to await: start()/stop() are synchronous\n * (docs/async-io-node-guidelines.md §3.8 is satisfied trivially, mirroring\n * NetworkCore). */\n get ready(): Promise<void> {\n return Promise.resolve();\n }\n\n // --- State setters ---\n\n // Deliberately NOT same-value guarded: a `reading` is a fresh sample, not a\n // settled state, so it must dispatch every time even when the values happen\n // to repeat (docs/sensor-tag-design.md §1.1).\n private _setReading(reading: WcsAmbientLightSensorReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsAmbientLightSensorErrorDetail | null): void {\n // Same-value guard (by error name + message): error is state-like, unlike\n // reading — a repeated identical error (same name and message) must not\n // redispatch. Note `error` is also STICKY: nothing calls _setError(null),\n // so a successful (re)start does not clear a prior failure — the monitoring\n // sensor family deliberately keeps the last observed error (docs/sensor-tag-design.md §1.5).\n if (this._error?.error === error?.error && this._error?.message === error?.message) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-ambient-light-sensor:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Start the sensor at the given `frequency` (Hz), or the platform default\n * when omitted. Idempotent while already started: a redundant start() does\n * not construct a second sensor instance (which would leak the first).\n * Restart with a different frequency via stop() + start().\n *\n * Synchronous, mirroring the native `Sensor.start()` — never throws\n * (docs/async-io-node-guidelines.md §3.6): both \"unsupported\" and a\n * synchronous constructor exception (permission denial, feature-policy\n * block) are converted to the `error` property instead of propagating.\n */\n start(frequency?: number): void {\n if (this._sensor) return;\n const sensor = this._createSensor(frequency);\n if (!sensor) return;\n sensor.addEventListener(\"reading\", this._onReading as EventListener);\n sensor.addEventListener(\"error\", this._onError as EventListener);\n this._sensor = sensor;\n try {\n sensor.start();\n } catch (e: any) {\n // Defensive: the platform contract says start()/stop() do not throw\n // (failures surface via the 'error' event), but never-throw is a hard\n // requirement here, so guard against a non-conformant implementation\n // too.\n this._teardownSensor();\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n }\n }\n\n /** Stop the sensor and detach its listeners. Safe to call when not started. */\n stop(): void {\n if (!this._sensor) return;\n try {\n this._sensor.stop();\n } catch {\n // Never-throw defensive guard, symmetric with start(). Teardown below\n // still runs so listeners are detached regardless.\n }\n this._teardownSensor();\n }\n\n /** Lifecycle alias for start(), so the Shell's connectedCallback can drive\n * this Core the same way as other IO nodes' observe()/dispose() pair. No\n * asynchronous probe, so the returned promise always resolves immediately. */\n observe(frequency?: number): Promise<void> {\n this.start(frequency);\n return this.ready;\n }\n\n /** Lifecycle alias for stop(), invoked from the Shell's disconnectedCallback. */\n dispose(): void {\n this.stop();\n }\n\n // --- Internal ---\n\n // Both call sites (start()'s catch, stop()) only ever invoke this once\n // `this._sensor` is already known non-null, so there is no null-guard here\n // (nothing to defend against).\n private _teardownSensor(): void {\n this._sensor!.removeEventListener(\"reading\", this._onReading as EventListener);\n this._sensor!.removeEventListener(\"error\", this._onError as EventListener);\n this._sensor = null;\n }\n\n /**\n * Construct the platform `AmbientLightSensor`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new AmbientLightSensor(...)`\n * anywhere else in this class — see docs/sensor-tag-design.md §1.5.\n *\n * API resolution is call-time (docs/async-io-node-guidelines.md §3.7):\n * re-checked on every start(), never cached, so tests can install/remove the\n * global freely and an unsupported environment is always reported correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).AmbientLightSensor;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"AmbientLightSensor is not supported\" });\n return null;\n }\n try {\n return new Ctor(frequency !== undefined ? { frequency } : undefined);\n } catch (e: any) {\n // SecurityError (permission denial, feature-policy block) or any other\n // synchronous construction failure. Mirrors the FetchCore._doFetch\n // try/catch structure (packages/fetch/src/core/FetchCore.ts) — a\n // synchronous constructor call here instead of an awaited fetch().\n this._setError({ error: e?.name ?? \"error\", message: e?.message ?? String(e) });\n return null;\n }\n }\n\n private _onReading = (event: Event): void => {\n const sensor = event.target as unknown as { illuminance: number | null };\n this._setReading({ illuminance: sensor.illuminance });\n };\n\n private _onError = (event: Event): void => {\n const err = (event as any).error as { name?: string; message?: string } | undefined;\n // Fallback is a meaningful constant, NOT String(err): a SensorErrorEvent\n // without an `error` field would otherwise stringify `undefined` into the\n // literal message \"undefined\" (aligned across the sensor family).\n this._setError({ error: err?.name ?? \"error\", message: err?.message ?? \"Sensor error\" });\n };\n}\n","import { IWcBindable, WcsAmbientLightSensorErrorDetail } from \"../types.js\";\nimport { AmbientLightSensorCore } from \"../core/AmbientLightSensorCore.js\";\n\n/**\n * `<wcs-ambient-light-sensor>` — declarative Generic Sensor API (`AmbientLightSensor`)\n * monitor + start/stop control.\n *\n * Unlike `<wcs-network>` / `<wcs-permission>` (pure monitors), this Shell is a\n * bidirectional node: `start`/`stop` commands (command-token: state → element)\n * alongside the `illuminance`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `AmbientLightSensor` constructor's `{ frequency }` option\n * (docs/sensor-tag-design.md §1.2). The getter normalizes it: a non-finite or\n * non-positive value (NaN, 0, negative) reads back as `null` — meaning \"no\n * frequency specified\" — so start() falls back to the platform default rather\n * than forwarding a value the sensor would reject. Any positive finite value is\n * passed through verbatim (no upper-bound clamping — an out-of-range-but-positive\n * rate is still left to the browser/sensor to reject via `error`).\n *\n * Permission handling is intentionally NOT implemented here. Compose with\n * `<wcs-permission name=\"ambient-light-sensor\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsAmbientLightSensor extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...AmbientLightSensorCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: AmbientLightSensorCore.wcBindable.commands,\n };\n\n private _core: AmbientLightSensorCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new AmbientLightSensorCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-ambient-light-sensor:error\": (d) => ({ error: d != null }),\n });\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 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 /**\n * Sampling frequency in Hz. Reads back `null` when unset, blank, or when the\n * attribute does not parse to a positive finite number (NaN, `\"0\"`, negative)\n * — in every such \"no usable value\" case the platform default applies.\n *\n * Note the deliberate set/get asymmetry: `set frequency(0)` (or any\n * non-positive/non-finite value) still writes the attribute verbatim for\n * transparency/inspectability, but the getter normalizes it back to `null`.\n * A round-trip through a non-positive value therefore does NOT preserve it —\n * that value carries no valid sampling meaning, so it is treated as \"unset\"\n * on read. Only positive finite frequencies survive a set→get round-trip.\n */\n get frequency(): number | null {\n const attr = this.getAttribute(\"frequency\");\n if (attr === null || attr.trim() === \"\") return null;\n const parsed = Number(attr);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n }\n\n set frequency(value: number | null | undefined) {\n if (value === null || value === undefined) {\n this.removeAttribute(\"frequency\");\n } else {\n this.setAttribute(\"frequency\", String(value));\n }\n }\n\n // --- Core delegated getters ---\n\n get illuminance(): number | null {\n return this._core.illuminance;\n }\n\n get error(): WcsAmbientLightSensorErrorDetail | null {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this.frequency ?? undefined);\n }\n\n stop(): void {\n this._core.stop();\n }\n\n // --- Lifecycle ---\n\n // Deliberately does NOT auto-start the sensor on connect. Unlike\n // Geolocation (whose default phase acquires a fix immediately unless\n // `manual` is set), AmbientLightSensor has no such \"connect implies observing\"\n // precedent in the design doc (docs/sensor-tag-design.md §1.3):\n // start/stop are the only commands, so connecting the element merely makes\n // it inert until a command-token `start` (or the `start()` method) is\n // invoked. This also keeps behavior predictable when composed with\n // `<wcs-permission name=\"ambient-light-sensor\">`: the caller decides when to start,\n // typically gated on `granted`.\n connectedCallback(): void {\n this.style.display = \"none\";\n // No asynchronous probe to await (§3.8); kept for SSR uniformity with\n // other IO nodes.\n this._connectedCallbackPromise = this._core.ready;\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapAmbientLightSensor(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsAmbientLightSensor } from \"./components/AmbientLightSensor.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.ambientLightSensor)) {\n customElements.define(config.tagNames.ambientLightSensor, WcsAmbientLightSensor);\n }\n}\n"],"names":["_config","tagNames","ambientLightSensor","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","NULL_READING","illuminance","AmbientLightSensorCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","commands","_target","_reading","_error","_sensor","constructor","target","super","this","error","ready","Promise","resolve","_setReading","reading","dispatchEvent","CustomEvent","bubbles","_setError","message","start","frequency","sensor","_createSensor","addEventListener","_onReading","_onError","_teardownSensor","String","stop","observe","dispose","removeEventListener","Ctor","globalThis","AmbientLightSensor","undefined","err","WcsAmbientLightSensor","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","_internals","_initInternals","_wireStates","d","debugStates","states","attachInternals","internals","add","delete","map","toStates","entries","debug","hasAttribute","on","toggleAttribute","attr","getAttribute","trim","parsed","Number","isFinite","value","removeAttribute","setAttribute","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapAmbientLightSensor","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/ambient-light-sensor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Declarative AmbientLightSensor component for Web Components. Framework-agnostic Generic Sensor API (AmbientLightSensor) monitor via wc-bindable-protocol.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.esm.js",
|