@wcstack/share 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,148 @@
1
+ # @wcstack/share
2
+
3
+ `@wcstack/share` は wcstack エコシステム向けのヘッドレスな Web Share コンポーネントです。
4
+
5
+ 視覚的な UI ウィジェットではありません。
6
+ `@wcstack/notification` が `Notification` をリアクティブな state と `notify` コマンドに変えるのと同じように、`navigator.share(data)`(クリック→ネイティブ共有シート→resolve/reject)を単一の宣言的コマンドに変える **command 専用の非同期プリミティブノード** です。
7
+
8
+ `@wcstack/state` と組み合わせると、`<wcs-share>` はパス契約で直接バインドできます:
9
+
10
+ - **command サーフェス**: `share(data)` — 単一の async command。`command.share: $command.doShare` として起動する
11
+ - **出力 state サーフェス**: `value`、`loading`、`error`、`cancelled`
12
+
13
+ これにより「記事を共有」ボタンを HTML 上で宣言的に表現できます — 成功・失敗・ユーザーがネイティブ共有シートを単に閉じただけ、という3つの異なる結果をそれぞれバインド可能な形で区別しつつ、UI 層で `navigator.share()` / `try`/`catch` の配線を書く必要がありません。
14
+
15
+ `@wcstack/share` は wcstack の Core/Shell アーキテクチャに従います:
16
+
17
+ - **Core**(`ShareCore`)が `navigator.share(data)` を単一の `_gen` 世代ガード・同値ガード付き `loading`/`error`/`cancelled` setter(`value` は対象外 — 成功のたびに発火する完了シグナル)・never-throw の `try`/`catch` で包む
18
+ - **Shell**(`<wcs-share>`)がそのコマンドを DOM ライフサイクルに接続し、`canShare(data)` を素の同期メソッドとして公開する
19
+ - **Binding Contract**(`static wcBindable`)が観測可能な `properties` と単一の `share` コマンドを宣言(そして意図的に **`inputs` も `abort` コマンドも持たない**)
20
+
21
+ ## なぜ存在するか — command 専用ノードであり、キャンセルはエラーではない
22
+
23
+ 他の wcstack IO ノードは、継続的な状態を監視する(`network`、`permission`)か、事前に何かを設定してその変化を観測する(`fetch` の `url`、`geolocation` の `enableHighAccuracy`)かのどちらかです。`navigator.share()` はどちらとも異なり、「呼ぶ→ネイティブ共有シート→resolve/reject」の一撃で完結するアクションであり、設定・監視すべき継続的な状態が存在しません。さらに(`fetch` と異なり)**進行中の呼び出しを中断する手段がありません** — `AbortSignal` オプションが無く、プラットフォームは同時に1つの共有シートしか許可しないため、`fetch` が必要とする「新規呼び出しが旧呼び出しを追い越して中断する」という配線自体が不要になります。
24
+
25
+ もう一つの重要な決定は **`cancelled` を `error` から分離する** ことです。ユーザーが単にネイティブ共有シートを閉じると、`navigator.share()` は `AbortError` で reject します — `<dialog>` を閉じるのと同じような操作です。これを `error` に含めてしまうと、`error` を条件にしたバインディング(例: 真の失敗時にのみ「共有に失敗しました」バナーを表示する)が、日常的で無害なユーザーキャンセルにも反応してしまい、UX 上の不具合になります。`<wcs-share>` は `cancelled` を独立した boolean/event として持つことで、`error` が **真のプラットフォーム障害だけ**(`NotAllowedError`、`TypeError` 等)を反映するようにしています。
26
+
27
+ > 設計の全経緯は [`docs/web-share-tag-design.md`](https://github.com/wcstack/wcstack/blob/main/docs/web-share-tag-design.md) を参照してください。
28
+
29
+ ## インストール
30
+
31
+ ```bash
32
+ npm install @wcstack/share
33
+ ```
34
+
35
+ ## クイックスタート
36
+
37
+ ### 1. 記事を共有する(キャンセルは失敗と区別して扱う)
38
+
39
+ ```html
40
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
41
+ <script type="module" src="https://esm.run/@wcstack/share/auto"></script>
42
+
43
+ <wcs-state>
44
+ <script type="module">
45
+ export default {
46
+ $commandTokens: ["doShare"],
47
+ loading: false,
48
+ error: null,
49
+ cancelled: false,
50
+ onShareClick() {
51
+ this.$command.doShare.emit({
52
+ title: document.title,
53
+ url: location.href,
54
+ });
55
+ },
56
+ };
57
+ </script>
58
+ </wcs-state>
59
+
60
+ <wcs-share
61
+ data-wcs="command.share: $command.doShare; loading: loading; error: error; cancelled: cancelled"
62
+ ></wcs-share>
63
+
64
+ <button data-wcs="onclick: onShareClick; disabled: loading">共有</button>
65
+ <template data-wcs="if: error">
66
+ <p>共有に失敗しました: <span data-wcs="textContent: error.message"></span></p>
67
+ </template>
68
+ ```
69
+
70
+ `share()` は実際のユーザー操作(クリックハンドラ)内から呼ばれる必要があるため、ボタンのクリックハンドラが直接 `$command.doShare.emit(...)` を呼びます — `<wcs-share>` 自身は `autoTrigger` のショートカットを持ちません([注意・制限](#注意・制限)を参照)。
71
+
72
+ ### 2. `canShare(data)` — 事前に実行可能性を確認する
73
+
74
+ ```html
75
+ <script type="module">
76
+ const shareEl = document.querySelector("wcs-share");
77
+ if (shareEl.canShare({ url: location.href })) {
78
+ // 共有ボタンを表示する
79
+ }
80
+ </script>
81
+ ```
82
+
83
+ ## 観測可能プロパティ(出力)
84
+
85
+ | プロパティ | イベント | 説明 |
86
+ | ----------- | ------------------------------- | ---- |
87
+ | `value` | `wcs-share:complete` | 直前に成功した `share()` 呼び出しへ渡された `data` オブジェクトのエコーバックで、「この共有は成功した」という合図(`navigator.share()` 自体はペイロードを持たない `Promise<void>` を返す)。成功した共有が一度も無ければ `null`。 |
88
+ | `loading` | `wcs-share:loading-changed` | `share()` 呼び出しが進行中なら `true`。 |
89
+ | `error` | `wcs-share:error` | 真のプラットフォーム障害(ユーザーが共有シートをキャンセルした場合を**除く**すべて)。まだ失敗が無い場合、または次の `share()` 呼び出しでリセットされた後は `null`。 |
90
+ | `cancelled` | `wcs-share:cancelled-changed` | ユーザーがネイティブ共有シートを閉じた(`AbortError`)場合に `true`。`error` を条件にしたバインディングが日常的なキャンセルに反応しないよう、`error` とは独立している。 |
91
+
92
+ `cancelled` と `error` はどちらも、実際に `navigator.share()` を呼び出す `share()` 呼び出しの **開始時** にリセットされる(`false` / `null`)ため、前回の呼び出しの古い結果がその呼び出しの結果に残り続けることはありません。唯一の例外が unsupported 早期リターン(`navigator.share` が存在しない場合。後述)です — このリセットが走る前に return するため、前回呼び出しの `cancelled` が `true` のまま残り、新たに設定された unsupported の `error` と同時に立つことがあります。`navigator.share` がセッション途中で消失するのは非現実的なため、これは限定的なエッジケースです。
93
+
94
+ ## コマンド
95
+
96
+ | コマンド | 非同期 | 説明 |
97
+ | ------- | ----- | ---- |
98
+ | `share` | あり | `{ title?, text?, url?, files? }` というオプションオブジェクト1個を位置引数として渡し `navigator.share(data)` を呼び出す。 |
99
+
100
+ **`abort` コマンドはありません** — Web Share API には呼び出し元が進行中の `share()` 呼び出しを中断する手段が存在しません。
101
+
102
+ ## `canShare(data)` — `wcBindable` に属さない素の同期メソッド
103
+
104
+ `navigator.canShare(data)` は同期・副作用無しの述語関数です。wc-bindable の `properties`(引数無しで観測する形)にも `commands`(起動してイベント経由で結果を受け取る形)にも合わないため、素のインスタンスメソッドとして直接公開されています:
105
+
106
+ ```typescript
107
+ const canShare: boolean = shareEl.canShare({ url: "https://example.com" });
108
+ ```
109
+
110
+ `navigator.canShare` が存在しない場合は例外を投げず `false` を返します。
111
+
112
+ ## 属性 / 入力
113
+
114
+ **無し。** `share(data)` の `data` は呼び出しごとに変わる値であり、属性としてあらかじめ要素に貼っておく設定値ではなく、コマンド引数です。
115
+
116
+ ## 注意・制限
117
+
118
+ - **`autoTrigger` を持たない。** `navigator.share()` は実際のユーザー操作の文脈内から呼び出す必要があります。ノード側が自動トリガーを提供しても、そのトリガー自体がジェスチャー文脈を継承しないため、`@wcstack/fullscreen` と同様にこのノードは自動トリガーを持ちません。クリックハンドラを直接 `$command.doShare.emit(...)` に配線してください。
119
+ - **`abort` コマンドを持たない。** 進行中の `navigator.share()` 呼び出しを中断するプラットフォーム機構が存在しません。
120
+ - **`cancelled` は `error` と独立している。** `AbortError`(ユーザーが共有シートを閉じた)は `cancelled` のみを設定し、`error` には触れません。それ以外の reject はすべて `error` のみを設定し、`cancelled` には触れません。
121
+ - **`unsupported` 専用フラグは持たない。** `navigator.share` が関数でない状態で `share()` を呼ぶと、即座に `error` が `{ message: "Web Share API is not supported in this browser." }` になり `null` で解決します — 非同期処理を開始しないため `_gen` は消費されません。事前に UI を隠したい場合は `canShare` または `typeof navigator.share` を確認してください。
122
+ - **SSR(`@wcstack/server`)。** `static hasConnectedCallbackPromise = true` を宣言し `connectedCallbackPromise` を公開します。非同期 probe が無いため、この promise は常に即座に settle します(`ready` は `Promise.resolve()` 固定)。
123
+ - **同値ガードは `loading`/`error`/`cancelled` に適用され、`value` には適用されない。** これら3つの setter は idempotent state であるため、値が実際に変化したとき(参照等価 `===`)のみ発火します。`value` は異なり、**成功完了シグナル**であって `wcs-share:complete` が唯一の成功通知であるため、成功した `share()` の**たびに**発火します(同値ガード無し)。したがって、`data` 引数無しの `share()`(`value` が既に `null` のときに `null` を echo)でも `wcs-share:complete` を発火し、**同一のオブジェクト参照**を `data` として渡す2回連続の成功した `share()` は `wcs-share:complete` を**2回**発火します(完了ごとに1回)。これは `@wcstack/clipboard`(`read`)や `@wcstack/broadcast`(`message`)が結果/イベント値を扱う方針と同じです — 完了は「発生」であって idempotent state ではありません。
124
+
125
+ ## ヘッドレス利用(`ShareCore`)
126
+
127
+ Core は DOM 非依存で、直接利用できます:
128
+
129
+ ```typescript
130
+ import { ShareCore } from "@wcstack/share";
131
+
132
+ const share = new ShareCore();
133
+ share.addEventListener("wcs-share:complete", (e) => {
134
+ console.log((e as CustomEvent).detail.value); // エコーバックされた data
135
+ });
136
+ share.addEventListener("wcs-share:cancelled-changed", (e) => {
137
+ console.log("cancelled:", (e as CustomEvent).detail);
138
+ });
139
+
140
+ await share.share({ title: "Article", url: location.href });
141
+
142
+ // 後始末:
143
+ share.dispose(); // 進行中の share() を無効化し、stale な resolve を破棄する
144
+ ```
145
+
146
+ ## ライセンス
147
+
148
+ MIT
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # @wcstack/share
2
+
3
+ `@wcstack/share` is a headless Web Share component for the wcstack ecosystem.
4
+
5
+ It is not a visual UI widget.
6
+ It is a **command-only async primitive node** that turns `navigator.share(data)` — click, native share sheet, resolve/reject — into a single declarative command, the same way `@wcstack/notification` turns `Notification` into reactive state plus a `notify` command.
7
+
8
+ With `@wcstack/state`, `<wcs-share>` can be bound directly through path contracts:
9
+
10
+ - **command surface**: `share(data)` — a single async command, invoked as `command.share: $command.doShare`
11
+ - **output state surface**: `value`, `loading`, `error`, `cancelled`
12
+
13
+ This means a "Share this article" button can be expressed declaratively in HTML — success, failure, and the user simply dismissing the native share sheet are three distinct, bindable outcomes — without writing `navigator.share()` / `try`/`catch` glue in your UI layer.
14
+
15
+ `@wcstack/share` follows the wcstack Core/Shell architecture:
16
+
17
+ - **Core** (`ShareCore`) wraps `navigator.share(data)` behind a single `_gen` generation guard, same-value-guarded `loading`/`error`/`cancelled` setters (`value` is exempt — a completion signal that fires on every success), and a never-throw `try`/`catch`
18
+ - **Shell** (`<wcs-share>`) connects that command to DOM lifecycle and exposes `canShare(data)` as a plain synchronous method
19
+ - **Binding Contract** (`static wcBindable`) declares observable `properties` and a single `share` command (deliberately **no `inputs`, no `abort` command**)
20
+
21
+ ## Why this exists — a command-only node, and cancellation is not an error
22
+
23
+ Every other wcstack IO node either monitors a continuous state (`network`, `permission`) or configures something ahead of time and observes it change (`fetch`'s `url`, `geolocation`'s `enableHighAccuracy`). `navigator.share()` is different: it is a one-shot "call → native share sheet → resolve/reject" action with no continuous state to configure or watch, and (unlike `fetch`) **no way to abort an in-flight call** — there is no `AbortSignal` option, and the platform allows only one modal share sheet at a time, so the "a new call supersedes the previous one" plumbing `fetch` needs has no counterpart here.
24
+
25
+ The other defining decision is **separating `cancelled` from `error`**. When a user simply closes the native share sheet, `navigator.share()` rejects with an `AbortError` — exactly like closing a `<dialog>`. Folding that into `error` would make a binding gated on `error` (e.g. showing a "sharing failed" banner only when it is set) also fire on routine, harmless user cancellation, which is a UX bug waiting to happen. `<wcs-share>` keeps `cancelled` as its own boolean/event, so `error` reflects **only genuine platform failures** (`NotAllowedError`, `TypeError`, etc.).
26
+
27
+ > See [`docs/web-share-tag-design.md`](https://github.com/wcstack/wcstack/blob/main/docs/web-share-tag-design.md) for the full design rationale.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm install @wcstack/share
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ### 1. Share an article, with cancellation handled separately from failure
38
+
39
+ ```html
40
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
41
+ <script type="module" src="https://esm.run/@wcstack/share/auto"></script>
42
+
43
+ <wcs-state>
44
+ <script type="module">
45
+ export default {
46
+ $commandTokens: ["doShare"],
47
+ loading: false,
48
+ error: null,
49
+ cancelled: false,
50
+ onShareClick() {
51
+ this.$command.doShare.emit({
52
+ title: document.title,
53
+ url: location.href,
54
+ });
55
+ },
56
+ };
57
+ </script>
58
+ </wcs-state>
59
+
60
+ <wcs-share
61
+ data-wcs="command.share: $command.doShare; loading: loading; error: error; cancelled: cancelled"
62
+ ></wcs-share>
63
+
64
+ <button data-wcs="onclick: onShareClick; disabled: loading">Share</button>
65
+ <template data-wcs="if: error">
66
+ <p>Sharing failed: <span data-wcs="textContent: error.message"></span></p>
67
+ </template>
68
+ ```
69
+
70
+ Because `share()` must run from within a real user gesture (a click handler), the button's click handler calls `$command.doShare.emit(...)` directly — `<wcs-share>` has no `autoTrigger` shortcut of its own (see [Notes & limitations](#notes--limitations)).
71
+
72
+ ### 2. `canShare(data)` — checking feasibility ahead of time
73
+
74
+ ```html
75
+ <script type="module">
76
+ const shareEl = document.querySelector("wcs-share");
77
+ if (shareEl.canShare({ url: location.href })) {
78
+ // show the Share button
79
+ }
80
+ </script>
81
+ ```
82
+
83
+ ## Observable Properties (outputs)
84
+
85
+ | Property | Event | Description |
86
+ | ----------- | ------------------------------- | ------------ |
87
+ | `value` | `wcs-share:complete` | An echo of the `data` object passed to the `share()` call that just completed successfully, signalling "this share succeeded" (`navigator.share()` itself resolves `Promise<void>` with no payload). `null` before any successful share. |
88
+ | `loading` | `wcs-share:loading-changed` | `true` while a `share()` call is in flight. |
89
+ | `error` | `wcs-share:error` | A genuine platform failure (anything **other than** the user cancelling the share sheet). `null` when there has been no failure yet, or after the next `share()` call resets it. |
90
+ | `cancelled` | `wcs-share:cancelled-changed` | `true` when the user dismissed the native share sheet (`AbortError`). Kept independent of `error` so bindings gated on `error` do not react to routine cancellation. |
91
+
92
+ `cancelled` and `error` are both reset (`false` / `null`) at the **start** of a `share()` call that goes on to actually invoke `navigator.share()`, so a stale outcome from a previous call never lingers into that call's result. The one exception is the unsupported early-return (`navigator.share` missing, see below): it returns before that reset runs, so a `cancelled` left over from an earlier call can still read `true` alongside the freshly-set unsupported `error`. This is a narrow edge case in practice — a page losing `navigator.share` mid-session is not a realistic scenario.
93
+
94
+ ## Commands
95
+
96
+ | Command | Async | Description |
97
+ | ------- | ----- | ------------ |
98
+ | `share` | yes | Invokes `navigator.share(data)` with a single options object (`{ title?, text?, url?, files? }`) passed as one positional argument. |
99
+
100
+ There is **no `abort` command** — the Web Share API offers no mechanism to cancel an in-flight `share()` call from the caller's side.
101
+
102
+ ## `canShare(data)` — a plain synchronous method, not part of `wcBindable`
103
+
104
+ `navigator.canShare(data)` is a synchronous, side-effect-free predicate. It does not fit the wc-bindable `properties` shape (an observable with no arguments) or the `commands` shape (fire-and-observe-via-event); it is exposed directly as a plain instance method:
105
+
106
+ ```typescript
107
+ const canShare: boolean = shareEl.canShare({ url: "https://example.com" });
108
+ ```
109
+
110
+ It returns `false` (rather than throwing) when `navigator.canShare` is absent.
111
+
112
+ ## Attributes / Inputs
113
+
114
+ **None.** `share(data)`'s `data` varies on every call — it is a command argument, not a value to park on the element ahead of time as an attribute.
115
+
116
+ ## Notes & limitations
117
+
118
+ - **No `autoTrigger`.** `navigator.share()` must be invoked from within a real user gesture. A node-provided auto-trigger would not itself carry that gesture context, so — like `@wcstack/fullscreen` — this node has none. Wire the click handler directly to `$command.doShare.emit(...)`.
119
+ - **No `abort` command.** There is no platform mechanism to cancel an in-flight `navigator.share()` call.
120
+ - **`cancelled` is independent of `error`.** `AbortError` (the user closed the share sheet) sets `cancelled`, never `error`. Every other rejection sets `error`, never `cancelled`.
121
+ - **`unsupported` has no dedicated flag.** Calling `share()` when `navigator.share` is not a function immediately sets `error` to `{ message: "Web Share API is not supported in this browser." }` and resolves with `null` — no `_gen` is consumed, since no asynchronous work is started. Check `canShare`, or `typeof navigator.share`, ahead of time if you want to hide the UI proactively.
122
+ - **SSR (`@wcstack/server`).** Declares `static hasConnectedCallbackPromise = true` and exposes `connectedCallbackPromise`; since there is no asynchronous probe, this promise always settles immediately (`ready` is `Promise.resolve()`).
123
+ - **Same-value guard applies to `loading`/`error`/`cancelled`, but NOT to `value`.** Those three setters only dispatch when the value actually changes (reference equality, `===`), since they are idempotent state. `value` is different: it is a **success-completion signal**, and `wcs-share:complete` is the *sole* success notification, so it fires on **every** successful `share()` — with no same-value guard. This means a data-less `share()` (echoing `null` when `value` is already `null`) still dispatches `wcs-share:complete`, and two consecutive successful `share()` calls passed the **same object reference** as `data` dispatch `wcs-share:complete` **twice** (once per completion). This matches how `@wcstack/clipboard` (`read`) and `@wcstack/broadcast` (`message`) treat result/event values — a completion is an occurrence, not idempotent state.
124
+
125
+ ## Headless usage (`ShareCore`)
126
+
127
+ The Core has no DOM dependency and can be used directly:
128
+
129
+ ```typescript
130
+ import { ShareCore } from "@wcstack/share";
131
+
132
+ const share = new ShareCore();
133
+ share.addEventListener("wcs-share:complete", (e) => {
134
+ console.log((e as CustomEvent).detail.value); // the echoed data
135
+ });
136
+ share.addEventListener("wcs-share:cancelled-changed", (e) => {
137
+ console.log("cancelled:", (e as CustomEvent).detail);
138
+ });
139
+
140
+ await share.share({ title: "Article", url: location.href });
141
+
142
+ // later, when done:
143
+ share.dispose(); // invalidate any in-flight share() so a stale resolve is dropped
144
+ ```
145
+
146
+ ## License
147
+
148
+ MIT
package/dist/auto.js ADDED
@@ -0,0 +1,3 @@
1
+ import { bootstrapShare } from "./index.esm.js";
2
+
3
+ bootstrapShare();
@@ -0,0 +1 @@
1
+ import{bootstrapShare}from"./index.esm.min.js";bootstrapShare();
@@ -0,0 +1,166 @@
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 share: string;
24
+ }
25
+ interface IWritableTagNames {
26
+ share?: string;
27
+ }
28
+ interface IConfig {
29
+ readonly tagNames: ITagNames;
30
+ }
31
+ interface IWritableConfig {
32
+ tagNames?: IWritableTagNames;
33
+ }
34
+
35
+ /**
36
+ * The data object passed to `navigator.share(data)` / `navigator.canShare(data)`.
37
+ * All fields are optional per the Web Share API; a caller typically supplies a
38
+ * subset (e.g. just `url`, or `title` + `text` + `url`, or `files`).
39
+ */
40
+ interface WcsShareData {
41
+ title?: string;
42
+ text?: string;
43
+ url?: string;
44
+ files?: File[];
45
+ }
46
+ /**
47
+ * Value types for ShareCore (headless) — the observable state properties.
48
+ * Use with `bind()` from a wc-bindable binding core for compile-time type checking.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const core = new ShareCore();
53
+ * bind(core, (name: keyof WcsShareCoreValues, value) => { ... });
54
+ * ```
55
+ */
56
+ interface WcsShareCoreValues {
57
+ /**
58
+ * The success signal: an echo of the `data` object passed to the `share()`
59
+ * call that just completed successfully (navigator.share() itself resolves
60
+ * `Promise<void>`, so `value` is synthesized rather than read off the API —
61
+ * see docs/web-share-tag-design.md §4). `null` before any successful share.
62
+ */
63
+ value: WcsShareData | null;
64
+ loading: boolean;
65
+ /**
66
+ * A true platform failure (anything other than the user cancelling the
67
+ * share sheet). `null` when there has been no failure yet or after a reset.
68
+ */
69
+ error: any;
70
+ /**
71
+ * `true` when the user dismissed the share sheet (AbortError). Kept
72
+ * separate from `error` so a binding gated on `error` does not react to a
73
+ * routine cancellation (docs/web-share-tag-design.md §3).
74
+ */
75
+ cancelled: boolean;
76
+ }
77
+ /**
78
+ * Value types for the Shell (`<wcs-share>`) — identical observable surface to
79
+ * the Core. The Shell adds no inputs: `share(data)`'s `data` is a per-call
80
+ * argument, not a declarative attribute (docs/web-share-tag-design.md §10).
81
+ */
82
+ type WcsShareValues = WcsShareCoreValues;
83
+
84
+ declare function bootstrapShare(userConfig?: IWritableConfig): void;
85
+
86
+ declare function getConfig(): IConfig;
87
+
88
+ /**
89
+ * Headless Web Share primitive. A thin, framework-agnostic wrapper around
90
+ * `navigator.share(data)` exposed through the wc-bindable protocol.
91
+ *
92
+ * This is a simplified derivative of `FetchCore._doFetch`
93
+ * (docs/web-share-tag-design.md §2): it keeps the single `_gen` generation
94
+ * guard, the same-value-guarded private setters, and the never-throw
95
+ * try/catch wrapper, but drops `AbortController`/`abort()` entirely —
96
+ * `navigator.share()` accepts no `AbortSignal` and there is no platform
97
+ * mechanism for a caller to cancel an in-flight share dialog. A share dialog
98
+ * is also a single system-modal surface (at most one open at a time), so the
99
+ * "a new call supersedes the previous one" plumbing that `FetchCore` needs
100
+ * has no counterpart here either.
101
+ */
102
+ declare class ShareCore extends EventTarget {
103
+ static wcBindable: IWcBindable;
104
+ private _target;
105
+ private _value;
106
+ private _loading;
107
+ private _error;
108
+ private _cancelled;
109
+ private _gen;
110
+ private _ready;
111
+ constructor(target?: EventTarget);
112
+ get ready(): Promise<void>;
113
+ get value(): WcsShareData | null;
114
+ get loading(): boolean;
115
+ get error(): any;
116
+ get cancelled(): boolean;
117
+ observe(): Promise<void>;
118
+ dispose(): void;
119
+ private _setLoading;
120
+ private _setValue;
121
+ private _setError;
122
+ private _setCancelled;
123
+ private _api;
124
+ share(data?: WcsShareData): Promise<WcsShareData | null>;
125
+ }
126
+
127
+ /**
128
+ * `<wcs-share>` — declarative Web Share API primitive.
129
+ *
130
+ * The smallest command-only Shell in the batch (docs/web-share-tag-design.md
131
+ * §10): no attributes at all. `share(data)`'s `data` is a per-call argument,
132
+ * not a declarative setting to park on the element ahead of time.
133
+ */
134
+ declare class WcsShare extends HTMLElement {
135
+ static hasConnectedCallbackPromise: boolean;
136
+ static wcBindable: IWcBindable;
137
+ private _core;
138
+ private _connectedCallbackPromise;
139
+ constructor();
140
+ get value(): WcsShareData | null;
141
+ get loading(): boolean;
142
+ get error(): any;
143
+ get cancelled(): boolean;
144
+ get connectedCallbackPromise(): Promise<void>;
145
+ share(data?: WcsShareData): Promise<WcsShareData | null>;
146
+ /**
147
+ * Synchronous, side-effect-free delegation to `navigator.canShare(data)`
148
+ * (docs/web-share-tag-design.md §6). Deliberately outside `wcBindable`
149
+ * (not a `properties`/`commands` entry): the platform method takes an
150
+ * argument that varies per call, which does not fit the "observe with no
151
+ * arguments" shape of a bindable property, and is synchronous, which does
152
+ * not fit the fire-and-observe-via-event shape of a command.
153
+ *
154
+ * No never-throw wrapping: the platform method itself is synchronous and
155
+ * side-effect-free, so a throw here would indicate a browser bug rather
156
+ * than a condition this Shell should paper over. `navigator.canShare` is
157
+ * still resolved defensively (some environments lack it even when `share`
158
+ * exists), returning `false` rather than throwing in that case.
159
+ */
160
+ canShare(data?: WcsShareData): boolean;
161
+ connectedCallback(): void;
162
+ disconnectedCallback(): void;
163
+ }
164
+
165
+ export { ShareCore, WcsShare, bootstrapShare, getConfig };
166
+ export type { IWritableConfig, IWritableTagNames, WcsShareCoreValues, WcsShareData, WcsShareValues };
@@ -0,0 +1,305 @@
1
+ const _config = {
2
+ tagNames: {
3
+ share: "wcs-share",
4
+ },
5
+ };
6
+ function deepFreeze(obj) {
7
+ if (obj === null || typeof obj !== "object")
8
+ return obj;
9
+ Object.freeze(obj);
10
+ for (const key of Object.keys(obj)) {
11
+ deepFreeze(obj[key]);
12
+ }
13
+ return obj;
14
+ }
15
+ function deepClone(obj) {
16
+ if (obj === null || typeof obj !== "object")
17
+ return obj;
18
+ const clone = {};
19
+ for (const key of Object.keys(obj)) {
20
+ clone[key] = deepClone(obj[key]);
21
+ }
22
+ return clone;
23
+ }
24
+ let frozenConfig = null;
25
+ const config = _config;
26
+ function getConfig() {
27
+ if (!frozenConfig) {
28
+ frozenConfig = deepFreeze(deepClone(_config));
29
+ }
30
+ return frozenConfig;
31
+ }
32
+ function setConfig(partialConfig) {
33
+ if (partialConfig.tagNames) {
34
+ Object.assign(_config.tagNames, partialConfig.tagNames);
35
+ }
36
+ frozenConfig = null;
37
+ }
38
+
39
+ /**
40
+ * Headless Web Share primitive. A thin, framework-agnostic wrapper around
41
+ * `navigator.share(data)` exposed through the wc-bindable protocol.
42
+ *
43
+ * This is a simplified derivative of `FetchCore._doFetch`
44
+ * (docs/web-share-tag-design.md §2): it keeps the single `_gen` generation
45
+ * guard, the same-value-guarded private setters, and the never-throw
46
+ * try/catch wrapper, but drops `AbortController`/`abort()` entirely —
47
+ * `navigator.share()` accepts no `AbortSignal` and there is no platform
48
+ * mechanism for a caller to cancel an in-flight share dialog. A share dialog
49
+ * is also a single system-modal surface (at most one open at a time), so the
50
+ * "a new call supersedes the previous one" plumbing that `FetchCore` needs
51
+ * has no counterpart here either.
52
+ */
53
+ class ShareCore extends EventTarget {
54
+ static wcBindable = {
55
+ protocol: "wc-bindable",
56
+ version: 1,
57
+ properties: [
58
+ { name: "value", event: "wcs-share:complete", getter: (e) => e.detail.value },
59
+ { name: "loading", event: "wcs-share:loading-changed" },
60
+ { name: "error", event: "wcs-share:error" },
61
+ { name: "cancelled", event: "wcs-share:cancelled-changed" },
62
+ ],
63
+ commands: [
64
+ { name: "share", async: true },
65
+ ],
66
+ };
67
+ _target;
68
+ _value = null;
69
+ _loading = false;
70
+ _error = null;
71
+ _cancelled = false;
72
+ // Generation guard (§3.4 of the guidelines): bumped ONLY by dispose(). A
73
+ // share() that settles after dispose() has a stale `gen` and MUST NOT write
74
+ // state to a torn-down element. Unlike FetchCore/EyedropperCore, share()
75
+ // itself does NOT bump `_gen` on each call: docs/web-share-tag-design.md §2
76
+ // deliberately drops the "a new call supersedes the previous one" plumbing
77
+ // those cores need, because the platform allows only one open share dialog
78
+ // at a time (a second concurrent share() rejects with InvalidStateError on
79
+ // its own). Bumping `_gen` per call would instead let a fast-failing second
80
+ // call incorrectly invalidate a still-pending first call's eventual
81
+ // success. Also not bumped on the unsupported early-return — no
82
+ // asynchronous work is started, so there is no generation to protect
83
+ // (docs/web-share-tag-design.md §8).
84
+ _gen = 0;
85
+ // SSR (§3.8): no asynchronous probe to await, so readiness is immediate.
86
+ _ready = Promise.resolve();
87
+ constructor(target) {
88
+ super();
89
+ this._target = target ?? this;
90
+ }
91
+ get ready() {
92
+ return this._ready;
93
+ }
94
+ get value() {
95
+ return this._value;
96
+ }
97
+ get loading() {
98
+ return this._loading;
99
+ }
100
+ get error() {
101
+ return this._error;
102
+ }
103
+ get cancelled() {
104
+ return this._cancelled;
105
+ }
106
+ // Lifecycle (§3.5). Share is command-driven with no subscription to
107
+ // establish, so observe() is an idempotent no-op that resolves once ready;
108
+ // dispose() only invalidates any in-flight share() (there is nothing to
109
+ // abort or unsubscribe).
110
+ observe() {
111
+ return this._ready;
112
+ }
113
+ dispose() {
114
+ this._gen++;
115
+ }
116
+ _setLoading(loading) {
117
+ if (this._loading === loading)
118
+ return;
119
+ this._loading = loading;
120
+ this._target.dispatchEvent(new CustomEvent("wcs-share:loading-changed", {
121
+ detail: loading,
122
+ bubbles: true,
123
+ }));
124
+ }
125
+ // Deliberately NO same-value guard (unlike error/loading/cancelled below).
126
+ // `value` is a success-completion signal, not idempotent state: it is written
127
+ // only on a successful share(), and wcs-share:complete is the *sole* success
128
+ // notification. Two consecutive successful shares — even with the same `data`
129
+ // object reference, or a data-less share echoing null when value is already
130
+ // null — are two distinct completions and must each re-fire wcs-share:complete
131
+ // so an `$on`/eventToken consumer (and a `value:` binding) sees every success.
132
+ // This matches clipboard `_setRead` / broadcast `_setMessage`, which carve
133
+ // result/event values out of the §3.3 guard for the same reason.
134
+ _setValue(value) {
135
+ this._value = value;
136
+ this._target.dispatchEvent(new CustomEvent("wcs-share:complete", {
137
+ detail: { value },
138
+ bubbles: true,
139
+ }));
140
+ }
141
+ _setError(error) {
142
+ if (this._error === error)
143
+ return;
144
+ this._error = error;
145
+ this._target.dispatchEvent(new CustomEvent("wcs-share:error", {
146
+ detail: error,
147
+ bubbles: true,
148
+ }));
149
+ }
150
+ _setCancelled(cancelled) {
151
+ if (this._cancelled === cancelled)
152
+ return;
153
+ this._cancelled = cancelled;
154
+ this._target.dispatchEvent(new CustomEvent("wcs-share:cancelled-changed", {
155
+ detail: cancelled,
156
+ bubbles: true,
157
+ }));
158
+ }
159
+ // API resolution is call-time, never cached (§3.7): lets tests install/remove
160
+ // navigator.share freely and lets an unsupported environment be detected
161
+ // correctly on every call.
162
+ _api() {
163
+ const nav = globalThis.navigator;
164
+ return typeof nav?.share === "function" ? nav.share.bind(nav) : undefined;
165
+ }
166
+ async share(data) {
167
+ // never-throw + unsupported (§8): resolve API at call time and bail out
168
+ // immediately if absent. No _gen bump — no asynchronous work is started,
169
+ // so there is no generation to protect, and navigator.share() itself is
170
+ // never invoked.
171
+ const shareFn = this._api();
172
+ if (!shareFn) {
173
+ this._setError({ message: "Web Share API is not supported in this browser." });
174
+ return null;
175
+ }
176
+ // Captured, not bumped (see the `_gen` field docs above): share() does
177
+ // not supersede a prior in-flight call, only dispose() invalidates.
178
+ const gen = this._gen;
179
+ this._setLoading(true);
180
+ // Reset the previous outcome before starting a new share so a stale
181
+ // cancelled/error does not linger into this call's result
182
+ // (docs/web-share-tag-design.md §3).
183
+ this._setError(null);
184
+ this._setCancelled(false);
185
+ try {
186
+ await shareFn(data);
187
+ // Stale completion (dispose() ran while the share dialog was open).
188
+ // Drop the result without writing state.
189
+ if (gen !== this._gen) {
190
+ return null;
191
+ }
192
+ // navigator.share() resolves `Promise<void>` — there is no payload to
193
+ // read off the API, so `value` is synthesized as an echo of the caller's
194
+ // `data`, signalling "this share completed successfully"
195
+ // (docs/web-share-tag-design.md §4).
196
+ this._setValue(data ?? null);
197
+ this._setLoading(false);
198
+ return data ?? null;
199
+ }
200
+ catch (e) {
201
+ // Stale completion (dispose() ran while the share dialog was open).
202
+ if (gen !== this._gen) {
203
+ return null;
204
+ }
205
+ if (e?.name === "AbortError") {
206
+ // The user dismissed the share sheet — a routine cancellation, not a
207
+ // platform failure. Kept out of `error` (docs/web-share-tag-design.md §3).
208
+ this._setCancelled(true);
209
+ }
210
+ else {
211
+ this._setError(e);
212
+ }
213
+ this._setLoading(false);
214
+ return null;
215
+ }
216
+ }
217
+ }
218
+
219
+ /**
220
+ * `<wcs-share>` — declarative Web Share API primitive.
221
+ *
222
+ * The smallest command-only Shell in the batch (docs/web-share-tag-design.md
223
+ * §10): no attributes at all. `share(data)`'s `data` is a per-call argument,
224
+ * not a declarative setting to park on the element ahead of time.
225
+ */
226
+ class WcsShare extends HTMLElement {
227
+ // SSR (§4.4): observe() completes synchronously, but the Shell still exposes
228
+ // connectedCallbackPromise so the state binder can await it uniformly across
229
+ // all IO nodes before snapshotting.
230
+ static hasConnectedCallbackPromise = true;
231
+ static wcBindable = {
232
+ ...ShareCore.wcBindable,
233
+ inputs: [],
234
+ // Core の commands をそのまま継承(単一情報源)。
235
+ commands: ShareCore.wcBindable.commands,
236
+ };
237
+ _core;
238
+ _connectedCallbackPromise = Promise.resolve();
239
+ constructor() {
240
+ super();
241
+ this._core = new ShareCore(this);
242
+ }
243
+ // --- Core delegated getters ---
244
+ get value() {
245
+ return this._core.value;
246
+ }
247
+ get loading() {
248
+ return this._core.loading;
249
+ }
250
+ get error() {
251
+ return this._core.error;
252
+ }
253
+ get cancelled() {
254
+ return this._core.cancelled;
255
+ }
256
+ get connectedCallbackPromise() {
257
+ return this._connectedCallbackPromise;
258
+ }
259
+ // --- Commands ---
260
+ share(data) {
261
+ return this._core.share(data);
262
+ }
263
+ /**
264
+ * Synchronous, side-effect-free delegation to `navigator.canShare(data)`
265
+ * (docs/web-share-tag-design.md §6). Deliberately outside `wcBindable`
266
+ * (not a `properties`/`commands` entry): the platform method takes an
267
+ * argument that varies per call, which does not fit the "observe with no
268
+ * arguments" shape of a bindable property, and is synchronous, which does
269
+ * not fit the fire-and-observe-via-event shape of a command.
270
+ *
271
+ * No never-throw wrapping: the platform method itself is synchronous and
272
+ * side-effect-free, so a throw here would indicate a browser bug rather
273
+ * than a condition this Shell should paper over. `navigator.canShare` is
274
+ * still resolved defensively (some environments lack it even when `share`
275
+ * exists), returning `false` rather than throwing in that case.
276
+ */
277
+ canShare(data) {
278
+ const nav = globalThis.navigator;
279
+ return typeof nav?.canShare === "function" ? nav.canShare(data) : false;
280
+ }
281
+ // --- Lifecycle ---
282
+ connectedCallback() {
283
+ this.style.display = "none";
284
+ this._connectedCallbackPromise = this._core.observe();
285
+ }
286
+ disconnectedCallback() {
287
+ this._core.dispose();
288
+ }
289
+ }
290
+
291
+ function registerComponents() {
292
+ if (!customElements.get(config.tagNames.share)) {
293
+ customElements.define(config.tagNames.share, WcsShare);
294
+ }
295
+ }
296
+
297
+ function bootstrapShare(userConfig) {
298
+ if (userConfig) {
299
+ setConfig(userConfig);
300
+ }
301
+ registerComponents();
302
+ }
303
+
304
+ export { ShareCore, WcsShare, bootstrapShare, getConfig };
305
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/ShareCore.ts","../src/components/Share.ts","../src/registerComponents.ts","../src/bootstrapShare.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n share: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n share: \"wcs-share\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WcsShareData } from \"../types.js\";\n\n/**\n * Headless Web Share primitive. A thin, framework-agnostic wrapper around\n * `navigator.share(data)` exposed through the wc-bindable protocol.\n *\n * This is a simplified derivative of `FetchCore._doFetch`\n * (docs/web-share-tag-design.md §2): it keeps the single `_gen` generation\n * guard, the same-value-guarded private setters, and the never-throw\n * try/catch wrapper, but drops `AbortController`/`abort()` entirely —\n * `navigator.share()` accepts no `AbortSignal` and there is no platform\n * mechanism for a caller to cancel an in-flight share dialog. A share dialog\n * is also a single system-modal surface (at most one open at a time), so the\n * \"a new call supersedes the previous one\" plumbing that `FetchCore` needs\n * has no counterpart here either.\n */\nexport class ShareCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: \"wcs-share:complete\", getter: (e: Event) => (e as CustomEvent).detail.value },\n { name: \"loading\", event: \"wcs-share:loading-changed\" },\n { name: \"error\", event: \"wcs-share:error\" },\n { name: \"cancelled\", event: \"wcs-share:cancelled-changed\" },\n ],\n commands: [\n { name: \"share\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _value: WcsShareData | 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 // share() that settles after dispose() has a stale `gen` and MUST NOT write\n // state to a torn-down element. Unlike FetchCore/EyedropperCore, share()\n // itself does NOT bump `_gen` on each call: docs/web-share-tag-design.md §2\n // deliberately drops the \"a new call supersedes the previous one\" plumbing\n // those cores need, because the platform allows only one open share dialog\n // at a time (a second concurrent share() rejects with InvalidStateError on\n // its own). Bumping `_gen` per call would instead let a fast-failing second\n // call incorrectly invalidate a still-pending first call's eventual\n // success. Also not bumped on the unsupported early-return — no\n // asynchronous work is started, so there is no generation to protect\n // (docs/web-share-tag-design.md §8).\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(): WcsShareData | 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). Share 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 share() (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-share:loading-changed\", {\n detail: loading,\n bubbles: true,\n }));\n }\n\n // Deliberately NO same-value guard (unlike error/loading/cancelled below).\n // `value` is a success-completion signal, not idempotent state: it is written\n // only on a successful share(), and wcs-share:complete is the *sole* success\n // notification. Two consecutive successful shares — even with the same `data`\n // object reference, or a data-less share echoing null when value is already\n // null — are two distinct completions and must each re-fire wcs-share:complete\n // so an `$on`/eventToken consumer (and a `value:` binding) sees every success.\n // This matches clipboard `_setRead` / broadcast `_setMessage`, which carve\n // result/event values out of the §3.3 guard for the same reason.\n private _setValue(value: WcsShareData | null): void {\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-share: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-share: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-share: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.share freely and lets an unsupported environment be detected\n // correctly on every call.\n private _api(): ((data?: WcsShareData) => Promise<void>) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav?.share === \"function\" ? nav.share.bind(nav) : undefined;\n }\n\n async share(data?: WcsShareData): Promise<WcsShareData | null> {\n // never-throw + unsupported (§8): resolve API at call time and bail out\n // immediately if absent. No _gen bump — no asynchronous work is started,\n // so there is no generation to protect, and navigator.share() itself is\n // never invoked.\n const shareFn = this._api();\n if (!shareFn) {\n this._setError({ message: \"Web Share API is not supported in this browser.\" });\n return null;\n }\n\n // Captured, not bumped (see the `_gen` field docs above): share() 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 share so a stale\n // cancelled/error does not linger into this call's result\n // (docs/web-share-tag-design.md §3).\n this._setError(null);\n this._setCancelled(false);\n\n try {\n await shareFn(data);\n\n // Stale completion (dispose() ran while the share dialog was open).\n // Drop the result without writing state.\n if (gen !== this._gen) {\n return null;\n }\n\n // navigator.share() resolves `Promise<void>` — there is no payload to\n // read off the API, so `value` is synthesized as an echo of the caller's\n // `data`, signalling \"this share completed successfully\"\n // (docs/web-share-tag-design.md §4).\n this._setValue(data ?? null);\n this._setLoading(false);\n return data ?? null;\n } catch (e: any) {\n // Stale completion (dispose() ran while the share dialog was open).\n if (gen !== this._gen) {\n return null;\n }\n if (e?.name === \"AbortError\") {\n // The user dismissed the share sheet — a routine cancellation, not a\n // platform failure. Kept out of `error` (docs/web-share-tag-design.md §3).\n this._setCancelled(true);\n } else {\n this._setError(e);\n }\n this._setLoading(false);\n return null;\n }\n }\n}\n","import { IWcBindable, WcsShareData } from \"../types.js\";\nimport { ShareCore } from \"../core/ShareCore.js\";\n\n/**\n * `<wcs-share>` — declarative Web Share API primitive.\n *\n * The smallest command-only Shell in the batch (docs/web-share-tag-design.md\n * §10): no attributes at all. `share(data)`'s `data` is a per-call argument,\n * not a declarative setting to park on the element ahead of time.\n */\nexport class WcsShare 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 ...ShareCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: ShareCore.wcBindable.commands,\n };\n\n private _core: ShareCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new ShareCore(this);\n }\n\n // --- Core delegated getters ---\n\n get value(): WcsShareData | 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 share(data?: WcsShareData): Promise<WcsShareData | null> {\n return this._core.share(data);\n }\n\n /**\n * Synchronous, side-effect-free delegation to `navigator.canShare(data)`\n * (docs/web-share-tag-design.md §6). Deliberately outside `wcBindable`\n * (not a `properties`/`commands` entry): the platform method takes an\n * argument that varies per call, which does not fit the \"observe with no\n * arguments\" shape of a bindable property, and is synchronous, which does\n * not fit the fire-and-observe-via-event shape of a command.\n *\n * No never-throw wrapping: the platform method itself is synchronous and\n * side-effect-free, so a throw here would indicate a browser bug rather\n * than a condition this Shell should paper over. `navigator.canShare` is\n * still resolved defensively (some environments lack it even when `share`\n * exists), returning `false` rather than throwing in that case.\n */\n canShare(data?: WcsShareData): boolean {\n const nav = (globalThis as any).navigator;\n return typeof nav?.canShare === \"function\" ? nav.canShare(data) : false;\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 { WcsShare } from \"./components/Share.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.share)) {\n customElements.define(config.tagNames.share, WcsShare);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapShare(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:{share:"wcs-share"}};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 r of Object.keys(e))t[r]=s(e[r]);return t}let r=null;const n=e;function a(){return r||(r=t(s(e))),r}class c extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"value",event:"wcs-share:complete",getter:e=>e.detail.value},{name:"loading",event:"wcs-share:loading-changed"},{name:"error",event:"wcs-share:error"},{name:"cancelled",event:"wcs-share:cancelled-changed"}],commands:[{name:"share",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-share:loading-changed",{detail:e,bubbles:!0})))}_setValue(e){this._value=e,this._target.dispatchEvent(new CustomEvent("wcs-share:complete",{detail:{value:e},bubbles:!0}))}_setError(e){this._error!==e&&(this._error=e,this._target.dispatchEvent(new CustomEvent("wcs-share:error",{detail:e,bubbles:!0})))}_setCancelled(e){this._cancelled!==e&&(this._cancelled=e,this._target.dispatchEvent(new CustomEvent("wcs-share:cancelled-changed",{detail:e,bubbles:!0})))}_api(){const e=globalThis.navigator;return"function"==typeof e?.share?e.share.bind(e):void 0}async share(e){const t=this._api();if(!t)return this._setError({message:"Web Share API is not supported in this browser."}),null;const s=this._gen;this._setLoading(!0),this._setError(null),this._setCancelled(!1);try{return await t(e),s!==this._gen?null:(this._setValue(e??null),this._setLoading(!1),e??null)}catch(e){return s!==this._gen||("AbortError"===e?.name?this._setCancelled(!0):this._setError(e),this._setLoading(!1)),null}}}class l extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...c.wcBindable,inputs:[],commands:c.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new c(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}share(e){return this._core.share(e)}canShare(e){const t=globalThis.navigator;return"function"==typeof t?.canShare&&t.canShare(e)}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}function o(t){var s;t&&((s=t).tagNames&&Object.assign(e.tagNames,s.tagNames),r=null),customElements.get(n.tagNames.share)||customElements.define(n.tagNames.share,l)}export{c as ShareCore,l as WcsShare,o as bootstrapShare,a 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/ShareCore.ts","../src/components/Share.ts","../src/bootstrapShare.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n share: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n share: \"wcs-share\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WcsShareData } from \"../types.js\";\n\n/**\n * Headless Web Share primitive. A thin, framework-agnostic wrapper around\n * `navigator.share(data)` exposed through the wc-bindable protocol.\n *\n * This is a simplified derivative of `FetchCore._doFetch`\n * (docs/web-share-tag-design.md §2): it keeps the single `_gen` generation\n * guard, the same-value-guarded private setters, and the never-throw\n * try/catch wrapper, but drops `AbortController`/`abort()` entirely —\n * `navigator.share()` accepts no `AbortSignal` and there is no platform\n * mechanism for a caller to cancel an in-flight share dialog. A share dialog\n * is also a single system-modal surface (at most one open at a time), so the\n * \"a new call supersedes the previous one\" plumbing that `FetchCore` needs\n * has no counterpart here either.\n */\nexport class ShareCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"value\", event: \"wcs-share:complete\", getter: (e: Event) => (e as CustomEvent).detail.value },\n { name: \"loading\", event: \"wcs-share:loading-changed\" },\n { name: \"error\", event: \"wcs-share:error\" },\n { name: \"cancelled\", event: \"wcs-share:cancelled-changed\" },\n ],\n commands: [\n { name: \"share\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _value: WcsShareData | 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 // share() that settles after dispose() has a stale `gen` and MUST NOT write\n // state to a torn-down element. Unlike FetchCore/EyedropperCore, share()\n // itself does NOT bump `_gen` on each call: docs/web-share-tag-design.md §2\n // deliberately drops the \"a new call supersedes the previous one\" plumbing\n // those cores need, because the platform allows only one open share dialog\n // at a time (a second concurrent share() rejects with InvalidStateError on\n // its own). Bumping `_gen` per call would instead let a fast-failing second\n // call incorrectly invalidate a still-pending first call's eventual\n // success. Also not bumped on the unsupported early-return — no\n // asynchronous work is started, so there is no generation to protect\n // (docs/web-share-tag-design.md §8).\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(): WcsShareData | 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). Share 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 share() (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-share:loading-changed\", {\n detail: loading,\n bubbles: true,\n }));\n }\n\n // Deliberately NO same-value guard (unlike error/loading/cancelled below).\n // `value` is a success-completion signal, not idempotent state: it is written\n // only on a successful share(), and wcs-share:complete is the *sole* success\n // notification. Two consecutive successful shares — even with the same `data`\n // object reference, or a data-less share echoing null when value is already\n // null — are two distinct completions and must each re-fire wcs-share:complete\n // so an `$on`/eventToken consumer (and a `value:` binding) sees every success.\n // This matches clipboard `_setRead` / broadcast `_setMessage`, which carve\n // result/event values out of the §3.3 guard for the same reason.\n private _setValue(value: WcsShareData | null): void {\n this._value = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-share: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-share: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-share: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.share freely and lets an unsupported environment be detected\n // correctly on every call.\n private _api(): ((data?: WcsShareData) => Promise<void>) | undefined {\n const nav = (globalThis as any).navigator;\n return typeof nav?.share === \"function\" ? nav.share.bind(nav) : undefined;\n }\n\n async share(data?: WcsShareData): Promise<WcsShareData | null> {\n // never-throw + unsupported (§8): resolve API at call time and bail out\n // immediately if absent. No _gen bump — no asynchronous work is started,\n // so there is no generation to protect, and navigator.share() itself is\n // never invoked.\n const shareFn = this._api();\n if (!shareFn) {\n this._setError({ message: \"Web Share API is not supported in this browser.\" });\n return null;\n }\n\n // Captured, not bumped (see the `_gen` field docs above): share() 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 share so a stale\n // cancelled/error does not linger into this call's result\n // (docs/web-share-tag-design.md §3).\n this._setError(null);\n this._setCancelled(false);\n\n try {\n await shareFn(data);\n\n // Stale completion (dispose() ran while the share dialog was open).\n // Drop the result without writing state.\n if (gen !== this._gen) {\n return null;\n }\n\n // navigator.share() resolves `Promise<void>` — there is no payload to\n // read off the API, so `value` is synthesized as an echo of the caller's\n // `data`, signalling \"this share completed successfully\"\n // (docs/web-share-tag-design.md §4).\n this._setValue(data ?? null);\n this._setLoading(false);\n return data ?? null;\n } catch (e: any) {\n // Stale completion (dispose() ran while the share dialog was open).\n if (gen !== this._gen) {\n return null;\n }\n if (e?.name === \"AbortError\") {\n // The user dismissed the share sheet — a routine cancellation, not a\n // platform failure. Kept out of `error` (docs/web-share-tag-design.md §3).\n this._setCancelled(true);\n } else {\n this._setError(e);\n }\n this._setLoading(false);\n return null;\n }\n }\n}\n","import { IWcBindable, WcsShareData } from \"../types.js\";\nimport { ShareCore } from \"../core/ShareCore.js\";\n\n/**\n * `<wcs-share>` — declarative Web Share API primitive.\n *\n * The smallest command-only Shell in the batch (docs/web-share-tag-design.md\n * §10): no attributes at all. `share(data)`'s `data` is a per-call argument,\n * not a declarative setting to park on the element ahead of time.\n */\nexport class WcsShare 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 ...ShareCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: ShareCore.wcBindable.commands,\n };\n\n private _core: ShareCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new ShareCore(this);\n }\n\n // --- Core delegated getters ---\n\n get value(): WcsShareData | 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 share(data?: WcsShareData): Promise<WcsShareData | null> {\n return this._core.share(data);\n }\n\n /**\n * Synchronous, side-effect-free delegation to `navigator.canShare(data)`\n * (docs/web-share-tag-design.md §6). Deliberately outside `wcBindable`\n * (not a `properties`/`commands` entry): the platform method takes an\n * argument that varies per call, which does not fit the \"observe with no\n * arguments\" shape of a bindable property, and is synchronous, which does\n * not fit the fire-and-observe-via-event shape of a command.\n *\n * No never-throw wrapping: the platform method itself is synchronous and\n * side-effect-free, so a throw here would indicate a browser bug rather\n * than a condition this Shell should paper over. `navigator.canShare` is\n * still resolved defensively (some environments lack it even when `share`\n * exists), returning `false` rather than throwing in that case.\n */\n canShare(data?: WcsShareData): boolean {\n const nav = (globalThis as any).navigator;\n return typeof nav?.canShare === \"function\" ? nav.canShare(data) : false;\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 bootstrapShare(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsShare } from \"./components/Share.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.share)) {\n customElements.define(config.tagNames.share, WcsShare);\n }\n}\n"],"names":["_config","tagNames","share","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","ShareCore","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","bind","undefined","data","shareFn","message","gen","WcsShare","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","connectedCallbackPromise","canShare","connectedCallback","style","display","disconnectedCallback","bootstrapShare","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"AAQA,MAAMA,EAA2B,CAC/BC,SAAU,CACRC,MAAO,cAIX,SAASC,EAAcC,GACrB,GAAY,OAARA,GAA+B,iBAARA,EAAkB,OAAOA,EACpDC,OAAOC,OAAOF,GACd,IAAK,MAAMG,KAAOF,OAAOG,KAAKJ,GAC5BD,EAAYC,EAAgCG,IAE9C,OAAOH,CACT,CAEA,SAASK,EAAaL,GACpB,GAAY,OAARA,GAA+B,iBAARA,EAAkB,OAAOA,EACpD,MAAMM,EAAiC,CAAA,EACvC,IAAK,MAAMH,KAAOF,OAAOG,KAAKJ,GAC5BM,EAAMH,GAAOE,EAAWL,EAAgCG,IAE1D,OAAOG,CACT,CAEA,IAAIC,EAA+B,KAE5B,MAAMC,EAAkBZ,WAEfa,IAId,OAHKF,IACHA,EAAeR,EAAWM,EAAUT,KAE/BW,CACT,CCzBM,MAAOG,UAAkBC,YAC7BC,kBAAiC,CAC/BC,SAAU,cACVC,QAAS,EACTC,WAAY,CACV,CAAEC,KAAM,QAASC,MAAO,qBAAsBC,OAASC,GAAcA,EAAkBC,OAAOC,OAC9F,CAAEL,KAAM,UAAWC,MAAO,6BAC1B,CAAED,KAAM,QAASC,MAAO,mBACxB,CAAED,KAAM,YAAaC,MAAO,gCAE9BK,SAAU,CACR,CAAEN,KAAM,QAASO,OAAO,KAIpBC,QACAC,OAA8B,KAC9BC,UAAoB,EACpBC,OAAc,KACdC,YAAsB,EAatBC,KAAO,EAEPC,OAAwBC,QAAQC,UAExC,WAAAC,CAAYC,GACVC,QACAC,KAAKZ,QAAUU,GAAUE,IAC3B,CAEA,SAAIC,GACF,OAAOD,KAAKN,MACd,CAEA,SAAIT,GACF,OAAOe,KAAKX,MACd,CAEA,WAAIa,GACF,OAAOF,KAAKV,QACd,CAEA,SAAIa,GACF,OAAOH,KAAKT,MACd,CAEA,aAAIa,GACF,OAAOJ,KAAKR,UACd,CAMA,OAAAa,GACE,OAAOL,KAAKN,MACd,CAEA,OAAAY,GACEN,KAAKP,MACP,CAEQ,WAAAc,CAAYL,GACdF,KAAKV,WAAaY,IACtBF,KAAKV,SAAWY,EAChBF,KAAKZ,QAAQoB,cAAc,IAAIC,YAAY,4BAA6B,CACtEzB,OAAQkB,EACRQ,SAAS,KAEb,CAWQ,SAAAC,CAAU1B,GAChBe,KAAKX,OAASJ,EACde,KAAKZ,QAAQoB,cAAc,IAAIC,YAAY,qBAAsB,CAC/DzB,OAAQ,CAAEC,SACVyB,SAAS,IAEb,CAEQ,SAAAE,CAAUT,GACZH,KAAKT,SAAWY,IACpBH,KAAKT,OAASY,EACdH,KAAKZ,QAAQoB,cAAc,IAAIC,YAAY,kBAAmB,CAC5DzB,OAAQmB,EACRO,SAAS,KAEb,CAEQ,aAAAG,CAAcT,GAChBJ,KAAKR,aAAeY,IACxBJ,KAAKR,WAAaY,EAClBJ,KAAKZ,QAAQoB,cAAc,IAAIC,YAAY,8BAA+B,CACxEzB,OAAQoB,EACRM,SAAS,KAEb,CAKQ,IAAAI,GACN,MAAMC,EAAOC,WAAmBC,UAChC,MAA6B,mBAAfF,GAAKrD,MAAuBqD,EAAIrD,MAAMwD,KAAKH,QAAOI,CAClE,CAEA,WAAMzD,CAAM0D,GAKV,MAAMC,EAAUrB,KAAKc,OACrB,IAAKO,EAEH,OADArB,KAAKY,UAAU,CAAEU,QAAS,oDACnB,KAKT,MAAMC,EAAMvB,KAAKP,KAEjBO,KAAKO,aAAY,GAIjBP,KAAKY,UAAU,MACfZ,KAAKa,eAAc,GAEnB,IAKE,aAJMQ,EAAQD,GAIVG,IAAQvB,KAAKP,KACR,MAOTO,KAAKW,UAAUS,GAAQ,MACvBpB,KAAKO,aAAY,GACVa,GAAQ,KACjB,CAAE,MAAOrC,GAEP,OAAIwC,IAAQvB,KAAKP,OAGD,eAAZV,GAAGH,KAGLoB,KAAKa,eAAc,GAEnBb,KAAKY,UAAU7B,GAEjBiB,KAAKO,aAAY,IATR,IAWX,CACF,ECxLI,MAAOiB,UAAiBC,YAI5BjD,oCAAqC,EAErCA,kBAAiC,IAC5BF,EAAUoD,WACbC,OAAQ,GAERzC,SAAUZ,EAAUoD,WAAWxC,UAGzB0C,MACAC,0BAA2ClC,QAAQC,UAE3D,WAAAC,GACEE,QACAC,KAAK4B,MAAQ,IAAItD,EAAU0B,KAC7B,CAIA,SAAIf,GACF,OAAOe,KAAK4B,MAAM3C,KACpB,CAEA,WAAIiB,GACF,OAAOF,KAAK4B,MAAM1B,OACpB,CAEA,SAAIC,GACF,OAAOH,KAAK4B,MAAMzB,KACpB,CAEA,aAAIC,GACF,OAAOJ,KAAK4B,MAAMxB,SACpB,CAEA,4BAAI0B,GACF,OAAO9B,KAAK6B,yBACd,CAIA,KAAAnE,CAAM0D,GACJ,OAAOpB,KAAK4B,MAAMlE,MAAM0D,EAC1B,CAgBA,QAAAW,CAASX,GACP,MAAML,EAAOC,WAAmBC,UAChC,MAAgC,mBAAlBF,GAAKgB,UAA0BhB,EAAIgB,SAASX,EAC5D,CAIA,iBAAAY,GACEhC,KAAKiC,MAAMC,QAAU,OACrBlC,KAAK6B,0BAA4B7B,KAAK4B,MAAMvB,SAC9C,CAEA,oBAAA8B,GACEnC,KAAK4B,MAAMtB,SACb,ECnFI,SAAU8B,EAAeC,GHuCzB,IAAoBC,EGtCpBD,KHsCoBC,EGrCZD,GHsCM5E,UAChBI,OAAO0E,OAAO/E,EAAQC,SAAU6E,EAAc7E,UAEhDU,EAAe,MI3CVqE,eAAeC,IAAIrE,EAAOX,SAASC,QACtC8E,eAAeE,OAAOtE,EAAOX,SAASC,MAAO8D,EDIjD"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@wcstack/share",
3
+ "version": "1.16.0",
4
+ "description": "Declarative Web Share component for Web Components. Framework-agnostic navigator.share primitive 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
+ "web-share",
34
+ "share",
35
+ "navigator-share",
36
+ "custom-elements",
37
+ "wc-bindable",
38
+ "declarative",
39
+ "zero-dependencies",
40
+ "framework-agnostic"
41
+ ],
42
+ "author": "mogera551",
43
+ "homepage": "https://wcstack.github.io",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/wcstack/wcstack.git",
47
+ "directory": "packages/share"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/wcstack/wcstack/issues"
51
+ },
52
+ "license": "MIT",
53
+ "devDependencies": {
54
+ "@eslint/js": "^9.39.1",
55
+ "@rollup/plugin-terser": "^0.4.4",
56
+ "@rollup/plugin-typescript": "^11.1.6",
57
+ "@vitest/coverage-v8": "^4.0.15",
58
+ "@vitest/ui": "^4.0.15",
59
+ "eslint": "^9.39.1",
60
+ "globals": "^16.5.0",
61
+ "happy-dom": "^20.0.11",
62
+ "rimraf": "^6.0.1",
63
+ "rollup": "^4.22.4",
64
+ "rollup-plugin-dts": "^6.1.1",
65
+ "rollup-plugin-copy": "^3.5.0",
66
+ "tslib": "^2.8.1",
67
+ "typescript": "^5.9.3",
68
+ "typescript-eslint": "^8.49.0",
69
+ "vitest": "^4.0.15"
70
+ }
71
+ }