@wcstack/permission 1.16.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -119,6 +119,62 @@ npm install @wcstack/permission
119
119
  - **SSR(`@wcstack/server`)。** `static hasConnectedCallbackPromise = true` を宣言し `connectedCallbackPromise` を公開するため、サーバレンダラは接続時 query の settle を待ってからスナップショットします。
120
120
  - **サイレント失敗処理(zero-log)。** wcstack のゼロ依存方針に沿い、`<wcs-permission>` はログも例外も出しません。Permissions API が無い場合、権限名が拒否される場合、`name` 属性が未指定/空の場合はいずれも静かに `state = "unsupported"` に解決します。`unsupported`(または `state`)をバインドして反応してください。
121
121
 
122
+ ## `:state()` による CSS スタイリング
123
+
124
+ `<wcs-permission>` は相互排他な 4 つの出力ステートを
125
+ [`ElementInternals` の `CustomStateSet`](https://developer.mozilla.org/ja/docs/Web/API/CustomStateSet)
126
+ に反映します。そのため `data-wcs` バインディングやクラスの手動トグルなしに、CSS の
127
+ `:state()` 疑似クラスで直接スタイリングできます。
128
+
129
+ | ステート | on になる条件 |
130
+ |----------|----------------|
131
+ | `granted` | `wcs-permission:change` が `"granted"` で発火 |
132
+ | `denied` | `wcs-permission:change` が `"denied"` で発火 |
133
+ | `prompt` | `wcs-permission:change` が `"prompt"` で発火 |
134
+ | `unsupported` | `wcs-permission:change` が `"unsupported"` で発火 |
135
+
136
+ この 4 つは**相互排他**です。1 回の `wcs-permission:change` イベントで 4 つ全てが
137
+ 同時に更新されるため、常にどれか 1 つだけが on になります。
138
+
139
+ ```css
140
+ wcs-permission:state(denied) ~ .fallback { display: block; }
141
+ wcs-permission:state(granted) ~ .fallback { display: none; } /* デフォルト */
142
+ ```
143
+
144
+ 属性やクラスと異なり `:state()` は要素の外部から書き込めないため、この出力ステートが
145
+ 入力と混同される心配がありません。
146
+
147
+ **対応ブラウザ**(新構文 `:state(x)`): Chrome/Edge 125+、Safari 17.4+、Firefox 126+。
148
+ 非対応の環境ではステートが一切 set されないだけです — `:state()` セレクタがマッチしなく
149
+ なりますが、`<wcs-permission>` 自体は通常どおり動作し続けます(graceful degradation・never-throw)。
150
+
151
+ **SSR:** `:state()` は HTML にシリアライズできないため、サーバーレンダリングされた
152
+ マークアップの初期ペイントにはこれらのステートは乗りません(`@wcstack/server` は無改変)。
153
+ ハイドレーション前の見た目を制御したい場合は、代わりに `wcs-permission:not(:defined)` と
154
+ 組み合わせてください。
155
+
156
+ ### デバッグ
157
+
158
+ カスタムステートは DevTools の Elements パネルには表示されず、`attachInternals()`
159
+ は同一要素に 2 回呼べないため、コンソールから直接覗く手段がありません。そのための
160
+ デバッグ専用の補助を 2 つ用意しています:
161
+
162
+ - `el.debugStates` — 現在 on になっているステート名の**スナップショット**配列
163
+ (例: `["granted"]`)。`wc-bindable` の一部ではなく(バインド対象ではない)、
164
+ 形状も契約として保証されません — デバッグ用途にのみ使ってください。
165
+ - `debug-states` 属性(opt-in・既定 OFF)は、ステート変化を要素の
166
+ `data-wcs-state-granted` / `data-wcs-state-denied` / `data-wcs-state-prompt` /
167
+ `data-wcs-state-unsupported` 属性にミラーします。
168
+ Elements パネルを開いておけば、トグルのたびにハイライトされます:
169
+
170
+ ```html
171
+ <wcs-permission name="geolocation" debug-states></wcs-permission>
172
+ ```
173
+
174
+ **CSS は `data-wcs-state-*` ではなく `:state()` に書いてください。** ミラーされた
175
+ 属性は、DevTools を開いた状態でステート変化を可視化するためだけのものであり、
176
+ スタイリング用の正式なフックではありません。
177
+
122
178
  ## ヘッドレス利用(`PermissionCore`)
123
179
 
124
180
  Core は DOM 非依存で、`@wc-bindable/core` の `bind()` と直接使えます:
package/README.md CHANGED
@@ -119,6 +119,63 @@ All five derive from the single `wcs-permission:change` event (the booleans chan
119
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
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
121
 
122
+ ## CSS styling with `:state()`
123
+
124
+ `<wcs-permission>` reflects its four mutually-exclusive output states onto its
125
+ [`ElementInternals` `CustomStateSet`](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet),
126
+ so you can style it directly from CSS with the `:state()` pseudo-class — no
127
+ `data-wcs` binding or extra class toggling required.
128
+
129
+ | State | On when |
130
+ |-------|---------|
131
+ | `granted` | `wcs-permission:change` fires with `"granted"` |
132
+ | `denied` | `wcs-permission:change` fires with `"denied"` |
133
+ | `prompt` | `wcs-permission:change` fires with `"prompt"` |
134
+ | `unsupported` | `wcs-permission:change` fires with `"unsupported"` |
135
+
136
+ These four states are **mutually exclusive**: a single `wcs-permission:change`
137
+ event updates all four together, so exactly one is ever on at a time.
138
+
139
+ ```css
140
+ wcs-permission:state(denied) ~ .fallback { display: block; }
141
+ wcs-permission:state(granted) ~ .fallback { display: none; } /* default */
142
+ ```
143
+
144
+ Unlike attributes or classes, `:state()` cannot be written from outside the
145
+ element, so there is no risk of confusing this output state with an input.
146
+
147
+ **Browser support** (`:state(x)` syntax): Chrome/Edge 125+, Safari 17.4+,
148
+ Firefox 126+. In older browsers the states are simply never set — `:state()`
149
+ selectors never match, but `<wcs-permission>` itself keeps working normally
150
+ (graceful degradation, never-throw).
151
+
152
+ **SSR**: `:state()` cannot be serialized into HTML, so server-rendered markup
153
+ never carries these states on first paint (`@wcstack/server` is unaffected).
154
+ If you need to style the pre-hydration gap, pair your rule with
155
+ `wcs-permission:not(:defined)` instead.
156
+
157
+ ### Debugging
158
+
159
+ Custom states are invisible in DevTools' Elements panel and `attachInternals()`
160
+ cannot be called twice, so there is no console way to inspect them directly.
161
+ Two debug-only aids are provided for that:
162
+
163
+ - `el.debugStates` — a **snapshot** array of the currently-on state names
164
+ (e.g. `["granted"]`). It is not part of `wc-bindable` (not a bind target)
165
+ and its shape is not a guaranteed contract — use it for debugging only.
166
+ - The `debug-states` attribute (opt-in, default off) mirrors state changes
167
+ onto `data-wcs-state-granted` / `data-wcs-state-denied` /
168
+ `data-wcs-state-prompt` / `data-wcs-state-unsupported` attributes on the
169
+ element, so the Elements panel highlights them as they toggle:
170
+
171
+ ```html
172
+ <wcs-permission name="geolocation" debug-states></wcs-permission>
173
+ ```
174
+
175
+ **Write your CSS against `:state()`, not `data-wcs-state-*`.** The mirrored
176
+ attributes exist purely to make state changes visible while debugging with
177
+ DevTools open; they are not a supported styling hook.
178
+
122
179
  ## Headless usage (`PermissionCore`)
123
180
 
124
181
  The Core has no DOM dependency and can be used directly with `bind()` from `@wc-bindable/core`:
package/dist/index.d.ts CHANGED
@@ -163,7 +163,11 @@ declare class WcsPermission extends HTMLElement {
163
163
  static wcBindable: IWcBindable;
164
164
  private _core;
165
165
  private _connectedCallbackPromise;
166
+ private _internals;
166
167
  constructor();
168
+ get debugStates(): string[];
169
+ private _initInternals;
170
+ private _wireStates;
167
171
  get name(): string;
168
172
  set name(value: string);
169
173
  get userVisibleOnly(): boolean;
package/dist/index.esm.js CHANGED
@@ -248,9 +248,67 @@ class WcsPermission extends HTMLElement {
248
248
  // (programmatically-created elements have no attributes at construction time).
249
249
  _core;
250
250
  _connectedCallbackPromise = Promise.resolve();
251
+ _internals = null;
251
252
  constructor() {
252
253
  super();
253
254
  this._core = new PermissionCore(null, this);
255
+ this._internals = this._initInternals();
256
+ this._wireStates({
257
+ "wcs-permission:change": (d) => ({
258
+ granted: d === "granted",
259
+ denied: d === "denied",
260
+ prompt: d === "prompt",
261
+ unsupported: d === "unsupported",
262
+ }),
263
+ });
264
+ }
265
+ // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of
266
+ // wc-bindable (not a bind target); see README "CSS styling with :state()".
267
+ // MUST NOT return the live CustomStateSet (that would let callers write
268
+ // states from outside, defeating the point of :state() being read-only).
269
+ get debugStates() {
270
+ return this._internals ? [...this._internals.states] : [];
271
+ }
272
+ _initInternals() {
273
+ // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent
274
+ // in happy-dom / older environments, and pre-125 Chromium rejects
275
+ // non-dashed state names from states.add() (probed and discarded here).
276
+ // Either case silently disables reflection — the component still works,
277
+ // it just doesn't expose :state() selectors.
278
+ try {
279
+ if (typeof this.attachInternals !== "function")
280
+ return null;
281
+ const internals = this.attachInternals();
282
+ internals.states.add("wcs-probe");
283
+ internals.states.delete("wcs-probe");
284
+ return internals;
285
+ }
286
+ catch {
287
+ return null;
288
+ }
289
+ }
290
+ _wireStates(map) {
291
+ if (this._internals === null)
292
+ return;
293
+ const states = this._internals.states;
294
+ for (const [event, toStates] of Object.entries(map)) {
295
+ this.addEventListener(event, (e) => {
296
+ const debug = this.hasAttribute("debug-states");
297
+ for (const [name, on] of Object.entries(toStates(e.detail))) {
298
+ try {
299
+ if (on) {
300
+ states.add(name);
301
+ }
302
+ else {
303
+ states.delete(name);
304
+ }
305
+ }
306
+ catch { /* never-throw */ }
307
+ if (debug)
308
+ this.toggleAttribute(`data-wcs-state-${name}`, on);
309
+ }
310
+ });
311
+ }
254
312
  }
255
313
  // --- Attribute accessors ---
256
314
  get name() {
@@ -1 +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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"}
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 private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new PermissionCore(null, this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-permission:change\": (d) => ({\n granted: d === \"granted\",\n denied: d === \"denied\",\n prompt: d === \"prompt\",\n unsupported: d === \"unsupported\",\n }),\n });\n }\n\n // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of\n // wc-bindable (not a bind target); see README \"CSS styling with :state()\".\n // MUST NOT return the live CustomStateSet (that would let callers write\n // states from outside, defeating the point of :state() being read-only).\n get debugStates(): string[] {\n return this._internals ? [...this._internals.states] : [];\n }\n\n private _initInternals(): ElementInternals | null {\n // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent\n // in happy-dom / older environments, and pre-125 Chromium rejects\n // non-dashed state names from states.add() (probed and discarded here).\n // Either case silently disables reflection — the component still works,\n // it just doesn't expose :state() selectors.\n try {\n if (typeof this.attachInternals !== \"function\") return null;\n const internals = this.attachInternals();\n internals.states.add(\"wcs-probe\");\n internals.states.delete(\"wcs-probe\");\n return internals;\n } catch {\n return null;\n }\n }\n\n private _wireStates(map: Record<string, (detail: any) => Record<string, boolean>>): void {\n if (this._internals === null) return;\n const states = this._internals.states;\n for (const [event, toStates] of Object.entries(map)) {\n this.addEventListener(event, (e) => {\n const debug = this.hasAttribute(\"debug-states\");\n for (const [name, on] of Object.entries(toStates((e as CustomEvent).detail))) {\n try {\n if (on) { states.add(name); } else { states.delete(name); }\n } catch { /* never-throw */ }\n if (debug) this.toggleAttribute(`data-wcs-state-${name}`, on);\n }\n });\n }\n }\n\n // --- Attribute accessors ---\n\n 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":"AAQA,MAAM,OAAO,GAAoB;AAC/B,IAAA,QAAQ,EAAE;AACR,QAAA,UAAU,EAAE,gBAAgB;AAC7B,KAAA;CACF;AAED,SAAS,UAAU,CAAI,GAAM,EAAA;AAC3B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;AACvD,IAAA,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAClC,QAAA,UAAU,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IACnD;AACA,IAAA,OAAO,GAAG;AACZ;AAEA,SAAS,SAAS,CAAI,GAAM,EAAA;AAC1B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;IACvD,MAAM,KAAK,GAA4B,EAAE;IACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAClC,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IAC/D;AACA,IAAA,OAAO,KAAU;AACnB;AAEA,IAAI,YAAY,GAAmB,IAAI;AAEhC,MAAM,MAAM,GAAY,OAAkB;SAEjC,SAAS,GAAA;IACvB,IAAI,CAAC,YAAY,EAAE;QACjB,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC/C;AACA,IAAA,OAAO,YAAY;AACrB;AAEM,SAAU,SAAS,CAAC,aAA8B,EAAA;AACtD,IAAA,IAAI,aAAa,CAAC,QAAQ,EAAE;QAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IACzD;IACA,YAAY,GAAG,IAAI;AACrB;;AC5CA;;;;;;;;;;;;;;;;;AAiBG;AACG,MAAO,cAAe,SAAQ,WAAW,CAAA;IAC7C,OAAO,UAAU,GAAgB;AAC/B,QAAA,QAAQ,EAAE,aAAa;AACvB,QAAA,OAAO,EAAE,CAAC;AACV,QAAA,UAAU,EAAE;AACV,YAAA,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE;YACjD,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,CAAQ,KAAM,CAAiB,CAAC,MAAM,KAAK,SAAS,EAAE;YAClH,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,CAAQ,KAAM,CAAiB,CAAC,MAAM,KAAK,QAAQ,EAAE;YAChH,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,CAAQ,KAAM,CAAiB,CAAC,MAAM,KAAK,QAAQ,EAAE;YAChH,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,CAAQ,KAAM,CAAiB,CAAC,MAAM,KAAK,aAAa,EAAE;AAC3H,SAAA;;AAED,QAAA,QAAQ,EAAE,EAAE;KACb;AAEO,IAAA,OAAO;IACP,WAAW,GAAmC,IAAI;IAElD,MAAM,GAAiC,QAAQ;;;IAI/C,iBAAiB,GAA4B,IAAI;;;;IAKjD,qBAAqB,GAAY,KAAK;;;;;;;IAQtC,QAAQ,GAAW,CAAC;;;;AAKpB,IAAA,MAAM,GAAkB,OAAO,CAAC,OAAO,EAAE;IAEjD,WAAA,CAAY,UAA2C,EAAE,MAAoB,EAAA;AAC3E,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,IAAI;;;;;QAK7B,IAAI,UAAU,EAAE;AACd,YAAA,IAAI,CAAC,WAAW,GAAG,UAAU;AAC7B,YAAA,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE;QACtC;IACF;AAEA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA,IAAA,IAAI,OAAO,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS;IAClC;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;IACjC;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;IACjC;AAEA,IAAA,IAAI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,aAAa;IACtC;;AAGA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;;AAIQ,IAAA,SAAS,CAAC,KAAmC,EAAA;;;AAGnD,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK;YAAE;AAC3B,QAAA,IAAI,CAAC,MAAM,GAAG,KAAK;QACnB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,uBAAuB,EAAE;AAClE,YAAA,MAAM,EAAE,KAAK;AACb,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;;AAIA;;;;;;;;;AASG;AACH,IAAA,OAAO,CAAC,UAAmC,EAAA;AACzC,QAAA,IAAI,CAAC,WAAW,GAAG,UAAU;AAC7B,QAAA,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE;AAC/B,YAAA,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE;QACtC;QACA,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA;;;;;;;;;;AAUG;IACH,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,qBAAqB,GAAG,KAAK;;;QAGlC,IAAI,CAAC,QAAQ,EAAE;AACf,QAAA,IAAI,IAAI,CAAC,iBAAiB,EAAE;YAC1B,IAAI,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,mBAAmB,CAAC;AAC9E,YAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI;QAC/B;IACF;;IAIQ,eAAe,GAAA;;;;;;AAMrB,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;AAC/C,YAAA,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;AAC7B,YAAA,OAAO,OAAO,CAAC,OAAO,EAAE;QAC1B;;;;AAIA,QAAA,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,WAAW,IAAI,OAAO,SAAS,CAAC,WAAW,CAAC,KAAK,KAAK,UAAU,EAAE;;;;AAInH,YAAA,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;;;;;AAK7B,YAAA,OAAO,OAAO,CAAC,OAAO,EAAE;QAC1B;AACA,QAAA,IAAI,CAAC,qBAAqB,GAAG,IAAI;AACjC,QAAA,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,QAAQ;;;;AAI3B,QAAA,OAAO,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,WAA8C,CAAC,CAAC,IAAI,CAC1F,CAAC,MAAM,KAAI;;;;AAIT,YAAA,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ;gBAAE;AAC3B,YAAA,IAAI,CAAC,iBAAiB,GAAG,MAAM;AAC/B,YAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAqC,CAAC;YAC5D,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,mBAAmB,CAAC;QAC7D,CAAC,EACD,MAAK;AACH,YAAA,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ;gBAAE;AAC3B,YAAA,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;AAC/B,QAAA,CAAC,CACF;IACH;AAEQ,IAAA,mBAAmB,GAAG,CAAC,KAAY,KAAU;AACnD,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;AAC/C,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAqC,CAAC;AAC9D,IAAA,CAAC;;;AC7MH;AACA;AACA;AACM,MAAO,aAAc,SAAQ,WAAW,CAAA;AAC5C,IAAA,OAAO,2BAA2B,GAAG,IAAI;IACzC,OAAO,UAAU,GAAgB;QAC/B,GAAG,cAAc,CAAC,UAAU;;;;;AAK5B,QAAA,MAAM,EAAE;AACN,YAAA,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE;AACnC,YAAA,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,mBAAmB,EAAE;AAC3D,YAAA,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE;AACtC,SAAA;;;AAGD,QAAA,QAAQ,EAAE,EAAE;KACb;;;;;AAMO,IAAA,KAAK;AACL,IAAA,yBAAyB,GAAkB,OAAO,CAAC,OAAO,EAAE;IAC5D,UAAU,GAA4B,IAAI;AAElD,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;QACP,IAAI,CAAC,KAAK,GAAG,IAAI,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC;AAC3C,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE;QACvC,IAAI,CAAC,WAAW,CAAC;AACf,YAAA,uBAAuB,EAAE,CAAC,CAAC,MAAM;gBAC/B,OAAO,EAAE,CAAC,KAAK,SAAS;gBACxB,MAAM,EAAE,CAAC,KAAK,QAAQ;gBACtB,MAAM,EAAE,CAAC,KAAK,QAAQ;gBACtB,WAAW,EAAE,CAAC,KAAK,aAAa;aACjC,CAAC;AACH,SAAA,CAAC;IACJ;;;;;AAMA,IAAA,IAAI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE;IAC3D;IAEQ,cAAc,GAAA;;;;;;AAMpB,QAAA,IAAI;AACF,YAAA,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,UAAU;AAAE,gBAAA,OAAO,IAAI;AAC3D,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,EAAE;AACxC,YAAA,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;AACjC,YAAA,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;AACpC,YAAA,OAAO,SAAS;QAClB;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,IAAI;QACb;IACF;AAEQ,IAAA,WAAW,CAAC,GAA6D,EAAA;AAC/E,QAAA,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI;YAAE;AAC9B,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM;AACrC,QAAA,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACnD,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,KAAI;gBACjC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC;AAC/C,gBAAA,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAE,CAAiB,CAAC,MAAM,CAAC,CAAC,EAAE;AAC5E,oBAAA,IAAI;wBACF,IAAI,EAAE,EAAE;AAAE,4BAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;wBAAE;6BAAO;AAAE,4BAAA,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;wBAAE;oBAC5D;AAAE,oBAAA,MAAM,oBAAoB;AAC5B,oBAAA,IAAI,KAAK;wBAAE,IAAI,CAAC,eAAe,CAAC,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAE,EAAE,EAAE,CAAC;gBAC/D;AACF,YAAA,CAAC,CAAC;QACJ;IACF;;AAIA,IAAA,IAAI,IAAI,GAAA;QACN,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE;IACxC;IAEA,IAAI,IAAI,CAAC,KAAa,EAAA;AACpB,QAAA,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAClC;AAEA,IAAA,IAAI,eAAe,GAAA;AACjB,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC;IAC/C;IAEA,IAAI,eAAe,CAAC,KAAc,EAAA;QAChC,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,mBAAmB,EAAE,EAAE,CAAC;QAC5C;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC;QAC3C;IACF;AAEA,IAAA,IAAI,KAAK,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;IACnC;IAEA,IAAI,KAAK,CAAC,KAAc,EAAA;QACtB,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;QAChC;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QAC/B;IACF;;AAIA,IAAA,IAAI,KAAK,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK;IACzB;AAEA,IAAA,IAAI,OAAO,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO;IAC3B;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM;IAC1B;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM;IAC1B;AAEA,IAAA,IAAI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW;IAC/B;;;;AAKA,IAAA,IAAI,wBAAwB,GAAA;QAC1B,OAAO,IAAI,CAAC,yBAAyB;IACvC;;;;;;IAQQ,WAAW,GAAA;QACjB,MAAM,UAAU,GAA4B,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QAC/D,IAAI,IAAI,CAAC,eAAe;AAAE,YAAA,UAAU,CAAC,eAAe,GAAG,IAAI;QAC3D,IAAI,IAAI,CAAC,KAAK;AAAE,YAAA,UAAU,CAAC,KAAK,GAAG,IAAI;AACvC,QAAA,OAAO,UAAU;IACnB;;IAIA,iBAAiB,GAAA;AACf,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;;;;;AAK3B,QAAA,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACzE;IAEA,oBAAoB,GAAA;AAClB,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IACtB;;;SC7Kc,kBAAkB,GAAA;AAChC,IAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;QACnD,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IAClE;AACF;;ACHM,SAAU,mBAAmB,CAAC,UAA4B,EAAA;IAC9D,IAAI,UAAU,EAAE;QACd,SAAS,CAAC,UAAU,CAAC;IACvB;AACA,IAAA,kBAAkB,EAAE;AACtB;;;;"}
@@ -1,2 +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};
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 n=e;function r(){return i||(i=t(s(e))),i}class a 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 o extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...a.wcBindable,inputs:[{name:"name",attribute:"name"},{name:"userVisibleOnly",attribute:"user-visible-only"},{name:"sysex",attribute:"sysex"}],commands:[]};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new a(null,this),this._internals=this._initInternals(),this._wireStates({"wcs-permission:change":e=>({granted:"granted"===e,denied:"denied"===e,prompt:"prompt"===e,unsupported:"unsupported"===e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[s,i]of Object.entries(e))this.addEventListener(s,e=>{const s=this.hasAttribute("debug-states");for(const[n,r]of Object.entries(i(e.detail))){try{r?t.add(n):t.delete(n)}catch{}s&&this.toggleAttribute(`data-wcs-state-${n}`,r)}})}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(n.tagNames.permission)||customElements.define(n.tagNames.permission,o)}export{a as PermissionCore,o as WcsPermission,u as bootstrapPermission,r as getConfig};
2
2
  //# sourceMappingURL=index.esm.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/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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"}
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 private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new PermissionCore(null, this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-permission:change\": (d) => ({\n granted: d === \"granted\",\n denied: d === \"denied\",\n prompt: d === \"prompt\",\n unsupported: d === \"unsupported\",\n }),\n });\n }\n\n // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of\n // wc-bindable (not a bind target); see README \"CSS styling with :state()\".\n // MUST NOT return the live CustomStateSet (that would let callers write\n // states from outside, defeating the point of :state() being read-only).\n get debugStates(): string[] {\n return this._internals ? [...this._internals.states] : [];\n }\n\n private _initInternals(): ElementInternals | null {\n // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent\n // in happy-dom / older environments, and pre-125 Chromium rejects\n // non-dashed state names from states.add() (probed and discarded here).\n // Either case silently disables reflection — the component still works,\n // it just doesn't expose :state() selectors.\n try {\n if (typeof this.attachInternals !== \"function\") return null;\n const internals = this.attachInternals();\n internals.states.add(\"wcs-probe\");\n internals.states.delete(\"wcs-probe\");\n return internals;\n } catch {\n return null;\n }\n }\n\n private _wireStates(map: Record<string, (detail: any) => Record<string, boolean>>): void {\n if (this._internals === null) return;\n const states = this._internals.states;\n for (const [event, toStates] of Object.entries(map)) {\n this.addEventListener(event, (e) => {\n const debug = this.hasAttribute(\"debug-states\");\n for (const [name, on] of Object.entries(toStates((e as CustomEvent).detail))) {\n try {\n if (on) { states.add(name); } else { states.delete(name); }\n } catch { /* never-throw */ }\n if (debug) this.toggleAttribute(`data-wcs-state-${name}`, on);\n }\n });\n }\n }\n\n // --- Attribute accessors ---\n\n 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","_internals","_initInternals","_wireStates","d","debugStates","states","attachInternals","internals","add","delete","map","toStates","entries","debug","hasAttribute","on","toggleAttribute","getAttribute","value","setAttribute","userVisibleOnly","removeAttribute","sysex","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapPermission","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/permission",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Declarative permission-state component for Web Components. Framework-agnostic Permissions API monitor via wc-bindable-protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",