@wcstack/permission 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md ADDED
@@ -0,0 +1,143 @@
1
+ # @wcstack/permission
2
+
3
+ `@wcstack/permission` は wcstack エコシステム向けのヘッドレスなパーミッション状態コンポーネントです。
4
+
5
+ 視覚的な UI ウィジェットではありません。
6
+ `@wcstack/geolocation` が端末の位置をリアクティブな state に変えるのと同じように、ブラウザのパーミッション許可状態をリアクティブな state に変える **非同期プリミティブノード** です。
7
+
8
+ `@wcstack/state` と組み合わせると、`<wcs-permission>` はパス契約で直接バインドできます:
9
+
10
+ - **入力サーフェス**: `name`、`user-visible-only`、`sysex`
11
+ - **出力 state サーフェス**: `state`、`granted`、`denied`、`prompt`、`unsupported`
12
+
13
+ これにより、パーミッションに応じた UI(バナー・ゲート・機能ヒント)を、UI 層で `navigator.permissions.query()` や `change` リスナーの配線を書かずに、HTML 上で宣言的に表現できます。
14
+
15
+ `@wcstack/permission` は [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md)(Core / Shell / Binding Contract)アーキテクチャに従います:
16
+
17
+ - **Core**(`PermissionCore`)が query・4 値 state・live `change` 追従を担当
18
+ - **Shell**(`<wcs-permission>`)がその state を DOM 属性とライフサイクルに接続
19
+ - **Binding Contract**(`static wcBindable`)が観測可能な `properties` を宣言(そして意図的に **コマンドを持たない**)
20
+
21
+ ## なぜ存在するか — read-only でコマンドの無いノード
22
+
23
+ 他の wcstack IO ノード(`<wcs-geo>`、`<wcs-ws>`、`<wcs-clipboard>` …)はいずれも「何かを実行し」つつ state を報告します。Permissions API はそれらと違い **read-only** です。`query()` はあっても標準の `request()` がありません。この API から許可を求めることはできず、許可要求は機能そのものの呼び出し(`getCurrentPosition()`、`Notification.requestPermission()` …)の副作用として起こります。
24
+
25
+ したがって `<wcs-permission>` は純粋な **要素 → state** プロデューサです。*監視する* だけで、*求めない*。**コマンドを一切持たない初の wcstack ノード** であり、command-token は適用されず event-token のみが成立します。許可を取りに行くのは機能ノード(`<wcs-geo>` など)の責務で、本ノードは現在の許可状態を live なバインド可能 state として反映するだけです。
26
+
27
+ パーミッションの変化は `change` リスナーの購読ではなく **状態遷移** になります。
28
+
29
+ > **secure context 必須。** Permissions API は secure context(HTTPS、または `localhost`)でのみ動作します。API が存在しない場合や、要求した権限名をブラウザが拒否する場合(対応はブラウザ差が大きい: Firefox は `clipboard-read` 非対応、Safari は複数の名前を欠く)、`<wcs-permission>` は例外を投げず `state = "unsupported"` を報告します。
30
+
31
+ ## インストール
32
+
33
+ ```bash
34
+ npm install @wcstack/permission
35
+ ```
36
+
37
+ ## クイックスタート
38
+
39
+ ### 1. 許可状態を監視して UI をゲートする
40
+
41
+ ```html
42
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
43
+ <script type="module" src="https://esm.run/@wcstack/permission/auto"></script>
44
+
45
+ <wcs-state>
46
+ <script type="module">
47
+ export default { granted: false };
48
+ </script>
49
+ </wcs-state>
50
+
51
+ <wcs-permission name="geolocation" data-wcs="granted: granted"></wcs-permission>
52
+
53
+ <!-- 監視結果のブール 1 つ: 許可されるまで表示。 -->
54
+ <div data-wcs="hidden: granted">続行するには位置情報を許可してください。</div>
55
+ ```
56
+
57
+ ### 2. 4 値の state
58
+
59
+ ```html
60
+ <wcs-permission name="camera"
61
+ data-wcs="state: camState"></wcs-permission>
62
+ ```
63
+
64
+ `state` は `"prompt"` / `"granted"` / `"denied"` / `"unsupported"` で、ユーザーがブラウザ設定で許可を変えると live に更新されます。
65
+
66
+ ### 3. 追加メンバーを取る descriptor
67
+
68
+ 一部の権限は名前だけでは足りません。対応するブール属性を使います:
69
+
70
+ ```html
71
+ <!-- push: query({ name: "push", userVisibleOnly: true }) -->
72
+ <wcs-permission name="push" user-visible-only data-wcs="state: pushPerm"></wcs-permission>
73
+
74
+ <!-- midi: query({ name: "midi", sysex: true }) -->
75
+ <wcs-permission name="midi" sysex data-wcs="state: midiPerm"></wcs-permission>
76
+ ```
77
+
78
+ ### 4. 監視役と取得役を並置する
79
+
80
+ `<wcs-permission>` が監視し、`<wcs-geo>` が要求する。ボタンが駆動するのは機能ノードで、permission ノードではありません。
81
+
82
+ ```html
83
+ <wcs-permission name="geolocation" data-wcs="granted: granted; denied: denied"></wcs-permission>
84
+ <wcs-geo manual data-wcs="command.getCurrentPosition: $command.locate; latitude: lat"></wcs-geo>
85
+
86
+ <button data-wcs="onclick: locate; disabled: denied">現在地を取得</button>
87
+ ```
88
+
89
+ 完全なデモは `examples/state-permission-banner` を参照。
90
+
91
+ ## 属性 / 入力
92
+
93
+ | 属性 | 型 | 既定値 | 説明 |
94
+ | ------------------- | ------- | ------ | -------------------------------------------------------------------- |
95
+ | `name` | string | `""` | query する権限名(例: `geolocation`、`notifications`、`camera`)。必須 — 空の `name` は query せず `state = "unsupported"` に倒れます。 |
96
+ | `user-visible-only` | boolean | `false`| descriptor に `userVisibleOnly: true` を追加(`push` 権限用)。 |
97
+ | `sysex` | boolean | `false`| descriptor に `sysex: true` を追加(`midi` 権限用)。 |
98
+
99
+ ## 観測可能プロパティ(出力)
100
+
101
+ | プロパティ | イベント | 説明 |
102
+ | ------------- | ---------------------- | ------------------------------------------------------------------- |
103
+ | `state` | `wcs-permission:change`| `"prompt"` / `"granted"` / `"denied"` / `"unsupported"`、live 追従。 |
104
+ | `granted` | `wcs-permission:change`| `state === "granted"` のとき `true`。`hidden@granted` 等に便利。 |
105
+ | `denied` | `wcs-permission:change`| `state === "denied"` のとき `true`。 |
106
+ | `prompt` | `wcs-permission:change`| `state === "prompt"` のとき `true`。 |
107
+ | `unsupported` | `wcs-permission:change`| この環境で query できないとき `true`。 |
108
+
109
+ 5 つすべては単一の `wcs-permission:change` イベントから派生します(ブール群は `state` と連動)。
110
+
111
+ ## コマンド
112
+
113
+ **無し。** Permissions API は read-only で、呼ぶべき `request()` がありません。許可の取得は機能ノードの責務(例: `<wcs-geo>` の `getCurrentPosition`)です。`<wcs-permission>` は純粋なモニタです。
114
+
115
+ ## 注意・制限
116
+
117
+ - **属性は接続時に読み取り、監視はしない。** `<wcs-permission>` は `observedAttributes` / `attributeChangedCallback` を実装しません。descriptor(`name` +追加メンバー)は接続時に固定され、接続後に `name` を命令的に変えても再 query しません。別の権限を監視するには別要素を使う(または再接続する)。
118
+ - **再接続で再 query。** 要素を取り外して再挿入すると `connectedCallback` が再実行され、query を再発行して `change` を再購読します(切断時に購読を解除するのと対称)。切断時にまだ解決していない in-flight な query は無効化され、その後に解決しても `state` を更新せず `change` リスナーも張りません。したがって素早い 切断→再接続 でも古い購読が残ることはありません。
119
+ - **SSR(`@wcstack/server`)。** `static hasConnectedCallbackPromise = true` を宣言し `connectedCallbackPromise` を公開するため、サーバレンダラは接続時 query の settle を待ってからスナップショットします。
120
+ - **サイレント失敗処理(zero-log)。** wcstack のゼロ依存方針に沿い、`<wcs-permission>` はログも例外も出しません。Permissions API が無い場合、権限名が拒否される場合、`name` 属性が未指定/空の場合はいずれも静かに `state = "unsupported"` に解決します。`unsupported`(または `state`)をバインドして反応してください。
121
+
122
+ ## ヘッドレス利用(`PermissionCore`)
123
+
124
+ Core は DOM 非依存で、`@wc-bindable/core` の `bind()` と直接使えます:
125
+
126
+ ```typescript
127
+ import { PermissionCore } from "@wcstack/permission";
128
+
129
+ const perm = new PermissionCore({ name: "geolocation" });
130
+ perm.addEventListener("wcs-permission:change", (e) => {
131
+ console.log((e as CustomEvent).detail); // "prompt" | "granted" | "denied" | "unsupported"
132
+ });
133
+
134
+ await perm.ready; // 初回 query が settle 済み
135
+ console.log(perm.granted);
136
+
137
+ // 後始末:
138
+ perm.dispose(); // live な `change` リスナーを外す
139
+ ```
140
+
141
+ ## ライセンス
142
+
143
+ MIT
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @wcstack/permission
2
+
3
+ `@wcstack/permission` is a headless permission-state component for the wcstack ecosystem.
4
+
5
+ It is not a visual UI widget.
6
+ It is an **async primitive node** that turns a browser permission grant into reactive state — the same way `@wcstack/geolocation` turns the device's location into reactive state.
7
+
8
+ With `@wcstack/state`, `<wcs-permission>` can be bound directly through path contracts:
9
+
10
+ - **input surface**: `name`, `user-visible-only`, `sysex`
11
+ - **output state surface**: `state`, `granted`, `denied`, `prompt`, `unsupported`
12
+
13
+ This means permission-aware UI — banners, gates, capability hints — can be expressed declaratively in HTML, without writing `navigator.permissions.query()` or `change`-listener glue in your UI layer.
14
+
15
+ `@wcstack/permission` follows the [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md) (Core / Shell / Binding Contract) architecture:
16
+
17
+ - **Core** (`PermissionCore`) handles the query, the four-value state, and live `change` tracking
18
+ - **Shell** (`<wcs-permission>`) connects that state to DOM attributes and lifecycle
19
+ - **Binding Contract** (`static wcBindable`) declares observable `properties` (and, deliberately, **no commands**)
20
+
21
+ ## Why this exists — a read-only, command-less node
22
+
23
+ Every other wcstack IO node (`<wcs-geo>`, `<wcs-ws>`, `<wcs-clipboard>`, …) both *does* something and reports state. The Permissions API is different: it is **read-only**. It has `query()` but no standard `request()`. You cannot ask for a grant through it — asking happens as a side effect of calling the feature itself (`getCurrentPosition()`, `Notification.requestPermission()`, …).
24
+
25
+ So `<wcs-permission>` is a pure **element → state** producer: it *watches*, it never *asks*. It is the first wcstack node with **no commands at all** — command-token does not apply, only event-token. Acquiring a grant is the job of the feature node (`<wcs-geo>` etc.); this node just reflects the current grant as bindable state, live.
26
+
27
+ A permission change becomes a **state transition**, not a `change`-listener subscription.
28
+
29
+ > **Secure context required.** The Permissions API only works in a secure context (HTTPS, or `localhost`). Where it is absent — or the browser rejects the requested permission name (support varies widely: Firefox has no `clipboard-read`, Safari omits several names) — `<wcs-permission>` reports `state = "unsupported"` instead of throwing.
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ npm install @wcstack/permission
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ### 1. Watch a grant and gate the UI
40
+
41
+ ```html
42
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
43
+ <script type="module" src="https://esm.run/@wcstack/permission/auto"></script>
44
+
45
+ <wcs-state>
46
+ <script type="module">
47
+ export default { granted: false };
48
+ </script>
49
+ </wcs-state>
50
+
51
+ <wcs-permission name="geolocation" data-wcs="granted: granted"></wcs-permission>
52
+
53
+ <!-- One boolean, straight from the watcher: shown until granted. -->
54
+ <div data-wcs="hidden: granted">Please allow location to continue.</div>
55
+ ```
56
+
57
+ ### 2. The four-value state
58
+
59
+ ```html
60
+ <wcs-permission name="camera"
61
+ data-wcs="state: camState"></wcs-permission>
62
+ ```
63
+
64
+ `state` is `"prompt"` / `"granted"` / `"denied"` / `"unsupported"`, and updates live when the user changes the grant in browser settings.
65
+
66
+ ### 3. Descriptors that take extra members
67
+
68
+ Some permissions need more than a name. Use the matching boolean attribute:
69
+
70
+ ```html
71
+ <!-- push: query({ name: "push", userVisibleOnly: true }) -->
72
+ <wcs-permission name="push" user-visible-only data-wcs="state: pushPerm"></wcs-permission>
73
+
74
+ <!-- midi: query({ name: "midi", sysex: true }) -->
75
+ <wcs-permission name="midi" sysex data-wcs="state: midiPerm"></wcs-permission>
76
+ ```
77
+
78
+ ### 4. Watcher + acquirer, side by side
79
+
80
+ `<wcs-permission>` watches; `<wcs-geo>` asks. The button drives the feature node, not the permission node.
81
+
82
+ ```html
83
+ <wcs-permission name="geolocation" data-wcs="granted: granted; denied: denied"></wcs-permission>
84
+ <wcs-geo manual data-wcs="command.getCurrentPosition: $command.locate; latitude: lat"></wcs-geo>
85
+
86
+ <button data-wcs="onclick: locate; disabled: denied">Locate me</button>
87
+ ```
88
+
89
+ See `examples/state-permission-banner` for the full demo.
90
+
91
+ ## Attributes / Inputs
92
+
93
+ | Attribute | Type | Default | Description |
94
+ | ------------------- | ------- | ------- | --------------------------------------------------------------------------- |
95
+ | `name` | string | `""` | The permission name to query (e.g. `geolocation`, `notifications`, `camera`). Required — an empty `name` short-circuits to `state = "unsupported"` without querying. |
96
+ | `user-visible-only` | boolean | `false` | Adds `userVisibleOnly: true` to the descriptor (for the `push` permission). |
97
+ | `sysex` | boolean | `false` | Adds `sysex: true` to the descriptor (for the `midi` permission). |
98
+
99
+ ## Observable Properties (outputs)
100
+
101
+ | Property | Event | Description |
102
+ | ------------- | ---------------------- | ----------------------------------------------------------------------- |
103
+ | `state` | `wcs-permission:change`| `"prompt"` / `"granted"` / `"denied"` / `"unsupported"`, tracked live. |
104
+ | `granted` | `wcs-permission:change`| `true` when `state === "granted"`. Convenience for `hidden@granted` etc. |
105
+ | `denied` | `wcs-permission:change`| `true` when `state === "denied"`. |
106
+ | `prompt` | `wcs-permission:change`| `true` when `state === "prompt"`. |
107
+ | `unsupported` | `wcs-permission:change`| `true` when the permission cannot be queried in this environment. |
108
+
109
+ All five derive from the single `wcs-permission:change` event (the booleans change in lockstep with `state`).
110
+
111
+ ## Commands
112
+
113
+ **None.** The Permissions API is read-only — there is no `request()` to call. Acquiring a grant is the feature node's responsibility (e.g. `<wcs-geo>`'s `getCurrentPosition`). `<wcs-permission>` is a pure monitor.
114
+
115
+ ## Notes & limitations
116
+
117
+ - **Attributes are read at connect time, not observed.** `<wcs-permission>` does not implement `observedAttributes` / `attributeChangedCallback`. The descriptor (`name` + extras) is fixed when the element connects; changing `name` imperatively after connect does not re-query. To watch a different permission, use a separate element (or re-connect).
118
+ - **Reconnect re-queries.** Removing and re-inserting the element runs `connectedCallback` again, re-issuing the query and re-subscribing to `change` (matching how it tears the subscription down on disconnect). A query still in flight when the element disconnects is invalidated: if it resolves afterwards it neither updates `state` nor attaches a `change` listener, so a rapid disconnect→reconnect cannot leak a stale subscription.
119
+ - **SSR (`@wcstack/server`).** Declares `static hasConnectedCallbackPromise = true` and exposes `connectedCallbackPromise`, so the server renderer waits for the connect-time query to settle before snapshotting.
120
+ - **Silent failure handling (zero-log).** Consistent with the rest of wcstack's zero-dependency philosophy, `<wcs-permission>` never logs or throws. A missing Permissions API, a browser that rejects the requested permission name, or a missing/empty `name` attribute all silently resolve to `state = "unsupported"`. Bind `unsupported` (or `state`) to react.
121
+
122
+ ## Headless usage (`PermissionCore`)
123
+
124
+ The Core has no DOM dependency and can be used directly with `bind()` from `@wc-bindable/core`:
125
+
126
+ ```typescript
127
+ import { PermissionCore } from "@wcstack/permission";
128
+
129
+ const perm = new PermissionCore({ name: "geolocation" });
130
+ perm.addEventListener("wcs-permission:change", (e) => {
131
+ console.log((e as CustomEvent).detail); // "prompt" | "granted" | "denied" | "unsupported"
132
+ });
133
+
134
+ await perm.ready; // first query has settled
135
+ console.log(perm.granted);
136
+
137
+ // later, when done:
138
+ perm.dispose(); // detach the live `change` listener
139
+ ```
140
+
141
+ ## License
142
+
143
+ MIT
package/dist/auto.js ADDED
@@ -0,0 +1,3 @@
1
+ import { bootstrapPermission } from "./index.esm.js";
2
+
3
+ bootstrapPermission();
@@ -0,0 +1 @@
1
+ import{bootstrapPermission}from"./index.esm.min.js";bootstrapPermission();
@@ -0,0 +1,183 @@
1
+ interface ITagNames {
2
+ readonly permission: string;
3
+ }
4
+ interface IWritableTagNames {
5
+ permission?: string;
6
+ }
7
+ interface IConfig {
8
+ readonly tagNames: ITagNames;
9
+ }
10
+ interface IWritableConfig {
11
+ tagNames?: IWritableTagNames;
12
+ }
13
+ interface IWcBindableProperty {
14
+ readonly name: string;
15
+ readonly event: string;
16
+ readonly getter?: (event: Event) => any;
17
+ }
18
+ interface IWcBindableInput {
19
+ readonly name: string;
20
+ readonly attribute?: string;
21
+ }
22
+ interface IWcBindableCommand {
23
+ readonly name: string;
24
+ readonly async?: boolean;
25
+ }
26
+ interface IWcBindable {
27
+ readonly protocol: "wc-bindable";
28
+ readonly version: number;
29
+ readonly properties: IWcBindableProperty[];
30
+ readonly inputs?: IWcBindableInput[];
31
+ readonly commands?: IWcBindableCommand[];
32
+ }
33
+ /**
34
+ * Permission state mirroring the Permissions API `PermissionState`
35
+ * (`"prompt"` / `"granted"` / `"denied"`) plus `"unsupported"` for environments
36
+ * without `navigator.permissions`, or where the requested permission name cannot
37
+ * be queried (the browser rejects the descriptor). This is the same four-value
38
+ * surface used by `@wcstack/geolocation` and `@wcstack/clipboard`.
39
+ */
40
+ type PermissionStateOrUnsupported = "prompt" | "granted" | "denied" | "unsupported";
41
+ /**
42
+ * Descriptor passed to `navigator.permissions.query()`. `name` is the permission
43
+ * name (e.g. `"geolocation"`, `"notifications"`, `"camera"`). The optional fields
44
+ * cover the descriptors that take extra members:
45
+ * - `userVisibleOnly` — required by the `"push"` permission.
46
+ * - `sysex` — used by the `"midi"` permission.
47
+ *
48
+ * Other members defined by future descriptors are allowed via the index
49
+ * signature so the Shell can forward unknown attributes without a type change.
50
+ */
51
+ interface WcsPermissionDescriptor {
52
+ name: string;
53
+ userVisibleOnly?: boolean;
54
+ sysex?: boolean;
55
+ [key: string]: unknown;
56
+ }
57
+ /**
58
+ * Value types for PermissionCore (headless) — the observable state properties.
59
+ * Use with `bind()` from `@wc-bindable/core` for compile-time type checking.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const core = new PermissionCore({ name: "geolocation" });
64
+ * bind(core, (name: keyof WcsPermissionCoreValues, value) => { ... });
65
+ * ```
66
+ */
67
+ interface WcsPermissionCoreValues {
68
+ state: PermissionStateOrUnsupported;
69
+ granted: boolean;
70
+ denied: boolean;
71
+ prompt: boolean;
72
+ unsupported: boolean;
73
+ }
74
+ /**
75
+ * Value types for the Shell (`<wcs-permission>`) — identical observable surface
76
+ * to the Core. The Shell adds no command-property: the Permissions API is
77
+ * read-only, so this element is a pure element → state monitor.
78
+ */
79
+ type WcsPermissionValues = WcsPermissionCoreValues;
80
+ /**
81
+ * Settable input surface for the Shell (`<wcs-permission>`) — the descriptor
82
+ * members exposed as attributes (`name`, `user-visible-only`, `sysex`). Mirrors
83
+ * the `inputs` entries of the wc-bindable manifest; use it for compile-time typing
84
+ * when a binding system or tooling writes these declaratively.
85
+ */
86
+ interface WcsPermissionInputs {
87
+ name: string;
88
+ userVisibleOnly: boolean;
89
+ sysex: boolean;
90
+ }
91
+
92
+ declare function bootstrapPermission(userConfig?: IWritableConfig): void;
93
+
94
+ declare function getConfig(): IConfig;
95
+
96
+ /**
97
+ * Headless permission-state primitive. A thin, framework-agnostic wrapper around
98
+ * the Permissions API exposed through the wc-bindable protocol.
99
+ *
100
+ * Unlike the other @wcstack IO nodes (geolocation / clipboard / sse / …), the
101
+ * Permissions API is **read-only**: it has `query()` but no standard `request()`.
102
+ * Asking the user for a grant is the job of the feature node (`<wcs-geo>` etc.);
103
+ * this node only *observes*. It is therefore a pure element → state monitor with
104
+ * **no commands** — command-token does not apply, only event-token.
105
+ *
106
+ * The single observable is `state` (`navigator.permissions.query(descriptor)`'s
107
+ * `PermissionState`, or `"unsupported"`), published via the `wcs-permission:change`
108
+ * event. `granted` / `denied` / `prompt` / `unsupported` are convenience booleans
109
+ * derived from that one event (mirroring how GeolocationCore exposes latitude/…
110
+ * from one `wcs-geo:position` event), so a binding like `hidden@granted` works
111
+ * directly. The live `change` event of the PermissionStatus is tracked so a grant
112
+ * flipping in browser settings flows into the declarative state.
113
+ */
114
+ declare class PermissionCore extends EventTarget {
115
+ static wcBindable: IWcBindable;
116
+ private _target;
117
+ private _descriptor;
118
+ private _state;
119
+ private _permissionStatus;
120
+ private _permissionSubscribed;
121
+ private _permGen;
122
+ private _ready;
123
+ constructor(descriptor?: WcsPermissionDescriptor | null, target?: EventTarget);
124
+ get state(): PermissionStateOrUnsupported;
125
+ get granted(): boolean;
126
+ get denied(): boolean;
127
+ get prompt(): boolean;
128
+ get unsupported(): boolean;
129
+ /** Resolves once the current (or initial) query settles. */
130
+ get ready(): Promise<void>;
131
+ private _setState;
132
+ /**
133
+ * Start observing `descriptor` (e.g. `{ name: "geolocation" }`). Idempotent
134
+ * while already subscribed — calling it again only updates the stored descriptor
135
+ * for a *future* re-subscription; it does **not** re-query, even when called with
136
+ * a different descriptor (the Shell binds at a fixed connect-time descriptor and
137
+ * does not re-query on a `name` change in v1). To switch permission mid-life,
138
+ * dispose() first, then observe() the new descriptor. On the first call, or after
139
+ * a dispose(), it issues the query and subscribes to the live `change` event.
140
+ * Returns a promise that resolves once that query settles, for SSR.
141
+ */
142
+ observe(descriptor: WcsPermissionDescriptor): Promise<void>;
143
+ /**
144
+ * Detach the live permission `change` listener. Call from the Shell's
145
+ * `disconnectedCallback` so a removed element does not leak the subscription.
146
+ * A later reconnect can re-subscribe via observe().
147
+ *
148
+ * Headless callers (using PermissionCore directly, without the Shell) own this
149
+ * lifecycle themselves: call dispose() when the observer is no longer needed,
150
+ * otherwise the live PermissionStatus `change` listener keeps this instance
151
+ * reachable for as long as the status is alive. dispose() is safe to call when
152
+ * never subscribed and may be paired with a later observe() to resume.
153
+ */
154
+ dispose(): void;
155
+ private _initPermission;
156
+ private _onPermissionChange;
157
+ }
158
+
159
+ declare class WcsPermission extends HTMLElement {
160
+ static hasConnectedCallbackPromise: boolean;
161
+ static wcBindable: IWcBindable;
162
+ private _core;
163
+ private _connectedCallbackPromise;
164
+ constructor();
165
+ get name(): string;
166
+ set name(value: string);
167
+ get userVisibleOnly(): boolean;
168
+ set userVisibleOnly(value: boolean);
169
+ get sysex(): boolean;
170
+ set sysex(value: boolean);
171
+ get state(): PermissionStateOrUnsupported;
172
+ get granted(): boolean;
173
+ get denied(): boolean;
174
+ get prompt(): boolean;
175
+ get unsupported(): boolean;
176
+ get connectedCallbackPromise(): Promise<void>;
177
+ private _descriptor;
178
+ connectedCallback(): void;
179
+ disconnectedCallback(): void;
180
+ }
181
+
182
+ export { PermissionCore, WcsPermission, bootstrapPermission, getConfig };
183
+ export type { IWritableConfig, IWritableTagNames, PermissionStateOrUnsupported, WcsPermissionCoreValues, WcsPermissionDescriptor, WcsPermissionInputs, WcsPermissionValues };
@@ -0,0 +1,347 @@
1
+ const _config = {
2
+ tagNames: {
3
+ permission: "wcs-permission",
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
+ /**
40
+ * Headless permission-state primitive. A thin, framework-agnostic wrapper around
41
+ * the Permissions API exposed through the wc-bindable protocol.
42
+ *
43
+ * Unlike the other @wcstack IO nodes (geolocation / clipboard / sse / …), the
44
+ * Permissions API is **read-only**: it has `query()` but no standard `request()`.
45
+ * Asking the user for a grant is the job of the feature node (`<wcs-geo>` etc.);
46
+ * this node only *observes*. It is therefore a pure element → state monitor with
47
+ * **no commands** — command-token does not apply, only event-token.
48
+ *
49
+ * The single observable is `state` (`navigator.permissions.query(descriptor)`'s
50
+ * `PermissionState`, or `"unsupported"`), published via the `wcs-permission:change`
51
+ * event. `granted` / `denied` / `prompt` / `unsupported` are convenience booleans
52
+ * derived from that one event (mirroring how GeolocationCore exposes latitude/…
53
+ * from one `wcs-geo:position` event), so a binding like `hidden@granted` works
54
+ * directly. The live `change` event of the PermissionStatus is tracked so a grant
55
+ * flipping in browser settings flows into the declarative state.
56
+ */
57
+ class PermissionCore extends EventTarget {
58
+ static wcBindable = {
59
+ protocol: "wc-bindable",
60
+ version: 1,
61
+ properties: [
62
+ { name: "state", event: "wcs-permission:change" },
63
+ { name: "granted", event: "wcs-permission:change", getter: (e) => e.detail === "granted" },
64
+ { name: "denied", event: "wcs-permission:change", getter: (e) => e.detail === "denied" },
65
+ { name: "prompt", event: "wcs-permission:change", getter: (e) => e.detail === "prompt" },
66
+ { name: "unsupported", event: "wcs-permission:change", getter: (e) => e.detail === "unsupported" },
67
+ ],
68
+ // No commands: the Permissions API is read-only (query-only). See class docs.
69
+ commands: [],
70
+ };
71
+ _target;
72
+ _descriptor = null;
73
+ _state = "prompt";
74
+ // Live PermissionStatus handle (when the Permissions API is available), kept so
75
+ // the `change` listener can be removed on dispose().
76
+ _permissionStatus = null;
77
+ // True once a permission subscription has been (or is being) established, and
78
+ // reset by dispose(). Guards observe() so a reconnect after dispose() re-queries
79
+ // while a redundant observe() on an already-live subscription does not.
80
+ _permissionSubscribed = false;
81
+ // Monotonic id of the current permission query. Bumped by every _initPermission()
82
+ // and by dispose(). Each in-flight query captures its id and, on resolve, bails
83
+ // unless it is still current — so a query superseded by a rapid (synchronous)
84
+ // disconnect→reconnect, or one that resolves after dispose(), never attaches a
85
+ // listener. A plain boolean cannot cover this: dispose()→observe() flips it
86
+ // false→true again, reopening the window for the stale query to slip through.
87
+ _permGen = 0;
88
+ // Resolves once the most recent query settles (or immediately when the API is
89
+ // unsupported). The Shell exposes this as connectedCallbackPromise so SSR can
90
+ // await the first probe before snapshotting the HTML.
91
+ _ready = Promise.resolve();
92
+ constructor(descriptor, target) {
93
+ super();
94
+ this._target = target ?? this;
95
+ // Headless ergonomics: when a descriptor is supplied up front, probe the
96
+ // permission state immediately so observers see the real value before the
97
+ // first read. The Shell passes nothing and drives the first query from
98
+ // connectedCallback via observe(), once the element's attributes resolve.
99
+ if (descriptor) {
100
+ this._descriptor = descriptor;
101
+ this._ready = this._initPermission();
102
+ }
103
+ }
104
+ get state() {
105
+ return this._state;
106
+ }
107
+ get granted() {
108
+ return this._state === "granted";
109
+ }
110
+ get denied() {
111
+ return this._state === "denied";
112
+ }
113
+ get prompt() {
114
+ return this._state === "prompt";
115
+ }
116
+ get unsupported() {
117
+ return this._state === "unsupported";
118
+ }
119
+ /** Resolves once the current (or initial) query settles. */
120
+ get ready() {
121
+ return this._ready;
122
+ }
123
+ // --- State setter with event dispatch ---
124
+ _setState(state) {
125
+ // Same-value guard: `state` is the only stored value and the derived booleans
126
+ // change in lockstep with it, so suppressing identical re-dispatches is safe.
127
+ if (this._state === state)
128
+ return;
129
+ this._state = state;
130
+ this._target.dispatchEvent(new CustomEvent("wcs-permission:change", {
131
+ detail: state,
132
+ bubbles: true,
133
+ }));
134
+ }
135
+ // --- Public API ---
136
+ /**
137
+ * Start observing `descriptor` (e.g. `{ name: "geolocation" }`). Idempotent
138
+ * while already subscribed — calling it again only updates the stored descriptor
139
+ * for a *future* re-subscription; it does **not** re-query, even when called with
140
+ * a different descriptor (the Shell binds at a fixed connect-time descriptor and
141
+ * does not re-query on a `name` change in v1). To switch permission mid-life,
142
+ * dispose() first, then observe() the new descriptor. On the first call, or after
143
+ * a dispose(), it issues the query and subscribes to the live `change` event.
144
+ * Returns a promise that resolves once that query settles, for SSR.
145
+ */
146
+ observe(descriptor) {
147
+ this._descriptor = descriptor;
148
+ if (!this._permissionSubscribed) {
149
+ this._ready = this._initPermission();
150
+ }
151
+ return this._ready;
152
+ }
153
+ /**
154
+ * Detach the live permission `change` listener. Call from the Shell's
155
+ * `disconnectedCallback` so a removed element does not leak the subscription.
156
+ * A later reconnect can re-subscribe via observe().
157
+ *
158
+ * Headless callers (using PermissionCore directly, without the Shell) own this
159
+ * lifecycle themselves: call dispose() when the observer is no longer needed,
160
+ * otherwise the live PermissionStatus `change` listener keeps this instance
161
+ * reachable for as long as the status is alive. dispose() is safe to call when
162
+ * never subscribed and may be paired with a later observe() to resume.
163
+ */
164
+ dispose() {
165
+ this._permissionSubscribed = false;
166
+ // Invalidate any in-flight query so its .then() bails instead of attaching a
167
+ // listener after teardown.
168
+ this._permGen++;
169
+ if (this._permissionStatus) {
170
+ this._permissionStatus.removeEventListener("change", this._onPermissionChange);
171
+ this._permissionStatus = null;
172
+ }
173
+ }
174
+ // --- Internal ---
175
+ _initPermission() {
176
+ // Guard a missing/empty permission name (e.g. a `<wcs-permission>` with no
177
+ // `name` attribute). Such a descriptor would only ever reject at query() and
178
+ // silently fall back to "unsupported", which is hard to diagnose. Short-circuit
179
+ // to "unsupported" without issuing a doomed query so the misconfiguration
180
+ // surfaces deterministically and no listener is attached.
181
+ if (!this._descriptor || !this._descriptor.name) {
182
+ this._setState("unsupported");
183
+ return Promise.resolve();
184
+ }
185
+ // The Permissions API is optional. When absent (or it rejects, e.g. the
186
+ // browser does not accept the requested permission name), report "unsupported"
187
+ // and leave it at that — there is nothing to retry.
188
+ if (typeof navigator === "undefined" || !navigator.permissions || typeof navigator.permissions.query !== "function") {
189
+ // Route through _setState (not a bare assignment) so observers stay in sync
190
+ // with the public state. The same-value guard means no redundant dispatch
191
+ // when the state does not actually change.
192
+ this._setState("unsupported");
193
+ // Intentionally does NOT set _permissionSubscribed: there is no listener to
194
+ // tear down, so a reconnect simply re-probes (idempotent — the same-value
195
+ // guard suppresses any dispatch and no listener is ever attached). Mirrors
196
+ // GeolocationCore's reinitPermission behavior in unsupported environments.
197
+ return Promise.resolve();
198
+ }
199
+ this._permissionSubscribed = true;
200
+ const gen = ++this._permGen;
201
+ // Cast: WcsPermissionDescriptor widens `name` to string (and allows extra
202
+ // descriptor members like userVisibleOnly / sysex) where the lib DOM type
203
+ // expects the PermissionName union.
204
+ return navigator.permissions.query(this._descriptor).then((status) => {
205
+ // Stale resolution: this query was superseded (rapid reconnect) or the
206
+ // element was disposed while it was in flight. Drop it so only the current
207
+ // subscription attaches a listener.
208
+ if (gen !== this._permGen)
209
+ return;
210
+ this._permissionStatus = status;
211
+ this._setState(status.state);
212
+ status.addEventListener("change", this._onPermissionChange);
213
+ }, () => {
214
+ if (gen !== this._permGen)
215
+ return;
216
+ this._setState("unsupported");
217
+ });
218
+ }
219
+ _onPermissionChange = (event) => {
220
+ const status = event.target;
221
+ this._setState(status.state);
222
+ };
223
+ }
224
+
225
+ // Named WcsPermission (not `Permission`) so the class does not shadow any global,
226
+ // and to match the <wcs-geo> / <wcs-ws> convention (WcsGeolocation /
227
+ // WcsWebSocket). The public export keeps the `WcsPermission` name unchanged.
228
+ class WcsPermission extends HTMLElement {
229
+ static hasConnectedCallbackPromise = true;
230
+ static wcBindable = {
231
+ ...PermissionCore.wcBindable,
232
+ // Shell-level settable surface. `name` is the permission name; the descriptor
233
+ // extras `user-visible-only` (push) and `sysex` (midi) are boolean flags that
234
+ // reflect idempotently, so a binding system that writes through
235
+ // inputs[].attribute is safe.
236
+ inputs: [
237
+ { name: "name", attribute: "name" },
238
+ { name: "userVisibleOnly", attribute: "user-visible-only" },
239
+ { name: "sysex", attribute: "sysex" },
240
+ ],
241
+ // No commands: read-only monitor (see PermissionCore). The Permissions API has
242
+ // no request() — acquiring a grant is the feature node's job.
243
+ commands: [],
244
+ };
245
+ // Created in the Shell constructor with no descriptor so the delegated getters
246
+ // work before connect (returning the default "prompt" / false). The actual
247
+ // query is driven from connectedCallback once the element's attributes resolve
248
+ // (programmatically-created elements have no attributes at construction time).
249
+ _core;
250
+ _connectedCallbackPromise = Promise.resolve();
251
+ constructor() {
252
+ super();
253
+ this._core = new PermissionCore(null, this);
254
+ }
255
+ // --- Attribute accessors ---
256
+ get name() {
257
+ return this.getAttribute("name") ?? "";
258
+ }
259
+ set name(value) {
260
+ this.setAttribute("name", value);
261
+ }
262
+ get userVisibleOnly() {
263
+ return this.hasAttribute("user-visible-only");
264
+ }
265
+ set userVisibleOnly(value) {
266
+ if (value) {
267
+ this.setAttribute("user-visible-only", "");
268
+ }
269
+ else {
270
+ this.removeAttribute("user-visible-only");
271
+ }
272
+ }
273
+ get sysex() {
274
+ return this.hasAttribute("sysex");
275
+ }
276
+ set sysex(value) {
277
+ if (value) {
278
+ this.setAttribute("sysex", "");
279
+ }
280
+ else {
281
+ this.removeAttribute("sysex");
282
+ }
283
+ }
284
+ // --- Core delegated getters ---
285
+ get state() {
286
+ return this._core.state;
287
+ }
288
+ get granted() {
289
+ return this._core.granted;
290
+ }
291
+ get denied() {
292
+ return this._core.denied;
293
+ }
294
+ get prompt() {
295
+ return this._core.prompt;
296
+ }
297
+ get unsupported() {
298
+ return this._core.unsupported;
299
+ }
300
+ // wc-bindable connectedCallbackPromise protocol: resolves once the connect-time
301
+ // query settles, so SSR (@wcstack/server render.ts) waits for the first probe
302
+ // before snapshotting the HTML. Mirrors WcsGeolocation.connectedCallbackPromise.
303
+ get connectedCallbackPromise() {
304
+ return this._connectedCallbackPromise;
305
+ }
306
+ // --- Internal ---
307
+ // Build the query descriptor from the current attributes. Only present extras
308
+ // are included so a bare `{ name }` is passed for permissions that take no
309
+ // additional members. The descriptor is fixed at connect time (v1 does not
310
+ // re-query on a `name` change — see README).
311
+ _descriptor() {
312
+ const descriptor = { name: this.name };
313
+ if (this.userVisibleOnly)
314
+ descriptor.userVisibleOnly = true;
315
+ if (this.sysex)
316
+ descriptor.sysex = true;
317
+ return descriptor;
318
+ }
319
+ // --- Lifecycle ---
320
+ connectedCallback() {
321
+ this.style.display = "none";
322
+ // Begin observing (or revive the subscription after a reconnect). The
323
+ // returned promise is held as connectedCallbackPromise for SSR. query() never
324
+ // rejects in a way that escapes — failures surface as the `unsupported`
325
+ // state — so no .catch() is needed.
326
+ this._connectedCallbackPromise = this._core.observe(this._descriptor());
327
+ }
328
+ disconnectedCallback() {
329
+ this._core.dispose();
330
+ }
331
+ }
332
+
333
+ function registerComponents() {
334
+ if (!customElements.get(config.tagNames.permission)) {
335
+ customElements.define(config.tagNames.permission, WcsPermission);
336
+ }
337
+ }
338
+
339
+ function bootstrapPermission(userConfig) {
340
+ if (userConfig) {
341
+ setConfig(userConfig);
342
+ }
343
+ registerComponents();
344
+ }
345
+
346
+ export { PermissionCore, WcsPermission, bootstrapPermission, getConfig };
347
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/PermissionCore.ts","../src/components/Permission.ts","../src/registerComponents.ts","../src/bootstrapPermission.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n permission: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n permission: \"wcs-permission\",\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 {\n IWcBindable, PermissionStateOrUnsupported, WcsPermissionDescriptor,\n} from \"../types.js\";\n\n/**\n * Headless permission-state primitive. A thin, framework-agnostic wrapper around\n * the Permissions API exposed through the wc-bindable protocol.\n *\n * Unlike the other @wcstack IO nodes (geolocation / clipboard / sse / …), the\n * Permissions API is **read-only**: it has `query()` but no standard `request()`.\n * Asking the user for a grant is the job of the feature node (`<wcs-geo>` etc.);\n * this node only *observes*. It is therefore a pure element → state monitor with\n * **no commands** — command-token does not apply, only event-token.\n *\n * The single observable is `state` (`navigator.permissions.query(descriptor)`'s\n * `PermissionState`, or `\"unsupported\"`), published via the `wcs-permission:change`\n * event. `granted` / `denied` / `prompt` / `unsupported` are convenience booleans\n * derived from that one event (mirroring how GeolocationCore exposes latitude/…\n * from one `wcs-geo:position` event), so a binding like `hidden@granted` works\n * directly. The live `change` event of the PermissionStatus is tracked so a grant\n * flipping in browser settings flows into the declarative state.\n */\nexport class PermissionCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"state\", event: \"wcs-permission:change\" },\n { name: \"granted\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"granted\" },\n { name: \"denied\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"denied\" },\n { name: \"prompt\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"prompt\" },\n { name: \"unsupported\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"unsupported\" },\n ],\n // No commands: the Permissions API is read-only (query-only). See class docs.\n commands: [],\n };\n\n private _target: EventTarget;\n private _descriptor: WcsPermissionDescriptor | null = null;\n\n private _state: PermissionStateOrUnsupported = \"prompt\";\n\n // Live PermissionStatus handle (when the Permissions API is available), kept so\n // the `change` listener can be removed on dispose().\n private _permissionStatus: PermissionStatus | null = null;\n\n // True once a permission subscription has been (or is being) established, and\n // reset by dispose(). Guards observe() so a reconnect after dispose() re-queries\n // while a redundant observe() on an already-live subscription does not.\n private _permissionSubscribed: boolean = false;\n\n // Monotonic id of the current permission query. Bumped by every _initPermission()\n // and by dispose(). Each in-flight query captures its id and, on resolve, bails\n // unless it is still current — so a query superseded by a rapid (synchronous)\n // disconnect→reconnect, or one that resolves after dispose(), never attaches a\n // listener. A plain boolean cannot cover this: dispose()→observe() flips it\n // false→true again, reopening the window for the stale query to slip through.\n private _permGen: number = 0;\n\n // Resolves once the most recent query settles (or immediately when the API is\n // unsupported). The Shell exposes this as connectedCallbackPromise so SSR can\n // await the first probe before snapshotting the HTML.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(descriptor?: WcsPermissionDescriptor | null, target?: EventTarget) {\n super();\n this._target = target ?? this;\n // Headless ergonomics: when a descriptor is supplied up front, probe the\n // permission state immediately so observers see the real value before the\n // first read. The Shell passes nothing and drives the first query from\n // connectedCallback via observe(), once the element's attributes resolve.\n if (descriptor) {\n this._descriptor = descriptor;\n this._ready = this._initPermission();\n }\n }\n\n get state(): PermissionStateOrUnsupported {\n return this._state;\n }\n\n get granted(): boolean {\n return this._state === \"granted\";\n }\n\n get denied(): boolean {\n return this._state === \"denied\";\n }\n\n get prompt(): boolean {\n return this._state === \"prompt\";\n }\n\n get unsupported(): boolean {\n return this._state === \"unsupported\";\n }\n\n /** Resolves once the current (or initial) query settles. */\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // --- State setter with event dispatch ---\n\n private _setState(state: PermissionStateOrUnsupported): void {\n // Same-value guard: `state` is the only stored value and the derived booleans\n // change in lockstep with it, so suppressing identical re-dispatches is safe.\n if (this._state === state) return;\n this._state = state;\n this._target.dispatchEvent(new CustomEvent(\"wcs-permission:change\", {\n detail: state,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Start observing `descriptor` (e.g. `{ name: \"geolocation\" }`). Idempotent\n * while already subscribed — calling it again only updates the stored descriptor\n * for a *future* re-subscription; it does **not** re-query, even when called with\n * a different descriptor (the Shell binds at a fixed connect-time descriptor and\n * does not re-query on a `name` change in v1). To switch permission mid-life,\n * dispose() first, then observe() the new descriptor. On the first call, or after\n * a dispose(), it issues the query and subscribes to the live `change` event.\n * Returns a promise that resolves once that query settles, for SSR.\n */\n observe(descriptor: WcsPermissionDescriptor): Promise<void> {\n this._descriptor = descriptor;\n if (!this._permissionSubscribed) {\n this._ready = this._initPermission();\n }\n return this._ready;\n }\n\n /**\n * Detach the live permission `change` listener. Call from the Shell's\n * `disconnectedCallback` so a removed element does not leak the subscription.\n * A later reconnect can re-subscribe via observe().\n *\n * Headless callers (using PermissionCore directly, without the Shell) own this\n * lifecycle themselves: call dispose() when the observer is no longer needed,\n * otherwise the live PermissionStatus `change` listener keeps this instance\n * reachable for as long as the status is alive. dispose() is safe to call when\n * never subscribed and may be paired with a later observe() to resume.\n */\n dispose(): void {\n this._permissionSubscribed = false;\n // Invalidate any in-flight query so its .then() bails instead of attaching a\n // listener after teardown.\n this._permGen++;\n if (this._permissionStatus) {\n this._permissionStatus.removeEventListener(\"change\", this._onPermissionChange);\n this._permissionStatus = null;\n }\n }\n\n // --- Internal ---\n\n private _initPermission(): Promise<void> {\n // Guard a missing/empty permission name (e.g. a `<wcs-permission>` with no\n // `name` attribute). Such a descriptor would only ever reject at query() and\n // silently fall back to \"unsupported\", which is hard to diagnose. Short-circuit\n // to \"unsupported\" without issuing a doomed query so the misconfiguration\n // surfaces deterministically and no listener is attached.\n if (!this._descriptor || !this._descriptor.name) {\n this._setState(\"unsupported\");\n return Promise.resolve();\n }\n // The Permissions API is optional. When absent (or it rejects, e.g. the\n // browser does not accept the requested permission name), report \"unsupported\"\n // and leave it at that — there is nothing to retry.\n if (typeof navigator === \"undefined\" || !navigator.permissions || typeof navigator.permissions.query !== \"function\") {\n // Route through _setState (not a bare assignment) so observers stay in sync\n // with the public state. The same-value guard means no redundant dispatch\n // when the state does not actually change.\n this._setState(\"unsupported\");\n // Intentionally does NOT set _permissionSubscribed: there is no listener to\n // tear down, so a reconnect simply re-probes (idempotent — the same-value\n // guard suppresses any dispatch and no listener is ever attached). Mirrors\n // GeolocationCore's reinitPermission behavior in unsupported environments.\n return Promise.resolve();\n }\n this._permissionSubscribed = true;\n const gen = ++this._permGen;\n // Cast: WcsPermissionDescriptor widens `name` to string (and allows extra\n // descriptor members like userVisibleOnly / sysex) where the lib DOM type\n // expects the PermissionName union.\n return navigator.permissions.query(this._descriptor as unknown as PermissionDescriptor).then(\n (status) => {\n // Stale resolution: this query was superseded (rapid reconnect) or the\n // element was disposed while it was in flight. Drop it so only the current\n // subscription attaches a listener.\n if (gen !== this._permGen) return;\n this._permissionStatus = status;\n this._setState(status.state as PermissionStateOrUnsupported);\n status.addEventListener(\"change\", this._onPermissionChange);\n },\n () => {\n if (gen !== this._permGen) return;\n this._setState(\"unsupported\");\n },\n );\n }\n\n private _onPermissionChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._setState(status.state as PermissionStateOrUnsupported);\n };\n}\n","import { IWcBindable, PermissionStateOrUnsupported, WcsPermissionDescriptor } from \"../types.js\";\nimport { PermissionCore } from \"../core/PermissionCore.js\";\n\n// Named WcsPermission (not `Permission`) so the class does not shadow any global,\n// and to match the <wcs-geo> / <wcs-ws> convention (WcsGeolocation /\n// WcsWebSocket). The public export keeps the `WcsPermission` name unchanged.\nexport class WcsPermission extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...PermissionCore.wcBindable,\n // Shell-level settable surface. `name` is the permission name; the descriptor\n // extras `user-visible-only` (push) and `sysex` (midi) are boolean flags that\n // reflect idempotently, so a binding system that writes through\n // inputs[].attribute is safe.\n inputs: [\n { name: \"name\", attribute: \"name\" },\n { name: \"userVisibleOnly\", attribute: \"user-visible-only\" },\n { name: \"sysex\", attribute: \"sysex\" },\n ],\n // No commands: read-only monitor (see PermissionCore). The Permissions API has\n // no request() — acquiring a grant is the feature node's job.\n commands: [],\n };\n\n // Created in the Shell constructor with no descriptor so the delegated getters\n // work before connect (returning the default \"prompt\" / false). The actual\n // query is driven from connectedCallback once the element's attributes resolve\n // (programmatically-created elements have no attributes at construction time).\n private _core: PermissionCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new PermissionCore(null, this);\n }\n\n // --- Attribute accessors ---\n\n get name(): string {\n return this.getAttribute(\"name\") ?? \"\";\n }\n\n set name(value: string) {\n this.setAttribute(\"name\", value);\n }\n\n get userVisibleOnly(): boolean {\n return this.hasAttribute(\"user-visible-only\");\n }\n\n set userVisibleOnly(value: boolean) {\n if (value) {\n this.setAttribute(\"user-visible-only\", \"\");\n } else {\n this.removeAttribute(\"user-visible-only\");\n }\n }\n\n get sysex(): boolean {\n return this.hasAttribute(\"sysex\");\n }\n\n set sysex(value: boolean) {\n if (value) {\n this.setAttribute(\"sysex\", \"\");\n } else {\n this.removeAttribute(\"sysex\");\n }\n }\n\n // --- Core delegated getters ---\n\n get state(): PermissionStateOrUnsupported {\n return this._core.state;\n }\n\n get granted(): boolean {\n return this._core.granted;\n }\n\n get denied(): boolean {\n return this._core.denied;\n }\n\n get prompt(): boolean {\n return this._core.prompt;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // wc-bindable connectedCallbackPromise protocol: resolves once the connect-time\n // query settles, so SSR (@wcstack/server render.ts) waits for the first probe\n // before snapshotting the HTML. Mirrors WcsGeolocation.connectedCallbackPromise.\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Internal ---\n\n // Build the query descriptor from the current attributes. Only present extras\n // are included so a bare `{ name }` is passed for permissions that take no\n // additional members. The descriptor is fixed at connect time (v1 does not\n // re-query on a `name` change — see README).\n private _descriptor(): WcsPermissionDescriptor {\n const descriptor: WcsPermissionDescriptor = { name: this.name };\n if (this.userVisibleOnly) descriptor.userVisibleOnly = true;\n if (this.sysex) descriptor.sysex = true;\n return descriptor;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n // Begin observing (or revive the subscription after a reconnect). The\n // returned promise is held as connectedCallbackPromise for SSR. query() never\n // rejects in a way that escapes — failures surface as the `unsupported`\n // state — so no .catch() is needed.\n this._connectedCallbackPromise = this._core.observe(this._descriptor());\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsPermission } from \"./components/Permission.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.permission)) {\n customElements.define(config.tagNames.permission, WcsPermission);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapPermission(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:{permission:"wcs-permission"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const s of Object.keys(e))t(e[s]);return e}function s(e){if(null===e||"object"!=typeof e)return e;const t={};for(const i of Object.keys(e))t[i]=s(e[i]);return t}let i=null;const r=e;function n(){return i||(i=t(s(e))),i}class o extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"state",event:"wcs-permission:change"},{name:"granted",event:"wcs-permission:change",getter:e=>"granted"===e.detail},{name:"denied",event:"wcs-permission:change",getter:e=>"denied"===e.detail},{name:"prompt",event:"wcs-permission:change",getter:e=>"prompt"===e.detail},{name:"unsupported",event:"wcs-permission:change",getter:e=>"unsupported"===e.detail}],commands:[]};_target;_descriptor=null;_state="prompt";_permissionStatus=null;_permissionSubscribed=!1;_permGen=0;_ready=Promise.resolve();constructor(e,t){super(),this._target=t??this,e&&(this._descriptor=e,this._ready=this._initPermission())}get state(){return this._state}get granted(){return"granted"===this._state}get denied(){return"denied"===this._state}get prompt(){return"prompt"===this._state}get unsupported(){return"unsupported"===this._state}get ready(){return this._ready}_setState(e){this._state!==e&&(this._state=e,this._target.dispatchEvent(new CustomEvent("wcs-permission:change",{detail:e,bubbles:!0})))}observe(e){return this._descriptor=e,this._permissionSubscribed||(this._ready=this._initPermission()),this._ready}dispose(){this._permissionSubscribed=!1,this._permGen++,this._permissionStatus&&(this._permissionStatus.removeEventListener("change",this._onPermissionChange),this._permissionStatus=null)}_initPermission(){if(!this._descriptor||!this._descriptor.name)return this._setState("unsupported"),Promise.resolve();if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return this._setState("unsupported"),Promise.resolve();this._permissionSubscribed=!0;const e=++this._permGen;return navigator.permissions.query(this._descriptor).then(t=>{e===this._permGen&&(this._permissionStatus=t,this._setState(t.state),t.addEventListener("change",this._onPermissionChange))},()=>{e===this._permGen&&this._setState("unsupported")})}_onPermissionChange=e=>{const t=e.target;this._setState(t.state)}}class a extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...o.wcBindable,inputs:[{name:"name",attribute:"name"},{name:"userVisibleOnly",attribute:"user-visible-only"},{name:"sysex",attribute:"sysex"}],commands:[]};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new o(null,this)}get name(){return this.getAttribute("name")??""}set name(e){this.setAttribute("name",e)}get userVisibleOnly(){return this.hasAttribute("user-visible-only")}set userVisibleOnly(e){e?this.setAttribute("user-visible-only",""):this.removeAttribute("user-visible-only")}get sysex(){return this.hasAttribute("sysex")}set sysex(e){e?this.setAttribute("sysex",""):this.removeAttribute("sysex")}get state(){return this._core.state}get granted(){return this._core.granted}get denied(){return this._core.denied}get prompt(){return this._core.prompt}get unsupported(){return this._core.unsupported}get connectedCallbackPromise(){return this._connectedCallbackPromise}_descriptor(){const e={name:this.name};return this.userVisibleOnly&&(e.userVisibleOnly=!0),this.sysex&&(e.sysex=!0),e}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.observe(this._descriptor())}disconnectedCallback(){this._core.dispose()}}function u(t){var s;t&&((s=t).tagNames&&Object.assign(e.tagNames,s.tagNames),i=null),customElements.get(r.tagNames.permission)||customElements.define(r.tagNames.permission,a)}export{o as PermissionCore,a as WcsPermission,u as bootstrapPermission,n 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/PermissionCore.ts","../src/components/Permission.ts","../src/bootstrapPermission.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n permission: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n permission: \"wcs-permission\",\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 {\n IWcBindable, PermissionStateOrUnsupported, WcsPermissionDescriptor,\n} from \"../types.js\";\n\n/**\n * Headless permission-state primitive. A thin, framework-agnostic wrapper around\n * the Permissions API exposed through the wc-bindable protocol.\n *\n * Unlike the other @wcstack IO nodes (geolocation / clipboard / sse / …), the\n * Permissions API is **read-only**: it has `query()` but no standard `request()`.\n * Asking the user for a grant is the job of the feature node (`<wcs-geo>` etc.);\n * this node only *observes*. It is therefore a pure element → state monitor with\n * **no commands** — command-token does not apply, only event-token.\n *\n * The single observable is `state` (`navigator.permissions.query(descriptor)`'s\n * `PermissionState`, or `\"unsupported\"`), published via the `wcs-permission:change`\n * event. `granted` / `denied` / `prompt` / `unsupported` are convenience booleans\n * derived from that one event (mirroring how GeolocationCore exposes latitude/…\n * from one `wcs-geo:position` event), so a binding like `hidden@granted` works\n * directly. The live `change` event of the PermissionStatus is tracked so a grant\n * flipping in browser settings flows into the declarative state.\n */\nexport class PermissionCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"state\", event: \"wcs-permission:change\" },\n { name: \"granted\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"granted\" },\n { name: \"denied\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"denied\" },\n { name: \"prompt\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"prompt\" },\n { name: \"unsupported\", event: \"wcs-permission:change\", getter: (e: Event) => (e as CustomEvent).detail === \"unsupported\" },\n ],\n // No commands: the Permissions API is read-only (query-only). See class docs.\n commands: [],\n };\n\n private _target: EventTarget;\n private _descriptor: WcsPermissionDescriptor | null = null;\n\n private _state: PermissionStateOrUnsupported = \"prompt\";\n\n // Live PermissionStatus handle (when the Permissions API is available), kept so\n // the `change` listener can be removed on dispose().\n private _permissionStatus: PermissionStatus | null = null;\n\n // True once a permission subscription has been (or is being) established, and\n // reset by dispose(). Guards observe() so a reconnect after dispose() re-queries\n // while a redundant observe() on an already-live subscription does not.\n private _permissionSubscribed: boolean = false;\n\n // Monotonic id of the current permission query. Bumped by every _initPermission()\n // and by dispose(). Each in-flight query captures its id and, on resolve, bails\n // unless it is still current — so a query superseded by a rapid (synchronous)\n // disconnect→reconnect, or one that resolves after dispose(), never attaches a\n // listener. A plain boolean cannot cover this: dispose()→observe() flips it\n // false→true again, reopening the window for the stale query to slip through.\n private _permGen: number = 0;\n\n // Resolves once the most recent query settles (or immediately when the API is\n // unsupported). The Shell exposes this as connectedCallbackPromise so SSR can\n // await the first probe before snapshotting the HTML.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(descriptor?: WcsPermissionDescriptor | null, target?: EventTarget) {\n super();\n this._target = target ?? this;\n // Headless ergonomics: when a descriptor is supplied up front, probe the\n // permission state immediately so observers see the real value before the\n // first read. The Shell passes nothing and drives the first query from\n // connectedCallback via observe(), once the element's attributes resolve.\n if (descriptor) {\n this._descriptor = descriptor;\n this._ready = this._initPermission();\n }\n }\n\n get state(): PermissionStateOrUnsupported {\n return this._state;\n }\n\n get granted(): boolean {\n return this._state === \"granted\";\n }\n\n get denied(): boolean {\n return this._state === \"denied\";\n }\n\n get prompt(): boolean {\n return this._state === \"prompt\";\n }\n\n get unsupported(): boolean {\n return this._state === \"unsupported\";\n }\n\n /** Resolves once the current (or initial) query settles. */\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // --- State setter with event dispatch ---\n\n private _setState(state: PermissionStateOrUnsupported): void {\n // Same-value guard: `state` is the only stored value and the derived booleans\n // change in lockstep with it, so suppressing identical re-dispatches is safe.\n if (this._state === state) return;\n this._state = state;\n this._target.dispatchEvent(new CustomEvent(\"wcs-permission:change\", {\n detail: state,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Start observing `descriptor` (e.g. `{ name: \"geolocation\" }`). Idempotent\n * while already subscribed — calling it again only updates the stored descriptor\n * for a *future* re-subscription; it does **not** re-query, even when called with\n * a different descriptor (the Shell binds at a fixed connect-time descriptor and\n * does not re-query on a `name` change in v1). To switch permission mid-life,\n * dispose() first, then observe() the new descriptor. On the first call, or after\n * a dispose(), it issues the query and subscribes to the live `change` event.\n * Returns a promise that resolves once that query settles, for SSR.\n */\n observe(descriptor: WcsPermissionDescriptor): Promise<void> {\n this._descriptor = descriptor;\n if (!this._permissionSubscribed) {\n this._ready = this._initPermission();\n }\n return this._ready;\n }\n\n /**\n * Detach the live permission `change` listener. Call from the Shell's\n * `disconnectedCallback` so a removed element does not leak the subscription.\n * A later reconnect can re-subscribe via observe().\n *\n * Headless callers (using PermissionCore directly, without the Shell) own this\n * lifecycle themselves: call dispose() when the observer is no longer needed,\n * otherwise the live PermissionStatus `change` listener keeps this instance\n * reachable for as long as the status is alive. dispose() is safe to call when\n * never subscribed and may be paired with a later observe() to resume.\n */\n dispose(): void {\n this._permissionSubscribed = false;\n // Invalidate any in-flight query so its .then() bails instead of attaching a\n // listener after teardown.\n this._permGen++;\n if (this._permissionStatus) {\n this._permissionStatus.removeEventListener(\"change\", this._onPermissionChange);\n this._permissionStatus = null;\n }\n }\n\n // --- Internal ---\n\n private _initPermission(): Promise<void> {\n // Guard a missing/empty permission name (e.g. a `<wcs-permission>` with no\n // `name` attribute). Such a descriptor would only ever reject at query() and\n // silently fall back to \"unsupported\", which is hard to diagnose. Short-circuit\n // to \"unsupported\" without issuing a doomed query so the misconfiguration\n // surfaces deterministically and no listener is attached.\n if (!this._descriptor || !this._descriptor.name) {\n this._setState(\"unsupported\");\n return Promise.resolve();\n }\n // The Permissions API is optional. When absent (or it rejects, e.g. the\n // browser does not accept the requested permission name), report \"unsupported\"\n // and leave it at that — there is nothing to retry.\n if (typeof navigator === \"undefined\" || !navigator.permissions || typeof navigator.permissions.query !== \"function\") {\n // Route through _setState (not a bare assignment) so observers stay in sync\n // with the public state. The same-value guard means no redundant dispatch\n // when the state does not actually change.\n this._setState(\"unsupported\");\n // Intentionally does NOT set _permissionSubscribed: there is no listener to\n // tear down, so a reconnect simply re-probes (idempotent — the same-value\n // guard suppresses any dispatch and no listener is ever attached). Mirrors\n // GeolocationCore's reinitPermission behavior in unsupported environments.\n return Promise.resolve();\n }\n this._permissionSubscribed = true;\n const gen = ++this._permGen;\n // Cast: WcsPermissionDescriptor widens `name` to string (and allows extra\n // descriptor members like userVisibleOnly / sysex) where the lib DOM type\n // expects the PermissionName union.\n return navigator.permissions.query(this._descriptor as unknown as PermissionDescriptor).then(\n (status) => {\n // Stale resolution: this query was superseded (rapid reconnect) or the\n // element was disposed while it was in flight. Drop it so only the current\n // subscription attaches a listener.\n if (gen !== this._permGen) return;\n this._permissionStatus = status;\n this._setState(status.state as PermissionStateOrUnsupported);\n status.addEventListener(\"change\", this._onPermissionChange);\n },\n () => {\n if (gen !== this._permGen) return;\n this._setState(\"unsupported\");\n },\n );\n }\n\n private _onPermissionChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._setState(status.state as PermissionStateOrUnsupported);\n };\n}\n","import { IWcBindable, PermissionStateOrUnsupported, WcsPermissionDescriptor } from \"../types.js\";\nimport { PermissionCore } from \"../core/PermissionCore.js\";\n\n// Named WcsPermission (not `Permission`) so the class does not shadow any global,\n// and to match the <wcs-geo> / <wcs-ws> convention (WcsGeolocation /\n// WcsWebSocket). The public export keeps the `WcsPermission` name unchanged.\nexport class WcsPermission extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...PermissionCore.wcBindable,\n // Shell-level settable surface. `name` is the permission name; the descriptor\n // extras `user-visible-only` (push) and `sysex` (midi) are boolean flags that\n // reflect idempotently, so a binding system that writes through\n // inputs[].attribute is safe.\n inputs: [\n { name: \"name\", attribute: \"name\" },\n { name: \"userVisibleOnly\", attribute: \"user-visible-only\" },\n { name: \"sysex\", attribute: \"sysex\" },\n ],\n // No commands: read-only monitor (see PermissionCore). The Permissions API has\n // no request() — acquiring a grant is the feature node's job.\n commands: [],\n };\n\n // Created in the Shell constructor with no descriptor so the delegated getters\n // work before connect (returning the default \"prompt\" / false). The actual\n // query is driven from connectedCallback once the element's attributes resolve\n // (programmatically-created elements have no attributes at construction time).\n private _core: PermissionCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new PermissionCore(null, this);\n }\n\n // --- Attribute accessors ---\n\n get name(): string {\n return this.getAttribute(\"name\") ?? \"\";\n }\n\n set name(value: string) {\n this.setAttribute(\"name\", value);\n }\n\n get userVisibleOnly(): boolean {\n return this.hasAttribute(\"user-visible-only\");\n }\n\n set userVisibleOnly(value: boolean) {\n if (value) {\n this.setAttribute(\"user-visible-only\", \"\");\n } else {\n this.removeAttribute(\"user-visible-only\");\n }\n }\n\n get sysex(): boolean {\n return this.hasAttribute(\"sysex\");\n }\n\n set sysex(value: boolean) {\n if (value) {\n this.setAttribute(\"sysex\", \"\");\n } else {\n this.removeAttribute(\"sysex\");\n }\n }\n\n // --- Core delegated getters ---\n\n get state(): PermissionStateOrUnsupported {\n return this._core.state;\n }\n\n get granted(): boolean {\n return this._core.granted;\n }\n\n get denied(): boolean {\n return this._core.denied;\n }\n\n get prompt(): boolean {\n return this._core.prompt;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // wc-bindable connectedCallbackPromise protocol: resolves once the connect-time\n // query settles, so SSR (@wcstack/server render.ts) waits for the first probe\n // before snapshotting the HTML. Mirrors WcsGeolocation.connectedCallbackPromise.\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Internal ---\n\n // Build the query descriptor from the current attributes. Only present extras\n // are included so a bare `{ name }` is passed for permissions that take no\n // additional members. The descriptor is fixed at connect time (v1 does not\n // re-query on a `name` change — see README).\n private _descriptor(): WcsPermissionDescriptor {\n const descriptor: WcsPermissionDescriptor = { name: this.name };\n if (this.userVisibleOnly) descriptor.userVisibleOnly = true;\n if (this.sysex) descriptor.sysex = true;\n return descriptor;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n // Begin observing (or revive the subscription after a reconnect). The\n // returned promise is held as connectedCallbackPromise for SSR. query() never\n // rejects in a way that escapes — failures surface as the `unsupported`\n // state — so no .catch() is needed.\n this._connectedCallbackPromise = this._core.observe(this._descriptor());\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 bootstrapPermission(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsPermission } from \"./components/Permission.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.permission)) {\n customElements.define(config.tagNames.permission, WcsPermission);\n }\n}\n"],"names":["_config","tagNames","permission","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","PermissionCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","commands","_target","_descriptor","_state","_permissionStatus","_permissionSubscribed","_permGen","_ready","Promise","resolve","constructor","descriptor","target","super","this","_initPermission","state","granted","denied","prompt","unsupported","ready","_setState","dispatchEvent","CustomEvent","bubbles","observe","dispose","removeEventListener","_onPermissionChange","navigator","permissions","query","gen","then","status","addEventListener","WcsPermission","HTMLElement","wcBindable","inputs","attribute","_core","_connectedCallbackPromise","getAttribute","value","setAttribute","userVisibleOnly","hasAttribute","removeAttribute","sysex","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapPermission","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@wcstack/permission",
3
+ "version": "1.13.1",
4
+ "description": "Declarative permission-state component for Web Components. Framework-agnostic Permissions API 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
+ "permissions",
34
+ "permission",
35
+ "permissions-api",
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/permission"
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
+ }