@wcstack/contacts 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md ADDED
@@ -0,0 +1,91 @@
1
+ # @wcstack/contacts
2
+
3
+ `@wcstack/contacts` は wcstack エコシステム向けのヘッドレスな Contact Picker コンポーネントです。
4
+
5
+ 視覚的な UI ウィジェットではありません。`navigator.contacts.select(properties, options)` を宣言的コマンド+観測可能stateに変える**非同期プリミティブノード**で、Web Share APIに対する`@wcstack/share`と同じ形をしています。
6
+
7
+ `@wcstack/state` と組み合わせると、`<wcs-contacts>` はパス契約で直接バインドできます:
8
+
9
+ - **入力サーフェス**: 無し — `select(properties, options)`の引数は呼び出しごと
10
+ - **出力 state サーフェス**: `value`、`loading`、`error`、`cancelled`
11
+
12
+ ## なぜ存在するか — Android Chrome限定、unsupportedが常態
13
+
14
+ Contact Picker APIは**Android Chromeでのみ**動作します。デスクトップブラウザ(およびiOS Safari)は`navigator.contacts`を一切持ちません。このノードは補助的なショートカットとして設計し、常に手入力の代替手段を用意してください。
15
+
16
+ > **2つの位置引数、プロトコル変更不要。** `select(properties, options)`はバッチ3で初めて2引数を取るcommandです。command-tokenの引数素通しは引数の個数を特別扱いしないため、無改造でそのまま動作します(`docs/contact-picker-tag-design.md` §2参照)。
17
+
18
+ > **`multiple: false`(既定)でも戻り値は配列。** 単一選択でも1要素の配列になります——素のオブジェクトを期待せず`value.0`のようにバインドしてください。
19
+
20
+ ## インストール
21
+
22
+ ```bash
23
+ npm install @wcstack/contacts
24
+ ```
25
+
26
+ ## クイックスタート
27
+
28
+ ```html
29
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
30
+ <script type="module" src="https://esm.run/@wcstack/contacts/auto"></script>
31
+
32
+ <wcs-state>
33
+ <script type="module">
34
+ export default {
35
+ picked: null,
36
+ pickContact() {
37
+ this.$command.select.emit(["name", "tel"], { multiple: false });
38
+ },
39
+ };
40
+ </script>
41
+ </wcs-state>
42
+
43
+ <wcs-contacts data-wcs="command.select: $command.select; value: picked"></wcs-contacts>
44
+
45
+ <button data-wcs="onclick: pickContact">連絡先を選択</button>
46
+ <p data-wcs="textContent: picked.0.name.0"></p>
47
+ ```
48
+
49
+ ## 観測可能プロパティ(出力)
50
+
51
+ | プロパティ | イベント | 説明 |
52
+ | ----------- | -------------------------------- | ---- |
53
+ | `value` | `wcs-contacts:complete` | 選択された連絡先の配列(`multiple: false`でも常に配列)、成功前は`null`。 |
54
+ | `loading` | `wcs-contacts:loading-changed` | ピッカーダイアログが開いている間`true`。 |
55
+ | `error` | `wcs-contacts:error` | 真のプラットフォーム失敗(`select()`が投げた`DOMException`/`Error`)、または unsupported 経路ではプレーンオブジェクト`{ message: "Contact Picker API is not supported in this browser." }`、無ければ`null`。 |
56
+ | `cancelled` | `wcs-contacts:cancelled-changed` | ユーザーがピッカーを閉じたら`true`(`error`とは分離)。 |
57
+
58
+ ## コマンド
59
+
60
+ | コマンド | 非同期 | 説明 |
61
+ | -------- | ------ | ---- |
62
+ | `select` | はい | `select(properties, options?)` — `properties`は`"name"`/`"email"`/`"tel"`/`"address"`/`"icon"`の配列、`options.multiple`既定`false`。never-throw: ユーザーキャンセルは`cancelled`、それ以外の失敗は`error`へ。`abort`コマンドは無し(Contact Picker APIはAbortSignalを受け付けない)。 |
63
+
64
+ ## 属性 / 入力
65
+
66
+ **無し。**
67
+
68
+ ## 注意・制限
69
+
70
+ - **Android Chrome限定。** unsupportedを例外ケースでなく既定として扱ってください。
71
+ - **`unsupported` 専用フラグは持たない。** `navigator.contacts.select` が関数でない状態で `select()` を呼ぶと、即座に `error` がプレーンオブジェクト `{ message: "Contact Picker API is not supported in this browser." }` になり `null` で解決します — 非同期処理を開始しないため `_gen` は消費されません。この形は実失敗(`error` に `DOMException`/`Error` が入る)とは異なるため、unsupported 経路で `error instanceof Error` や `error.name` を前提にしないでください。
72
+ - **`getProperties()`はv1スコープ外**(対応フィールドの非同期事前確認、`docs/contact-picker-tag-design.md` §4参照)。
73
+ - `@wcstack/share`/`@wcstack/eyedropper`とアーキタイプを共有: 単一`_gen`世代ガード、never-throw、AbortController無し。
74
+
75
+ ## ヘッドレス利用(`ContactsCore`)
76
+
77
+ ```typescript
78
+ import { ContactsCore } from "@wcstack/contacts";
79
+
80
+ const core = new ContactsCore();
81
+ core.addEventListener("wcs-contacts:complete", (e) => {
82
+ console.log((e as CustomEvent).detail.value); // ContactInfo[]
83
+ });
84
+
85
+ await core.select(["name", "tel"], { multiple: true });
86
+ core.dispose();
87
+ ```
88
+
89
+ ## ライセンス
90
+
91
+ MIT
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @wcstack/contacts
2
+
3
+ `@wcstack/contacts` is a headless Contact Picker component for the wcstack ecosystem.
4
+
5
+ It is not a visual UI widget.
6
+ It is an **async primitive node** that turns `navigator.contacts.select(properties, options)` into declarative command + observable state, the same shape `@wcstack/share` establishes for the Web Share API.
7
+
8
+ With `@wcstack/state`, `<wcs-contacts>` can be bound directly through path contracts:
9
+
10
+ - **input surface**: none — `select(properties, options)`'s arguments are per-call
11
+ - **output state surface**: `value`, `loading`, `error`, `cancelled`
12
+
13
+ ## Why this exists — Android Chrome only, unsupported is the common case
14
+
15
+ The Contact Picker API works **only on Android Chrome**. Desktop browsers (and iOS Safari) entirely lack `navigator.contacts`. Design any UI around this being a supplementary shortcut, not the primary input method — always keep a manual-entry fallback.
16
+
17
+ > **Two positional arguments, no protocol change needed.** `select(properties, options)` is the first batch-3 member to take two arguments instead of one. The command-token argument pass-through does not special-case argument count — it works unmodified (see `docs/contact-picker-tag-design.md` §2).
18
+
19
+ > **`multiple: false` (the default) still resolves to an array.** Even a single selection is a one-element array — bind `value.0` (or iterate) rather than expecting a bare object.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install @wcstack/contacts
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```html
30
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
31
+ <script type="module" src="https://esm.run/@wcstack/contacts/auto"></script>
32
+
33
+ <wcs-state>
34
+ <script type="module">
35
+ export default {
36
+ picked: null,
37
+ pickContact() {
38
+ this.$command.select.emit(["name", "tel"], { multiple: false });
39
+ },
40
+ };
41
+ </script>
42
+ </wcs-state>
43
+
44
+ <wcs-contacts data-wcs="command.select: $command.select; value: picked"></wcs-contacts>
45
+
46
+ <button data-wcs="onclick: pickContact">Pick a contact</button>
47
+ <p data-wcs="textContent: picked.0.name.0"></p>
48
+ ```
49
+
50
+ ## Observable Properties (outputs)
51
+
52
+ | Property | Event | Description |
53
+ | ----------- | ----------------------------- | ------------ |
54
+ | `value` | `wcs-contacts:complete` | The array of picked contacts (always an array, even with `multiple: false`), or `null` before any successful selection. |
55
+ | `loading` | `wcs-contacts:loading-changed` | `true` while the picker dialog is open. |
56
+ | `error` | `wcs-contacts:error` | A true platform failure (a `DOMException`/`Error` from `select()`), or the plain object `{ message: "Contact Picker API is not supported in this browser." }` on the unsupported path, or `null`. |
57
+ | `cancelled` | `wcs-contacts:cancelled-changed` | `true` when the user dismissed the picker (kept separate from `error`). |
58
+
59
+ ## Commands
60
+
61
+ | Command | Async | Description |
62
+ | ------- | ----- | ------------ |
63
+ | `select` | yes | `select(properties, options?)` — `properties` is an array of `"name"`/`"email"`/`"tel"`/`"address"`/`"icon"`; `options.multiple` defaults to `false`. Never-throw: a user-cancelled picker surfaces via `cancelled`, any other failure via `error`. No `abort` command — the Contact Picker API accepts no `AbortSignal`. |
64
+
65
+ ## Attributes / Inputs
66
+
67
+ **None.**
68
+
69
+ ## Notes & limitations
70
+
71
+ - **Android Chrome only.** Treat `unsupported` as the default, not an edge case.
72
+ - **`unsupported` has no dedicated flag.** Calling `select()` when `navigator.contacts.select` is not a function immediately sets `error` to the plain object `{ message: "Contact Picker API is not supported in this browser." }` and resolves with `null` — no `_gen` is consumed, since no asynchronous work is started. This shape differs from a real failure, where `error` holds a `DOMException`/`Error`; do not assume `error instanceof Error` or `error.name` on the unsupported path.
73
+ - **`getProperties()` is out of scope for v1** (an async pre-check for supported fields) — see `docs/contact-picker-tag-design.md` §4.
74
+ - Shares its architecture with `@wcstack/share`/`@wcstack/eyedropper`: single `_gen` generation guard, never-throw, no `AbortController`.
75
+
76
+ ## Headless usage (`ContactsCore`)
77
+
78
+ ```typescript
79
+ import { ContactsCore } from "@wcstack/contacts";
80
+
81
+ const core = new ContactsCore();
82
+ core.addEventListener("wcs-contacts:complete", (e) => {
83
+ console.log((e as CustomEvent).detail.value); // ContactInfo[]
84
+ });
85
+
86
+ await core.select(["name", "tel"], { multiple: true });
87
+ core.dispose();
88
+ ```
89
+
90
+ ## License
91
+
92
+ MIT
package/dist/auto.js ADDED
@@ -0,0 +1,3 @@
1
+ import { bootstrapContacts } from "./index.esm.js";
2
+
3
+ bootstrapContacts();
@@ -0,0 +1 @@
1
+ import{bootstrapContacts}from"./index.esm.min.js";bootstrapContacts();
@@ -0,0 +1,172 @@
1
+ interface IWcBindableProperty {
2
+ readonly name: string;
3
+ readonly event: string;
4
+ readonly getter?: (event: Event) => any;
5
+ }
6
+ interface IWcBindableInput {
7
+ readonly name: string;
8
+ readonly attribute?: string;
9
+ }
10
+ interface IWcBindableCommand {
11
+ readonly name: string;
12
+ readonly async?: boolean;
13
+ }
14
+ interface IWcBindable {
15
+ readonly protocol: "wc-bindable";
16
+ readonly version: 1;
17
+ readonly properties: readonly IWcBindableProperty[];
18
+ readonly inputs?: readonly IWcBindableInput[];
19
+ readonly commands?: readonly IWcBindableCommand[];
20
+ }
21
+
22
+ interface ITagNames {
23
+ readonly contacts: string;
24
+ }
25
+ interface IWritableTagNames {
26
+ contacts?: string;
27
+ }
28
+ interface IConfig {
29
+ readonly tagNames: ITagNames;
30
+ }
31
+ interface IWritableConfig {
32
+ tagNames?: IWritableTagNames;
33
+ }
34
+
35
+ /** Field names selectable via the Contact Picker API's `properties` argument. */
36
+ type ContactProperty = "name" | "email" | "tel" | "address" | "icon";
37
+ /** Options for `select(properties, options)` — `multiple` defaults to `false`. */
38
+ interface ContactsSelectOptions {
39
+ multiple?: boolean;
40
+ }
41
+ /**
42
+ * A postal address entry returned for the `"address"` contact property, mirroring
43
+ * the Contact Picker API's `ContactAddress` shape (MDN). Every field is optional:
44
+ * the platform populates only what it has, and field coverage varies by browser /
45
+ * OS. An index signature is kept so a future platform field does not force callers
46
+ * into a type assertion — consistent with the design doc's "pass the platform
47
+ * return value straight through to `value`" principle (docs/contact-picker-tag-design.md §3).
48
+ */
49
+ interface ContactAddress {
50
+ addressLine?: string[];
51
+ city?: string;
52
+ country?: string;
53
+ dependentLocality?: string;
54
+ organization?: string;
55
+ phone?: string;
56
+ postalCode?: string;
57
+ recipient?: string;
58
+ region?: string;
59
+ sortingCode?: string;
60
+ [key: string]: unknown;
61
+ }
62
+ /**
63
+ * A single contact returned by `navigator.contacts.select()`. Every field is an
64
+ * array (a contact can have multiple emails, phone numbers, etc.) and only the
65
+ * fields requested via `properties` are populated.
66
+ */
67
+ interface ContactInfo {
68
+ name?: string[];
69
+ email?: string[];
70
+ tel?: string[];
71
+ address?: ContactAddress[];
72
+ icon?: Blob[];
73
+ }
74
+ /**
75
+ * Value types for ContactsCore (headless) — the observable state properties.
76
+ * Use with `bind()` from a wc-bindable binding core for compile-time type checking.
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const core = new ContactsCore();
81
+ * bind(core, (name: keyof WcsContactsCoreValues, value) => { ... });
82
+ * ```
83
+ */
84
+ interface WcsContactsCoreValues {
85
+ value: ContactInfo[] | null;
86
+ loading: boolean;
87
+ error: any;
88
+ cancelled: boolean;
89
+ }
90
+ /**
91
+ * Value types for the Shell (`<wcs-contacts>`) — identical observable surface
92
+ * to the Core. The Shell adds no inputs: `select(properties, options)`'s
93
+ * arguments are per-call, not declarative attributes.
94
+ */
95
+ type WcsContactsValues = WcsContactsCoreValues;
96
+
97
+ declare function bootstrapContacts(userConfig?: IWritableConfig): void;
98
+
99
+ declare function getConfig(): IConfig;
100
+
101
+ /**
102
+ * Headless Contact Picker primitive. A thin, framework-agnostic wrapper around
103
+ * `navigator.contacts.select(properties, options)` exposed through the
104
+ * wc-bindable protocol.
105
+ *
106
+ * This is the same simplified derivative of `FetchCore._doFetch` that
107
+ * `@wcstack/share`'s `ShareCore` establishes (docs/contact-picker-tag-design.md
108
+ * §1): single `_gen` generation guard, same-value-guarded private setters,
109
+ * never-throw try/catch, no `AbortController`/`abort()` — the Contact Picker
110
+ * API accepts no `AbortSignal` and, like the Web Share dialog, the picker is a
111
+ * single system-modal surface (at most one open at a time).
112
+ *
113
+ * The one structural difference from `ShareCore`: `select()` takes **two**
114
+ * positional arguments (`properties`, `options`) rather than one — the first
115
+ * batch-3 member to do so. The command-token argument pass-through
116
+ * (spec-proposal-command-token-arguments.md) does not special-case argument
117
+ * count, so this requires no protocol change.
118
+ */
119
+ declare class ContactsCore extends EventTarget {
120
+ static wcBindable: IWcBindable;
121
+ private _target;
122
+ private _value;
123
+ private _loading;
124
+ private _error;
125
+ private _cancelled;
126
+ private _gen;
127
+ private _ready;
128
+ constructor(target?: EventTarget);
129
+ get ready(): Promise<void>;
130
+ get value(): ContactInfo[] | null;
131
+ get loading(): boolean;
132
+ get error(): any;
133
+ get cancelled(): boolean;
134
+ observe(): Promise<void>;
135
+ dispose(): void;
136
+ private _setLoading;
137
+ private _setValue;
138
+ private _setError;
139
+ private _setCancelled;
140
+ private _api;
141
+ select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null>;
142
+ }
143
+
144
+ /**
145
+ * `<wcs-contacts>` — declarative Contact Picker API primitive.
146
+ *
147
+ * A thin command-only Shell (mirrors `<wcs-share>`): no attributes at all.
148
+ * `select(properties, options)`'s arguments are per-call, not a declarative
149
+ * setting to park on the element ahead of time.
150
+ *
151
+ * **Android Chrome only.** Desktop browsers entirely lack `navigator.contacts`
152
+ * — treat `unsupported` as the default state, not an edge case, in any
153
+ * example or consuming UI.
154
+ */
155
+ declare class WcsContacts extends HTMLElement {
156
+ static hasConnectedCallbackPromise: boolean;
157
+ static wcBindable: IWcBindable;
158
+ private _core;
159
+ private _connectedCallbackPromise;
160
+ constructor();
161
+ get value(): ContactInfo[] | null;
162
+ get loading(): boolean;
163
+ get error(): any;
164
+ get cancelled(): boolean;
165
+ get connectedCallbackPromise(): Promise<void>;
166
+ select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[] | null>;
167
+ connectedCallback(): void;
168
+ disconnectedCallback(): void;
169
+ }
170
+
171
+ export { ContactsCore, WcsContacts, bootstrapContacts, getConfig };
172
+ export type { ContactAddress, ContactInfo, ContactProperty, ContactsSelectOptions, IWritableConfig, IWritableTagNames, WcsContactsCoreValues, WcsContactsValues };
@@ -0,0 +1,290 @@
1
+ const _config = {
2
+ tagNames: {
3
+ contacts: "wcs-contacts",
4
+ },
5
+ };
6
+ function deepFreeze(obj) {
7
+ if (obj === null || typeof obj !== "object")
8
+ return obj;
9
+ Object.freeze(obj);
10
+ for (const key of Object.keys(obj)) {
11
+ deepFreeze(obj[key]);
12
+ }
13
+ return obj;
14
+ }
15
+ function deepClone(obj) {
16
+ if (obj === null || typeof obj !== "object")
17
+ return obj;
18
+ const clone = {};
19
+ for (const key of Object.keys(obj)) {
20
+ clone[key] = deepClone(obj[key]);
21
+ }
22
+ return clone;
23
+ }
24
+ let frozenConfig = null;
25
+ // Note: this is the live, mutable internal config. It is not part of the public
26
+ // package exports (see exports.ts) — only `getConfig()` (frozen snapshot) is
27
+ // surfaced; `setConfig()` is applied internally via `bootstrapContacts()` and is
28
+ // not re-exported from the package root — but a deep path import (`.../src/config.js`)
29
+ // can still reach and mutate it. Accepted as-is for cross-package consistency: every
30
+ // @wcstack package follows this same shape. Use `getConfig()` for a frozen, safe read.
31
+ const config = _config;
32
+ function getConfig() {
33
+ if (!frozenConfig) {
34
+ frozenConfig = deepFreeze(deepClone(_config));
35
+ }
36
+ return frozenConfig;
37
+ }
38
+ function setConfig(partialConfig) {
39
+ if (partialConfig.tagNames) {
40
+ Object.assign(_config.tagNames, partialConfig.tagNames);
41
+ }
42
+ frozenConfig = null;
43
+ }
44
+
45
+ /**
46
+ * Headless Contact Picker primitive. A thin, framework-agnostic wrapper around
47
+ * `navigator.contacts.select(properties, options)` exposed through the
48
+ * wc-bindable protocol.
49
+ *
50
+ * This is the same simplified derivative of `FetchCore._doFetch` that
51
+ * `@wcstack/share`'s `ShareCore` establishes (docs/contact-picker-tag-design.md
52
+ * §1): single `_gen` generation guard, same-value-guarded private setters,
53
+ * never-throw try/catch, no `AbortController`/`abort()` — the Contact Picker
54
+ * API accepts no `AbortSignal` and, like the Web Share dialog, the picker is a
55
+ * single system-modal surface (at most one open at a time).
56
+ *
57
+ * The one structural difference from `ShareCore`: `select()` takes **two**
58
+ * positional arguments (`properties`, `options`) rather than one — the first
59
+ * batch-3 member to do so. The command-token argument pass-through
60
+ * (spec-proposal-command-token-arguments.md) does not special-case argument
61
+ * count, so this requires no protocol change.
62
+ */
63
+ class ContactsCore extends EventTarget {
64
+ static wcBindable = {
65
+ protocol: "wc-bindable",
66
+ version: 1,
67
+ properties: [
68
+ { name: "value", event: "wcs-contacts:complete", getter: (e) => e.detail.value },
69
+ { name: "loading", event: "wcs-contacts:loading-changed" },
70
+ { name: "error", event: "wcs-contacts:error" },
71
+ { name: "cancelled", event: "wcs-contacts:cancelled-changed" },
72
+ ],
73
+ commands: [
74
+ { name: "select", async: true },
75
+ ],
76
+ };
77
+ _target;
78
+ _value = null;
79
+ _loading = false;
80
+ _error = null;
81
+ _cancelled = false;
82
+ // Generation guard (§3.4 of the guidelines): bumped ONLY by dispose(). A
83
+ // select() that settles after dispose() has a stale `gen` and MUST NOT
84
+ // write state to a torn-down element. Unlike FetchCore/EyedropperCore,
85
+ // select() itself does NOT bump `_gen` on each call: the archetype
86
+ // (docs/web-share-tag-design.md §2, adopted verbatim by
87
+ // docs/contact-picker-tag-design.md §1) deliberately drops the "a new call
88
+ // supersedes the previous one" plumbing those cores need, because the
89
+ // contact picker is a single system-modal surface (a second concurrent
90
+ // select() rejects with InvalidStateError on its own). Bumping `_gen` per
91
+ // call would instead let a fast-failing second call incorrectly invalidate
92
+ // a still-pending first call's eventual success. Also not bumped on the
93
+ // unsupported early-return — no asynchronous work is started, so there is
94
+ // no generation to protect.
95
+ _gen = 0;
96
+ // SSR (§3.8): no asynchronous probe to await, so readiness is immediate.
97
+ _ready = Promise.resolve();
98
+ constructor(target) {
99
+ super();
100
+ this._target = target ?? this;
101
+ }
102
+ get ready() {
103
+ return this._ready;
104
+ }
105
+ get value() {
106
+ return this._value;
107
+ }
108
+ get loading() {
109
+ return this._loading;
110
+ }
111
+ get error() {
112
+ return this._error;
113
+ }
114
+ get cancelled() {
115
+ return this._cancelled;
116
+ }
117
+ // Lifecycle (§3.5). Select is command-driven with no subscription to
118
+ // establish, so observe() is an idempotent no-op that resolves once ready;
119
+ // dispose() only invalidates any in-flight select() (there is nothing to
120
+ // abort or unsubscribe).
121
+ observe() {
122
+ return this._ready;
123
+ }
124
+ dispose() {
125
+ this._gen++;
126
+ }
127
+ _setLoading(loading) {
128
+ if (this._loading === loading)
129
+ return;
130
+ this._loading = loading;
131
+ this._target.dispatchEvent(new CustomEvent("wcs-contacts:loading-changed", {
132
+ detail: loading,
133
+ bubbles: true,
134
+ }));
135
+ }
136
+ _setValue(value) {
137
+ if (this._value === value)
138
+ return;
139
+ this._value = value;
140
+ this._target.dispatchEvent(new CustomEvent("wcs-contacts:complete", {
141
+ detail: { value },
142
+ bubbles: true,
143
+ }));
144
+ }
145
+ _setError(error) {
146
+ if (this._error === error)
147
+ return;
148
+ this._error = error;
149
+ this._target.dispatchEvent(new CustomEvent("wcs-contacts:error", {
150
+ detail: error,
151
+ bubbles: true,
152
+ }));
153
+ }
154
+ _setCancelled(cancelled) {
155
+ if (this._cancelled === cancelled)
156
+ return;
157
+ this._cancelled = cancelled;
158
+ this._target.dispatchEvent(new CustomEvent("wcs-contacts:cancelled-changed", {
159
+ detail: cancelled,
160
+ bubbles: true,
161
+ }));
162
+ }
163
+ // API resolution is call-time, never cached (§3.7): lets tests install/remove
164
+ // navigator.contacts freely and lets an unsupported environment (the common
165
+ // case — desktop browsers entirely lack this API) be detected correctly on
166
+ // every call.
167
+ _api() {
168
+ const nav = globalThis.navigator;
169
+ return typeof nav?.contacts?.select === "function" ? nav.contacts.select.bind(nav.contacts) : undefined;
170
+ }
171
+ async select(properties, options) {
172
+ // never-throw + unsupported: resolve API at call time and bail out
173
+ // immediately if absent. No _gen bump — no asynchronous work is started.
174
+ const selectFn = this._api();
175
+ if (!selectFn) {
176
+ this._setError({ message: "Contact Picker API is not supported in this browser." });
177
+ return null;
178
+ }
179
+ // Captured, not bumped (see the `_gen` field docs above): select() does
180
+ // not supersede a prior in-flight call, only dispose() invalidates.
181
+ const gen = this._gen;
182
+ this._setLoading(true);
183
+ // Reset the previous outcome before starting a new select so a stale
184
+ // cancelled/error does not linger into this call's result.
185
+ this._setError(null);
186
+ this._setCancelled(false);
187
+ try {
188
+ const contacts = await selectFn(properties, options);
189
+ // Stale completion (dispose() ran while the picker was open).
190
+ if (gen !== this._gen) {
191
+ return null;
192
+ }
193
+ // `multiple` does not change the result shape — even a single
194
+ // selection resolves to a one-element array (docs/contact-picker-tag-design.md §3).
195
+ this._setValue(contacts);
196
+ this._setLoading(false);
197
+ return contacts;
198
+ }
199
+ catch (e) {
200
+ // Stale completion (dispose() ran while the picker was open).
201
+ if (gen !== this._gen) {
202
+ return null;
203
+ }
204
+ if (e?.name === "AbortError") {
205
+ // The user dismissed the contact picker — a routine cancellation, not
206
+ // a platform failure. Kept out of `error`.
207
+ this._setCancelled(true);
208
+ }
209
+ else {
210
+ this._setError(e);
211
+ }
212
+ this._setLoading(false);
213
+ return null;
214
+ }
215
+ }
216
+ }
217
+
218
+ /**
219
+ * `<wcs-contacts>` — declarative Contact Picker API primitive.
220
+ *
221
+ * A thin command-only Shell (mirrors `<wcs-share>`): no attributes at all.
222
+ * `select(properties, options)`'s arguments are per-call, not a declarative
223
+ * setting to park on the element ahead of time.
224
+ *
225
+ * **Android Chrome only.** Desktop browsers entirely lack `navigator.contacts`
226
+ * — treat `unsupported` as the default state, not an edge case, in any
227
+ * example or consuming UI.
228
+ */
229
+ class WcsContacts extends HTMLElement {
230
+ // SSR (§4.4): observe() completes synchronously, but the Shell still exposes
231
+ // connectedCallbackPromise so the state binder can await it uniformly across
232
+ // all IO nodes before snapshotting.
233
+ static hasConnectedCallbackPromise = true;
234
+ static wcBindable = {
235
+ ...ContactsCore.wcBindable,
236
+ inputs: [],
237
+ // Core の commands をそのまま継承(単一情報源)。
238
+ commands: ContactsCore.wcBindable.commands,
239
+ };
240
+ _core;
241
+ _connectedCallbackPromise = Promise.resolve();
242
+ constructor() {
243
+ super();
244
+ this._core = new ContactsCore(this);
245
+ }
246
+ // --- Core delegated getters ---
247
+ get value() {
248
+ return this._core.value;
249
+ }
250
+ get loading() {
251
+ return this._core.loading;
252
+ }
253
+ get error() {
254
+ return this._core.error;
255
+ }
256
+ get cancelled() {
257
+ return this._core.cancelled;
258
+ }
259
+ get connectedCallbackPromise() {
260
+ return this._connectedCallbackPromise;
261
+ }
262
+ // --- Commands ---
263
+ select(properties, options) {
264
+ return this._core.select(properties, options);
265
+ }
266
+ // --- Lifecycle ---
267
+ connectedCallback() {
268
+ this.style.display = "none";
269
+ this._connectedCallbackPromise = this._core.observe();
270
+ }
271
+ disconnectedCallback() {
272
+ this._core.dispose();
273
+ }
274
+ }
275
+
276
+ function registerComponents() {
277
+ if (!customElements.get(config.tagNames.contacts)) {
278
+ customElements.define(config.tagNames.contacts, WcsContacts);
279
+ }
280
+ }
281
+
282
+ function bootstrapContacts(userConfig) {
283
+ if (userConfig) {
284
+ setConfig(userConfig);
285
+ }
286
+ registerComponents();
287
+ }
288
+
289
+ export { ContactsCore, WcsContacts, bootstrapContacts, getConfig };
290
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +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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"}
@@ -0,0 +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};
2
+ //# sourceMappingURL=index.esm.min.js.map
@@ -0,0 +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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"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@wcstack/contacts",
3
+ "version": "1.16.0",
4
+ "description": "Declarative Contact Picker component for Web Components. Framework-agnostic navigator.contacts.select() wrapper via wc-bindable-protocol.",
5
+ "type": "module",
6
+ "main": "./dist/index.esm.js",
7
+ "module": "./dist/index.esm.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.esm.js"
13
+ },
14
+ "./auto": "./dist/auto.min.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "clean": "rimraf dist .tsc-out",
21
+ "build": "rimraf dist .tsc-out && tsc && rollup -c",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
25
+ "lint": "eslint src",
26
+ "version:patch": "npm version patch",
27
+ "version:minor": "npm version minor",
28
+ "version:major": "npm version major",
29
+ "prepublishOnly": "npm run build && npm run test:coverage"
30
+ },
31
+ "keywords": [
32
+ "web-components",
33
+ "contact-picker",
34
+ "contacts",
35
+ "custom-elements",
36
+ "wc-bindable",
37
+ "declarative",
38
+ "zero-dependencies",
39
+ "framework-agnostic"
40
+ ],
41
+ "author": "mogera551",
42
+ "homepage": "https://wcstack.github.io",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/wcstack/wcstack.git",
46
+ "directory": "packages/contacts"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/wcstack/wcstack/issues"
50
+ },
51
+ "license": "MIT",
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.39.1",
54
+ "@rollup/plugin-terser": "^0.4.4",
55
+ "@rollup/plugin-typescript": "^11.1.6",
56
+ "@vitest/coverage-v8": "^4.0.15",
57
+ "@vitest/ui": "^4.0.15",
58
+ "eslint": "^9.39.1",
59
+ "globals": "^16.5.0",
60
+ "happy-dom": "^20.0.11",
61
+ "rimraf": "^6.0.1",
62
+ "rollup": "^4.22.4",
63
+ "rollup-plugin-dts": "^6.1.1",
64
+ "rollup-plugin-copy": "^3.5.0",
65
+ "tslib": "^2.8.1",
66
+ "typescript": "^5.9.3",
67
+ "typescript-eslint": "^8.49.0",
68
+ "vitest": "^4.0.15"
69
+ }
70
+ }