@wcstack/network 1.16.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 ADDED
@@ -0,0 +1,167 @@
1
+ # @wcstack/network
2
+
3
+ `@wcstack/network` は wcstack エコシステム向けのヘッドレスな Network Information コンポーネントです。
4
+
5
+ 視覚的な UI ウィジェットではありません。
6
+ `@wcstack/permission` がパーミッション許可状態をリアクティブな state に変えるのと同じように、ブラウザの回線品質シグナルをリアクティブな state に変える **非同期プリミティブノード** です。
7
+
8
+ `@wcstack/state` と組み合わせると、`<wcs-network>` はパス契約で直接バインドできます:
9
+
10
+ - **入力サーフェス**: 無し — `navigator.connection` は設定すべきものを持たない単一のグローバル
11
+ - **出力 state サーフェス**: `effectiveType`、`downlink`、`rtt`、`saveData`、`supported`
12
+
13
+ これにより、適応的な読み込み制御(低速回線時の画像品質低下、データセーバー時の自動再生停止)を、UI 層で `navigator.connection` や `change` リスナーの配線を書かずに、HTML 上で宣言的に表現できます。
14
+
15
+ `@wcstack/network` は [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md)(Core / Shell / Binding Contract)アーキテクチャに従います:
16
+
17
+ - **Core**(`NetworkCore`)が `navigator.connection` を読み取り、live な `change` イベントを追従
18
+ - **Shell**(`<wcs-network>`)がその state を DOM ライフサイクルに接続
19
+ - **Binding Contract**(`static wcBindable`)が観測可能な `properties` を宣言(そして意図的に **コマンドも属性も持たない**)
20
+
21
+ ## なぜ存在するか — wcstack で最小の Shell、そして unsupported が常態
22
+
23
+ 他の wcstack IO ノードはいずれも何かしらを設定する(`target`、`name`、`url`)。`navigator.connection` は指し示す対象を持たない単一のグローバルオブジェクトなので、`<wcs-network>` は **属性を一切持ちません**。
24
+
25
+ さらに重要な点として、**Firefox と Safari は `navigator.connection` を実装していません。** 大半の IO ノードでは unsupported は稀なフォールバックですが、本ノードでは多くのユーザーにとって既定の現実です。「発火すれば使う、発火しなければ既定の挙動にフォールバックする」という漸進的強化を前提に UI を設計してください。このデータが必ず得られる前提で組んではいけません。
26
+
27
+ > **secure context 不要。** `@wcstack/geolocation` や `@wcstack/permission` と異なり、Network Information API に secure-context 制約はありません。
28
+
29
+ ## インストール
30
+
31
+ ```bash
32
+ npm install @wcstack/network
33
+ ```
34
+
35
+ ## クイックスタート
36
+
37
+ ### 1. 回線品質に応じた画像品質の切り替え
38
+
39
+ ```html
40
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
41
+ <script type="module" src="https://esm.run/@wcstack/network/auto"></script>
42
+
43
+ <wcs-state>
44
+ <script type="module">
45
+ export default {
46
+ effectiveType: null,
47
+ get lowQuality() {
48
+ return this.effectiveType === "2g" || this.effectiveType === "slow-2g";
49
+ },
50
+ get imgSrc() {
51
+ return this.lowQuality ? "/thumb.jpg" : "/full.jpg";
52
+ },
53
+ // 初回スナップショットはバインド確立前に発火するため、一度だけ pull する(「注意・制限」参照)。
54
+ async $connectedCallback() {
55
+ await customElements.whenDefined("wcs-network");
56
+ this.effectiveType = document.querySelector("wcs-network").effectiveType;
57
+ },
58
+ };
59
+ </script>
60
+ </wcs-state>
61
+
62
+ <wcs-network data-wcs="effectiveType: effectiveType"></wcs-network>
63
+
64
+ <img data-wcs="attr.src: imgSrc">
65
+ ```
66
+
67
+ このページの全例に共通するタイミング規則が1つあります: `<wcs-network>` はスナップショットを `wcs-network:change` イベントで公開しますが、*初回*のスナップショットは接続時に同期発火するため、`@wcstack/state` がバインドリスナーを張るより先に流れてしまい、バインドしたパスは*次*の回線変化からしか更新されません。上の `$connectedCallback` ブロックはその初回スナップショットを一度だけ pull しています。これが無いと、読込時点で既に低速回線だった場合にページが適応しません(「注意・制限」参照)。
68
+
69
+ ### 2. データセーバーを尊重する
70
+
71
+ ```html
72
+ <wcs-state>
73
+ <script type="module">
74
+ export default {
75
+ saveData: null,
76
+ // 例1と同じ初期 pull — 読込時点で既にデータセーバーONの場合があるため。
77
+ async $connectedCallback() {
78
+ await customElements.whenDefined("wcs-network");
79
+ this.saveData = document.querySelector("wcs-network").saveData;
80
+ },
81
+ };
82
+ </script>
83
+ </wcs-state>
84
+
85
+ <wcs-network data-wcs="saveData: saveData"></wcs-network>
86
+ <video data-wcs="autoplay: saveData|falsy" muted loop></video>
87
+ ```
88
+
89
+ `saveData` は `boolean | null`(未知・非対応の間は `null`)なので、boolean 限定の `|not` ではなく null 許容の `|falsy` フィルタを使います: データセーバーが有効と*判明*しない限り autoplay を維持する、という漸進的強化の既定動作になります。`autoplay` を `attr.autoplay` でなくプロパティとしてバインドしているのは、boolean content attribute は属性バインドでは無効化(属性削除)できないためです。
90
+
91
+ ### 3. 非対応環境では回線品質 UI を隠す
92
+
93
+ ```html
94
+ <wcs-state>
95
+ <script type="module">
96
+ export default {
97
+ netSupported: false,
98
+ effectiveType: null,
99
+ async $connectedCallback() {
100
+ await customElements.whenDefined("wcs-network");
101
+ const net = document.querySelector("wcs-network");
102
+ this.netSupported = net.supported;
103
+ this.effectiveType = net.effectiveType;
104
+ },
105
+ };
106
+ </script>
107
+ </wcs-state>
108
+
109
+ <wcs-network data-wcs="supported: netSupported; effectiveType: effectiveType"></wcs-network>
110
+ <div data-wcs="hidden: netSupported|not">回線: <span data-wcs="textContent: effectiveType"></span></div>
111
+ ```
112
+
113
+ バインドする state パスは必ず事前に宣言してください — 未宣言パスへのバインドは初期化時に例外になります。例2の `saveData` と異なり `supported` は厳密な boolean(`null` になり得ない)なので、ここでは `|not` が安全です。この例では初期 pull が*必須*です: `supported` は接続時に一度だけ確定し、安定した回線では以後 `change` が二度と発火しないため、pull が無いと対応ブラウザでも UI が永久に隠れたままになります。非対応ブラウザでは pull が `false` を読むだけなので、UI はそのまま隠れ続けます。
114
+
115
+ ## 観測可能プロパティ(出力)
116
+
117
+ | プロパティ | イベント | 説明 |
118
+ | ---------------- | -------------------- | ---- |
119
+ | `effectiveType` | `wcs-network:change` | `"slow-2g"` / `"2g"` / `"3g"` / `"4g"`、非対応環境では `null`。 |
120
+ | `downlink` | `wcs-network:change` | 概算ダウンリンク帯域(Mbps)、非対応環境では `null`。 |
121
+ | `rtt` | `wcs-network:change` | 概算往復遅延(ms)、非対応環境では `null`。 |
122
+ | `saveData` | `wcs-network:change` | ユーザーがデータセーバーモードを有効にしていれば `true`、非対応環境では `null`。 |
123
+ | `supported` | `wcs-network:change` | 実在する `navigator.connection` が見つかれば `true`、それ以外は `false`。 |
124
+
125
+ 5 つすべては単一の `wcs-network:change` イベントから派生します(ネイティブ API が全フィールドを1つの `change` イベントで報告するのと同じく、スナップショット全体を1つのイベントにまとめて発火します)。また、`supported` が `true` の環境でも、ブラウザが該当フィールドを欠落または期待外の型で報告した場合、4 つのデータフィールドは個別に `null` へ正規化されます。
126
+
127
+ `downlinkMax` と接続種別(wifi/cellular等)の `type` は意図的に公開していません — 理由は `docs/network-tag-design.md` §2 を参照(実用上の使用例が乏しい、`type` はfingerprinting対策によりブラウザ間で信頼性が低い)。
128
+
129
+ ## コマンド
130
+
131
+ **無し。** `navigator.connection` は read-only で、呼ぶべきアクションがありません。`<wcs-network>` は純粋なモニタです。
132
+
133
+ ## 属性 / 入力
134
+
135
+ **無し。** `navigator.connection` は単一のグローバルであり、インスタンスごとに設定すべきものがありません。
136
+
137
+ ## 注意・制限
138
+
139
+ - **Firefox と Safari は `navigator.connection` を実装していません。** これらのブラウザでは `supported` は `false` のまま、他の4プロパティは `null` のままです — これを例外ケースでなく常態として設計してください。
140
+ - **初回スナップショットはバインドに届きません。** 最初の `wcs-network:change` は `connectedCallback` 中に同期発火しますが、`@wcstack/state` のバインドリスナー確立はそれより後です(バインド構築は後続の microtask に遅延 — `docs/timing-and-firing-contract.md` §4.1 参照)。イベントは後から購読した相手に再送されないため、バインドしたパスは*次*の回線変化からしか更新されません。初期値が重要な場合(`supported` と `saveData` ではほぼ常に重要)は、クイックスタートの各例のように `$connectedCallback` で一度だけ pull してください。これは全 monitor ノード共通の wc-bindable イベント契約の性質であり、本パッケージ固有の癖ではありません。
141
+ - **`_gen` 世代ガードが無い。** 他の大半の wcstack IO ノードと異なり、`navigator.connection` の `change` イベント購読・購読解除は完全に同期的です。dispose() とレースしうる非同期probeの解決が存在しません。詳細は `docs/network-tag-design.md` §5。
142
+ - **再接続で再購読。** 要素を取り外して再挿入すると、切断時に `change` リスナーを解除し、再接続時に(その時点の `navigator.connection` に対して)再確立します。
143
+ - **SSR(`@wcstack/server`)。** `static hasConnectedCallbackPromise = true` を宣言し `connectedCallbackPromise` を公開しますが、`observe()` が同期的なため、この promise は常に即座に settle します。
144
+ - **同値ガード。** ブラウザが万一同じ値で `change` を重複発火しても、フィールド単位の防御的比較により冗長な dispatch を抑止します。
145
+
146
+ ## ヘッドレス利用(`NetworkCore`)
147
+
148
+ Core は DOM 非依存で、`@wc-bindable/core` の `bind()` と直接使えます:
149
+
150
+ ```typescript
151
+ import { NetworkCore } from "@wcstack/network";
152
+
153
+ const net = new NetworkCore();
154
+ net.addEventListener("wcs-network:change", (e) => {
155
+ console.log((e as CustomEvent).detail); // { effectiveType, downlink, rtt, saveData, supported }
156
+ });
157
+
158
+ net.observe(); // 同期的 — データ取得にpromiseを待つ必要は無い
159
+ console.log(net.effectiveType);
160
+
161
+ // 後始末:
162
+ net.dispose(); // live な `change` リスナーを外す
163
+ ```
164
+
165
+ ## ライセンス
166
+
167
+ MIT
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # @wcstack/network
2
+
3
+ `@wcstack/network` is a headless Network Information component for the wcstack ecosystem.
4
+
5
+ It is not a visual UI widget.
6
+ It is an **async primitive node** that turns the browser's connection-quality signal into reactive state — the same way `@wcstack/permission` turns a permission grant into reactive state.
7
+
8
+ With `@wcstack/state`, `<wcs-network>` can be bound directly through path contracts:
9
+
10
+ - **input surface**: none — `navigator.connection` is a single global with nothing to configure
11
+ - **output state surface**: `effectiveType`, `downlink`, `rtt`, `saveData`, `supported`
12
+
13
+ This means adaptive-loading UI — lower image quality on a slow connection, pausing autoplay when Data Saver is on — can be expressed declaratively in HTML, without writing `navigator.connection` or `change`-listener glue in your UI layer.
14
+
15
+ `@wcstack/network` follows the [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md) (Core / Shell / Binding Contract) architecture:
16
+
17
+ - **Core** (`NetworkCore`) reads `navigator.connection` and tracks its live `change` event
18
+ - **Shell** (`<wcs-network>`) connects that state to DOM lifecycle
19
+ - **Binding Contract** (`static wcBindable`) declares observable `properties` (and, deliberately, **no commands and no inputs**)
20
+
21
+ ## Why this exists — the smallest Shell in wcstack, and unsupported is the common case
22
+
23
+ Every other wcstack IO node configures *something* (a `target`, a `name`, a `url`). `navigator.connection` is a single global object with nothing to point at, so `<wcs-network>` takes **no attributes at all**.
24
+
25
+ More importantly: **Firefox and Safari do not implement `navigator.connection`.** Unlike most IO nodes, where "unsupported" is a rare fallback, here it is the default reality for a large share of users. Design your UI around graceful degradation — "if it fires, use it; if it never fires, fall back to sensible defaults" — not around the assumption that this data will be available.
26
+
27
+ > **No secure-context requirement.** Unlike `@wcstack/geolocation` or `@wcstack/permission`, the Network Information API has no secure-context restriction.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm install @wcstack/network
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ### 1. Adaptive image quality
38
+
39
+ ```html
40
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
41
+ <script type="module" src="https://esm.run/@wcstack/network/auto"></script>
42
+
43
+ <wcs-state>
44
+ <script type="module">
45
+ export default {
46
+ effectiveType: null,
47
+ get lowQuality() {
48
+ return this.effectiveType === "2g" || this.effectiveType === "slow-2g";
49
+ },
50
+ get imgSrc() {
51
+ return this.lowQuality ? "/thumb.jpg" : "/full.jpg";
52
+ },
53
+ // The initial snapshot fires before bindings attach — pull it once (see Notes).
54
+ async $connectedCallback() {
55
+ await customElements.whenDefined("wcs-network");
56
+ this.effectiveType = document.querySelector("wcs-network").effectiveType;
57
+ },
58
+ };
59
+ </script>
60
+ </wcs-state>
61
+
62
+ <wcs-network data-wcs="effectiveType: effectiveType"></wcs-network>
63
+
64
+ <img data-wcs="attr.src: imgSrc">
65
+ ```
66
+
67
+ One timing rule applies to every example on this page: `<wcs-network>` publishes its snapshot through `wcs-network:change` events, and the *first* snapshot fires synchronously at connect — before `@wcstack/state` has attached its binding listeners — so bound paths only start updating from the *next* connection change. The `$connectedCallback` block pulls that initial snapshot once; without it, this page would not adapt when the connection is already slow at load time (see Notes & limitations).
68
+
69
+ ### 2. Respect Data Saver
70
+
71
+ ```html
72
+ <wcs-state>
73
+ <script type="module">
74
+ export default {
75
+ saveData: null,
76
+ // Initial pull, as in example 1 — Data Saver may already be on at load time.
77
+ async $connectedCallback() {
78
+ await customElements.whenDefined("wcs-network");
79
+ this.saveData = document.querySelector("wcs-network").saveData;
80
+ },
81
+ };
82
+ </script>
83
+ </wcs-state>
84
+
85
+ <wcs-network data-wcs="saveData: saveData"></wcs-network>
86
+ <video data-wcs="autoplay: saveData|falsy" muted loop></video>
87
+ ```
88
+
89
+ `saveData` is `boolean | null` (`null` while unknown or unsupported), so the null-tolerant `|falsy` filter is used instead of the boolean-only `|not`: autoplay stays on unless Data Saver is *known* to be on — the graceful-degradation default. `autoplay` is bound as a property (not `attr.autoplay`) because a boolean content attribute cannot be switched off through an attribute binding.
90
+
91
+ ### 3. Hide connection-quality UI when unsupported
92
+
93
+ ```html
94
+ <wcs-state>
95
+ <script type="module">
96
+ export default {
97
+ netSupported: false,
98
+ effectiveType: null,
99
+ async $connectedCallback() {
100
+ await customElements.whenDefined("wcs-network");
101
+ const net = document.querySelector("wcs-network");
102
+ this.netSupported = net.supported;
103
+ this.effectiveType = net.effectiveType;
104
+ },
105
+ };
106
+ </script>
107
+ </wcs-state>
108
+
109
+ <wcs-network data-wcs="supported: netSupported; effectiveType: effectiveType"></wcs-network>
110
+ <div data-wcs="hidden: netSupported|not">Connection: <span data-wcs="textContent: effectiveType"></span></div>
111
+ ```
112
+
113
+ Every bound state path must be declared up front — binding an undeclared path throws at initialization. Unlike `saveData` above, `supported` is a strict boolean (never `null`), so `|not` is safe here. The initial pull is *essential* in this example: `supported` is set exactly once at connect, and a stable connection never fires another `change` — without the pull the UI would stay hidden forever even in supporting browsers. In an unsupported browser the pull reads `false`, so the UI simply stays hidden.
114
+
115
+ ## Observable Properties (outputs)
116
+
117
+ | Property | Event | Description |
118
+ | ---------------- | -------------------- | ------------ |
119
+ | `effectiveType` | `wcs-network:change` | `"slow-2g"` / `"2g"` / `"3g"` / `"4g"`, or `null` when unsupported. |
120
+ | `downlink` | `wcs-network:change` | Estimated downlink bandwidth in Mbps, or `null` when unsupported. |
121
+ | `rtt` | `wcs-network:change` | Estimated round-trip time in ms, or `null` when unsupported. |
122
+ | `saveData` | `wcs-network:change` | `true` when the user has enabled Data Saver mode, or `null` when unsupported. |
123
+ | `supported` | `wcs-network:change` | `true` once a real `navigator.connection` was found; `false` otherwise. |
124
+
125
+ All five derive from the single `wcs-network:change` event (a full snapshot dispatched together, mirroring how the native API reports all fields on one `change` event). The four data fields also normalize to `null` individually — even when `supported` is `true` — if the browser reports a field as missing or with an unexpected type.
126
+
127
+ `downlinkMax` and connection `type` (wifi/cellular/…) are intentionally not surfaced — see `docs/network-tag-design.md` §2 for the rationale (low real-world utility, and `type` is unreliable across browsers for fingerprinting-mitigation reasons).
128
+
129
+ ## Commands
130
+
131
+ **None.** `navigator.connection` is read-only — there is no action to invoke. `<wcs-network>` is a pure monitor.
132
+
133
+ ## Attributes / Inputs
134
+
135
+ **None.** `navigator.connection` is a single global; there is nothing per-instance to configure.
136
+
137
+ ## Notes & limitations
138
+
139
+ - **Firefox and Safari do not implement `navigator.connection`.** `supported` stays `false` and the other four properties stay `null` in those browsers — design around this as the common case, not an edge case.
140
+ - **The initial snapshot does not reach bindings.** The first `wcs-network:change` fires synchronously during `connectedCallback` — before `@wcstack/state` attaches its binding listeners (binding setup is deferred to a later microtask; see `docs/timing-and-firing-contract.md` §4.1) — and events are not replayed to late subscribers, so bound paths update only from the *next* connection change. If the initial value matters (it almost always does for `supported` and `saveData`), pull it once in `$connectedCallback` as the Quick Start examples do. This is a property of the wc-bindable event contract shared by all monitor nodes, not a quirk of this package.
141
+ - **No `_gen` generation guard.** Unlike most wcstack IO nodes, subscribing to `navigator.connection`'s `change` event is fully synchronous — there is no asynchronous probe whose stale resolution could race a `dispose()`. See `docs/network-tag-design.md` §5.
142
+ - **Reconnect re-subscribes.** Removing and re-inserting the element tears down the `change` listener on disconnect and re-establishes it (against whatever `navigator.connection` currently is) on reconnect.
143
+ - **SSR (`@wcstack/server`).** Declares `static hasConnectedCallbackPromise = true` and exposes `connectedCallbackPromise`, though since `observe()` is synchronous this promise always settles immediately.
144
+ - **Same-value guard.** A defensive field-by-field comparison suppresses a redundant `change` dispatch if the browser were ever to fire `change` with identical values.
145
+
146
+ ## Headless usage (`NetworkCore`)
147
+
148
+ The Core has no DOM dependency and can be used directly with `bind()` from `@wc-bindable/core`:
149
+
150
+ ```typescript
151
+ import { NetworkCore } from "@wcstack/network";
152
+
153
+ const net = new NetworkCore();
154
+ net.addEventListener("wcs-network:change", (e) => {
155
+ console.log((e as CustomEvent).detail); // { effectiveType, downlink, rtt, saveData, supported }
156
+ });
157
+
158
+ net.observe(); // synchronous — no promise to await for data
159
+ console.log(net.effectiveType);
160
+
161
+ // later, when done:
162
+ net.dispose(); // detach the live `change` listener
163
+ ```
164
+
165
+ ## License
166
+
167
+ MIT
package/dist/auto.js ADDED
@@ -0,0 +1,3 @@
1
+ import { bootstrapNetwork } from "./index.esm.js";
2
+
3
+ bootstrapNetwork();
@@ -0,0 +1 @@
1
+ import{bootstrapNetwork}from"./index.esm.min.js";bootstrapNetwork();
@@ -0,0 +1,132 @@
1
+ interface IWcBindableProperty {
2
+ readonly name: string;
3
+ readonly event: string;
4
+ readonly getter?: (event: Event) => any;
5
+ }
6
+ interface IWcBindableInput {
7
+ readonly name: string;
8
+ readonly attribute?: string;
9
+ }
10
+ interface IWcBindableCommand {
11
+ readonly name: string;
12
+ readonly async?: boolean;
13
+ }
14
+ interface IWcBindable {
15
+ readonly protocol: "wc-bindable";
16
+ readonly version: 1;
17
+ readonly properties: readonly IWcBindableProperty[];
18
+ readonly inputs?: readonly IWcBindableInput[];
19
+ readonly commands?: readonly IWcBindableCommand[];
20
+ }
21
+
22
+ interface ITagNames {
23
+ readonly network: string;
24
+ }
25
+ interface IWritableTagNames {
26
+ network?: string;
27
+ }
28
+ interface IConfig {
29
+ readonly tagNames: ITagNames;
30
+ }
31
+ interface IWritableConfig {
32
+ tagNames?: IWritableTagNames;
33
+ }
34
+
35
+ /**
36
+ * A single snapshot of `navigator.connection` (Network Information API), or the
37
+ * unsupported default. `effectiveType`/`downlink`/`rtt`/`saveData` are `null`
38
+ * when the API is absent (`supported === false`) — and each also normalizes to
39
+ * `null` individually, even while `supported` is `true`, when the browser
40
+ * reports that field as missing or with an unexpected type; `downlinkMax` and
41
+ * `type` are intentionally not surfaced (see docs/network-tag-design.md §2).
42
+ */
43
+ interface WcsNetworkSnapshot {
44
+ effectiveType: string | null;
45
+ downlink: number | null;
46
+ rtt: number | null;
47
+ saveData: boolean | null;
48
+ supported: boolean;
49
+ }
50
+ /**
51
+ * Value types for NetworkCore (headless) — the observable state properties.
52
+ * Use with `bind()` from a wc-bindable binding core for compile-time type checking.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const core = new NetworkCore();
57
+ * bind(core, (name: keyof WcsNetworkCoreValues, value) => { ... });
58
+ * ```
59
+ */
60
+ type WcsNetworkCoreValues = WcsNetworkSnapshot;
61
+ /**
62
+ * Value types for the Shell (`<wcs-network>`) — identical observable surface to
63
+ * the Core. The Shell adds no inputs and no commands: `navigator.connection` is a
64
+ * single global with nothing to configure and no request()-style action.
65
+ */
66
+ type WcsNetworkValues = WcsNetworkCoreValues;
67
+
68
+ declare function bootstrapNetwork(userConfig?: IWritableConfig): void;
69
+
70
+ declare function getConfig(): IConfig;
71
+
72
+ /**
73
+ * Headless Network Information primitive. A thin, framework-agnostic wrapper
74
+ * around `navigator.connection` exposed through the wc-bindable protocol.
75
+ *
76
+ * Unlike most wcstack IO nodes, this Core needs no `_gen` generation guard
77
+ * (§3.4): subscribing/unsubscribing to `navigator.connection`'s `change` event
78
+ * is fully synchronous, so there is no asynchronous probe whose stale
79
+ * resolution could race a dispose() (docs/network-tag-design.md §5).
80
+ *
81
+ * `navigator.connection` is unimplemented in Firefox/Safari — unsupported is
82
+ * the common case here, not an edge case (docs/network-tag-design.md §0). All
83
+ * four data fields collapse to `null` and `supported` to `false` in that case.
84
+ */
85
+ declare class NetworkCore extends EventTarget {
86
+ static wcBindable: IWcBindable;
87
+ private _target;
88
+ private _snapshot;
89
+ private _connection;
90
+ private _subscribed;
91
+ private _ready;
92
+ constructor(target?: EventTarget);
93
+ get ready(): Promise<void>;
94
+ get effectiveType(): string | null;
95
+ get downlink(): number | null;
96
+ get rtt(): number | null;
97
+ get saveData(): boolean | null;
98
+ get supported(): boolean;
99
+ observe(): Promise<void>;
100
+ dispose(): void;
101
+ private _api;
102
+ private _read;
103
+ private _onChange;
104
+ private _apply;
105
+ }
106
+
107
+ /**
108
+ * `<wcs-network>` — declarative Network Information API monitor.
109
+ *
110
+ * The smallest Shell in the batch (docs/network-tag-design.md §9): no
111
+ * attributes at all. `navigator.connection` is a single global with nothing to
112
+ * configure, unlike target-based nodes (`intersection`/`resize`) or
113
+ * descriptor-based ones (`permission`).
114
+ */
115
+ declare class WcsNetwork extends HTMLElement {
116
+ static hasConnectedCallbackPromise: boolean;
117
+ static wcBindable: IWcBindable;
118
+ private _core;
119
+ private _connectedCallbackPromise;
120
+ constructor();
121
+ get effectiveType(): string | null;
122
+ get downlink(): number | null;
123
+ get rtt(): number | null;
124
+ get saveData(): boolean | null;
125
+ get supported(): boolean;
126
+ get connectedCallbackPromise(): Promise<void>;
127
+ connectedCallback(): void;
128
+ disconnectedCallback(): void;
129
+ }
130
+
131
+ export { NetworkCore, WcsNetwork, bootstrapNetwork, getConfig };
132
+ export type { IWritableConfig, IWritableTagNames, WcsNetworkCoreValues, WcsNetworkSnapshot, WcsNetworkValues };
@@ -0,0 +1,245 @@
1
+ const _config = {
2
+ tagNames: {
3
+ network: "wcs-network",
4
+ },
5
+ };
6
+ function deepFreeze(obj) {
7
+ if (obj === null || typeof obj !== "object")
8
+ return obj;
9
+ Object.freeze(obj);
10
+ for (const key of Object.keys(obj)) {
11
+ deepFreeze(obj[key]);
12
+ }
13
+ return obj;
14
+ }
15
+ function deepClone(obj) {
16
+ if (obj === null || typeof obj !== "object")
17
+ return obj;
18
+ const clone = {};
19
+ for (const key of Object.keys(obj)) {
20
+ clone[key] = deepClone(obj[key]);
21
+ }
22
+ return clone;
23
+ }
24
+ let frozenConfig = null;
25
+ const config = _config;
26
+ function getConfig() {
27
+ if (!frozenConfig) {
28
+ frozenConfig = deepFreeze(deepClone(_config));
29
+ }
30
+ return frozenConfig;
31
+ }
32
+ function setConfig(partialConfig) {
33
+ if (partialConfig.tagNames) {
34
+ Object.assign(_config.tagNames, partialConfig.tagNames);
35
+ }
36
+ frozenConfig = null;
37
+ }
38
+
39
+ const UNSUPPORTED_SNAPSHOT = Object.freeze({
40
+ effectiveType: null,
41
+ downlink: null,
42
+ rtt: null,
43
+ saveData: null,
44
+ supported: false,
45
+ });
46
+ /**
47
+ * Headless Network Information primitive. A thin, framework-agnostic wrapper
48
+ * around `navigator.connection` exposed through the wc-bindable protocol.
49
+ *
50
+ * Unlike most wcstack IO nodes, this Core needs no `_gen` generation guard
51
+ * (§3.4): subscribing/unsubscribing to `navigator.connection`'s `change` event
52
+ * is fully synchronous, so there is no asynchronous probe whose stale
53
+ * resolution could race a dispose() (docs/network-tag-design.md §5).
54
+ *
55
+ * `navigator.connection` is unimplemented in Firefox/Safari — unsupported is
56
+ * the common case here, not an edge case (docs/network-tag-design.md §0). All
57
+ * four data fields collapse to `null` and `supported` to `false` in that case.
58
+ */
59
+ class NetworkCore extends EventTarget {
60
+ static wcBindable = {
61
+ protocol: "wc-bindable",
62
+ version: 1,
63
+ properties: [
64
+ { name: "effectiveType", event: "wcs-network:change", getter: (e) => e.detail.effectiveType },
65
+ { name: "downlink", event: "wcs-network:change", getter: (e) => e.detail.downlink },
66
+ { name: "rtt", event: "wcs-network:change", getter: (e) => e.detail.rtt },
67
+ { name: "saveData", event: "wcs-network:change", getter: (e) => e.detail.saveData },
68
+ { name: "supported", event: "wcs-network:change", getter: (e) => e.detail.supported },
69
+ ],
70
+ // Pure monitor: navigator.connection has no request()/action method to invoke.
71
+ commands: [],
72
+ };
73
+ _target;
74
+ _snapshot = UNSUPPORTED_SNAPSHOT;
75
+ // The live NetworkInformation object the `change` listener is attached to (kept
76
+ // so dispose() can remove it precisely; not read for anything else).
77
+ _connection = null;
78
+ // True once observe() has attached the live listener (or determined there is
79
+ // nothing to attach to). Guards observe() so a redundant call does not
80
+ // re-subscribe; dispose() resets it so a later observe() resumes cleanly.
81
+ _subscribed = false;
82
+ // SSR (§3.8): no asynchronous probe to await — observe() completes
83
+ // synchronously, so readiness is immediate.
84
+ _ready = Promise.resolve();
85
+ constructor(target) {
86
+ super();
87
+ this._target = target ?? this;
88
+ }
89
+ get ready() {
90
+ return this._ready;
91
+ }
92
+ get effectiveType() {
93
+ return this._snapshot.effectiveType;
94
+ }
95
+ get downlink() {
96
+ return this._snapshot.downlink;
97
+ }
98
+ get rtt() {
99
+ return this._snapshot.rtt;
100
+ }
101
+ get saveData() {
102
+ return this._snapshot.saveData;
103
+ }
104
+ get supported() {
105
+ return this._snapshot.supported;
106
+ }
107
+ // Lifecycle (§3.5). Idempotent: a second observe() while already subscribed
108
+ // is a no-op (no double listener, no redundant dispatch). Synchronous overall
109
+ // (no probe to await), so the returned promise is only for API uniformity
110
+ // with other IO nodes.
111
+ observe() {
112
+ if (!this._subscribed) {
113
+ this._subscribed = true;
114
+ const api = this._api();
115
+ if (api) {
116
+ this._connection = api;
117
+ api.addEventListener("change", this._onChange);
118
+ }
119
+ this._apply(this._read());
120
+ }
121
+ return this._ready;
122
+ }
123
+ dispose() {
124
+ this._subscribed = false;
125
+ if (this._connection) {
126
+ this._connection.removeEventListener("change", this._onChange);
127
+ this._connection = null;
128
+ }
129
+ }
130
+ // API resolution is call-time, never cached (§3.7): lets tests install/remove
131
+ // navigator.connection freely and lets an unsupported environment be detected
132
+ // correctly on every observe()/reading.
133
+ _api() {
134
+ const nav = globalThis.navigator;
135
+ return typeof nav !== "undefined" && nav.connection ? nav.connection : undefined;
136
+ }
137
+ _read() {
138
+ const c = this._api();
139
+ if (!c) {
140
+ return UNSUPPORTED_SNAPSHOT;
141
+ }
142
+ return {
143
+ effectiveType: typeof c.effectiveType === "string" ? c.effectiveType : null,
144
+ downlink: typeof c.downlink === "number" ? c.downlink : null,
145
+ rtt: typeof c.rtt === "number" ? c.rtt : null,
146
+ saveData: typeof c.saveData === "boolean" ? c.saveData : null,
147
+ supported: true,
148
+ };
149
+ }
150
+ _onChange = () => {
151
+ this._apply(this._read());
152
+ };
153
+ // Same-value guard (§3.3 MUST): the native `change` event already fires only
154
+ // on a real change, but this Core still verifies field-by-field before
155
+ // dispatching — defense in depth against a browser quirk double-firing
156
+ // `change` with identical values.
157
+ _apply(next) {
158
+ const prev = this._snapshot;
159
+ if (prev.effectiveType === next.effectiveType &&
160
+ prev.downlink === next.downlink &&
161
+ prev.rtt === next.rtt &&
162
+ prev.saveData === next.saveData &&
163
+ prev.supported === next.supported) {
164
+ return;
165
+ }
166
+ this._snapshot = next;
167
+ this._target.dispatchEvent(new CustomEvent("wcs-network:change", {
168
+ detail: next,
169
+ // Family-wide MUST (async-io-node-guidelines.md §3.3): the event bubbles
170
+ // from the Shell element so document-level consumers can delegate.
171
+ bubbles: true,
172
+ }));
173
+ }
174
+ }
175
+
176
+ /**
177
+ * `<wcs-network>` — declarative Network Information API monitor.
178
+ *
179
+ * The smallest Shell in the batch (docs/network-tag-design.md §9): no
180
+ * attributes at all. `navigator.connection` is a single global with nothing to
181
+ * configure, unlike target-based nodes (`intersection`/`resize`) or
182
+ * descriptor-based ones (`permission`).
183
+ */
184
+ class WcsNetwork extends HTMLElement {
185
+ // SSR (§4.4): observe() completes synchronously, but the Shell still exposes
186
+ // connectedCallbackPromise so SSR (@wcstack/server render.ts) can await it
187
+ // uniformly across all IO nodes before snapshotting the HTML. Mirrors
188
+ // WcsPermission.connectedCallbackPromise.
189
+ static hasConnectedCallbackPromise = true;
190
+ static wcBindable = {
191
+ ...NetworkCore.wcBindable,
192
+ inputs: [],
193
+ // Core の commands をそのまま継承(単一情報源)。permission と同型。
194
+ commands: NetworkCore.wcBindable.commands,
195
+ };
196
+ _core;
197
+ _connectedCallbackPromise = Promise.resolve();
198
+ constructor() {
199
+ super();
200
+ this._core = new NetworkCore(this);
201
+ }
202
+ // --- Core delegated getters ---
203
+ get effectiveType() {
204
+ return this._core.effectiveType;
205
+ }
206
+ get downlink() {
207
+ return this._core.downlink;
208
+ }
209
+ get rtt() {
210
+ return this._core.rtt;
211
+ }
212
+ get saveData() {
213
+ return this._core.saveData;
214
+ }
215
+ get supported() {
216
+ return this._core.supported;
217
+ }
218
+ get connectedCallbackPromise() {
219
+ return this._connectedCallbackPromise;
220
+ }
221
+ // --- Lifecycle ---
222
+ connectedCallback() {
223
+ this.style.display = "none";
224
+ this._connectedCallbackPromise = this._core.observe();
225
+ }
226
+ disconnectedCallback() {
227
+ this._core.dispose();
228
+ }
229
+ }
230
+
231
+ function registerComponents() {
232
+ if (!customElements.get(config.tagNames.network)) {
233
+ customElements.define(config.tagNames.network, WcsNetwork);
234
+ }
235
+ }
236
+
237
+ function bootstrapNetwork(userConfig) {
238
+ if (userConfig) {
239
+ setConfig(userConfig);
240
+ }
241
+ registerComponents();
242
+ }
243
+
244
+ export { NetworkCore, WcsNetwork, bootstrapNetwork, getConfig };
245
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/NetworkCore.ts","../src/components/Network.ts","../src/registerComponents.ts","../src/bootstrapNetwork.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n network: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n network: \"wcs-network\",\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, WcsNetworkSnapshot } from \"../types.js\";\n\nconst UNSUPPORTED_SNAPSHOT: WcsNetworkSnapshot = Object.freeze({\n effectiveType: null,\n downlink: null,\n rtt: null,\n saveData: null,\n supported: false,\n});\n\n/**\n * Headless Network Information primitive. A thin, framework-agnostic wrapper\n * around `navigator.connection` exposed through the wc-bindable protocol.\n *\n * Unlike most wcstack IO nodes, this Core needs no `_gen` generation guard\n * (§3.4): subscribing/unsubscribing to `navigator.connection`'s `change` event\n * is fully synchronous, so there is no asynchronous probe whose stale\n * resolution could race a dispose() (docs/network-tag-design.md §5).\n *\n * `navigator.connection` is unimplemented in Firefox/Safari — unsupported is\n * the common case here, not an edge case (docs/network-tag-design.md §0). All\n * four data fields collapse to `null` and `supported` to `false` in that case.\n */\nexport class NetworkCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"effectiveType\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.effectiveType },\n { name: \"downlink\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.downlink },\n { name: \"rtt\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.rtt },\n { name: \"saveData\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.saveData },\n { name: \"supported\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.supported },\n ],\n // Pure monitor: navigator.connection has no request()/action method to invoke.\n commands: [],\n };\n\n private _target: EventTarget;\n private _snapshot: WcsNetworkSnapshot = UNSUPPORTED_SNAPSHOT;\n\n // The live NetworkInformation object the `change` listener is attached to (kept\n // so dispose() can remove it precisely; not read for anything else).\n private _connection: EventTarget | null = null;\n\n // True once observe() has attached the live listener (or determined there is\n // nothing to attach to). Guards observe() so a redundant call does not\n // re-subscribe; dispose() resets it so a later observe() resumes cleanly.\n private _subscribed = false;\n\n // SSR (§3.8): no asynchronous probe to await — observe() completes\n // synchronously, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get effectiveType(): string | null {\n return this._snapshot.effectiveType;\n }\n\n get downlink(): number | null {\n return this._snapshot.downlink;\n }\n\n get rtt(): number | null {\n return this._snapshot.rtt;\n }\n\n get saveData(): boolean | null {\n return this._snapshot.saveData;\n }\n\n get supported(): boolean {\n return this._snapshot.supported;\n }\n\n // Lifecycle (§3.5). Idempotent: a second observe() while already subscribed\n // is a no-op (no double listener, no redundant dispatch). Synchronous overall\n // (no probe to await), so the returned promise is only for API uniformity\n // with other IO nodes.\n observe(): Promise<void> {\n if (!this._subscribed) {\n this._subscribed = true;\n const api = this._api();\n if (api) {\n this._connection = api;\n api.addEventListener(\"change\", this._onChange);\n }\n this._apply(this._read());\n }\n return this._ready;\n }\n\n dispose(): void {\n this._subscribed = false;\n if (this._connection) {\n this._connection.removeEventListener(\"change\", this._onChange);\n this._connection = null;\n }\n }\n\n // API resolution is call-time, never cached (§3.7): lets tests install/remove\n // navigator.connection freely and lets an unsupported environment be detected\n // correctly on every observe()/reading.\n private _api(): (EventTarget & { effectiveType?: string; downlink?: number; rtt?: number; saveData?: boolean }) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav !== \"undefined\" && nav.connection ? nav.connection : undefined;\n }\n\n private _read(): WcsNetworkSnapshot {\n const c = this._api();\n if (!c) {\n return UNSUPPORTED_SNAPSHOT;\n }\n return {\n effectiveType: typeof c.effectiveType === \"string\" ? c.effectiveType : null,\n downlink: typeof c.downlink === \"number\" ? c.downlink : null,\n rtt: typeof c.rtt === \"number\" ? c.rtt : null,\n saveData: typeof c.saveData === \"boolean\" ? c.saveData : null,\n supported: true,\n };\n }\n\n private _onChange = (): void => {\n this._apply(this._read());\n };\n\n // Same-value guard (§3.3 MUST): the native `change` event already fires only\n // on a real change, but this Core still verifies field-by-field before\n // dispatching — defense in depth against a browser quirk double-firing\n // `change` with identical values.\n private _apply(next: WcsNetworkSnapshot): void {\n const prev = this._snapshot;\n if (\n prev.effectiveType === next.effectiveType &&\n prev.downlink === next.downlink &&\n prev.rtt === next.rtt &&\n prev.saveData === next.saveData &&\n prev.supported === next.supported\n ) {\n return;\n }\n this._snapshot = next;\n this._target.dispatchEvent(new CustomEvent(\"wcs-network:change\", {\n detail: next,\n // Family-wide MUST (async-io-node-guidelines.md §3.3): the event bubbles\n // from the Shell element so document-level consumers can delegate.\n bubbles: true,\n }));\n }\n}\n","import { IWcBindable } from \"../types.js\";\nimport { NetworkCore } from \"../core/NetworkCore.js\";\n\n/**\n * `<wcs-network>` — declarative Network Information API monitor.\n *\n * The smallest Shell in the batch (docs/network-tag-design.md §9): no\n * attributes at all. `navigator.connection` is a single global with nothing to\n * configure, unlike target-based nodes (`intersection`/`resize`) or\n * descriptor-based ones (`permission`).\n */\nexport class WcsNetwork extends HTMLElement {\n // SSR (§4.4): observe() completes synchronously, but the Shell still exposes\n // connectedCallbackPromise so SSR (@wcstack/server render.ts) can await it\n // uniformly across all IO nodes before snapshotting the HTML. Mirrors\n // WcsPermission.connectedCallbackPromise.\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...NetworkCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。permission と同型。\n commands: NetworkCore.wcBindable.commands,\n };\n\n private _core: NetworkCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new NetworkCore(this);\n }\n\n // --- Core delegated getters ---\n\n get effectiveType(): string | null {\n return this._core.effectiveType;\n }\n\n get downlink(): number | null {\n return this._core.downlink;\n }\n\n get rtt(): number | null {\n return this._core.rtt;\n }\n\n get saveData(): boolean | null {\n return this._core.saveData;\n }\n\n get supported(): boolean {\n return this._core.supported;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsNetwork } from \"./components/Network.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.network)) {\n customElements.define(config.tagNames.network, WcsNetwork);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapNetwork(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e={tagNames:{network:"wcs-network"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const n of Object.keys(e))t(e[n]);return e}function n(e){if(null===e||"object"!=typeof e)return e;const t={};for(const s of Object.keys(e))t[s]=n(e[s]);return t}let s=null;const o=e;function r(){return s||(s=t(n(e))),s}const a=Object.freeze({effectiveType:null,downlink:null,rtt:null,saveData:null,supported:!1});class i extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"effectiveType",event:"wcs-network:change",getter:e=>e.detail.effectiveType},{name:"downlink",event:"wcs-network:change",getter:e=>e.detail.downlink},{name:"rtt",event:"wcs-network:change",getter:e=>e.detail.rtt},{name:"saveData",event:"wcs-network:change",getter:e=>e.detail.saveData},{name:"supported",event:"wcs-network:change",getter:e=>e.detail.supported}],commands:[]};_target;_snapshot=a;_connection=null;_subscribed=!1;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get ready(){return this._ready}get effectiveType(){return this._snapshot.effectiveType}get downlink(){return this._snapshot.downlink}get rtt(){return this._snapshot.rtt}get saveData(){return this._snapshot.saveData}get supported(){return this._snapshot.supported}observe(){if(!this._subscribed){this._subscribed=!0;const e=this._api();e&&(this._connection=e,e.addEventListener("change",this._onChange)),this._apply(this._read())}return this._ready}dispose(){this._subscribed=!1,this._connection&&(this._connection.removeEventListener("change",this._onChange),this._connection=null)}_api(){const e=globalThis.navigator;return void 0!==e&&e.connection?e.connection:void 0}_read(){const e=this._api();return e?{effectiveType:"string"==typeof e.effectiveType?e.effectiveType:null,downlink:"number"==typeof e.downlink?e.downlink:null,rtt:"number"==typeof e.rtt?e.rtt:null,saveData:"boolean"==typeof e.saveData?e.saveData:null,supported:!0}:a}_onChange=()=>{this._apply(this._read())};_apply(e){const t=this._snapshot;t.effectiveType===e.effectiveType&&t.downlink===e.downlink&&t.rtt===e.rtt&&t.saveData===e.saveData&&t.supported===e.supported||(this._snapshot=e,this._target.dispatchEvent(new CustomEvent("wcs-network:change",{detail:e,bubbles:!0})))}}class c extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...i.wcBindable,inputs:[],commands:i.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new i(this)}get effectiveType(){return this._core.effectiveType}get downlink(){return this._core.downlink}get rtt(){return this._core.rtt}get saveData(){return this._core.saveData}get supported(){return this._core.supported}get connectedCallbackPromise(){return this._connectedCallbackPromise}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}function l(t){var n;t&&((n=t).tagNames&&Object.assign(e.tagNames,n.tagNames),s=null),customElements.get(o.tagNames.network)||customElements.define(o.tagNames.network,c)}export{i as NetworkCore,c as WcsNetwork,l as bootstrapNetwork,r as getConfig};
2
+ //# sourceMappingURL=index.esm.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/NetworkCore.ts","../src/components/Network.ts","../src/bootstrapNetwork.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n network: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n network: \"wcs-network\",\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, WcsNetworkSnapshot } from \"../types.js\";\n\nconst UNSUPPORTED_SNAPSHOT: WcsNetworkSnapshot = Object.freeze({\n effectiveType: null,\n downlink: null,\n rtt: null,\n saveData: null,\n supported: false,\n});\n\n/**\n * Headless Network Information primitive. A thin, framework-agnostic wrapper\n * around `navigator.connection` exposed through the wc-bindable protocol.\n *\n * Unlike most wcstack IO nodes, this Core needs no `_gen` generation guard\n * (§3.4): subscribing/unsubscribing to `navigator.connection`'s `change` event\n * is fully synchronous, so there is no asynchronous probe whose stale\n * resolution could race a dispose() (docs/network-tag-design.md §5).\n *\n * `navigator.connection` is unimplemented in Firefox/Safari — unsupported is\n * the common case here, not an edge case (docs/network-tag-design.md §0). All\n * four data fields collapse to `null` and `supported` to `false` in that case.\n */\nexport class NetworkCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"effectiveType\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.effectiveType },\n { name: \"downlink\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.downlink },\n { name: \"rtt\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.rtt },\n { name: \"saveData\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.saveData },\n { name: \"supported\", event: \"wcs-network:change\", getter: (e: Event) => (e as CustomEvent).detail.supported },\n ],\n // Pure monitor: navigator.connection has no request()/action method to invoke.\n commands: [],\n };\n\n private _target: EventTarget;\n private _snapshot: WcsNetworkSnapshot = UNSUPPORTED_SNAPSHOT;\n\n // The live NetworkInformation object the `change` listener is attached to (kept\n // so dispose() can remove it precisely; not read for anything else).\n private _connection: EventTarget | null = null;\n\n // True once observe() has attached the live listener (or determined there is\n // nothing to attach to). Guards observe() so a redundant call does not\n // re-subscribe; dispose() resets it so a later observe() resumes cleanly.\n private _subscribed = false;\n\n // SSR (§3.8): no asynchronous probe to await — observe() completes\n // synchronously, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get effectiveType(): string | null {\n return this._snapshot.effectiveType;\n }\n\n get downlink(): number | null {\n return this._snapshot.downlink;\n }\n\n get rtt(): number | null {\n return this._snapshot.rtt;\n }\n\n get saveData(): boolean | null {\n return this._snapshot.saveData;\n }\n\n get supported(): boolean {\n return this._snapshot.supported;\n }\n\n // Lifecycle (§3.5). Idempotent: a second observe() while already subscribed\n // is a no-op (no double listener, no redundant dispatch). Synchronous overall\n // (no probe to await), so the returned promise is only for API uniformity\n // with other IO nodes.\n observe(): Promise<void> {\n if (!this._subscribed) {\n this._subscribed = true;\n const api = this._api();\n if (api) {\n this._connection = api;\n api.addEventListener(\"change\", this._onChange);\n }\n this._apply(this._read());\n }\n return this._ready;\n }\n\n dispose(): void {\n this._subscribed = false;\n if (this._connection) {\n this._connection.removeEventListener(\"change\", this._onChange);\n this._connection = null;\n }\n }\n\n // API resolution is call-time, never cached (§3.7): lets tests install/remove\n // navigator.connection freely and lets an unsupported environment be detected\n // correctly on every observe()/reading.\n private _api(): (EventTarget & { effectiveType?: string; downlink?: number; rtt?: number; saveData?: boolean }) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav !== \"undefined\" && nav.connection ? nav.connection : undefined;\n }\n\n private _read(): WcsNetworkSnapshot {\n const c = this._api();\n if (!c) {\n return UNSUPPORTED_SNAPSHOT;\n }\n return {\n effectiveType: typeof c.effectiveType === \"string\" ? c.effectiveType : null,\n downlink: typeof c.downlink === \"number\" ? c.downlink : null,\n rtt: typeof c.rtt === \"number\" ? c.rtt : null,\n saveData: typeof c.saveData === \"boolean\" ? c.saveData : null,\n supported: true,\n };\n }\n\n private _onChange = (): void => {\n this._apply(this._read());\n };\n\n // Same-value guard (§3.3 MUST): the native `change` event already fires only\n // on a real change, but this Core still verifies field-by-field before\n // dispatching — defense in depth against a browser quirk double-firing\n // `change` with identical values.\n private _apply(next: WcsNetworkSnapshot): void {\n const prev = this._snapshot;\n if (\n prev.effectiveType === next.effectiveType &&\n prev.downlink === next.downlink &&\n prev.rtt === next.rtt &&\n prev.saveData === next.saveData &&\n prev.supported === next.supported\n ) {\n return;\n }\n this._snapshot = next;\n this._target.dispatchEvent(new CustomEvent(\"wcs-network:change\", {\n detail: next,\n // Family-wide MUST (async-io-node-guidelines.md §3.3): the event bubbles\n // from the Shell element so document-level consumers can delegate.\n bubbles: true,\n }));\n }\n}\n","import { IWcBindable } from \"../types.js\";\nimport { NetworkCore } from \"../core/NetworkCore.js\";\n\n/**\n * `<wcs-network>` — declarative Network Information API monitor.\n *\n * The smallest Shell in the batch (docs/network-tag-design.md §9): no\n * attributes at all. `navigator.connection` is a single global with nothing to\n * configure, unlike target-based nodes (`intersection`/`resize`) or\n * descriptor-based ones (`permission`).\n */\nexport class WcsNetwork extends HTMLElement {\n // SSR (§4.4): observe() completes synchronously, but the Shell still exposes\n // connectedCallbackPromise so SSR (@wcstack/server render.ts) can await it\n // uniformly across all IO nodes before snapshotting the HTML. Mirrors\n // WcsPermission.connectedCallbackPromise.\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...NetworkCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。permission と同型。\n commands: NetworkCore.wcBindable.commands,\n };\n\n private _core: NetworkCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new NetworkCore(this);\n }\n\n // --- Core delegated getters ---\n\n get effectiveType(): string | null {\n return this._core.effectiveType;\n }\n\n get downlink(): number | null {\n return this._core.downlink;\n }\n\n get rtt(): number | null {\n return this._core.rtt;\n }\n\n get saveData(): boolean | null {\n return this._core.saveData;\n }\n\n get supported(): boolean {\n return this._core.supported;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\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 bootstrapNetwork(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsNetwork } from \"./components/Network.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.network)) {\n customElements.define(config.tagNames.network, WcsNetwork);\n }\n}\n"],"names":["_config","tagNames","network","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","UNSUPPORTED_SNAPSHOT","effectiveType","downlink","rtt","saveData","supported","NetworkCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","commands","_target","_snapshot","_connection","_subscribed","_ready","Promise","resolve","constructor","target","super","this","ready","observe","api","_api","addEventListener","_onChange","_apply","_read","dispose","removeEventListener","nav","globalThis","navigator","connection","undefined","c","next","prev","dispatchEvent","CustomEvent","bubbles","WcsNetwork","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapNetwork","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@wcstack/network",
3
+ "version": "1.16.0",
4
+ "description": "Declarative Network Information component for Web Components. Framework-agnostic navigator.connection monitor via wc-bindable-protocol.",
5
+ "type": "module",
6
+ "main": "./dist/index.esm.js",
7
+ "module": "./dist/index.esm.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.esm.js"
13
+ },
14
+ "./auto": "./dist/auto.min.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "clean": "rimraf dist .tsc-out",
21
+ "build": "rimraf dist .tsc-out && tsc && rollup -c",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
25
+ "lint": "eslint src",
26
+ "version:patch": "npm version patch",
27
+ "version:minor": "npm version minor",
28
+ "version:major": "npm version major",
29
+ "prepublishOnly": "npm run build && npm run test:coverage"
30
+ },
31
+ "keywords": [
32
+ "web-components",
33
+ "network-information",
34
+ "connection",
35
+ "adaptive-loading",
36
+ "custom-elements",
37
+ "wc-bindable",
38
+ "declarative",
39
+ "zero-dependencies",
40
+ "framework-agnostic"
41
+ ],
42
+ "author": "mogera551",
43
+ "homepage": "https://wcstack.github.io",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/wcstack/wcstack.git",
47
+ "directory": "packages/network"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/wcstack/wcstack/issues"
51
+ },
52
+ "license": "MIT",
53
+ "devDependencies": {
54
+ "@eslint/js": "^9.39.1",
55
+ "@rollup/plugin-terser": "^0.4.4",
56
+ "@rollup/plugin-typescript": "^11.1.6",
57
+ "@vitest/coverage-v8": "^4.0.15",
58
+ "@vitest/ui": "^4.0.15",
59
+ "eslint": "^9.39.1",
60
+ "globals": "^16.5.0",
61
+ "happy-dom": "^20.0.11",
62
+ "rimraf": "^6.0.1",
63
+ "rollup": "^4.22.4",
64
+ "rollup-plugin-dts": "^6.1.1",
65
+ "rollup-plugin-copy": "^3.5.0",
66
+ "tslib": "^2.8.1",
67
+ "typescript": "^5.9.3",
68
+ "typescript-eslint": "^8.49.0",
69
+ "vitest": "^4.0.15"
70
+ }
71
+ }