@wcstack/contacts 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
@@ -65,6 +65,60 @@ npm install @wcstack/contacts
65
65
 
66
66
  **無し。**
67
67
 
68
+ ## `:state()` による CSS スタイリング
69
+
70
+ `<wcs-contacts>` は 3 つの boolean 出力ステートを
71
+ [`ElementInternals` の `CustomStateSet`](https://developer.mozilla.org/ja/docs/Web/API/CustomStateSet)
72
+ に反映します。そのため `data-wcs` バインディングやクラスの手動トグルなしに、CSS の
73
+ `:state()` 疑似クラスで直接スタイリングできます。
74
+
75
+ | ステート | on になる条件 |
76
+ |----------|----------------|
77
+ | `loading` | `wcs-contacts:loading-changed` が `true` で発火(`false` でクリア) |
78
+ | `cancelled` | `wcs-contacts:cancelled-changed` が `true` で発火(`false` でクリア) |
79
+ | `error` | `wcs-contacts:error` が非 `null` の detail で発火(`null` でクリア) |
80
+
81
+ ```css
82
+ wcs-contacts:state(loading) ~ .spinner { display: block; }
83
+ wcs-contacts:state(loading) ~ .spinner { display: none; } /* デフォルト */
84
+
85
+ form:has(wcs-contacts:state(error)) .banner { display: block; }
86
+ form:has(wcs-contacts:state(cancelled)) .hint { display: block; }
87
+ ```
88
+
89
+ 属性やクラスと異なり `:state()` は要素の外部から書き込めないため、この出力ステートが
90
+ 入力と混同される心配がありません。
91
+
92
+ **対応ブラウザ**(新構文 `:state(x)`): Chrome/Edge 125+、Safari 17.4+、Firefox 126+。
93
+ 非対応の環境ではステートが一切 set されないだけです — `:state()` セレクタがマッチしなく
94
+ なりますが、`<wcs-contacts>` 自体は通常どおり動作し続けます(graceful degradation・never-throw)。
95
+
96
+ **SSR:** `:state()` は HTML にシリアライズできないため、サーバーレンダリングされた
97
+ マークアップの初期ペイントにはこれらのステートは乗りません(`@wcstack/server` は無改変)。
98
+ ハイドレーション前の見た目を制御したい場合は、代わりに `wcs-contacts:not(:defined)` と組み合わせてください。
99
+
100
+ ### デバッグ
101
+
102
+ カスタムステートは DevTools の Elements パネルには表示されず、`attachInternals()`
103
+ は同一要素に 2 回呼べないため、コンソールから直接覗く手段がありません。そのための
104
+ デバッグ専用の補助を 2 つ用意しています:
105
+
106
+ - `el.debugStates` — 現在 on になっているステート名の**スナップショット**配列
107
+ (例: `["loading"]`)。`wc-bindable` の一部ではなく(バインド対象ではない)、
108
+ 形状も契約として保証されません — デバッグ用途にのみ使ってください。
109
+ - `debug-states` 属性(opt-in・既定 OFF)は、ステート変化を要素の
110
+ `data-wcs-state-loading` / `data-wcs-state-cancelled` /
111
+ `data-wcs-state-error` 属性にミラーします。
112
+ Elements パネルを開いておけば、トグルのたびにハイライトされます:
113
+
114
+ ```html
115
+ <wcs-contacts debug-states></wcs-contacts>
116
+ ```
117
+
118
+ **CSS は `data-wcs-state-*` ではなく `:state()` に書いてください。** ミラーされた
119
+ 属性は、DevTools を開いた状態でステート変化を可視化するためだけのものであり、
120
+ スタイリング用の正式なフックではありません。
121
+
68
122
  ## 注意・制限
69
123
 
70
124
  - **Android Chrome限定。** unsupportedを例外ケースでなく既定として扱ってください。
package/README.md CHANGED
@@ -66,6 +66,62 @@ npm install @wcstack/contacts
66
66
 
67
67
  **None.**
68
68
 
69
+ ## CSS styling with `:state()`
70
+
71
+ `<wcs-contacts>` reflects three boolean output states onto its
72
+ [`ElementInternals` `CustomStateSet`](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet),
73
+ so you can style it directly from CSS with the `:state()` pseudo-class — no
74
+ `data-wcs` binding or extra class toggling required.
75
+
76
+ | State | On when |
77
+ |-------|---------|
78
+ | `loading` | `wcs-contacts:loading-changed` fires with `true` (cleared on `false`) |
79
+ | `cancelled` | `wcs-contacts:cancelled-changed` fires with `true` (cleared on `false`) |
80
+ | `error` | `wcs-contacts:error` fires with a non-`null` detail (cleared on `null`) |
81
+
82
+ ```css
83
+ wcs-contacts:state(loading) ~ .spinner { display: block; }
84
+ wcs-contacts:state(loading) ~ .spinner { display: none; } /* default */
85
+
86
+ form:has(wcs-contacts:state(error)) .banner { display: block; }
87
+ form:has(wcs-contacts:state(cancelled)) .hint { display: block; }
88
+ ```
89
+
90
+ Unlike attributes or classes, `:state()` cannot be written from outside the
91
+ element, so there is no risk of confusing this output state with an input.
92
+
93
+ **Browser support** (`:state(x)` syntax): Chrome/Edge 125+, Safari 17.4+,
94
+ Firefox 126+. In older browsers the states are simply never set — `:state()`
95
+ selectors never match, but `<wcs-contacts>` itself keeps working normally
96
+ (graceful degradation, never-throw).
97
+
98
+ **SSR**: `:state()` cannot be serialized into HTML, so server-rendered markup
99
+ never carries these states on first paint (`@wcstack/server` is unaffected).
100
+ If you need to style the pre-hydration gap, pair your rule with
101
+ `wcs-contacts:not(:defined)` instead.
102
+
103
+ ### Debugging
104
+
105
+ Custom states are invisible in DevTools' Elements panel and `attachInternals()`
106
+ cannot be called twice, so there is no console way to inspect them directly.
107
+ Two debug-only aids are provided for that:
108
+
109
+ - `el.debugStates` — a **snapshot** array of the currently-on state names
110
+ (e.g. `["loading"]`). It is not part of `wc-bindable` (not a bind target)
111
+ and its shape is not a guaranteed contract — use it for debugging only.
112
+ - The `debug-states` attribute (opt-in, default off) mirrors state changes
113
+ onto `data-wcs-state-loading` / `data-wcs-state-cancelled` /
114
+ `data-wcs-state-error` attributes on the element, so the Elements panel
115
+ highlights them as they toggle:
116
+
117
+ ```html
118
+ <wcs-contacts debug-states></wcs-contacts>
119
+ ```
120
+
121
+ **Write your CSS against `:state()`, not `data-wcs-state-*`.** The mirrored
122
+ attributes exist purely to make state changes visible while debugging with
123
+ DevTools open; they are not a supported styling hook.
124
+
69
125
  ## Notes & limitations
70
126
 
71
127
  - **Android Chrome only.** Treat `unsupported` as the default, not an edge case.
package/dist/index.d.ts CHANGED
@@ -157,7 +157,11 @@ declare class WcsContacts extends HTMLElement {
157
157
  static wcBindable: IWcBindable;
158
158
  private _core;
159
159
  private _connectedCallbackPromise;
160
+ private _internals;
160
161
  constructor();
162
+ get debugStates(): string[];
163
+ private _initInternals;
164
+ private _wireStates;
161
165
  get value(): ContactInfo[] | null;
162
166
  get loading(): boolean;
163
167
  get error(): any;
package/dist/index.esm.js CHANGED
@@ -239,9 +239,64 @@ class WcsContacts extends HTMLElement {
239
239
  };
240
240
  _core;
241
241
  _connectedCallbackPromise = Promise.resolve();
242
+ _internals = null;
242
243
  constructor() {
243
244
  super();
244
245
  this._core = new ContactsCore(this);
246
+ this._internals = this._initInternals();
247
+ this._wireStates({
248
+ "wcs-contacts:loading-changed": (d) => ({ loading: d === true }),
249
+ "wcs-contacts:cancelled-changed": (d) => ({ cancelled: d === true }),
250
+ "wcs-contacts:error": (d) => ({ error: d != null }),
251
+ });
252
+ }
253
+ // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of
254
+ // wc-bindable (not a bind target); see README "CSS styling with :state()".
255
+ // MUST NOT return the live CustomStateSet (that would let callers write
256
+ // states from outside, defeating the point of :state() being read-only).
257
+ get debugStates() {
258
+ return this._internals ? [...this._internals.states] : [];
259
+ }
260
+ _initInternals() {
261
+ // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent
262
+ // in happy-dom / older environments, and pre-125 Chromium rejects
263
+ // non-dashed state names from states.add() (probed and discarded here).
264
+ // Either case silently disables reflection — the component still works,
265
+ // it just doesn't expose :state() selectors.
266
+ try {
267
+ if (typeof this.attachInternals !== "function")
268
+ return null;
269
+ const internals = this.attachInternals();
270
+ internals.states.add("wcs-probe");
271
+ internals.states.delete("wcs-probe");
272
+ return internals;
273
+ }
274
+ catch {
275
+ return null;
276
+ }
277
+ }
278
+ _wireStates(map) {
279
+ if (this._internals === null)
280
+ return;
281
+ const states = this._internals.states;
282
+ for (const [event, toStates] of Object.entries(map)) {
283
+ this.addEventListener(event, (e) => {
284
+ const debug = this.hasAttribute("debug-states");
285
+ for (const [name, on] of Object.entries(toStates(e.detail))) {
286
+ try {
287
+ if (on) {
288
+ states.add(name);
289
+ }
290
+ else {
291
+ states.delete(name);
292
+ }
293
+ }
294
+ catch { /* never-throw */ }
295
+ if (debug)
296
+ this.toggleAttribute(`data-wcs-state-${name}`, on);
297
+ }
298
+ });
299
+ }
245
300
  }
246
301
  // --- Core delegated getters ---
247
302
  get value() {
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/ContactsCore.ts","../src/components/Contacts.ts","../src/registerComponents.ts","../src/bootstrapContacts.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n contacts: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n contacts: \"wcs-contacts\",\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\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (frozen snapshot) is\n// surfaced; `setConfig()` is applied internally via `bootstrapContacts()` and is\n// not re-exported from the package root — but a deep path import (`.../src/config.js`)\n// can still reach and mutate it. Accepted as-is for cross-package consistency: every\n// @wcstack package follows this same shape. Use `getConfig()` for a frozen, safe read.\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 { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\n\n/**\n * Headless Contact Picker primitive. A thin, framework-agnostic wrapper around\n * `navigator.contacts.select(properties, options)` exposed through the\n * wc-bindable protocol.\n *\n * This is the same simplified derivative of `FetchCore._doFetch` that\n * `@wcstack/share`'s `ShareCore` establishes (docs/contact-picker-tag-design.md\n * §1): single `_gen` generation guard, same-value-guarded private setters,\n * never-throw try/catch, no `AbortController`/`abort()` — the Contact Picker\n * API accepts no `AbortSignal` and, like the Web Share dialog, the picker is a\n * single system-modal surface (at most one open at a time).\n *\n * The one structural difference from `ShareCore`: `select()` takes **two**\n * positional arguments (`properties`, `options`) rather than one — the first\n * batch-3 member to do so. The command-token argument pass-through\n * (spec-proposal-command-token-arguments.md) does not special-case argument\n * count, so this requires no protocol change.\n */\nexport class ContactsCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: \"wcs-contacts:complete\", getter: (e: Event) => (e as CustomEvent).detail.value },\n { name: \"loading\", event: \"wcs-contacts:loading-changed\" },\n { name: \"error\", event: \"wcs-contacts:error\" },\n { name: \"cancelled\", event: \"wcs-contacts:cancelled-changed\" },\n ],\n commands: [\n { name: \"select\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _value: ContactInfo[] | null = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _cancelled: boolean = false;\n // Generation guard (§3.4 of the guidelines): bumped ONLY by dispose(). A\n // select() that settles after dispose() has a stale `gen` and MUST NOT\n // write state to a torn-down element. Unlike FetchCore/EyedropperCore,\n // select() itself does NOT bump `_gen` on each call: the archetype\n // (docs/web-share-tag-design.md §2, adopted verbatim by\n // docs/contact-picker-tag-design.md §1) deliberately drops the \"a new call\n // supersedes the previous one\" plumbing those cores need, because the\n // contact picker is a single system-modal surface (a second concurrent\n // select() rejects with InvalidStateError on its own). Bumping `_gen` per\n // call would instead let a fast-failing second call incorrectly invalidate\n // a still-pending first call's eventual success. Also not bumped on the\n // unsupported early-return — no asynchronous work is started, so there is\n // no generation to protect.\n private _gen = 0;\n // SSR (§3.8): no asynchronous probe to await, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get value(): ContactInfo[] | null {\n return this._value;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get cancelled(): boolean {\n return this._cancelled;\n }\n\n // Lifecycle (§3.5). Select is command-driven with no subscription to\n // establish, so observe() is an idempotent no-op that resolves once ready;\n // dispose() only invalidates any in-flight select() (there is nothing to\n // abort or unsubscribe).\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n }\n\n private _setLoading(loading: boolean): void {\n if (this._loading === loading) return;\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:loading-changed\", {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setValue(value: ContactInfo[] | null): void {\n if (this._value === value) return;\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:complete\", {\n detail: { value },\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setCancelled(cancelled: boolean): void {\n if (this._cancelled === cancelled) return;\n this._cancelled = cancelled;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:cancelled-changed\", {\n detail: cancelled,\n bubbles: true,\n }));\n }\n\n // API resolution is call-time, never cached (§3.7): lets tests install/remove\n // navigator.contacts freely and lets an unsupported environment (the common\n // case — desktop browsers entirely lack this API) be detected correctly on\n // every call.\n private _api(): ((properties: ContactProperty[], options?: ContactsSelectOptions) => Promise<ContactInfo[]>) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav?.contacts?.select === \"function\" ? nav.contacts.select.bind(nav.contacts) : undefined;\n }\n\n async select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n // never-throw + unsupported: resolve API at call time and bail out\n // immediately if absent. No _gen bump — no asynchronous work is started.\n const selectFn = this._api();\n if (!selectFn) {\n this._setError({ message: \"Contact Picker API is not supported in this browser.\" });\n return null;\n }\n\n // Captured, not bumped (see the `_gen` field docs above): select() does\n // not supersede a prior in-flight call, only dispose() invalidates.\n const gen = this._gen;\n\n this._setLoading(true);\n // Reset the previous outcome before starting a new select so a stale\n // cancelled/error does not linger into this call's result.\n this._setError(null);\n this._setCancelled(false);\n\n try {\n const contacts = await selectFn(properties, options);\n\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n\n // `multiple` does not change the result shape — even a single\n // selection resolves to a one-element array (docs/contact-picker-tag-design.md §3).\n this._setValue(contacts);\n this._setLoading(false);\n return contacts;\n } catch (e: any) {\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n if (e?.name === \"AbortError\") {\n // The user dismissed the contact picker — a routine cancellation, not\n // a platform failure. Kept out of `error`.\n this._setCancelled(true);\n } else {\n this._setError(e);\n }\n this._setLoading(false);\n return null;\n }\n }\n}\n","import { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\nimport { ContactsCore } from \"../core/ContactsCore.js\";\n\n/**\n * `<wcs-contacts>` — declarative Contact Picker API primitive.\n *\n * A thin command-only Shell (mirrors `<wcs-share>`): no attributes at all.\n * `select(properties, options)`'s arguments are per-call, not a declarative\n * setting to park on the element ahead of time.\n *\n * **Android Chrome only.** Desktop browsers entirely lack `navigator.contacts`\n * — treat `unsupported` as the default state, not an edge case, in any\n * example or consuming UI.\n */\nexport class WcsContacts extends HTMLElement {\n // SSR (§4.4): observe() completes synchronously, but the Shell still exposes\n // connectedCallbackPromise so the state binder can await it uniformly across\n // all IO nodes before snapshotting.\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...ContactsCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: ContactsCore.wcBindable.commands,\n };\n\n private _core: ContactsCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new ContactsCore(this);\n }\n\n // --- Core delegated getters ---\n\n get value(): ContactInfo[] | null {\n return this._core.value;\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get cancelled(): boolean {\n return this._core.cancelled;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n return this._core.select(properties, options);\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsContacts } from \"./components/Contacts.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.contacts)) {\n customElements.define(config.tagNames.contacts, WcsContacts);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapContacts(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/ContactsCore.ts","../src/components/Contacts.ts","../src/registerComponents.ts","../src/bootstrapContacts.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n contacts: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n contacts: \"wcs-contacts\",\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\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (frozen snapshot) is\n// surfaced; `setConfig()` is applied internally via `bootstrapContacts()` and is\n// not re-exported from the package root — but a deep path import (`.../src/config.js`)\n// can still reach and mutate it. Accepted as-is for cross-package consistency: every\n// @wcstack package follows this same shape. Use `getConfig()` for a frozen, safe read.\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 { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\n\n/**\n * Headless Contact Picker primitive. A thin, framework-agnostic wrapper around\n * `navigator.contacts.select(properties, options)` exposed through the\n * wc-bindable protocol.\n *\n * This is the same simplified derivative of `FetchCore._doFetch` that\n * `@wcstack/share`'s `ShareCore` establishes (docs/contact-picker-tag-design.md\n * §1): single `_gen` generation guard, same-value-guarded private setters,\n * never-throw try/catch, no `AbortController`/`abort()` — the Contact Picker\n * API accepts no `AbortSignal` and, like the Web Share dialog, the picker is a\n * single system-modal surface (at most one open at a time).\n *\n * The one structural difference from `ShareCore`: `select()` takes **two**\n * positional arguments (`properties`, `options`) rather than one — the first\n * batch-3 member to do so. The command-token argument pass-through\n * (spec-proposal-command-token-arguments.md) does not special-case argument\n * count, so this requires no protocol change.\n */\nexport class ContactsCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: \"wcs-contacts:complete\", getter: (e: Event) => (e as CustomEvent).detail.value },\n { name: \"loading\", event: \"wcs-contacts:loading-changed\" },\n { name: \"error\", event: \"wcs-contacts:error\" },\n { name: \"cancelled\", event: \"wcs-contacts:cancelled-changed\" },\n ],\n commands: [\n { name: \"select\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _value: ContactInfo[] | null = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _cancelled: boolean = false;\n // Generation guard (§3.4 of the guidelines): bumped ONLY by dispose(). A\n // select() that settles after dispose() has a stale `gen` and MUST NOT\n // write state to a torn-down element. Unlike FetchCore/EyedropperCore,\n // select() itself does NOT bump `_gen` on each call: the archetype\n // (docs/web-share-tag-design.md §2, adopted verbatim by\n // docs/contact-picker-tag-design.md §1) deliberately drops the \"a new call\n // supersedes the previous one\" plumbing those cores need, because the\n // contact picker is a single system-modal surface (a second concurrent\n // select() rejects with InvalidStateError on its own). Bumping `_gen` per\n // call would instead let a fast-failing second call incorrectly invalidate\n // a still-pending first call's eventual success. Also not bumped on the\n // unsupported early-return — no asynchronous work is started, so there is\n // no generation to protect.\n private _gen = 0;\n // SSR (§3.8): no asynchronous probe to await, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get value(): ContactInfo[] | null {\n return this._value;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get cancelled(): boolean {\n return this._cancelled;\n }\n\n // Lifecycle (§3.5). Select is command-driven with no subscription to\n // establish, so observe() is an idempotent no-op that resolves once ready;\n // dispose() only invalidates any in-flight select() (there is nothing to\n // abort or unsubscribe).\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n }\n\n private _setLoading(loading: boolean): void {\n if (this._loading === loading) return;\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:loading-changed\", {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setValue(value: ContactInfo[] | null): void {\n if (this._value === value) return;\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:complete\", {\n detail: { value },\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setCancelled(cancelled: boolean): void {\n if (this._cancelled === cancelled) return;\n this._cancelled = cancelled;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:cancelled-changed\", {\n detail: cancelled,\n bubbles: true,\n }));\n }\n\n // API resolution is call-time, never cached (§3.7): lets tests install/remove\n // navigator.contacts freely and lets an unsupported environment (the common\n // case — desktop browsers entirely lack this API) be detected correctly on\n // every call.\n private _api(): ((properties: ContactProperty[], options?: ContactsSelectOptions) => Promise<ContactInfo[]>) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav?.contacts?.select === \"function\" ? nav.contacts.select.bind(nav.contacts) : undefined;\n }\n\n async select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n // never-throw + unsupported: resolve API at call time and bail out\n // immediately if absent. No _gen bump — no asynchronous work is started.\n const selectFn = this._api();\n if (!selectFn) {\n this._setError({ message: \"Contact Picker API is not supported in this browser.\" });\n return null;\n }\n\n // Captured, not bumped (see the `_gen` field docs above): select() does\n // not supersede a prior in-flight call, only dispose() invalidates.\n const gen = this._gen;\n\n this._setLoading(true);\n // Reset the previous outcome before starting a new select so a stale\n // cancelled/error does not linger into this call's result.\n this._setError(null);\n this._setCancelled(false);\n\n try {\n const contacts = await selectFn(properties, options);\n\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n\n // `multiple` does not change the result shape — even a single\n // selection resolves to a one-element array (docs/contact-picker-tag-design.md §3).\n this._setValue(contacts);\n this._setLoading(false);\n return contacts;\n } catch (e: any) {\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n if (e?.name === \"AbortError\") {\n // The user dismissed the contact picker — a routine cancellation, not\n // a platform failure. Kept out of `error`.\n this._setCancelled(true);\n } else {\n this._setError(e);\n }\n this._setLoading(false);\n return null;\n }\n }\n}\n","import { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\nimport { ContactsCore } from \"../core/ContactsCore.js\";\n\n/**\n * `<wcs-contacts>` — declarative Contact Picker API primitive.\n *\n * A thin command-only Shell (mirrors `<wcs-share>`): no attributes at all.\n * `select(properties, options)`'s arguments are per-call, not a declarative\n * setting to park on the element ahead of time.\n *\n * **Android Chrome only.** Desktop browsers entirely lack `navigator.contacts`\n * — treat `unsupported` as the default state, not an edge case, in any\n * example or consuming UI.\n */\nexport class WcsContacts extends HTMLElement {\n // SSR (§4.4): observe() completes synchronously, but the Shell still exposes\n // connectedCallbackPromise so the state binder can await it uniformly across\n // all IO nodes before snapshotting.\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...ContactsCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: ContactsCore.wcBindable.commands,\n };\n\n private _core: ContactsCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new ContactsCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-contacts:loading-changed\": (d) => ({ loading: d === true }),\n \"wcs-contacts:cancelled-changed\": (d) => ({ cancelled: d === true }),\n \"wcs-contacts:error\": (d) => ({ error: d != null }),\n });\n }\n\n // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of\n // wc-bindable (not a bind target); see README \"CSS styling with :state()\".\n // MUST NOT return the live CustomStateSet (that would let callers write\n // states from outside, defeating the point of :state() being read-only).\n get debugStates(): string[] {\n return this._internals ? [...this._internals.states] : [];\n }\n\n private _initInternals(): ElementInternals | null {\n // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent\n // in happy-dom / older environments, and pre-125 Chromium rejects\n // non-dashed state names from states.add() (probed and discarded here).\n // Either case silently disables reflection — the component still works,\n // it just doesn't expose :state() selectors.\n try {\n if (typeof this.attachInternals !== \"function\") return null;\n const internals = this.attachInternals();\n internals.states.add(\"wcs-probe\");\n internals.states.delete(\"wcs-probe\");\n return internals;\n } catch {\n return null;\n }\n }\n\n private _wireStates(map: Record<string, (detail: any) => Record<string, boolean>>): void {\n if (this._internals === null) return;\n const states = this._internals.states;\n for (const [event, toStates] of Object.entries(map)) {\n this.addEventListener(event, (e) => {\n const debug = this.hasAttribute(\"debug-states\");\n for (const [name, on] of Object.entries(toStates((e as CustomEvent).detail))) {\n try {\n if (on) { states.add(name); } else { states.delete(name); }\n } catch { /* never-throw */ }\n if (debug) this.toggleAttribute(`data-wcs-state-${name}`, on);\n }\n });\n }\n }\n\n // --- Core delegated getters ---\n\n get value(): ContactInfo[] | null {\n return this._core.value;\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get cancelled(): boolean {\n return this._core.cancelled;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n return this._core.select(properties, options);\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsContacts } from \"./components/Contacts.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.contacts)) {\n customElements.define(config.tagNames.contacts, WcsContacts);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapContacts(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e={tagNames:{contacts:"wcs-contacts"}};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 n of Object.keys(e))t[n]=s(e[n]);return t}let n=null;const c=e;function r(){return n||(n=t(s(e))),n}class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"value",event:"wcs-contacts:complete",getter:e=>e.detail.value},{name:"loading",event:"wcs-contacts:loading-changed"},{name:"error",event:"wcs-contacts:error"},{name:"cancelled",event:"wcs-contacts:cancelled-changed"}],commands:[{name:"select",async:!0}]};_target;_value=null;_loading=!1;_error=null;_cancelled=!1;_gen=0;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get ready(){return this._ready}get value(){return this._value}get loading(){return this._loading}get error(){return this._error}get cancelled(){return this._cancelled}observe(){return this._ready}dispose(){this._gen++}_setLoading(e){this._loading!==e&&(this._loading=e,this._target.dispatchEvent(new CustomEvent("wcs-contacts:loading-changed",{detail:e,bubbles:!0})))}_setValue(e){this._value!==e&&(this._value=e,this._target.dispatchEvent(new CustomEvent("wcs-contacts:complete",{detail:{value:e},bubbles:!0})))}_setError(e){this._error!==e&&(this._error=e,this._target.dispatchEvent(new CustomEvent("wcs-contacts:error",{detail:e,bubbles:!0})))}_setCancelled(e){this._cancelled!==e&&(this._cancelled=e,this._target.dispatchEvent(new CustomEvent("wcs-contacts:cancelled-changed",{detail:e,bubbles:!0})))}_api(){const e=globalThis.navigator;return"function"==typeof e?.contacts?.select?e.contacts.select.bind(e.contacts):void 0}async select(e,t){const s=this._api();if(!s)return this._setError({message:"Contact Picker API is not supported in this browser."}),null;const n=this._gen;this._setLoading(!0),this._setError(null),this._setCancelled(!1);try{const c=await s(e,t);return n!==this._gen?null:(this._setValue(c),this._setLoading(!1),c)}catch(e){return n!==this._gen||("AbortError"===e?.name?this._setCancelled(!0):this._setError(e),this._setLoading(!1)),null}}}class o extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...a.wcBindable,inputs:[],commands:a.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new a(this)}get value(){return this._core.value}get loading(){return this._core.loading}get error(){return this._core.error}get cancelled(){return this._core.cancelled}get connectedCallbackPromise(){return this._connectedCallbackPromise}select(e,t){return this._core.select(e,t)}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}function l(t){var s;t&&((s=t).tagNames&&Object.assign(e.tagNames,s.tagNames),n=null),customElements.get(c.tagNames.contacts)||customElements.define(c.tagNames.contacts,o)}export{a as ContactsCore,o as WcsContacts,l as bootstrapContacts,r as getConfig};
1
+ const t={tagNames:{contacts:"wcs-contacts"}};function e(t){if(null===t||"object"!=typeof t)return t;Object.freeze(t);for(const s of Object.keys(t))e(t[s]);return t}function s(t){if(null===t||"object"!=typeof t)return t;const e={};for(const n of Object.keys(t))e[n]=s(t[n]);return e}let n=null;const c=t;function a(){return n||(n=e(s(t))),n}class r extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"value",event:"wcs-contacts:complete",getter:t=>t.detail.value},{name:"loading",event:"wcs-contacts:loading-changed"},{name:"error",event:"wcs-contacts:error"},{name:"cancelled",event:"wcs-contacts:cancelled-changed"}],commands:[{name:"select",async:!0}]};_target;_value=null;_loading=!1;_error=null;_cancelled=!1;_gen=0;_ready=Promise.resolve();constructor(t){super(),this._target=t??this}get ready(){return this._ready}get value(){return this._value}get loading(){return this._loading}get error(){return this._error}get cancelled(){return this._cancelled}observe(){return this._ready}dispose(){this._gen++}_setLoading(t){this._loading!==t&&(this._loading=t,this._target.dispatchEvent(new CustomEvent("wcs-contacts:loading-changed",{detail:t,bubbles:!0})))}_setValue(t){this._value!==t&&(this._value=t,this._target.dispatchEvent(new CustomEvent("wcs-contacts:complete",{detail:{value:t},bubbles:!0})))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-contacts:error",{detail:t,bubbles:!0})))}_setCancelled(t){this._cancelled!==t&&(this._cancelled=t,this._target.dispatchEvent(new CustomEvent("wcs-contacts:cancelled-changed",{detail:t,bubbles:!0})))}_api(){const t=globalThis.navigator;return"function"==typeof t?.contacts?.select?t.contacts.select.bind(t.contacts):void 0}async select(t,e){const s=this._api();if(!s)return this._setError({message:"Contact Picker API is not supported in this browser."}),null;const n=this._gen;this._setLoading(!0),this._setError(null),this._setCancelled(!1);try{const c=await s(t,e);return n!==this._gen?null:(this._setValue(c),this._setLoading(!1),c)}catch(t){return n!==this._gen||("AbortError"===t?.name?this._setCancelled(!0):this._setError(t),this._setLoading(!1)),null}}}class i extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...r.wcBindable,inputs:[],commands:r.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new r(this),this._internals=this._initInternals(),this._wireStates({"wcs-contacts:loading-changed":t=>({loading:!0===t}),"wcs-contacts:cancelled-changed":t=>({cancelled:!0===t}),"wcs-contacts:error":t=>({error:null!=t})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const t=this.attachInternals();return t.states.add("wcs-probe"),t.states.delete("wcs-probe"),t}catch{return null}}_wireStates(t){if(null===this._internals)return;const e=this._internals.states;for(const[s,n]of Object.entries(t))this.addEventListener(s,t=>{const s=this.hasAttribute("debug-states");for(const[c,a]of Object.entries(n(t.detail))){try{a?e.add(c):e.delete(c)}catch{}s&&this.toggleAttribute(`data-wcs-state-${c}`,a)}})}get value(){return this._core.value}get loading(){return this._core.loading}get error(){return this._core.error}get cancelled(){return this._core.cancelled}get connectedCallbackPromise(){return this._connectedCallbackPromise}select(t,e){return this._core.select(t,e)}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}function l(e){var s;e&&((s=e).tagNames&&Object.assign(t.tagNames,s.tagNames),n=null),customElements.get(c.tagNames.contacts)||customElements.define(c.tagNames.contacts,i)}export{r as ContactsCore,i as WcsContacts,l as bootstrapContacts,a 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/ContactsCore.ts","../src/components/Contacts.ts","../src/bootstrapContacts.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n contacts: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n contacts: \"wcs-contacts\",\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\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (frozen snapshot) is\n// surfaced; `setConfig()` is applied internally via `bootstrapContacts()` and is\n// not re-exported from the package root — but a deep path import (`.../src/config.js`)\n// can still reach and mutate it. Accepted as-is for cross-package consistency: every\n// @wcstack package follows this same shape. Use `getConfig()` for a frozen, safe read.\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 { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\n\n/**\n * Headless Contact Picker primitive. A thin, framework-agnostic wrapper around\n * `navigator.contacts.select(properties, options)` exposed through the\n * wc-bindable protocol.\n *\n * This is the same simplified derivative of `FetchCore._doFetch` that\n * `@wcstack/share`'s `ShareCore` establishes (docs/contact-picker-tag-design.md\n * §1): single `_gen` generation guard, same-value-guarded private setters,\n * never-throw try/catch, no `AbortController`/`abort()` — the Contact Picker\n * API accepts no `AbortSignal` and, like the Web Share dialog, the picker is a\n * single system-modal surface (at most one open at a time).\n *\n * The one structural difference from `ShareCore`: `select()` takes **two**\n * positional arguments (`properties`, `options`) rather than one — the first\n * batch-3 member to do so. The command-token argument pass-through\n * (spec-proposal-command-token-arguments.md) does not special-case argument\n * count, so this requires no protocol change.\n */\nexport class ContactsCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: \"wcs-contacts:complete\", getter: (e: Event) => (e as CustomEvent).detail.value },\n { name: \"loading\", event: \"wcs-contacts:loading-changed\" },\n { name: \"error\", event: \"wcs-contacts:error\" },\n { name: \"cancelled\", event: \"wcs-contacts:cancelled-changed\" },\n ],\n commands: [\n { name: \"select\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _value: ContactInfo[] | null = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _cancelled: boolean = false;\n // Generation guard (§3.4 of the guidelines): bumped ONLY by dispose(). A\n // select() that settles after dispose() has a stale `gen` and MUST NOT\n // write state to a torn-down element. Unlike FetchCore/EyedropperCore,\n // select() itself does NOT bump `_gen` on each call: the archetype\n // (docs/web-share-tag-design.md §2, adopted verbatim by\n // docs/contact-picker-tag-design.md §1) deliberately drops the \"a new call\n // supersedes the previous one\" plumbing those cores need, because the\n // contact picker is a single system-modal surface (a second concurrent\n // select() rejects with InvalidStateError on its own). Bumping `_gen` per\n // call would instead let a fast-failing second call incorrectly invalidate\n // a still-pending first call's eventual success. Also not bumped on the\n // unsupported early-return — no asynchronous work is started, so there is\n // no generation to protect.\n private _gen = 0;\n // SSR (§3.8): no asynchronous probe to await, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get value(): ContactInfo[] | null {\n return this._value;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get cancelled(): boolean {\n return this._cancelled;\n }\n\n // Lifecycle (§3.5). Select is command-driven with no subscription to\n // establish, so observe() is an idempotent no-op that resolves once ready;\n // dispose() only invalidates any in-flight select() (there is nothing to\n // abort or unsubscribe).\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n }\n\n private _setLoading(loading: boolean): void {\n if (this._loading === loading) return;\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:loading-changed\", {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setValue(value: ContactInfo[] | null): void {\n if (this._value === value) return;\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:complete\", {\n detail: { value },\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setCancelled(cancelled: boolean): void {\n if (this._cancelled === cancelled) return;\n this._cancelled = cancelled;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:cancelled-changed\", {\n detail: cancelled,\n bubbles: true,\n }));\n }\n\n // API resolution is call-time, never cached (§3.7): lets tests install/remove\n // navigator.contacts freely and lets an unsupported environment (the common\n // case — desktop browsers entirely lack this API) be detected correctly on\n // every call.\n private _api(): ((properties: ContactProperty[], options?: ContactsSelectOptions) => Promise<ContactInfo[]>) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav?.contacts?.select === \"function\" ? nav.contacts.select.bind(nav.contacts) : undefined;\n }\n\n async select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n // never-throw + unsupported: resolve API at call time and bail out\n // immediately if absent. No _gen bump — no asynchronous work is started.\n const selectFn = this._api();\n if (!selectFn) {\n this._setError({ message: \"Contact Picker API is not supported in this browser.\" });\n return null;\n }\n\n // Captured, not bumped (see the `_gen` field docs above): select() does\n // not supersede a prior in-flight call, only dispose() invalidates.\n const gen = this._gen;\n\n this._setLoading(true);\n // Reset the previous outcome before starting a new select so a stale\n // cancelled/error does not linger into this call's result.\n this._setError(null);\n this._setCancelled(false);\n\n try {\n const contacts = await selectFn(properties, options);\n\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n\n // `multiple` does not change the result shape — even a single\n // selection resolves to a one-element array (docs/contact-picker-tag-design.md §3).\n this._setValue(contacts);\n this._setLoading(false);\n return contacts;\n } catch (e: any) {\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n if (e?.name === \"AbortError\") {\n // The user dismissed the contact picker — a routine cancellation, not\n // a platform failure. Kept out of `error`.\n this._setCancelled(true);\n } else {\n this._setError(e);\n }\n this._setLoading(false);\n return null;\n }\n }\n}\n","import { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\nimport { ContactsCore } from \"../core/ContactsCore.js\";\n\n/**\n * `<wcs-contacts>` — declarative Contact Picker API primitive.\n *\n * A thin command-only Shell (mirrors `<wcs-share>`): no attributes at all.\n * `select(properties, options)`'s arguments are per-call, not a declarative\n * setting to park on the element ahead of time.\n *\n * **Android Chrome only.** Desktop browsers entirely lack `navigator.contacts`\n * — treat `unsupported` as the default state, not an edge case, in any\n * example or consuming UI.\n */\nexport class WcsContacts extends HTMLElement {\n // SSR (§4.4): observe() completes synchronously, but the Shell still exposes\n // connectedCallbackPromise so the state binder can await it uniformly across\n // all IO nodes before snapshotting.\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...ContactsCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: ContactsCore.wcBindable.commands,\n };\n\n private _core: ContactsCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new ContactsCore(this);\n }\n\n // --- Core delegated getters ---\n\n get value(): ContactInfo[] | null {\n return this._core.value;\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get cancelled(): boolean {\n return this._core.cancelled;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n return this._core.select(properties, options);\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapContacts(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsContacts } from \"./components/Contacts.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.contacts)) {\n customElements.define(config.tagNames.contacts, WcsContacts);\n }\n}\n"],"names":["_config","tagNames","contacts","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","ContactsCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","value","commands","async","_target","_value","_loading","_error","_cancelled","_gen","_ready","Promise","resolve","constructor","target","super","this","ready","loading","error","cancelled","observe","dispose","_setLoading","dispatchEvent","CustomEvent","bubbles","_setValue","_setError","_setCancelled","_api","nav","globalThis","navigator","select","bind","undefined","options","selectFn","message","gen","WcsContacts","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapContacts","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/ContactsCore.ts","../src/components/Contacts.ts","../src/bootstrapContacts.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n contacts: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n contacts: \"wcs-contacts\",\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\n// Note: this is the live, mutable internal config. It is not part of the public\n// package exports (see exports.ts) — only `getConfig()` (frozen snapshot) is\n// surfaced; `setConfig()` is applied internally via `bootstrapContacts()` and is\n// not re-exported from the package root — but a deep path import (`.../src/config.js`)\n// can still reach and mutate it. Accepted as-is for cross-package consistency: every\n// @wcstack package follows this same shape. Use `getConfig()` for a frozen, safe read.\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 { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\n\n/**\n * Headless Contact Picker primitive. A thin, framework-agnostic wrapper around\n * `navigator.contacts.select(properties, options)` exposed through the\n * wc-bindable protocol.\n *\n * This is the same simplified derivative of `FetchCore._doFetch` that\n * `@wcstack/share`'s `ShareCore` establishes (docs/contact-picker-tag-design.md\n * §1): single `_gen` generation guard, same-value-guarded private setters,\n * never-throw try/catch, no `AbortController`/`abort()` — the Contact Picker\n * API accepts no `AbortSignal` and, like the Web Share dialog, the picker is a\n * single system-modal surface (at most one open at a time).\n *\n * The one structural difference from `ShareCore`: `select()` takes **two**\n * positional arguments (`properties`, `options`) rather than one — the first\n * batch-3 member to do so. The command-token argument pass-through\n * (spec-proposal-command-token-arguments.md) does not special-case argument\n * count, so this requires no protocol change.\n */\nexport class ContactsCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: \"wcs-contacts:complete\", getter: (e: Event) => (e as CustomEvent).detail.value },\n { name: \"loading\", event: \"wcs-contacts:loading-changed\" },\n { name: \"error\", event: \"wcs-contacts:error\" },\n { name: \"cancelled\", event: \"wcs-contacts:cancelled-changed\" },\n ],\n commands: [\n { name: \"select\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _value: ContactInfo[] | null = null;\n private _loading: boolean = false;\n private _error: any = null;\n private _cancelled: boolean = false;\n // Generation guard (§3.4 of the guidelines): bumped ONLY by dispose(). A\n // select() that settles after dispose() has a stale `gen` and MUST NOT\n // write state to a torn-down element. Unlike FetchCore/EyedropperCore,\n // select() itself does NOT bump `_gen` on each call: the archetype\n // (docs/web-share-tag-design.md §2, adopted verbatim by\n // docs/contact-picker-tag-design.md §1) deliberately drops the \"a new call\n // supersedes the previous one\" plumbing those cores need, because the\n // contact picker is a single system-modal surface (a second concurrent\n // select() rejects with InvalidStateError on its own). Bumping `_gen` per\n // call would instead let a fast-failing second call incorrectly invalidate\n // a still-pending first call's eventual success. Also not bumped on the\n // unsupported early-return — no asynchronous work is started, so there is\n // no generation to protect.\n private _gen = 0;\n // SSR (§3.8): no asynchronous probe to await, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get value(): ContactInfo[] | null {\n return this._value;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): any {\n return this._error;\n }\n\n get cancelled(): boolean {\n return this._cancelled;\n }\n\n // Lifecycle (§3.5). Select is command-driven with no subscription to\n // establish, so observe() is an idempotent no-op that resolves once ready;\n // dispose() only invalidates any in-flight select() (there is nothing to\n // abort or unsubscribe).\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n }\n\n private _setLoading(loading: boolean): void {\n if (this._loading === loading) return;\n this._loading = loading;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:loading-changed\", {\n detail: loading,\n bubbles: true,\n }));\n }\n\n private _setValue(value: ContactInfo[] | null): void {\n if (this._value === value) return;\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:complete\", {\n detail: { value },\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setCancelled(cancelled: boolean): void {\n if (this._cancelled === cancelled) return;\n this._cancelled = cancelled;\n this._target.dispatchEvent(new CustomEvent(\"wcs-contacts:cancelled-changed\", {\n detail: cancelled,\n bubbles: true,\n }));\n }\n\n // API resolution is call-time, never cached (§3.7): lets tests install/remove\n // navigator.contacts freely and lets an unsupported environment (the common\n // case — desktop browsers entirely lack this API) be detected correctly on\n // every call.\n private _api(): ((properties: ContactProperty[], options?: ContactsSelectOptions) => Promise<ContactInfo[]>) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav?.contacts?.select === \"function\" ? nav.contacts.select.bind(nav.contacts) : undefined;\n }\n\n async select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n // never-throw + unsupported: resolve API at call time and bail out\n // immediately if absent. No _gen bump — no asynchronous work is started.\n const selectFn = this._api();\n if (!selectFn) {\n this._setError({ message: \"Contact Picker API is not supported in this browser.\" });\n return null;\n }\n\n // Captured, not bumped (see the `_gen` field docs above): select() does\n // not supersede a prior in-flight call, only dispose() invalidates.\n const gen = this._gen;\n\n this._setLoading(true);\n // Reset the previous outcome before starting a new select so a stale\n // cancelled/error does not linger into this call's result.\n this._setError(null);\n this._setCancelled(false);\n\n try {\n const contacts = await selectFn(properties, options);\n\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n\n // `multiple` does not change the result shape — even a single\n // selection resolves to a one-element array (docs/contact-picker-tag-design.md §3).\n this._setValue(contacts);\n this._setLoading(false);\n return contacts;\n } catch (e: any) {\n // Stale completion (dispose() ran while the picker was open).\n if (gen !== this._gen) {\n return null;\n }\n if (e?.name === \"AbortError\") {\n // The user dismissed the contact picker — a routine cancellation, not\n // a platform failure. Kept out of `error`.\n this._setCancelled(true);\n } else {\n this._setError(e);\n }\n this._setLoading(false);\n return null;\n }\n }\n}\n","import { ContactInfo, ContactProperty, ContactsSelectOptions, IWcBindable } from \"../types.js\";\nimport { ContactsCore } from \"../core/ContactsCore.js\";\n\n/**\n * `<wcs-contacts>` — declarative Contact Picker API primitive.\n *\n * A thin command-only Shell (mirrors `<wcs-share>`): no attributes at all.\n * `select(properties, options)`'s arguments are per-call, not a declarative\n * setting to park on the element ahead of time.\n *\n * **Android Chrome only.** Desktop browsers entirely lack `navigator.contacts`\n * — treat `unsupported` as the default state, not an edge case, in any\n * example or consuming UI.\n */\nexport class WcsContacts extends HTMLElement {\n // SSR (§4.4): observe() completes synchronously, but the Shell still exposes\n // connectedCallbackPromise so the state binder can await it uniformly across\n // all IO nodes before snapshotting.\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...ContactsCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: ContactsCore.wcBindable.commands,\n };\n\n private _core: ContactsCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _internals: ElementInternals | null = null;\n\n constructor() {\n super();\n this._core = new ContactsCore(this);\n this._internals = this._initInternals();\n this._wireStates({\n \"wcs-contacts:loading-changed\": (d) => ({ loading: d === true }),\n \"wcs-contacts:cancelled-changed\": (d) => ({ cancelled: d === true }),\n \"wcs-contacts:error\": (d) => ({ error: d != null }),\n });\n }\n\n // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of\n // wc-bindable (not a bind target); see README \"CSS styling with :state()\".\n // MUST NOT return the live CustomStateSet (that would let callers write\n // states from outside, defeating the point of :state() being read-only).\n get debugStates(): string[] {\n return this._internals ? [...this._internals.states] : [];\n }\n\n private _initInternals(): ElementInternals | null {\n // never-throw (async-io-node-guidelines.md §3.6): attachInternals is absent\n // in happy-dom / older environments, and pre-125 Chromium rejects\n // non-dashed state names from states.add() (probed and discarded here).\n // Either case silently disables reflection — the component still works,\n // it just doesn't expose :state() selectors.\n try {\n if (typeof this.attachInternals !== \"function\") return null;\n const internals = this.attachInternals();\n internals.states.add(\"wcs-probe\");\n internals.states.delete(\"wcs-probe\");\n return internals;\n } catch {\n return null;\n }\n }\n\n private _wireStates(map: Record<string, (detail: any) => Record<string, boolean>>): void {\n if (this._internals === null) return;\n const states = this._internals.states;\n for (const [event, toStates] of Object.entries(map)) {\n this.addEventListener(event, (e) => {\n const debug = this.hasAttribute(\"debug-states\");\n for (const [name, on] of Object.entries(toStates((e as CustomEvent).detail))) {\n try {\n if (on) { states.add(name); } else { states.delete(name); }\n } catch { /* never-throw */ }\n if (debug) this.toggleAttribute(`data-wcs-state-${name}`, on);\n }\n });\n }\n }\n\n // --- Core delegated getters ---\n\n get value(): ContactInfo[] | null {\n return this._core.value;\n }\n\n get loading(): boolean {\n return this._core.loading;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get cancelled(): boolean {\n return this._core.cancelled;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands ---\n\n select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null> {\n return this._core.select(properties, options);\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapContacts(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsContacts } from \"./components/Contacts.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.contacts)) {\n customElements.define(config.tagNames.contacts, WcsContacts);\n }\n}\n"],"names":["_config","tagNames","contacts","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","ContactsCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","value","commands","async","_target","_value","_loading","_error","_cancelled","_gen","_ready","Promise","resolve","constructor","target","super","this","ready","loading","error","cancelled","observe","dispose","_setLoading","dispatchEvent","CustomEvent","bubbles","_setValue","_setError","_setCancelled","_api","nav","globalThis","navigator","select","bind","undefined","options","selectFn","message","gen","WcsContacts","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","_internals","_initInternals","_wireStates","d","debugStates","states","attachInternals","internals","add","delete","map","toStates","entries","addEventListener","debug","hasAttribute","on","toggleAttribute","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapContacts","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/contacts",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Declarative Contact Picker component for Web Components. Framework-agnostic navigator.contacts.select() wrapper via wc-bindable-protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",