@wcstack/magnetometer 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
@@ -110,6 +110,53 @@ npm install @wcstack/magnetometer
110
110
  | `start` | いいえ | センサーを構築(never-throw: コンストラクタの同期例外はキャッチし`error`へ)し読み取りを開始する。開始済みの間は冪等(再度呼んでも何もしない)。 |
111
111
  | `stop` | いいえ | センサーを停止しリスナーを解除する。未開始でも安全に呼べる。 |
112
112
 
113
+ ## `:state()` による CSS スタイリング
114
+
115
+ `<wcs-magnetometer>` は 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-magnetometer:error` が非 `null` の detail で発火(`null` でクリア) |
123
+
124
+ ```css
125
+ wcs-magnetometer:state(error) ~ .fallback { display: block; }
126
+ ```
127
+
128
+ 属性やクラスと異なり `:state()` は要素の外部から書き込めないため、この出力ステートが
129
+ 入力と混同される心配がありません。
130
+
131
+ **対応ブラウザ**(新構文 `:state(x)`): Chrome/Edge 125+、Safari 17.4+、Firefox 126+。
132
+ 非対応の環境ではステートが一切 set されないだけです — `:state()` セレクタがマッチしなく
133
+ なりますが、`<wcs-magnetometer>` 自体は通常どおり動作し続けます(graceful degradation・never-throw)。
134
+
135
+ **SSR:** `:state()` は HTML にシリアライズできないため、サーバーレンダリングされた
136
+ マークアップの初期ペイントにはこのステートは乗りません(`@wcstack/server` は無改変)。
137
+ ハイドレーション前の見た目を制御したい場合は、代わりに `wcs-magnetometer:not(:defined)` と組み合わせてください。
138
+
139
+ ### デバッグ
140
+
141
+ カスタムステートは DevTools の Elements パネルには表示されず、`attachInternals()`
142
+ は同一要素に 2 回呼べないため、コンソールから直接覗く手段がありません。そのための
143
+ デバッグ専用の補助を 2 つ用意しています:
144
+
145
+ - `el.debugStates` — 現在 on になっているステート名の**スナップショット**配列
146
+ (例: `["error"]`)。`wc-bindable` の一部ではなく(バインド対象ではない)、
147
+ 形状も契約として保証されません — デバッグ用途にのみ使ってください。
148
+ - `debug-states` 属性(opt-in・既定 OFF)は、ステート変化を要素の
149
+ `data-wcs-state-error` 属性にミラーします。
150
+ Elements パネルを開いておけば、トグルのたびにハイライトされます:
151
+
152
+ ```html
153
+ <wcs-magnetometer debug-states></wcs-magnetometer>
154
+ ```
155
+
156
+ **CSS は `data-wcs-state-*` ではなく `:state()` に書いてください。** ミラーされた
157
+ 属性は、DevTools を開いた状態でステート変化を可視化するためだけのものであり、
158
+ スタイリング用の正式なフックではありません。
159
+
113
160
  ## 注意・制限
114
161
 
115
162
  - **`_gen`世代ガードは無し。** `start()`/`stop()`は同期的な購読/購読解除のトグルであり、`dispose()`とレースしうる非同期probeが存在しません(`docs/sensor-tag-design.md` §1.5)。
package/README.md CHANGED
@@ -113,6 +113,55 @@ Every bound state path must be declared up front — binding an undeclared path
113
113
  | `start` | no | Construct the sensor (never-throw: a synchronous constructor exception is caught and surfaced via `error`) and begin reading. Idempotent while already started (a redundant call is a no-op). |
114
114
  | `stop` | no | Stop the sensor and detach its listeners. Safe to call when not started. |
115
115
 
116
+ ## CSS styling with `:state()`
117
+
118
+ `<wcs-magnetometer>` reflects one boolean output state onto its
119
+ [`ElementInternals` `CustomStateSet`](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet),
120
+ so you can style it directly from CSS with the `:state()` pseudo-class — no
121
+ `data-wcs` binding or extra class toggling required.
122
+
123
+ | State | On when |
124
+ |-------|---------|
125
+ | `error` | `wcs-magnetometer:error` fires with a non-`null` detail (cleared on `null`) |
126
+
127
+ ```css
128
+ wcs-magnetometer:state(error) ~ .fallback { display: block; }
129
+ ```
130
+
131
+ Unlike attributes or classes, `:state()` cannot be written from outside the
132
+ element, so there is no risk of confusing this output state with an input.
133
+
134
+ **Browser support** (`:state(x)` syntax): Chrome/Edge 125+, Safari 17.4+,
135
+ Firefox 126+. In older browsers the states are simply never set — `:state()`
136
+ selectors never match, but `<wcs-magnetometer>` itself keeps working normally
137
+ (graceful degradation, never-throw).
138
+
139
+ **SSR**: `:state()` cannot be serialized into HTML, so server-rendered markup
140
+ never carries these states on first paint (`@wcstack/server` is unaffected).
141
+ If you need to style the pre-hydration gap, pair your rule with
142
+ `wcs-magnetometer:not(:defined)` instead.
143
+
144
+ ### Debugging
145
+
146
+ Custom states are invisible in DevTools' Elements panel and `attachInternals()`
147
+ cannot be called twice, so there is no console way to inspect them directly.
148
+ Two debug-only aids are provided for that:
149
+
150
+ - `el.debugStates` — a **snapshot** array of the currently-on state names
151
+ (e.g. `["error"]`). It is not part of `wc-bindable` (not a bind target)
152
+ and its shape is not a guaranteed contract — use it for debugging only.
153
+ - The `debug-states` attribute (opt-in, default off) mirrors state changes
154
+ onto a `data-wcs-state-error` attribute on the element, so the Elements
155
+ panel highlights it as it toggles:
156
+
157
+ ```html
158
+ <wcs-magnetometer debug-states></wcs-magnetometer>
159
+ ```
160
+
161
+ **Write your CSS against `:state()`, not `data-wcs-state-*`.** The mirrored
162
+ attribute exists purely to make state changes visible while debugging with
163
+ DevTools open; it is not a supported styling hook.
164
+
116
165
  ## Notes & limitations
117
166
 
118
167
  - **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
@@ -188,7 +188,11 @@ declare class WcsMagnetometer extends HTMLElement {
188
188
  static wcBindable: IWcBindable;
189
189
  private _core;
190
190
  private _connectedCallbackPromise;
191
+ private _internals;
191
192
  constructor();
193
+ get debugStates(): string[];
194
+ private _initInternals;
195
+ private _wireStates;
192
196
  /**
193
197
  * Sampling frequency in Hz. Reads back `null` when unset, blank, or when the
194
198
  * attribute does not parse to a positive finite number (NaN, `"0"`, negative)
package/dist/index.esm.js CHANGED
@@ -276,9 +276,62 @@ class WcsMagnetometer extends HTMLElement {
276
276
  };
277
277
  _core;
278
278
  _connectedCallbackPromise = Promise.resolve();
279
+ _internals = null;
279
280
  constructor() {
280
281
  super();
281
282
  this._core = new MagnetometerCore(this);
283
+ this._internals = this._initInternals();
284
+ this._wireStates({
285
+ "wcs-magnetometer:error": (d) => ({ error: d != null }),
286
+ });
287
+ }
288
+ // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of
289
+ // wc-bindable (not a bind target); see README "CSS styling with :state()".
290
+ // MUST NOT return the live CustomStateSet (that would let callers write
291
+ // states from outside, defeating the point of :state() being read-only).
292
+ get debugStates() {
293
+ return this._internals ? [...this._internals.states] : [];
294
+ }
295
+ _initInternals() {
296
+ // never-throw (docs/async-io-node-guidelines.md §3.6): attachInternals is
297
+ // absent in happy-dom / older environments, and pre-125 Chromium rejects
298
+ // non-dashed state names from states.add() (probed and discarded here).
299
+ // Either case silently disables reflection — the component still works,
300
+ // it just doesn't expose :state() selectors.
301
+ try {
302
+ if (typeof this.attachInternals !== "function")
303
+ return null;
304
+ const internals = this.attachInternals();
305
+ internals.states.add("wcs-probe");
306
+ internals.states.delete("wcs-probe");
307
+ return internals;
308
+ }
309
+ catch {
310
+ return null;
311
+ }
312
+ }
313
+ _wireStates(map) {
314
+ if (this._internals === null)
315
+ return;
316
+ const states = this._internals.states;
317
+ for (const [event, toStates] of Object.entries(map)) {
318
+ this.addEventListener(event, (e) => {
319
+ const debug = this.hasAttribute("debug-states");
320
+ for (const [name, on] of Object.entries(toStates(e.detail))) {
321
+ try {
322
+ if (on) {
323
+ states.add(name);
324
+ }
325
+ else {
326
+ states.delete(name);
327
+ }
328
+ }
329
+ catch { /* never-throw */ }
330
+ if (debug)
331
+ this.toggleAttribute(`data-wcs-state-${name}`, on);
332
+ }
333
+ });
334
+ }
282
335
  }
283
336
  // --- Attribute accessors ---
284
337
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/MagnetometerCore.ts","../src/components/Magnetometer.ts","../src/registerComponents.ts","../src/bootstrapMagnetometer.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n magnetometer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n magnetometer: \"wcs-magnetometer\",\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, WcsMagnetometerReading, WcsMagnetometerErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsMagnetometerReading = Object.freeze({ x: null, y: null, z: null });\n\n/**\n * Headless Magnetometer primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `Magnetometer` 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 `Magnetometer`\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 * `x`/`y`/`z` are three getters derived from the single `wcs-magnetometer:reading`\n * event (mirroring how NetworkCore exposes effectiveType/downlink/… from one\n * `wcs-network:change` event): the native `reading` event already reports all\n * three axes together, so they are not split into independent events. `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-magnetometer: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=\"magnetometer\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class MagnetometerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"x\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.x },\n { name: \"y\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.y },\n { name: \"z\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.z },\n { name: \"error\", event: \"wcs-magnetometer:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsMagnetometerReading = NULL_READING;\n private _error: WcsMagnetometerErrorDetail | 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\n // §3.5 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 x(): number | null {\n return this._reading.x;\n }\n\n get y(): number | null {\n return this._reading.y;\n }\n\n get z(): number | null {\n return this._reading.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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: WcsMagnetometerReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-magnetometer:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsMagnetometerErrorDetail | 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-magnetometer: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 `Magnetometer`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new Magnetometer(...)`\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\n * the global freely and an unsupported environment is always reported\n * correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).Magnetometer;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"Magnetometer 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 { x: number | null; y: number | null; z: number | null };\n this._setReading({ x: sensor.x, y: sensor.y, z: sensor.z });\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, WcsMagnetometerErrorDetail } from \"../types.js\";\nimport { MagnetometerCore } from \"../core/MagnetometerCore.js\";\n\n/**\n * `<wcs-magnetometer>` — declarative Generic Sensor API (`Magnetometer`)\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 `x`/`y`/`z`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `Magnetometer` 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=\"magnetometer\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsMagnetometer extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...MagnetometerCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: MagnetometerCore.wcBindable.commands,\n };\n\n private _core: MagnetometerCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new MagnetometerCore(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 * This value is read only at `start()` time. There is no\n * `attributeChangedCallback`, and `MagnetometerCore.start()` is idempotent\n * while already started (a redundant call is a no-op), so setting\n * `frequency` (attribute or property) on an already-running sensor has no\n * effect until the caller `stop()`s and `start()`s again (see the README's\n * \"Notes & limitations\").\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 x(): number | null {\n return this._core.x;\n }\n\n get y(): number | null {\n return this._core.y;\n }\n\n get z(): number | null {\n return this._core.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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), Magnetometer 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=\"magnetometer\">`: 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 (docs/async-io-node-guidelines.md §3.8);\n // kept for SSR uniformity with other IO nodes.\n this._connectedCallbackPromise = this._core.ready;\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsMagnetometer } from \"./components/Magnetometer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.magnetometer)) {\n customElements.define(config.tagNames.magnetometer, WcsMagnetometer);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapMagnetometer(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/MagnetometerCore.ts","../src/components/Magnetometer.ts","../src/registerComponents.ts","../src/bootstrapMagnetometer.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n magnetometer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n magnetometer: \"wcs-magnetometer\",\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, WcsMagnetometerReading, WcsMagnetometerErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsMagnetometerReading = Object.freeze({ x: null, y: null, z: null });\n\n/**\n * Headless Magnetometer primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `Magnetometer` 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 `Magnetometer`\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 * `x`/`y`/`z` are three getters derived from the single `wcs-magnetometer:reading`\n * event (mirroring how NetworkCore exposes effectiveType/downlink/… from one\n * `wcs-network:change` event): the native `reading` event already reports all\n * three axes together, so they are not split into independent events. `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-magnetometer: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=\"magnetometer\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class MagnetometerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"x\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.x },\n { name: \"y\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.y },\n { name: \"z\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.z },\n { name: \"error\", event: \"wcs-magnetometer:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsMagnetometerReading = NULL_READING;\n private _error: WcsMagnetometerErrorDetail | 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\n // §3.5 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 x(): number | null {\n return this._reading.x;\n }\n\n get y(): number | null {\n return this._reading.y;\n }\n\n get z(): number | null {\n return this._reading.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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: WcsMagnetometerReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-magnetometer:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsMagnetometerErrorDetail | 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-magnetometer: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 `Magnetometer`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new Magnetometer(...)`\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\n * the global freely and an unsupported environment is always reported\n * correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).Magnetometer;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"Magnetometer 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 { x: number | null; y: number | null; z: number | null };\n this._setReading({ x: sensor.x, y: sensor.y, z: sensor.z });\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, WcsMagnetometerErrorDetail } from \"../types.js\";\nimport { MagnetometerCore } from \"../core/MagnetometerCore.js\";\n\n/**\n * `<wcs-magnetometer>` — declarative Generic Sensor API (`Magnetometer`)\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 `x`/`y`/`z`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `Magnetometer` 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=\"magnetometer\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsMagnetometer extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...MagnetometerCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: MagnetometerCore.wcBindable.commands,\n };\n\n private _core: MagnetometerCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new MagnetometerCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-magnetometer: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 (docs/async-io-node-guidelines.md §3.6): attachInternals is\n // absent 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 * This value is read only at `start()` time. There is no\n * `attributeChangedCallback`, and `MagnetometerCore.start()` is idempotent\n * while already started (a redundant call is a no-op), so setting\n * `frequency` (attribute or property) on an already-running sensor has no\n * effect until the caller `stop()`s and `start()`s again (see the README's\n * \"Notes & limitations\").\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 x(): number | null {\n return this._core.x;\n }\n\n get y(): number | null {\n return this._core.y;\n }\n\n get z(): number | null {\n return this._core.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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), Magnetometer 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=\"magnetometer\">`: 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 (docs/async-io-node-guidelines.md §3.8);\n // kept for SSR uniformity with other IO nodes.\n this._connectedCallbackPromise = this._core.ready;\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsMagnetometer } from \"./components/Magnetometer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.magnetometer)) {\n customElements.define(config.tagNames.magnetometer, WcsMagnetometer);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapMagnetometer(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e={tagNames:{magnetometer:"wcs-magnetometer"}};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 o(){return s||(s=t(r(e))),s}const i=Object.freeze({x:null,y:null,z:null});class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"x",event:"wcs-magnetometer:reading",getter:e=>e.detail.x},{name:"y",event:"wcs-magnetometer:reading",getter:e=>e.detail.y},{name:"z",event:"wcs-magnetometer:reading",getter:e=>e.detail.z},{name:"error",event:"wcs-magnetometer:error"}],commands:[{name:"start"},{name:"stop"}]};_target;_reading=i;_error=null;_sensor=null;constructor(e){super(),this._target=e??this}get x(){return this._reading.x}get y(){return this._reading.y}get z(){return this._reading.z}get error(){return this._error}get ready(){return Promise.resolve()}_setReading(e){this._reading=e,this._target.dispatchEvent(new CustomEvent("wcs-magnetometer: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-magnetometer: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.Magnetometer;if("function"!=typeof t)return this._setError({error:"unsupported",message:"Magnetometer 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({x:t.x,y:t.y,z:t.z})};_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();constructor(){super(),this._core=new a(this)}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 x(){return this._core.x}get y(){return this._core.y}get z(){return this._core.z}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 m(t){var r;t&&((r=t).tagNames&&Object.assign(e.tagNames,r.tagNames),s=null),customElements.get(n.tagNames.magnetometer)||customElements.define(n.tagNames.magnetometer,c)}export{a as MagnetometerCore,c as WcsMagnetometer,m as bootstrapMagnetometer,o as getConfig};
1
+ const e={tagNames:{magnetometer:"wcs-magnetometer"}};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 o(){return s||(s=t(r(e))),s}const i=Object.freeze({x:null,y:null,z:null});class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"x",event:"wcs-magnetometer:reading",getter:e=>e.detail.x},{name:"y",event:"wcs-magnetometer:reading",getter:e=>e.detail.y},{name:"z",event:"wcs-magnetometer:reading",getter:e=>e.detail.z},{name:"error",event:"wcs-magnetometer:error"}],commands:[{name:"start"},{name:"stop"}]};_target;_reading=i;_error=null;_sensor=null;constructor(e){super(),this._target=e??this}get x(){return this._reading.x}get y(){return this._reading.y}get z(){return this._reading.z}get error(){return this._error}get ready(){return Promise.resolve()}_setReading(e){this._reading=e,this._target.dispatchEvent(new CustomEvent("wcs-magnetometer: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-magnetometer: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.Magnetometer;if("function"!=typeof t)return this._setError({error:"unsupported",message:"Magnetometer 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({x:t.x,y:t.y,z:t.z})};_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-magnetometer: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,o]of Object.entries(s(e.detail))){try{o?t.add(n):t.delete(n)}catch{}r&&this.toggleAttribute(`data-wcs-state-${n}`,o)}})}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 x(){return this._core.x}get y(){return this._core.y}get z(){return this._core.z}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.magnetometer)||customElements.define(n.tagNames.magnetometer,c)}export{a as MagnetometerCore,c as WcsMagnetometer,l as bootstrapMagnetometer,o 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/MagnetometerCore.ts","../src/components/Magnetometer.ts","../src/bootstrapMagnetometer.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n magnetometer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n magnetometer: \"wcs-magnetometer\",\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, WcsMagnetometerReading, WcsMagnetometerErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsMagnetometerReading = Object.freeze({ x: null, y: null, z: null });\n\n/**\n * Headless Magnetometer primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `Magnetometer` 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 `Magnetometer`\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 * `x`/`y`/`z` are three getters derived from the single `wcs-magnetometer:reading`\n * event (mirroring how NetworkCore exposes effectiveType/downlink/… from one\n * `wcs-network:change` event): the native `reading` event already reports all\n * three axes together, so they are not split into independent events. `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-magnetometer: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=\"magnetometer\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class MagnetometerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"x\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.x },\n { name: \"y\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.y },\n { name: \"z\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.z },\n { name: \"error\", event: \"wcs-magnetometer:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsMagnetometerReading = NULL_READING;\n private _error: WcsMagnetometerErrorDetail | 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\n // §3.5 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 x(): number | null {\n return this._reading.x;\n }\n\n get y(): number | null {\n return this._reading.y;\n }\n\n get z(): number | null {\n return this._reading.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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: WcsMagnetometerReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-magnetometer:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsMagnetometerErrorDetail | 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-magnetometer: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 `Magnetometer`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new Magnetometer(...)`\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\n * the global freely and an unsupported environment is always reported\n * correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).Magnetometer;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"Magnetometer 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 { x: number | null; y: number | null; z: number | null };\n this._setReading({ x: sensor.x, y: sensor.y, z: sensor.z });\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, WcsMagnetometerErrorDetail } from \"../types.js\";\nimport { MagnetometerCore } from \"../core/MagnetometerCore.js\";\n\n/**\n * `<wcs-magnetometer>` — declarative Generic Sensor API (`Magnetometer`)\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 `x`/`y`/`z`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `Magnetometer` 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=\"magnetometer\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsMagnetometer extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...MagnetometerCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: MagnetometerCore.wcBindable.commands,\n };\n\n private _core: MagnetometerCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new MagnetometerCore(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 * This value is read only at `start()` time. There is no\n * `attributeChangedCallback`, and `MagnetometerCore.start()` is idempotent\n * while already started (a redundant call is a no-op), so setting\n * `frequency` (attribute or property) on an already-running sensor has no\n * effect until the caller `stop()`s and `start()`s again (see the README's\n * \"Notes & limitations\").\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 x(): number | null {\n return this._core.x;\n }\n\n get y(): number | null {\n return this._core.y;\n }\n\n get z(): number | null {\n return this._core.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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), Magnetometer 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=\"magnetometer\">`: 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 (docs/async-io-node-guidelines.md §3.8);\n // kept for SSR uniformity with 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 bootstrapMagnetometer(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsMagnetometer } from \"./components/Magnetometer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.magnetometer)) {\n customElements.define(config.tagNames.magnetometer, WcsMagnetometer);\n }\n}\n"],"names":["_config","tagNames","magnetometer","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","NULL_READING","x","y","z","MagnetometerCore","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","Magnetometer","undefined","err","WcsMagnetometer","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","attr","getAttribute","trim","parsed","Number","isFinite","value","removeAttribute","setAttribute","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapMagnetometer","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/MagnetometerCore.ts","../src/components/Magnetometer.ts","../src/bootstrapMagnetometer.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n magnetometer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n magnetometer: \"wcs-magnetometer\",\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, WcsMagnetometerReading, WcsMagnetometerErrorDetail } from \"../types.js\";\n\nconst NULL_READING: WcsMagnetometerReading = Object.freeze({ x: null, y: null, z: null });\n\n/**\n * Headless Magnetometer primitive. A thin, framework-agnostic wrapper around\n * the Generic Sensor API's `Magnetometer` 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 `Magnetometer`\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 * `x`/`y`/`z` are three getters derived from the single `wcs-magnetometer:reading`\n * event (mirroring how NetworkCore exposes effectiveType/downlink/… from one\n * `wcs-network:change` event): the native `reading` event already reports all\n * three axes together, so they are not split into independent events. `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-magnetometer: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=\"magnetometer\">` instead — see\n * docs/sensor-tag-design.md §\"2番目の決定: Permissions APIとの合成\".\n */\nexport class MagnetometerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"x\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.x },\n { name: \"y\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.y },\n { name: \"z\", event: \"wcs-magnetometer:reading\", getter: (e: Event) => (e as CustomEvent).detail.z },\n { name: \"error\", event: \"wcs-magnetometer:error\" },\n ],\n commands: [{ name: \"start\" }, { name: \"stop\" }],\n };\n\n private _target: EventTarget;\n private _reading: WcsMagnetometerReading = NULL_READING;\n private _error: WcsMagnetometerErrorDetail | 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\n // §3.5 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 x(): number | null {\n return this._reading.x;\n }\n\n get y(): number | null {\n return this._reading.y;\n }\n\n get z(): number | null {\n return this._reading.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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: WcsMagnetometerReading): void {\n this._reading = reading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-magnetometer:reading\", {\n detail: reading,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsMagnetometerErrorDetail | 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-magnetometer: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 `Magnetometer`, guarding both non-support and a\n * synchronous constructor exception. Never calls the raw `new Magnetometer(...)`\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\n * the global freely and an unsupported environment is always reported\n * correctly.\n */\n private _createSensor(frequency?: number): (EventTarget & { start(): void; stop(): void }) | null {\n const Ctor = (globalThis as any).Magnetometer;\n if (typeof Ctor !== \"function\") {\n this._setError({ error: \"unsupported\", message: \"Magnetometer 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 { x: number | null; y: number | null; z: number | null };\n this._setReading({ x: sensor.x, y: sensor.y, z: sensor.z });\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, WcsMagnetometerErrorDetail } from \"../types.js\";\nimport { MagnetometerCore } from \"../core/MagnetometerCore.js\";\n\n/**\n * `<wcs-magnetometer>` — declarative Generic Sensor API (`Magnetometer`)\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 `x`/`y`/`z`/`error` observable surface (event-token: element →\n * state). The `frequency` attribute is the sole configuration input, forwarded\n * to the platform `Magnetometer` 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=\"magnetometer\">` instead (see the README's permission\n * example, \"Gate on permission, then start\", and docs/sensor-tag-design.md).\n */\nexport class WcsMagnetometer extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...MagnetometerCore.wcBindable,\n inputs: [{ name: \"frequency\" }],\n // Core の commands をそのまま継承(単一情報源)。\n commands: MagnetometerCore.wcBindable.commands,\n };\n\n private _core: MagnetometerCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new MagnetometerCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-magnetometer: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 (docs/async-io-node-guidelines.md §3.6): attachInternals is\n // absent 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 * This value is read only at `start()` time. There is no\n * `attributeChangedCallback`, and `MagnetometerCore.start()` is idempotent\n * while already started (a redundant call is a no-op), so setting\n * `frequency` (attribute or property) on an already-running sensor has no\n * effect until the caller `stop()`s and `start()`s again (see the README's\n * \"Notes & limitations\").\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 x(): number | null {\n return this._core.x;\n }\n\n get y(): number | null {\n return this._core.y;\n }\n\n get z(): number | null {\n return this._core.z;\n }\n\n get error(): WcsMagnetometerErrorDetail | 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), Magnetometer 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=\"magnetometer\">`: 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 (docs/async-io-node-guidelines.md §3.8);\n // kept for SSR uniformity with 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 bootstrapMagnetometer(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsMagnetometer } from \"./components/Magnetometer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.magnetometer)) {\n customElements.define(config.tagNames.magnetometer, WcsMagnetometer);\n }\n}\n"],"names":["_config","tagNames","magnetometer","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","NULL_READING","x","y","z","MagnetometerCore","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","Magnetometer","undefined","err","WcsMagnetometer","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","bootstrapMagnetometer","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/magnetometer",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "Declarative Magnetometer component for Web Components. Framework-agnostic Generic Sensor API (Magnetometer) monitor via wc-bindable-protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",