@wcstack/broadcast 1.13.1 → 1.15.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/dist/index.d.ts CHANGED
@@ -1,19 +1,3 @@
1
- interface ITagNames {
2
- readonly broadcast: string;
3
- }
4
- interface IWritableTagNames {
5
- broadcast?: string;
6
- }
7
- interface IConfig {
8
- readonly autoTrigger: boolean;
9
- readonly triggerAttribute: string;
10
- readonly tagNames: ITagNames;
11
- }
12
- interface IWritableConfig {
13
- autoTrigger?: boolean;
14
- triggerAttribute?: string;
15
- tagNames?: IWritableTagNames;
16
- }
17
1
  interface IWcBindableProperty {
18
2
  readonly name: string;
19
3
  readonly event: string;
@@ -29,11 +13,29 @@ interface IWcBindableCommand {
29
13
  }
30
14
  interface IWcBindable {
31
15
  readonly protocol: "wc-bindable";
32
- readonly version: number;
33
- readonly properties: IWcBindableProperty[];
34
- readonly inputs?: IWcBindableInput[];
35
- readonly commands?: IWcBindableCommand[];
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 broadcast: string;
36
24
  }
25
+ interface IWritableTagNames {
26
+ broadcast?: string;
27
+ }
28
+ interface IConfig {
29
+ readonly autoTrigger: boolean;
30
+ readonly triggerAttribute: string;
31
+ readonly tagNames: ITagNames;
32
+ }
33
+ interface IWritableConfig {
34
+ autoTrigger?: boolean;
35
+ triggerAttribute?: string;
36
+ tagNames?: IWritableTagNames;
37
+ }
38
+
37
39
  /**
38
40
  * Normalized BroadcastChannel failure. `name` mirrors the `DOMException.name`
39
41
  * (e.g. `DataCloneError` when a posted value is not structured-cloneable,
@@ -47,7 +49,7 @@ interface WcsBroadcastErrorDetail {
47
49
  }
48
50
  /**
49
51
  * Value types for BroadcastCore (headless) — the observable state properties.
50
- * Use with `bind()` from `@wc-bindable/core` for compile-time type checking.
52
+ * Use with `bind()` from `a wc-bindable binding core` for compile-time type checking.
51
53
  *
52
54
  * @example
53
55
  * ```typescript
@@ -126,9 +128,13 @@ declare class BroadcastCore extends EventTarget {
126
128
  private _name;
127
129
  private _message;
128
130
  private _error;
131
+ private _gen;
132
+ private _ready;
129
133
  constructor(target?: EventTarget);
134
+ get ready(): Promise<void>;
130
135
  get message(): any;
131
136
  get error(): WcsBroadcastErrorDetail | null;
137
+ observe(): Promise<void>;
132
138
  private _setMessage;
133
139
  private _setError;
134
140
  /**
@@ -174,7 +180,9 @@ declare class WcsBroadcast extends HTMLElement {
174
180
  static wcBindable: IWcBindable;
175
181
  static get observedAttributes(): string[];
176
182
  private _core;
183
+ private _connectedCallbackPromise;
177
184
  constructor();
185
+ get connectedCallbackPromise(): Promise<void>;
178
186
  get name(): string;
179
187
  set name(value: string);
180
188
  get manual(): boolean;
package/dist/index.esm.js CHANGED
@@ -89,16 +89,37 @@ class BroadcastCore extends EventTarget {
89
89
  _name = null;
90
90
  _message = null;
91
91
  _error = null;
92
+ // Generation guard (§3.4): bumped on dispose(). An incoming message /
93
+ // messageerror that fires after the Shell disconnected (a peer posted between
94
+ // disconnect and the channel actually closing, or a queued event drains late)
95
+ // has a stale `gen` and MUST NOT write state to a torn-down element. A boolean
96
+ // flag is insufficient: dispose→reconnect would let a stale event slip through.
97
+ _gen = 0;
98
+ // SSR (§3.8): a channel opens synchronously (no asynchronous probe to await),
99
+ // so readiness is immediate.
100
+ _ready = Promise.resolve();
92
101
  constructor(target) {
93
102
  super();
94
103
  this._target = target ?? this;
95
104
  }
105
+ get ready() {
106
+ return this._ready;
107
+ }
96
108
  get message() {
97
109
  return this._message;
98
110
  }
99
111
  get error() {
100
112
  return this._error;
101
113
  }
114
+ // --- Lifecycle (§3.5) ---
115
+ // observe() establishes monitoring. BroadcastChannel is command-driven (the
116
+ // Shell calls open(name) from connectedCallback / attributeChangedCallback),
117
+ // so there is no subscription for observe() to set up here: it is an idempotent
118
+ // no-op that resolves once ready. It exists for skeleton symmetry with the
119
+ // monitor-style nodes so a host can uniformly await observe() == ready.
120
+ observe() {
121
+ return this._ready;
122
+ }
102
123
  // --- State setters with event dispatch ---
103
124
  // Deliberately NO same-value guard (unlike `error` below). A received message
104
125
  // is an event, not idempotent state: a peer posting the same value twice is
@@ -148,7 +169,28 @@ class BroadcastCore extends EventTarget {
148
169
  return;
149
170
  this._closeChannel();
150
171
  this._setError(null);
172
+ // Capture the generation for this channel (§3.4). The listeners below close
173
+ // over `gen`; an event that fires after dispose() (which bumps _gen) is
174
+ // recognised as stale and dropped without writing state to a torn-down
175
+ // element. The handlers are stored so _closeChannel() can remove them by the
176
+ // same reference.
177
+ const gen = ++this._gen;
151
178
  const channel = new BroadcastChannel(name);
179
+ this._onMessage = (event) => {
180
+ if (gen !== this._gen)
181
+ return;
182
+ this._setMessage(event.data);
183
+ };
184
+ // Fired when a peer posted a value this context cannot deserialize. The event
185
+ // carries no usable payload, so report a synthetic DataError.
186
+ this._onMessageError = () => {
187
+ if (gen !== this._gen)
188
+ return;
189
+ this._setError({
190
+ name: "DataError",
191
+ message: "Failed to deserialize a message received on the channel.",
192
+ });
193
+ };
152
194
  channel.addEventListener("message", this._onMessage);
153
195
  channel.addEventListener("messageerror", this._onMessageError);
154
196
  this._channel = channel;
@@ -196,21 +238,19 @@ class BroadcastCore extends EventTarget {
196
238
  * reconnect, and it is naturally overwritten by the next incoming message.
197
239
  */
198
240
  dispose() {
241
+ // Bump the generation first (§3.4) so any message/messageerror that drains
242
+ // after teardown is recognised as stale, then close the channel and reset the
243
+ // error shadow silently.
244
+ this._gen++;
199
245
  this._closeChannel();
200
246
  this._error = null;
201
247
  }
202
248
  // --- Internal ---
203
- _onMessage = (event) => {
204
- this._setMessage(event.data);
205
- };
206
- // Fired when a peer posted a value this context cannot deserialize. The event
207
- // carries no usable payload, so report a synthetic DataError.
208
- _onMessageError = () => {
209
- this._setError({
210
- name: "DataError",
211
- message: "Failed to deserialize a message received on the channel.",
212
- });
213
- };
249
+ // Per-channel listeners, (re)created in open() so each closes over its own
250
+ // generation (§3.4). null while no channel is open; the real handlers are
251
+ // installed by open() and removed by the same reference in _closeChannel().
252
+ _onMessage = null;
253
+ _onMessageError = null;
214
254
  _closeChannel() {
215
255
  if (!this._channel)
216
256
  return;
@@ -219,6 +259,8 @@ class BroadcastCore extends EventTarget {
219
259
  this._channel.close();
220
260
  this._channel = null;
221
261
  this._name = null;
262
+ this._onMessage = null;
263
+ this._onMessageError = null;
222
264
  }
223
265
  _hasBroadcastChannel() {
224
266
  return typeof BroadcastChannel !== "undefined";
@@ -328,9 +370,10 @@ function registerAutoTrigger() {
328
370
  // Named WcsBroadcast (not `Broadcast`) to match the <wcs-clipboard> WcsClipboard
329
371
  // / <wcs-ws> WcsWebSocket convention and avoid shadowing any global.
330
372
  class WcsBroadcast extends HTMLElement {
331
- // The channel opens synchronously in connectedCallback (no async init), so no
332
- // connectedCallbackPromise is needed mirrors <wcs-ws>.
333
- static hasConnectedCallbackPromise = false;
373
+ // SSR (§4.4): the channel opens synchronously in connectedCallback, so the
374
+ // Core's observe() resolves immediately; we still expose connectedCallbackPromise
375
+ // so a state binder can uniformly await readiness before snapshotting.
376
+ static hasConnectedCallbackPromise = true;
334
377
  static wcBindable = {
335
378
  ...BroadcastCore.wcBindable,
336
379
  // Shell-level settable surface. `name` selects the channel; `manual`
@@ -349,10 +392,14 @@ class WcsBroadcast extends HTMLElement {
349
392
  };
350
393
  static get observedAttributes() { return ["name"]; }
351
394
  _core;
395
+ _connectedCallbackPromise = Promise.resolve();
352
396
  constructor() {
353
397
  super();
354
398
  this._core = new BroadcastCore(this);
355
399
  }
400
+ get connectedCallbackPromise() {
401
+ return this._connectedCallbackPromise;
402
+ }
356
403
  // --- Attribute accessors ---
357
404
  get name() {
358
405
  return this.getAttribute("name") || "";
@@ -404,6 +451,9 @@ class WcsBroadcast extends HTMLElement {
404
451
  if (!this.manual && this.name) {
405
452
  this._core.open(this.name);
406
453
  }
454
+ // SSR (§4.4): expose the Core's readiness as connectedCallbackPromise. The
455
+ // channel opens synchronously above, so observe() resolves immediately.
456
+ this._connectedCallbackPromise = this._core.observe();
407
457
  }
408
458
  disconnectedCallback() {
409
459
  // Deliberately does NOT call unregisterAutoTrigger(). The autoTrigger click
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/BroadcastCore.ts","../src/autoTrigger.ts","../src/components/Broadcast.ts","../src/registerComponents.ts","../src/bootstrapBroadcast.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n broadcast: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-broadcast-target\",\n tagNames: {\n broadcast: \"wcs-broadcast\",\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// Live reference to the mutable internal config: reads always reflect the latest\n// setConfig() call. The readonly IConfig type only blocks callers from writing\n// through it — the underlying object still changes. If you need a stable,\n// frozen snapshot that won't move under you, use getConfig() instead.\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 (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\n\n/**\n * Headless cross-tab messaging primitive. A thin, framework-agnostic wrapper\n * around the BroadcastChannel API exposed through the wc-bindable protocol.\n *\n * BroadcastChannel is a same-origin pub/sub bus: every context (tab, iframe,\n * worker) that opens a channel with the same `name` receives every other\n * context's posts — but NOT its own. This self-exclusion is the whole point:\n * `post` is a `state → element` action (command-token) and an incoming\n * `message` is an `element → state` notification (event-token), but the two\n * only close the loop *across* a context boundary. Within a single tab a lone\n * `<wcs-broadcast>` never hears itself; open the page in two tabs to see the\n * round-trip.\n *\n * Unlike WebSocketCore there is no connection state, no reconnect, and no wire\n * encoding: a channel is \"open\" the moment it is constructed, and payloads ride\n * the browser's structured clone (objects pass through as-is, no JSON\n * round-trip). The only failure surfaces are a non-cloneable `post`\n * (`DataCloneError`), a `messageerror` (a peer posted something this context\n * cannot deserialize), and an absent `BroadcastChannel` constructor\n * (`unsupported`). All three flow through the `error` property — the Core never\n * throws — symmetrical with FetchCore / ClipboardCore.\n */\nexport class BroadcastCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"message\", event: \"wcs-broadcast:message\" },\n { name: \"error\", event: \"wcs-broadcast:error\" },\n ],\n commands: [\n { name: \"open\" },\n { name: \"post\" },\n { name: \"close\" },\n ],\n };\n\n private _target: EventTarget;\n private _channel: BroadcastChannel | null = null;\n private _name: string | null = null;\n private _message: any = null;\n private _error: WcsBroadcastErrorDetail | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get message(): any {\n return this._message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._error;\n }\n\n // --- State setters with event dispatch ---\n\n // Deliberately NO same-value guard (unlike `error` below). A received message\n // is an event, not idempotent state: a peer posting the same value twice is\n // two distinct occurrences and must re-fire wcs-broadcast:message each time so\n // a `message:` binding and any `eventToken.message:` subscriber see both.\n private _setMessage(message: any): void {\n this._message = message;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:message\", {\n detail: message,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsBroadcastErrorDetail | null): void {\n // Same-value guard. `error` has no derived state, so suppressing redundant\n // null→null dispatches (e.g. a successful open clearing an already-null\n // error) avoids spurious events. Reference identity is sufficient: each\n // failure builds a fresh object, and the clear path always passes null.\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Join the named channel. Any previously-open channel is closed first, so\n * calling `open()` again switches channels. When the BroadcastChannel\n * constructor is unavailable this surfaces an `unsupported` error and leaves\n * the Core channel-less (a later `post()` then errors loudly rather than\n * silently dropping).\n */\n open(name: string): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n // Idempotent on the same channel: re-opening the channel we are already on\n // is pure churn (BroadcastChannel has no reconnect semantics). This also\n // absorbs the custom-element *upgrade* path — when a connected element with\n // a `name` attribute is upgraded (autoloader defines the tag after the\n // markup exists), the spec fires attributeChangedCallback (isConnected ===\n // true) *and* connectedCallback, so the Shell calls open() twice. Without\n // this guard that would create a channel and immediately tear it down.\n if (this._channel && this._name === name) return;\n this._closeChannel();\n this._setError(null);\n const channel = new BroadcastChannel(name);\n channel.addEventListener(\"message\", this._onMessage);\n channel.addEventListener(\"messageerror\", this._onMessageError);\n this._channel = channel;\n this._name = name;\n }\n\n /**\n * Post a structured-cloneable value to every other context on the channel.\n * The local context never receives it (self-exclusion). Never throws:\n * a non-cloneable value surfaces as a `DataCloneError` through `error`, and\n * posting with no open channel surfaces an `InvalidStateError`.\n */\n post(data: any): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (!this._channel) {\n this._setError({\n name: \"InvalidStateError\",\n message: \"Channel is not open. Call open(name) before post().\",\n });\n return;\n }\n try {\n this._channel.postMessage(data);\n } catch (err) {\n this._setError(this._normalizeError(err));\n }\n }\n\n /** Leave the channel. Idempotent — a no-op when no channel is open. */\n close(): void {\n this._closeChannel();\n }\n\n /**\n * Tear the Core down for a disconnected Shell: close the channel and reset the\n * error shadow silently (no dispatch on a torn-down element). A later\n * reconnect re-opens via the Shell's connectedCallback.\n *\n * Asymmetry by design: `_message` is deliberately NOT reset. `error` is\n * transient connection state — a stale error from a previous channel would be\n * misleading after a reconnect, so it is cleared. `message` is the last value\n * received (an event payload), not connection state; it is retained as the\n * Core's last-known datum so a binding still reads it across a disconnect/\n * reconnect, and it is naturally overwritten by the next incoming message.\n */\n dispose(): void {\n this._closeChannel();\n this._error = null;\n }\n\n // --- Internal ---\n\n private _onMessage = (event: MessageEvent): void => {\n this._setMessage(event.data);\n };\n\n // Fired when a peer posted a value this context cannot deserialize. The event\n // carries no usable payload, so report a synthetic DataError.\n private _onMessageError = (): void => {\n this._setError({\n name: \"DataError\",\n message: \"Failed to deserialize a message received on the channel.\",\n });\n };\n\n private _closeChannel(): void {\n if (!this._channel) return;\n this._channel.removeEventListener(\"message\", this._onMessage);\n this._channel.removeEventListener(\"messageerror\", this._onMessageError);\n this._channel.close();\n this._channel = null;\n this._name = null;\n }\n\n private _hasBroadcastChannel(): boolean {\n return typeof BroadcastChannel !== \"undefined\";\n }\n\n private _normalizeError(err: unknown): WcsBroadcastErrorDetail {\n if (err instanceof Error) {\n // DOMException is an Error subclass; its `name` (DataCloneError, etc.) is\n // the meaningful discriminator for consumers switching on failure kind.\n return { name: err.name, message: err.message };\n }\n return { name: \"Error\", message: String(err) };\n }\n\n private _unsupportedError(): WcsBroadcastErrorDetail {\n return {\n name: \"NotSupportedError\",\n message: \"BroadcastChannel is not available in this environment.\",\n };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsBroadcast } from \"./components/Broadcast.js\";\n\nlet registered = false;\n\n// Attribute names for the optional post-on-click DOM trigger (clipboard.js-style\n// DX). The element carrying `data-broadcast-target` points at a <wcs-broadcast>\n// by id; the text to post comes from either a literal `data-broadcast-text` or\n// a `data-broadcast-from` CSS selector resolving to a source element.\nconst TEXT_ATTRIBUTE = \"data-broadcast-text\";\nconst FROM_ATTRIBUTE = \"data-broadcast-from\";\n\nfunction resolveText(triggerElement: Element): string | null {\n // Literal text wins when present (including an empty string — posting \"\" is a\n // legitimate request). The `?? \"\"` right-hand side is defensive and\n // unreachable: hasAttribute() just returned true, so getAttribute() cannot be\n // null here. It exists only to satisfy the `string | null` return type — do\n // not chase coverage on it (the DOM contract makes the null branch impossible).\n if (triggerElement.hasAttribute(TEXT_ATTRIBUTE)) {\n return triggerElement.getAttribute(TEXT_ATTRIBUTE) ?? \"\";\n }\n const selector = triggerElement.getAttribute(FROM_ATTRIBUTE);\n if (!selector) return null;\n // A user-authored selector can be syntactically invalid (e.g. `[data-*` or a\n // bare `:not()`), which makes querySelector throw a SyntaxError. Swallow it\n // and treat the source as unresolvable — the same \"nothing to post\" path as a\n // selector that matches no element — so one bad attribute never crashes the\n // document-level click handler and kills autoTrigger for the whole tab.\n let source: Element | null;\n try {\n source = document.querySelector(selector);\n } catch {\n return null;\n }\n if (!source) return null;\n // Read a form control's `value`; fall back to text content. A bare\n // `\"value\" in source` check is too broad — it also matches <button>,\n // <li value>, <progress>, etc. (which carry an unrelated `value`), posting the\n // wrong thing. Narrow to the text-bearing controls a user actually points\n // `data-broadcast-from` at; everything else falls through to textContent.\n if (\n source instanceof HTMLInputElement ||\n source instanceof HTMLTextAreaElement ||\n source instanceof HTMLSelectElement\n ) {\n return source.value;\n }\n // `?? \"\"` is defensive: per the DOM spec only Document / DocumentType /\n // Notation nodes have a null `textContent`, and querySelector only ever\n // returns an Element (whose textContent is always a string). The branch is\n // therefore unreachable in practice and kept solely for the `string | null`\n // type — not worth a contrived test.\n return source.textContent ?? \"\";\n}\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const broadcastId = triggerElement.getAttribute(config.triggerAttribute);\n if (!broadcastId) return;\n\n // Resolve the registered constructor at call time instead of importing\n // WcsBroadcast as a value (avoids a components ⇄ autoTrigger import cycle:\n // Broadcast.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the same identity guarantee.\n const BroadcastCtor = customElements.get(config.tagNames.broadcast);\n const broadcastElement = document.getElementById(broadcastId);\n if (!BroadcastCtor || !(broadcastElement instanceof BroadcastCtor)) return;\n\n const text = resolveText(triggerElement);\n // No resolvable source: leave the click alone (do not preventDefault) so the\n // element's default action is unaffected.\n if (text === null) return;\n\n // Suppress the default action so a post can run without navigating. Intentional:\n // do not attach data-broadcast-target to an element whose default action you\n // also want (a real <a href> link). See README \"Optional DOM Triggering\".\n event.preventDefault();\n (broadcastElement as WcsBroadcast).post(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\nimport { BroadcastCore } from \"../core/BroadcastCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n// Named WcsBroadcast (not `Broadcast`) to match the <wcs-clipboard> WcsClipboard\n// / <wcs-ws> WcsWebSocket convention and avoid shadowing any global.\nexport class WcsBroadcast extends HTMLElement {\n // The channel opens synchronously in connectedCallback (no async init), so no\n // connectedCallbackPromise is needed — mirrors <wcs-ws>.\n static hasConnectedCallbackPromise = false;\n static wcBindable: IWcBindable = {\n ...BroadcastCore.wcBindable,\n // Shell-level settable surface. `name` selects the channel; `manual`\n // suppresses auto-open on connect. There is no momentary `post` property:\n // posting needs an argument (the payload), so element actions run via\n // command-token (`command.post: $command.ping`) or the DOM autoTrigger, not\n // a value-derived setter — keeping `post` a plain command keeps the\n // command-token wiring (`command.post:`) readable.\n inputs: [\n { name: \"name\", attribute: \"name\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Commands are identical to the Core's — no rename needed since the `name` /\n // `manual` attribute accessors do not collide with open/post/close.\n commands: BroadcastCore.wcBindable.commands,\n };\n static get observedAttributes(): string[] { return [\"name\"]; }\n\n private _core: BroadcastCore;\n\n constructor() {\n super();\n this._core = new BroadcastCore(this);\n }\n\n // --- Attribute accessors ---\n\n get name(): string {\n return this.getAttribute(\"name\") || \"\";\n }\n\n set name(value: string) {\n this.setAttribute(\"name\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get message(): any {\n return this._core.message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n open(): void {\n if (this.name) {\n this._core.open(this.name);\n }\n }\n\n post(data: any): void {\n this._core.post(data);\n }\n\n close(): void {\n this._core.close();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (name === \"name\" && this.isConnected && !this.manual && newValue) {\n this._core.open(newValue);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.name) {\n this._core.open(this.name);\n }\n }\n\n disconnectedCallback(): void {\n // Deliberately does NOT call unregisterAutoTrigger(). The autoTrigger click\n // listener is a single process-wide document listener (registerAutoTrigger\n // is idempotent), shared by every <wcs-broadcast> on the page — not owned by\n // this element. Tearing it down when the last element disconnects would\n // break a later-inserted trigger, so it is intentionally left installed for\n // the document's lifetime (one passive listener, negligible cost). This\n // mirrors <wcs-clipboard>, which registers but never unregisters either.\n // unregisterAutoTrigger stays exported purely as a symmetric teardown hook\n // for tests / advanced manual control; the production lifecycle never calls\n // it.\n this._core.dispose();\n }\n}\n","import { WcsBroadcast } from \"./components/Broadcast.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.broadcast)) {\n customElements.define(config.tagNames.broadcast, WcsBroadcast);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapBroadcast(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"AAUA,MAAM,OAAO,GAAoB;AAC/B,IAAA,WAAW,EAAE,IAAI;AACjB,IAAA,gBAAgB,EAAE,uBAAuB;AACzC,IAAA,QAAQ,EAAE;AACR,QAAA,SAAS,EAAE,eAAe;AAC3B,KAAA;CACF;AAED,SAAS,UAAU,CAAI,GAAM,EAAA;AAC3B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;AACvD,IAAA,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAClC,QAAA,UAAU,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IACnD;AACA,IAAA,OAAO,GAAG;AACZ;AAEA,SAAS,SAAS,CAAI,GAAM,EAAA;AAC1B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;IACvD,MAAM,KAAK,GAA4B,EAAE;IACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAClC,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IAC/D;AACA,IAAA,OAAO,KAAU;AACnB;AAEA,IAAI,YAAY,GAAmB,IAAI;AAEvC;AACA;AACA;AACA;AACO,MAAM,MAAM,GAAY,OAAkB;SAEjC,SAAS,GAAA;IACvB,IAAI,CAAC,YAAY,EAAE;QACjB,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC/C;AACA,IAAA,OAAO,YAAY;AACrB;AAEM,SAAU,SAAS,CAAC,aAA8B,EAAA;AACtD,IAAA,IAAI,OAAO,aAAa,CAAC,WAAW,KAAK,SAAS,EAAE;AAClD,QAAA,OAAO,CAAC,WAAW,GAAG,aAAa,CAAC,WAAW;IACjD;AACA,IAAA,IAAI,OAAO,aAAa,CAAC,gBAAgB,KAAK,QAAQ,EAAE;AACtD,QAAA,OAAO,CAAC,gBAAgB,GAAG,aAAa,CAAC,gBAAgB;IAC3D;AACA,IAAA,IAAI,aAAa,CAAC,QAAQ,EAAE;QAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IACzD;IACA,YAAY,GAAG,IAAI;AACrB;;AC5DA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,MAAO,aAAc,SAAQ,WAAW,CAAA;IAC5C,OAAO,UAAU,GAAgB;AAC/B,QAAA,QAAQ,EAAE,aAAa;AACvB,QAAA,OAAO,EAAE,CAAC;AACV,QAAA,UAAU,EAAE;AACV,YAAA,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,EAAE;AACnD,YAAA,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,qBAAqB,EAAE;AAChD,SAAA;AACD,QAAA,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,MAAM,EAAE;YAChB,EAAE,IAAI,EAAE,MAAM,EAAE;YAChB,EAAE,IAAI,EAAE,OAAO,EAAE;AAClB,SAAA;KACF;AAEO,IAAA,OAAO;IACP,QAAQ,GAA4B,IAAI;IACxC,KAAK,GAAkB,IAAI;IAC3B,QAAQ,GAAQ,IAAI;IACpB,MAAM,GAAmC,IAAI;AAErD,IAAA,WAAA,CAAY,MAAoB,EAAA;AAC9B,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,IAAI;IAC/B;AAEA,IAAA,IAAI,OAAO,GAAA;QACT,OAAO,IAAI,CAAC,QAAQ;IACtB;AAEA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;;;;;;AAQQ,IAAA,WAAW,CAAC,OAAY,EAAA;AAC9B,QAAA,IAAI,CAAC,QAAQ,GAAG,OAAO;QACvB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,uBAAuB,EAAE;AAClE,YAAA,MAAM,EAAE,OAAO;AACf,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;AAEQ,IAAA,SAAS,CAAC,KAAqC,EAAA;;;;;AAKrD,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK;YAAE;AAC3B,QAAA,IAAI,CAAC,MAAM,GAAG,KAAK;QACnB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,qBAAqB,EAAE;AAChE,YAAA,MAAM,EAAE,KAAK;AACb,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;;AAIA;;;;;;AAMG;AACH,IAAA,IAAI,CAAC,IAAY,EAAA;AACf,QAAA,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE;YAChC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACxC;QACF;;;;;;;;QAQA,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE;QAC1C,IAAI,CAAC,aAAa,EAAE;AACpB,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;AACpB,QAAA,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAC1C,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;QACpD,OAAO,CAAC,gBAAgB,CAAC,cAAc,EAAE,IAAI,CAAC,eAAe,CAAC;AAC9D,QAAA,IAAI,CAAC,QAAQ,GAAG,OAAO;AACvB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;IACnB;AAEA;;;;;AAKG;AACH,IAAA,IAAI,CAAC,IAAS,EAAA;AACZ,QAAA,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE;YAChC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACxC;QACF;AACA,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,IAAI,CAAC,SAAS,CAAC;AACb,gBAAA,IAAI,EAAE,mBAAmB;AACzB,gBAAA,OAAO,EAAE,qDAAqD;AAC/D,aAAA,CAAC;YACF;QACF;AACA,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC;QACjC;QAAE,OAAO,GAAG,EAAE;YACZ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C;IACF;;IAGA,KAAK,GAAA;QACH,IAAI,CAAC,aAAa,EAAE;IACtB;AAEA;;;;;;;;;;;AAWG;IACH,OAAO,GAAA;QACL,IAAI,CAAC,aAAa,EAAE;AACpB,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI;IACpB;;AAIQ,IAAA,UAAU,GAAG,CAAC,KAAmB,KAAU;AACjD,QAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC;AAC9B,IAAA,CAAC;;;IAIO,eAAe,GAAG,MAAW;QACnC,IAAI,CAAC,SAAS,CAAC;AACb,YAAA,IAAI,EAAE,WAAW;AACjB,YAAA,OAAO,EAAE,0DAA0D;AACpE,SAAA,CAAC;AACJ,IAAA,CAAC;IAEO,aAAa,GAAA;QACnB,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;QACpB,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;QAC7D,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,cAAc,EAAE,IAAI,CAAC,eAAe,CAAC;AACvE,QAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;AACrB,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;AACpB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;IACnB;IAEQ,oBAAoB,GAAA;AAC1B,QAAA,OAAO,OAAO,gBAAgB,KAAK,WAAW;IAChD;AAEQ,IAAA,eAAe,CAAC,GAAY,EAAA;AAClC,QAAA,IAAI,GAAG,YAAY,KAAK,EAAE;;;AAGxB,YAAA,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE;QACjD;AACA,QAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE;IAChD;IAEQ,iBAAiB,GAAA;QACvB,OAAO;AACL,YAAA,IAAI,EAAE,mBAAmB;AACzB,YAAA,OAAO,EAAE,wDAAwD;SAClE;IACH;;;AC1MF,IAAI,UAAU,GAAG,KAAK;AAEtB;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAG,qBAAqB;AAC5C,MAAM,cAAc,GAAG,qBAAqB;AAE5C,SAAS,WAAW,CAAC,cAAuB,EAAA;;;;;;AAM1C,IAAA,IAAI,cAAc,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE;QAC/C,OAAO,cAAc,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,EAAE;IAC1D;IACA,MAAM,QAAQ,GAAG,cAAc,CAAC,YAAY,CAAC,cAAc,CAAC;AAC5D,IAAA,IAAI,CAAC,QAAQ;AAAE,QAAA,OAAO,IAAI;;;;;;AAM1B,IAAA,IAAI,MAAsB;AAC1B,IAAA,IAAI;AACF,QAAA,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC;IAC3C;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,IAAI;IACb;AACA,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,IAAI;;;;;;IAMxB,IACE,MAAM,YAAY,gBAAgB;AAClC,QAAA,MAAM,YAAY,mBAAmB;QACrC,MAAM,YAAY,iBAAiB,EACnC;QACA,OAAO,MAAM,CAAC,KAAK;IACrB;;;;;;AAMA,IAAA,OAAO,MAAM,CAAC,WAAW,IAAI,EAAE;AACjC;AAEA,SAAS,WAAW,CAAC,KAAY,EAAA;AAC/B,IAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM;AAC3B,IAAA,IAAI,EAAE,MAAM,YAAY,OAAO,CAAC;QAAE;AAElC,IAAA,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAU,CAAA,CAAA,EAAI,MAAM,CAAC,gBAAgB,CAAA,CAAA,CAAG,CAAC;AAC9E,IAAA,IAAI,CAAC,cAAc;QAAE;IAErB,MAAM,WAAW,GAAG,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,gBAAgB,CAAC;AACxE,IAAA,IAAI,CAAC,WAAW;QAAE;;;;;AAMlB,IAAA,MAAM,aAAa,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;IACnE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC;IAC7D,IAAI,CAAC,aAAa,IAAI,EAAE,gBAAgB,YAAY,aAAa,CAAC;QAAE;AAEpE,IAAA,MAAM,IAAI,GAAG,WAAW,CAAC,cAAc,CAAC;;;IAGxC,IAAI,IAAI,KAAK,IAAI;QAAE;;;;IAKnB,KAAK,CAAC,cAAc,EAAE;AACrB,IAAA,gBAAiC,CAAC,IAAI,CAAC,IAAI,CAAC;AAC/C;SAEgB,mBAAmB,GAAA;AACjC,IAAA,IAAI,UAAU;QAAE;IAChB,UAAU,GAAG,IAAI;AACjB,IAAA,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;AACjD;;ACpFA;AACA;AACM,MAAO,YAAa,SAAQ,WAAW,CAAA;;;AAG3C,IAAA,OAAO,2BAA2B,GAAG,KAAK;IAC1C,OAAO,UAAU,GAAgB;QAC/B,GAAG,aAAa,CAAC,UAAU;;;;;;;AAO3B,QAAA,MAAM,EAAE;AACN,YAAA,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE;AACnC,YAAA,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE;AACxC,SAAA;;;AAGD,QAAA,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,QAAQ;KAC5C;IACD,WAAW,kBAAkB,GAAA,EAAe,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAErD,IAAA,KAAK;AAEb,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;QACP,IAAI,CAAC,KAAK,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC;IACtC;;AAIA,IAAA,IAAI,IAAI,GAAA;QACN,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE;IACxC;IAEA,IAAI,IAAI,CAAC,KAAa,EAAA;AACpB,QAAA,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAClC;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;IACpC;IAEA,IAAI,MAAM,CAAC,KAAc,EAAA;QACvB,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;QACjC;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;QAChC;IACF;;AAIA,IAAA,IAAI,OAAO,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO;IAC3B;AAEA,IAAA,IAAI,KAAK,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK;IACzB;;IAIA,IAAI,GAAA;AACF,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE;YACb,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B;IACF;AAEA,IAAA,IAAI,CAAC,IAAS,EAAA;AACZ,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;IACvB;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;IACpB;;AAIA,IAAA,wBAAwB,CAAC,IAAY,EAAE,SAAwB,EAAE,QAAuB,EAAA;AACtF,QAAA,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,QAAQ,EAAE;AACnE,YAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC3B;IACF;IAEA,iBAAiB,GAAA;AACf,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;AAC3B,QAAA,IAAI,MAAM,CAAC,WAAW,EAAE;AACtB,YAAA,mBAAmB,EAAE;QACvB;QACA,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE;YAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B;IACF;IAEA,oBAAoB,GAAA;;;;;;;;;;;AAWlB,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IACtB;;;SC/Gc,kBAAkB,GAAA;AAChC,IAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAClD,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IAChE;AACF;;ACHM,SAAU,kBAAkB,CAAC,UAA4B,EAAA;IAC7D,IAAI,UAAU,EAAE;QACd,SAAS,CAAC,UAAU,CAAC;IACvB;AACA,IAAA,kBAAkB,EAAE;AACtB;;;;"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/BroadcastCore.ts","../src/autoTrigger.ts","../src/components/Broadcast.ts","../src/registerComponents.ts","../src/bootstrapBroadcast.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n broadcast: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-broadcast-target\",\n tagNames: {\n broadcast: \"wcs-broadcast\",\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// Live reference to the mutable internal config: reads always reflect the latest\n// setConfig() call. The readonly IConfig type only blocks callers from writing\n// through it — the underlying object still changes. If you need a stable,\n// frozen snapshot that won't move under you, use getConfig() instead.\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 (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\n\n/**\n * Headless cross-tab messaging primitive. A thin, framework-agnostic wrapper\n * around the BroadcastChannel API exposed through the wc-bindable protocol.\n *\n * BroadcastChannel is a same-origin pub/sub bus: every context (tab, iframe,\n * worker) that opens a channel with the same `name` receives every other\n * context's posts — but NOT its own. This self-exclusion is the whole point:\n * `post` is a `state → element` action (command-token) and an incoming\n * `message` is an `element → state` notification (event-token), but the two\n * only close the loop *across* a context boundary. Within a single tab a lone\n * `<wcs-broadcast>` never hears itself; open the page in two tabs to see the\n * round-trip.\n *\n * Unlike WebSocketCore there is no connection state, no reconnect, and no wire\n * encoding: a channel is \"open\" the moment it is constructed, and payloads ride\n * the browser's structured clone (objects pass through as-is, no JSON\n * round-trip). The only failure surfaces are a non-cloneable `post`\n * (`DataCloneError`), a `messageerror` (a peer posted something this context\n * cannot deserialize), and an absent `BroadcastChannel` constructor\n * (`unsupported`). All three flow through the `error` property — the Core never\n * throws — symmetrical with FetchCore / ClipboardCore.\n */\nexport class BroadcastCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"message\", event: \"wcs-broadcast:message\" },\n { name: \"error\", event: \"wcs-broadcast:error\" },\n ],\n commands: [\n { name: \"open\" },\n { name: \"post\" },\n { name: \"close\" },\n ],\n };\n\n private _target: EventTarget;\n private _channel: BroadcastChannel | null = null;\n private _name: string | null = null;\n private _message: any = null;\n private _error: WcsBroadcastErrorDetail | null = null;\n // Generation guard (§3.4): bumped on dispose(). An incoming message /\n // messageerror that fires after the Shell disconnected (a peer posted between\n // disconnect and the channel actually closing, or a queued event drains late)\n // has a stale `gen` and MUST NOT write state to a torn-down element. A boolean\n // flag is insufficient: dispose→reconnect would let a stale event slip through.\n private _gen = 0;\n // SSR (§3.8): a channel opens synchronously (no asynchronous probe to await),\n // 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 message(): any {\n return this._message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._error;\n }\n\n // --- Lifecycle (§3.5) ---\n\n // observe() establishes monitoring. BroadcastChannel is command-driven (the\n // Shell calls open(name) from connectedCallback / attributeChangedCallback),\n // so there is no subscription for observe() to set up here: it is an idempotent\n // no-op that resolves once ready. It exists for skeleton symmetry with the\n // monitor-style nodes so a host can uniformly await observe() == ready.\n observe(): Promise<void> {\n return this._ready;\n }\n\n // --- State setters with event dispatch ---\n\n // Deliberately NO same-value guard (unlike `error` below). A received message\n // is an event, not idempotent state: a peer posting the same value twice is\n // two distinct occurrences and must re-fire wcs-broadcast:message each time so\n // a `message:` binding and any `eventToken.message:` subscriber see both.\n private _setMessage(message: any): void {\n this._message = message;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:message\", {\n detail: message,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsBroadcastErrorDetail | null): void {\n // Same-value guard. `error` has no derived state, so suppressing redundant\n // null→null dispatches (e.g. a successful open clearing an already-null\n // error) avoids spurious events. Reference identity is sufficient: each\n // failure builds a fresh object, and the clear path always passes null.\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Join the named channel. Any previously-open channel is closed first, so\n * calling `open()` again switches channels. When the BroadcastChannel\n * constructor is unavailable this surfaces an `unsupported` error and leaves\n * the Core channel-less (a later `post()` then errors loudly rather than\n * silently dropping).\n */\n open(name: string): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n // Idempotent on the same channel: re-opening the channel we are already on\n // is pure churn (BroadcastChannel has no reconnect semantics). This also\n // absorbs the custom-element *upgrade* path — when a connected element with\n // a `name` attribute is upgraded (autoloader defines the tag after the\n // markup exists), the spec fires attributeChangedCallback (isConnected ===\n // true) *and* connectedCallback, so the Shell calls open() twice. Without\n // this guard that would create a channel and immediately tear it down.\n if (this._channel && this._name === name) return;\n this._closeChannel();\n this._setError(null);\n // Capture the generation for this channel (§3.4). The listeners below close\n // over `gen`; an event that fires after dispose() (which bumps _gen) is\n // recognised as stale and dropped without writing state to a torn-down\n // element. The handlers are stored so _closeChannel() can remove them by the\n // same reference.\n const gen = ++this._gen;\n const channel = new BroadcastChannel(name);\n this._onMessage = (event: MessageEvent): void => {\n if (gen !== this._gen) return;\n this._setMessage(event.data);\n };\n // Fired when a peer posted a value this context cannot deserialize. The event\n // carries no usable payload, so report a synthetic DataError.\n this._onMessageError = (): void => {\n if (gen !== this._gen) return;\n this._setError({\n name: \"DataError\",\n message: \"Failed to deserialize a message received on the channel.\",\n });\n };\n channel.addEventListener(\"message\", this._onMessage);\n channel.addEventListener(\"messageerror\", this._onMessageError);\n this._channel = channel;\n this._name = name;\n }\n\n /**\n * Post a structured-cloneable value to every other context on the channel.\n * The local context never receives it (self-exclusion). Never throws:\n * a non-cloneable value surfaces as a `DataCloneError` through `error`, and\n * posting with no open channel surfaces an `InvalidStateError`.\n */\n post(data: any): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (!this._channel) {\n this._setError({\n name: \"InvalidStateError\",\n message: \"Channel is not open. Call open(name) before post().\",\n });\n return;\n }\n try {\n this._channel.postMessage(data);\n } catch (err) {\n this._setError(this._normalizeError(err));\n }\n }\n\n /** Leave the channel. Idempotent — a no-op when no channel is open. */\n close(): void {\n this._closeChannel();\n }\n\n /**\n * Tear the Core down for a disconnected Shell: close the channel and reset the\n * error shadow silently (no dispatch on a torn-down element). A later\n * reconnect re-opens via the Shell's connectedCallback.\n *\n * Asymmetry by design: `_message` is deliberately NOT reset. `error` is\n * transient connection state — a stale error from a previous channel would be\n * misleading after a reconnect, so it is cleared. `message` is the last value\n * received (an event payload), not connection state; it is retained as the\n * Core's last-known datum so a binding still reads it across a disconnect/\n * reconnect, and it is naturally overwritten by the next incoming message.\n */\n dispose(): void {\n // Bump the generation first (§3.4) so any message/messageerror that drains\n // after teardown is recognised as stale, then close the channel and reset the\n // error shadow silently.\n this._gen++;\n this._closeChannel();\n this._error = null;\n }\n\n // --- Internal ---\n\n // Per-channel listeners, (re)created in open() so each closes over its own\n // generation (§3.4). null while no channel is open; the real handlers are\n // installed by open() and removed by the same reference in _closeChannel().\n private _onMessage: ((event: MessageEvent) => void) | null = null;\n private _onMessageError: (() => void) | null = null;\n\n private _closeChannel(): void {\n if (!this._channel) return;\n this._channel.removeEventListener(\"message\", this._onMessage!);\n this._channel.removeEventListener(\"messageerror\", this._onMessageError!);\n this._channel.close();\n this._channel = null;\n this._name = null;\n this._onMessage = null;\n this._onMessageError = null;\n }\n\n private _hasBroadcastChannel(): boolean {\n return typeof BroadcastChannel !== \"undefined\";\n }\n\n private _normalizeError(err: unknown): WcsBroadcastErrorDetail {\n if (err instanceof Error) {\n // DOMException is an Error subclass; its `name` (DataCloneError, etc.) is\n // the meaningful discriminator for consumers switching on failure kind.\n return { name: err.name, message: err.message };\n }\n return { name: \"Error\", message: String(err) };\n }\n\n private _unsupportedError(): WcsBroadcastErrorDetail {\n return {\n name: \"NotSupportedError\",\n message: \"BroadcastChannel is not available in this environment.\",\n };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsBroadcast } from \"./components/Broadcast.js\";\n\nlet registered = false;\n\n// Attribute names for the optional post-on-click DOM trigger (clipboard.js-style\n// DX). The element carrying `data-broadcast-target` points at a <wcs-broadcast>\n// by id; the text to post comes from either a literal `data-broadcast-text` or\n// a `data-broadcast-from` CSS selector resolving to a source element.\nconst TEXT_ATTRIBUTE = \"data-broadcast-text\";\nconst FROM_ATTRIBUTE = \"data-broadcast-from\";\n\nfunction resolveText(triggerElement: Element): string | null {\n // Literal text wins when present (including an empty string — posting \"\" is a\n // legitimate request). The `?? \"\"` right-hand side is defensive and\n // unreachable: hasAttribute() just returned true, so getAttribute() cannot be\n // null here. It exists only to satisfy the `string | null` return type — do\n // not chase coverage on it (the DOM contract makes the null branch impossible).\n if (triggerElement.hasAttribute(TEXT_ATTRIBUTE)) {\n return triggerElement.getAttribute(TEXT_ATTRIBUTE) ?? \"\";\n }\n const selector = triggerElement.getAttribute(FROM_ATTRIBUTE);\n if (!selector) return null;\n // A user-authored selector can be syntactically invalid (e.g. `[data-*` or a\n // bare `:not()`), which makes querySelector throw a SyntaxError. Swallow it\n // and treat the source as unresolvable — the same \"nothing to post\" path as a\n // selector that matches no element — so one bad attribute never crashes the\n // document-level click handler and kills autoTrigger for the whole tab.\n let source: Element | null;\n try {\n source = document.querySelector(selector);\n } catch {\n return null;\n }\n if (!source) return null;\n // Read a form control's `value`; fall back to text content. A bare\n // `\"value\" in source` check is too broad — it also matches <button>,\n // <li value>, <progress>, etc. (which carry an unrelated `value`), posting the\n // wrong thing. Narrow to the text-bearing controls a user actually points\n // `data-broadcast-from` at; everything else falls through to textContent.\n if (\n source instanceof HTMLInputElement ||\n source instanceof HTMLTextAreaElement ||\n source instanceof HTMLSelectElement\n ) {\n return source.value;\n }\n // `?? \"\"` is defensive: per the DOM spec only Document / DocumentType /\n // Notation nodes have a null `textContent`, and querySelector only ever\n // returns an Element (whose textContent is always a string). The branch is\n // therefore unreachable in practice and kept solely for the `string | null`\n // type — not worth a contrived test.\n return source.textContent ?? \"\";\n}\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const broadcastId = triggerElement.getAttribute(config.triggerAttribute);\n if (!broadcastId) return;\n\n // Resolve the registered constructor at call time instead of importing\n // WcsBroadcast as a value (avoids a components ⇄ autoTrigger import cycle:\n // Broadcast.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the same identity guarantee.\n const BroadcastCtor = customElements.get(config.tagNames.broadcast);\n const broadcastElement = document.getElementById(broadcastId);\n if (!BroadcastCtor || !(broadcastElement instanceof BroadcastCtor)) return;\n\n const text = resolveText(triggerElement);\n // No resolvable source: leave the click alone (do not preventDefault) so the\n // element's default action is unaffected.\n if (text === null) return;\n\n // Suppress the default action so a post can run without navigating. Intentional:\n // do not attach data-broadcast-target to an element whose default action you\n // also want (a real <a href> link). See README \"Optional DOM Triggering\".\n event.preventDefault();\n (broadcastElement as WcsBroadcast).post(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\nimport { BroadcastCore } from \"../core/BroadcastCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n// Named WcsBroadcast (not `Broadcast`) to match the <wcs-clipboard> WcsClipboard\n// / <wcs-ws> WcsWebSocket convention and avoid shadowing any global.\nexport class WcsBroadcast extends HTMLElement {\n // SSR (§4.4): the channel opens synchronously in connectedCallback, so the\n // Core's observe() resolves immediately; we still expose connectedCallbackPromise\n // so a state binder can uniformly await readiness before snapshotting.\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...BroadcastCore.wcBindable,\n // Shell-level settable surface. `name` selects the channel; `manual`\n // suppresses auto-open on connect. There is no momentary `post` property:\n // posting needs an argument (the payload), so element actions run via\n // command-token (`command.post: $command.ping`) or the DOM autoTrigger, not\n // a value-derived setter — keeping `post` a plain command keeps the\n // command-token wiring (`command.post:`) readable.\n inputs: [\n { name: \"name\", attribute: \"name\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Commands are identical to the Core's — no rename needed since the `name` /\n // `manual` attribute accessors do not collide with open/post/close.\n commands: BroadcastCore.wcBindable.commands,\n };\n static get observedAttributes(): string[] { return [\"name\"]; }\n\n private _core: BroadcastCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new BroadcastCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get name(): string {\n return this.getAttribute(\"name\") || \"\";\n }\n\n set name(value: string) {\n this.setAttribute(\"name\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get message(): any {\n return this._core.message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n open(): void {\n if (this.name) {\n this._core.open(this.name);\n }\n }\n\n post(data: any): void {\n this._core.post(data);\n }\n\n close(): void {\n this._core.close();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (name === \"name\" && this.isConnected && !this.manual && newValue) {\n this._core.open(newValue);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.name) {\n this._core.open(this.name);\n }\n // SSR (§4.4): expose the Core's readiness as connectedCallbackPromise. The\n // channel opens synchronously above, so observe() resolves immediately.\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n // Deliberately does NOT call unregisterAutoTrigger(). The autoTrigger click\n // listener is a single process-wide document listener (registerAutoTrigger\n // is idempotent), shared by every <wcs-broadcast> on the page — not owned by\n // this element. Tearing it down when the last element disconnects would\n // break a later-inserted trigger, so it is intentionally left installed for\n // the document's lifetime (one passive listener, negligible cost). This\n // mirrors <wcs-clipboard>, which registers but never unregisters either.\n // unregisterAutoTrigger stays exported purely as a symmetric teardown hook\n // for tests / advanced manual control; the production lifecycle never calls\n // it.\n this._core.dispose();\n }\n}\n","import { WcsBroadcast } from \"./components/Broadcast.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.broadcast)) {\n customElements.define(config.tagNames.broadcast, WcsBroadcast);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapBroadcast(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"AAUA,MAAM,OAAO,GAAoB;AAC/B,IAAA,WAAW,EAAE,IAAI;AACjB,IAAA,gBAAgB,EAAE,uBAAuB;AACzC,IAAA,QAAQ,EAAE;AACR,QAAA,SAAS,EAAE,eAAe;AAC3B,KAAA;CACF;AAED,SAAS,UAAU,CAAI,GAAM,EAAA;AAC3B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;AACvD,IAAA,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAClC,QAAA,UAAU,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IACnD;AACA,IAAA,OAAO,GAAG;AACZ;AAEA,SAAS,SAAS,CAAI,GAAM,EAAA;AAC1B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;IACvD,MAAM,KAAK,GAA4B,EAAE;IACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAClC,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IAC/D;AACA,IAAA,OAAO,KAAU;AACnB;AAEA,IAAI,YAAY,GAAmB,IAAI;AAEvC;AACA;AACA;AACA;AACO,MAAM,MAAM,GAAY,OAAkB;SAEjC,SAAS,GAAA;IACvB,IAAI,CAAC,YAAY,EAAE;QACjB,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC/C;AACA,IAAA,OAAO,YAAY;AACrB;AAEM,SAAU,SAAS,CAAC,aAA8B,EAAA;AACtD,IAAA,IAAI,OAAO,aAAa,CAAC,WAAW,KAAK,SAAS,EAAE;AAClD,QAAA,OAAO,CAAC,WAAW,GAAG,aAAa,CAAC,WAAW;IACjD;AACA,IAAA,IAAI,OAAO,aAAa,CAAC,gBAAgB,KAAK,QAAQ,EAAE;AACtD,QAAA,OAAO,CAAC,gBAAgB,GAAG,aAAa,CAAC,gBAAgB;IAC3D;AACA,IAAA,IAAI,aAAa,CAAC,QAAQ,EAAE;QAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IACzD;IACA,YAAY,GAAG,IAAI;AACrB;;AC5DA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,MAAO,aAAc,SAAQ,WAAW,CAAA;IAC5C,OAAO,UAAU,GAAgB;AAC/B,QAAA,QAAQ,EAAE,aAAa;AACvB,QAAA,OAAO,EAAE,CAAC;AACV,QAAA,UAAU,EAAE;AACV,YAAA,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,EAAE;AACnD,YAAA,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,qBAAqB,EAAE;AAChD,SAAA;AACD,QAAA,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,MAAM,EAAE;YAChB,EAAE,IAAI,EAAE,MAAM,EAAE;YAChB,EAAE,IAAI,EAAE,OAAO,EAAE;AAClB,SAAA;KACF;AAEO,IAAA,OAAO;IACP,QAAQ,GAA4B,IAAI;IACxC,KAAK,GAAkB,IAAI;IAC3B,QAAQ,GAAQ,IAAI;IACpB,MAAM,GAAmC,IAAI;;;;;;IAM7C,IAAI,GAAG,CAAC;;;AAGR,IAAA,MAAM,GAAkB,OAAO,CAAC,OAAO,EAAE;AAEjD,IAAA,WAAA,CAAY,MAAoB,EAAA;AAC9B,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,IAAI;IAC/B;AAEA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;AAEA,IAAA,IAAI,OAAO,GAAA;QACT,OAAO,IAAI,CAAC,QAAQ;IACtB;AAEA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;;;;;;;IASA,OAAO,GAAA;QACL,OAAO,IAAI,CAAC,MAAM;IACpB;;;;;;AAQQ,IAAA,WAAW,CAAC,OAAY,EAAA;AAC9B,QAAA,IAAI,CAAC,QAAQ,GAAG,OAAO;QACvB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,uBAAuB,EAAE;AAClE,YAAA,MAAM,EAAE,OAAO;AACf,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;AAEQ,IAAA,SAAS,CAAC,KAAqC,EAAA;;;;;AAKrD,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK;YAAE;AAC3B,QAAA,IAAI,CAAC,MAAM,GAAG,KAAK;QACnB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,qBAAqB,EAAE;AAChE,YAAA,MAAM,EAAE,KAAK;AACb,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;;AAIA;;;;;;AAMG;AACH,IAAA,IAAI,CAAC,IAAY,EAAA;AACf,QAAA,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE;YAChC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACxC;QACF;;;;;;;;QAQA,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE;QAC1C,IAAI,CAAC,aAAa,EAAE;AACpB,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;;;;;;AAMpB,QAAA,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,IAAI;AACvB,QAAA,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC;AAC1C,QAAA,IAAI,CAAC,UAAU,GAAG,CAAC,KAAmB,KAAU;AAC9C,YAAA,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI;gBAAE;AACvB,YAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC;AAC9B,QAAA,CAAC;;;AAGD,QAAA,IAAI,CAAC,eAAe,GAAG,MAAW;AAChC,YAAA,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI;gBAAE;YACvB,IAAI,CAAC,SAAS,CAAC;AACb,gBAAA,IAAI,EAAE,WAAW;AACjB,gBAAA,OAAO,EAAE,0DAA0D;AACpE,aAAA,CAAC;AACJ,QAAA,CAAC;QACD,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;QACpD,OAAO,CAAC,gBAAgB,CAAC,cAAc,EAAE,IAAI,CAAC,eAAe,CAAC;AAC9D,QAAA,IAAI,CAAC,QAAQ,GAAG,OAAO;AACvB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;IACnB;AAEA;;;;;AAKG;AACH,IAAA,IAAI,CAAC,IAAS,EAAA;AACZ,QAAA,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE;YAChC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACxC;QACF;AACA,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,IAAI,CAAC,SAAS,CAAC;AACb,gBAAA,IAAI,EAAE,mBAAmB;AACzB,gBAAA,OAAO,EAAE,qDAAqD;AAC/D,aAAA,CAAC;YACF;QACF;AACA,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC;QACjC;QAAE,OAAO,GAAG,EAAE;YACZ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C;IACF;;IAGA,KAAK,GAAA;QACH,IAAI,CAAC,aAAa,EAAE;IACtB;AAEA;;;;;;;;;;;AAWG;IACH,OAAO,GAAA;;;;QAIL,IAAI,CAAC,IAAI,EAAE;QACX,IAAI,CAAC,aAAa,EAAE;AACpB,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI;IACpB;;;;;IAOQ,UAAU,GAA2C,IAAI;IACzD,eAAe,GAAwB,IAAI;IAE3C,aAAa,GAAA;QACnB,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;QACpB,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAW,CAAC;QAC9D,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,cAAc,EAAE,IAAI,CAAC,eAAgB,CAAC;AACxE,QAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;AACrB,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;AACpB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;AACjB,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI;AACtB,QAAA,IAAI,CAAC,eAAe,GAAG,IAAI;IAC7B;IAEQ,oBAAoB,GAAA;AAC1B,QAAA,OAAO,OAAO,gBAAgB,KAAK,WAAW;IAChD;AAEQ,IAAA,eAAe,CAAC,GAAY,EAAA;AAClC,QAAA,IAAI,GAAG,YAAY,KAAK,EAAE;;;AAGxB,YAAA,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE;QACjD;AACA,QAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE;IAChD;IAEQ,iBAAiB,GAAA;QACvB,OAAO;AACL,YAAA,IAAI,EAAE,mBAAmB;AACzB,YAAA,OAAO,EAAE,wDAAwD;SAClE;IACH;;;ACpPF,IAAI,UAAU,GAAG,KAAK;AAEtB;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAG,qBAAqB;AAC5C,MAAM,cAAc,GAAG,qBAAqB;AAE5C,SAAS,WAAW,CAAC,cAAuB,EAAA;;;;;;AAM1C,IAAA,IAAI,cAAc,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE;QAC/C,OAAO,cAAc,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,EAAE;IAC1D;IACA,MAAM,QAAQ,GAAG,cAAc,CAAC,YAAY,CAAC,cAAc,CAAC;AAC5D,IAAA,IAAI,CAAC,QAAQ;AAAE,QAAA,OAAO,IAAI;;;;;;AAM1B,IAAA,IAAI,MAAsB;AAC1B,IAAA,IAAI;AACF,QAAA,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC;IAC3C;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,IAAI;IACb;AACA,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,IAAI;;;;;;IAMxB,IACE,MAAM,YAAY,gBAAgB;AAClC,QAAA,MAAM,YAAY,mBAAmB;QACrC,MAAM,YAAY,iBAAiB,EACnC;QACA,OAAO,MAAM,CAAC,KAAK;IACrB;;;;;;AAMA,IAAA,OAAO,MAAM,CAAC,WAAW,IAAI,EAAE;AACjC;AAEA,SAAS,WAAW,CAAC,KAAY,EAAA;AAC/B,IAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM;AAC3B,IAAA,IAAI,EAAE,MAAM,YAAY,OAAO,CAAC;QAAE;AAElC,IAAA,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAU,CAAA,CAAA,EAAI,MAAM,CAAC,gBAAgB,CAAA,CAAA,CAAG,CAAC;AAC9E,IAAA,IAAI,CAAC,cAAc;QAAE;IAErB,MAAM,WAAW,GAAG,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,gBAAgB,CAAC;AACxE,IAAA,IAAI,CAAC,WAAW;QAAE;;;;;AAMlB,IAAA,MAAM,aAAa,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;IACnE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC;IAC7D,IAAI,CAAC,aAAa,IAAI,EAAE,gBAAgB,YAAY,aAAa,CAAC;QAAE;AAEpE,IAAA,MAAM,IAAI,GAAG,WAAW,CAAC,cAAc,CAAC;;;IAGxC,IAAI,IAAI,KAAK,IAAI;QAAE;;;;IAKnB,KAAK,CAAC,cAAc,EAAE;AACrB,IAAA,gBAAiC,CAAC,IAAI,CAAC,IAAI,CAAC;AAC/C;SAEgB,mBAAmB,GAAA;AACjC,IAAA,IAAI,UAAU;QAAE;IAChB,UAAU,GAAG,IAAI;AACjB,IAAA,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;AACjD;;ACpFA;AACA;AACM,MAAO,YAAa,SAAQ,WAAW,CAAA;;;;AAI3C,IAAA,OAAO,2BAA2B,GAAG,IAAI;IACzC,OAAO,UAAU,GAAgB;QAC/B,GAAG,aAAa,CAAC,UAAU;;;;;;;AAO3B,QAAA,MAAM,EAAE;AACN,YAAA,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE;AACnC,YAAA,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE;AACxC,SAAA;;;AAGD,QAAA,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,QAAQ;KAC5C;IACD,WAAW,kBAAkB,GAAA,EAAe,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAErD,IAAA,KAAK;AACL,IAAA,yBAAyB,GAAkB,OAAO,CAAC,OAAO,EAAE;AAEpE,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;QACP,IAAI,CAAC,KAAK,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC;IACtC;AAEA,IAAA,IAAI,wBAAwB,GAAA;QAC1B,OAAO,IAAI,CAAC,yBAAyB;IACvC;;AAIA,IAAA,IAAI,IAAI,GAAA;QACN,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE;IACxC;IAEA,IAAI,IAAI,CAAC,KAAa,EAAA;AACpB,QAAA,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAClC;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;IACpC;IAEA,IAAI,MAAM,CAAC,KAAc,EAAA;QACvB,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;QACjC;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;QAChC;IACF;;AAIA,IAAA,IAAI,OAAO,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO;IAC3B;AAEA,IAAA,IAAI,KAAK,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK;IACzB;;IAIA,IAAI,GAAA;AACF,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE;YACb,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B;IACF;AAEA,IAAA,IAAI,CAAC,IAAS,EAAA;AACZ,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;IACvB;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;IACpB;;AAIA,IAAA,wBAAwB,CAAC,IAAY,EAAE,SAAwB,EAAE,QAAuB,EAAA;AACtF,QAAA,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,QAAQ,EAAE;AACnE,YAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC3B;IACF;IAEA,iBAAiB,GAAA;AACf,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;AAC3B,QAAA,IAAI,MAAM,CAAC,WAAW,EAAE;AACtB,YAAA,mBAAmB,EAAE;QACvB;QACA,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE;YAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B;;;QAGA,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IACvD;IAEA,oBAAoB,GAAA;;;;;;;;;;;AAWlB,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IACtB;;;SCxHc,kBAAkB,GAAA;AAChC,IAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAClD,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IAChE;AACF;;ACHM,SAAU,kBAAkB,CAAC,UAA4B,EAAA;IAC7D,IAAI,UAAU,EAAE;QACd,SAAS,CAAC,UAAU,CAAC;IACvB;AACA,IAAA,kBAAkB,EAAE;AACtB;;;;"}
@@ -1,2 +1,2 @@
1
- const e={autoTrigger:!0,triggerAttribute:"data-broadcast-target",tagNames:{broadcast:"wcs-broadcast"}};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 o extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"message",event:"wcs-broadcast:message"},{name:"error",event:"wcs-broadcast:error"}],commands:[{name:"open"},{name:"post"},{name:"close"}]};_target;_channel=null;_name=null;_message=null;_error=null;constructor(e){super(),this._target=e??this}get message(){return this._message}get error(){return this._error}_setMessage(e){this._message=e,this._target.dispatchEvent(new CustomEvent("wcs-broadcast:message",{detail:e,bubbles:!0}))}_setError(e){this._error!==e&&(this._error=e,this._target.dispatchEvent(new CustomEvent("wcs-broadcast:error",{detail:e,bubbles:!0})))}open(e){if(!this._hasBroadcastChannel())return void this._setError(this._unsupportedError());if(this._channel&&this._name===e)return;this._closeChannel(),this._setError(null);const t=new BroadcastChannel(e);t.addEventListener("message",this._onMessage),t.addEventListener("messageerror",this._onMessageError),this._channel=t,this._name=e}post(e){if(this._hasBroadcastChannel())if(this._channel)try{this._channel.postMessage(e)}catch(e){this._setError(this._normalizeError(e))}else this._setError({name:"InvalidStateError",message:"Channel is not open. Call open(name) before post()."});else this._setError(this._unsupportedError())}close(){this._closeChannel()}dispose(){this._closeChannel(),this._error=null}_onMessage=e=>{this._setMessage(e.data)};_onMessageError=()=>{this._setError({name:"DataError",message:"Failed to deserialize a message received on the channel."})};_closeChannel(){this._channel&&(this._channel.removeEventListener("message",this._onMessage),this._channel.removeEventListener("messageerror",this._onMessageError),this._channel.close(),this._channel=null,this._name=null)}_hasBroadcastChannel(){return"undefined"!=typeof BroadcastChannel}_normalizeError(e){return e instanceof Error?{name:e.name,message:e.message}:{name:"Error",message:String(e)}}_unsupportedError(){return{name:"NotSupportedError",message:"BroadcastChannel is not available in this environment."}}}let i=!1;const c="data-broadcast-text";function l(e){const t=e.target;if(!(t instanceof Element))return;const s=t.closest(`[${n.triggerAttribute}]`);if(!s)return;const r=s.getAttribute(n.triggerAttribute);if(!r)return;const a=customElements.get(n.tagNames.broadcast),o=document.getElementById(r);if(!(a&&o instanceof a))return;const i=function(e){if(e.hasAttribute(c))return e.getAttribute(c)??"";const t=e.getAttribute("data-broadcast-from");if(!t)return null;let s;try{s=document.querySelector(t)}catch{return null}return s?s instanceof HTMLInputElement||s instanceof HTMLTextAreaElement||s instanceof HTMLSelectElement?s.value:s.textContent??"":null}(s);null!==i&&(e.preventDefault(),o.post(i))}class u extends HTMLElement{static hasConnectedCallbackPromise=!1;static wcBindable={...o.wcBindable,inputs:[{name:"name",attribute:"name"},{name:"manual",attribute:"manual"}],commands:o.wcBindable.commands};static get observedAttributes(){return["name"]}_core;constructor(){super(),this._core=new o(this)}get name(){return this.getAttribute("name")||""}set name(e){this.setAttribute("name",e)}get manual(){return this.hasAttribute("manual")}set manual(e){e?this.setAttribute("manual",""):this.removeAttribute("manual")}get message(){return this._core.message}get error(){return this._core.error}open(){this.name&&this._core.open(this.name)}post(e){this._core.post(e)}close(){this._core.close()}attributeChangedCallback(e,t,s){"name"===e&&this.isConnected&&!this.manual&&s&&this._core.open(s)}connectedCallback(){this.style.display="none",n.autoTrigger&&(i||(i=!0,document.addEventListener("click",l))),!this.manual&&this.name&&this._core.open(this.name)}disconnectedCallback(){this._core.dispose()}}function h(t){var s;t&&("boolean"==typeof(s=t).autoTrigger&&(e.autoTrigger=s.autoTrigger),"string"==typeof s.triggerAttribute&&(e.triggerAttribute=s.triggerAttribute),s.tagNames&&Object.assign(e.tagNames,s.tagNames),r=null),customElements.get(n.tagNames.broadcast)||customElements.define(n.tagNames.broadcast,u)}export{o as BroadcastCore,u as WcsBroadcast,h as bootstrapBroadcast,a as getConfig};
1
+ const e={autoTrigger:!0,triggerAttribute:"data-broadcast-target",tagNames:{broadcast:"wcs-broadcast"}};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 o extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"message",event:"wcs-broadcast:message"},{name:"error",event:"wcs-broadcast:error"}],commands:[{name:"open"},{name:"post"},{name:"close"}]};_target;_channel=null;_name=null;_message=null;_error=null;_gen=0;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get ready(){return this._ready}get message(){return this._message}get error(){return this._error}observe(){return this._ready}_setMessage(e){this._message=e,this._target.dispatchEvent(new CustomEvent("wcs-broadcast:message",{detail:e,bubbles:!0}))}_setError(e){this._error!==e&&(this._error=e,this._target.dispatchEvent(new CustomEvent("wcs-broadcast:error",{detail:e,bubbles:!0})))}open(e){if(!this._hasBroadcastChannel())return void this._setError(this._unsupportedError());if(this._channel&&this._name===e)return;this._closeChannel(),this._setError(null);const t=++this._gen,s=new BroadcastChannel(e);this._onMessage=e=>{t===this._gen&&this._setMessage(e.data)},this._onMessageError=()=>{t===this._gen&&this._setError({name:"DataError",message:"Failed to deserialize a message received on the channel."})},s.addEventListener("message",this._onMessage),s.addEventListener("messageerror",this._onMessageError),this._channel=s,this._name=e}post(e){if(this._hasBroadcastChannel())if(this._channel)try{this._channel.postMessage(e)}catch(e){this._setError(this._normalizeError(e))}else this._setError({name:"InvalidStateError",message:"Channel is not open. Call open(name) before post()."});else this._setError(this._unsupportedError())}close(){this._closeChannel()}dispose(){this._gen++,this._closeChannel(),this._error=null}_onMessage=null;_onMessageError=null;_closeChannel(){this._channel&&(this._channel.removeEventListener("message",this._onMessage),this._channel.removeEventListener("messageerror",this._onMessageError),this._channel.close(),this._channel=null,this._name=null,this._onMessage=null,this._onMessageError=null)}_hasBroadcastChannel(){return"undefined"!=typeof BroadcastChannel}_normalizeError(e){return e instanceof Error?{name:e.name,message:e.message}:{name:"Error",message:String(e)}}_unsupportedError(){return{name:"NotSupportedError",message:"BroadcastChannel is not available in this environment."}}}let i=!1;const c="data-broadcast-text";function l(e){const t=e.target;if(!(t instanceof Element))return;const s=t.closest(`[${n.triggerAttribute}]`);if(!s)return;const r=s.getAttribute(n.triggerAttribute);if(!r)return;const a=customElements.get(n.tagNames.broadcast),o=document.getElementById(r);if(!(a&&o instanceof a))return;const i=function(e){if(e.hasAttribute(c))return e.getAttribute(c)??"";const t=e.getAttribute("data-broadcast-from");if(!t)return null;let s;try{s=document.querySelector(t)}catch{return null}return s?s instanceof HTMLInputElement||s instanceof HTMLTextAreaElement||s instanceof HTMLSelectElement?s.value:s.textContent??"":null}(s);null!==i&&(e.preventDefault(),o.post(i))}class u extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...o.wcBindable,inputs:[{name:"name",attribute:"name"},{name:"manual",attribute:"manual"}],commands:o.wcBindable.commands};static get observedAttributes(){return["name"]}_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new o(this)}get connectedCallbackPromise(){return this._connectedCallbackPromise}get name(){return this.getAttribute("name")||""}set name(e){this.setAttribute("name",e)}get manual(){return this.hasAttribute("manual")}set manual(e){e?this.setAttribute("manual",""):this.removeAttribute("manual")}get message(){return this._core.message}get error(){return this._core.error}open(){this.name&&this._core.open(this.name)}post(e){this._core.post(e)}close(){this._core.close()}attributeChangedCallback(e,t,s){"name"===e&&this.isConnected&&!this.manual&&s&&this._core.open(s)}connectedCallback(){this.style.display="none",n.autoTrigger&&(i||(i=!0,document.addEventListener("click",l))),!this.manual&&this.name&&this._core.open(this.name),this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}function h(t){var s;t&&("boolean"==typeof(s=t).autoTrigger&&(e.autoTrigger=s.autoTrigger),"string"==typeof s.triggerAttribute&&(e.triggerAttribute=s.triggerAttribute),s.tagNames&&Object.assign(e.tagNames,s.tagNames),r=null),customElements.get(n.tagNames.broadcast)||customElements.define(n.tagNames.broadcast,u)}export{o as BroadcastCore,u as WcsBroadcast,h as bootstrapBroadcast,a as getConfig};
2
2
  //# sourceMappingURL=index.esm.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/BroadcastCore.ts","../src/autoTrigger.ts","../src/components/Broadcast.ts","../src/bootstrapBroadcast.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n broadcast: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-broadcast-target\",\n tagNames: {\n broadcast: \"wcs-broadcast\",\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// Live reference to the mutable internal config: reads always reflect the latest\n// setConfig() call. The readonly IConfig type only blocks callers from writing\n// through it — the underlying object still changes. If you need a stable,\n// frozen snapshot that won't move under you, use getConfig() instead.\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 (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\n\n/**\n * Headless cross-tab messaging primitive. A thin, framework-agnostic wrapper\n * around the BroadcastChannel API exposed through the wc-bindable protocol.\n *\n * BroadcastChannel is a same-origin pub/sub bus: every context (tab, iframe,\n * worker) that opens a channel with the same `name` receives every other\n * context's posts — but NOT its own. This self-exclusion is the whole point:\n * `post` is a `state → element` action (command-token) and an incoming\n * `message` is an `element → state` notification (event-token), but the two\n * only close the loop *across* a context boundary. Within a single tab a lone\n * `<wcs-broadcast>` never hears itself; open the page in two tabs to see the\n * round-trip.\n *\n * Unlike WebSocketCore there is no connection state, no reconnect, and no wire\n * encoding: a channel is \"open\" the moment it is constructed, and payloads ride\n * the browser's structured clone (objects pass through as-is, no JSON\n * round-trip). The only failure surfaces are a non-cloneable `post`\n * (`DataCloneError`), a `messageerror` (a peer posted something this context\n * cannot deserialize), and an absent `BroadcastChannel` constructor\n * (`unsupported`). All three flow through the `error` property — the Core never\n * throws — symmetrical with FetchCore / ClipboardCore.\n */\nexport class BroadcastCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"message\", event: \"wcs-broadcast:message\" },\n { name: \"error\", event: \"wcs-broadcast:error\" },\n ],\n commands: [\n { name: \"open\" },\n { name: \"post\" },\n { name: \"close\" },\n ],\n };\n\n private _target: EventTarget;\n private _channel: BroadcastChannel | null = null;\n private _name: string | null = null;\n private _message: any = null;\n private _error: WcsBroadcastErrorDetail | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get message(): any {\n return this._message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._error;\n }\n\n // --- State setters with event dispatch ---\n\n // Deliberately NO same-value guard (unlike `error` below). A received message\n // is an event, not idempotent state: a peer posting the same value twice is\n // two distinct occurrences and must re-fire wcs-broadcast:message each time so\n // a `message:` binding and any `eventToken.message:` subscriber see both.\n private _setMessage(message: any): void {\n this._message = message;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:message\", {\n detail: message,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsBroadcastErrorDetail | null): void {\n // Same-value guard. `error` has no derived state, so suppressing redundant\n // null→null dispatches (e.g. a successful open clearing an already-null\n // error) avoids spurious events. Reference identity is sufficient: each\n // failure builds a fresh object, and the clear path always passes null.\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Join the named channel. Any previously-open channel is closed first, so\n * calling `open()` again switches channels. When the BroadcastChannel\n * constructor is unavailable this surfaces an `unsupported` error and leaves\n * the Core channel-less (a later `post()` then errors loudly rather than\n * silently dropping).\n */\n open(name: string): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n // Idempotent on the same channel: re-opening the channel we are already on\n // is pure churn (BroadcastChannel has no reconnect semantics). This also\n // absorbs the custom-element *upgrade* path — when a connected element with\n // a `name` attribute is upgraded (autoloader defines the tag after the\n // markup exists), the spec fires attributeChangedCallback (isConnected ===\n // true) *and* connectedCallback, so the Shell calls open() twice. Without\n // this guard that would create a channel and immediately tear it down.\n if (this._channel && this._name === name) return;\n this._closeChannel();\n this._setError(null);\n const channel = new BroadcastChannel(name);\n channel.addEventListener(\"message\", this._onMessage);\n channel.addEventListener(\"messageerror\", this._onMessageError);\n this._channel = channel;\n this._name = name;\n }\n\n /**\n * Post a structured-cloneable value to every other context on the channel.\n * The local context never receives it (self-exclusion). Never throws:\n * a non-cloneable value surfaces as a `DataCloneError` through `error`, and\n * posting with no open channel surfaces an `InvalidStateError`.\n */\n post(data: any): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (!this._channel) {\n this._setError({\n name: \"InvalidStateError\",\n message: \"Channel is not open. Call open(name) before post().\",\n });\n return;\n }\n try {\n this._channel.postMessage(data);\n } catch (err) {\n this._setError(this._normalizeError(err));\n }\n }\n\n /** Leave the channel. Idempotent — a no-op when no channel is open. */\n close(): void {\n this._closeChannel();\n }\n\n /**\n * Tear the Core down for a disconnected Shell: close the channel and reset the\n * error shadow silently (no dispatch on a torn-down element). A later\n * reconnect re-opens via the Shell's connectedCallback.\n *\n * Asymmetry by design: `_message` is deliberately NOT reset. `error` is\n * transient connection state — a stale error from a previous channel would be\n * misleading after a reconnect, so it is cleared. `message` is the last value\n * received (an event payload), not connection state; it is retained as the\n * Core's last-known datum so a binding still reads it across a disconnect/\n * reconnect, and it is naturally overwritten by the next incoming message.\n */\n dispose(): void {\n this._closeChannel();\n this._error = null;\n }\n\n // --- Internal ---\n\n private _onMessage = (event: MessageEvent): void => {\n this._setMessage(event.data);\n };\n\n // Fired when a peer posted a value this context cannot deserialize. The event\n // carries no usable payload, so report a synthetic DataError.\n private _onMessageError = (): void => {\n this._setError({\n name: \"DataError\",\n message: \"Failed to deserialize a message received on the channel.\",\n });\n };\n\n private _closeChannel(): void {\n if (!this._channel) return;\n this._channel.removeEventListener(\"message\", this._onMessage);\n this._channel.removeEventListener(\"messageerror\", this._onMessageError);\n this._channel.close();\n this._channel = null;\n this._name = null;\n }\n\n private _hasBroadcastChannel(): boolean {\n return typeof BroadcastChannel !== \"undefined\";\n }\n\n private _normalizeError(err: unknown): WcsBroadcastErrorDetail {\n if (err instanceof Error) {\n // DOMException is an Error subclass; its `name` (DataCloneError, etc.) is\n // the meaningful discriminator for consumers switching on failure kind.\n return { name: err.name, message: err.message };\n }\n return { name: \"Error\", message: String(err) };\n }\n\n private _unsupportedError(): WcsBroadcastErrorDetail {\n return {\n name: \"NotSupportedError\",\n message: \"BroadcastChannel is not available in this environment.\",\n };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsBroadcast } from \"./components/Broadcast.js\";\n\nlet registered = false;\n\n// Attribute names for the optional post-on-click DOM trigger (clipboard.js-style\n// DX). The element carrying `data-broadcast-target` points at a <wcs-broadcast>\n// by id; the text to post comes from either a literal `data-broadcast-text` or\n// a `data-broadcast-from` CSS selector resolving to a source element.\nconst TEXT_ATTRIBUTE = \"data-broadcast-text\";\nconst FROM_ATTRIBUTE = \"data-broadcast-from\";\n\nfunction resolveText(triggerElement: Element): string | null {\n // Literal text wins when present (including an empty string — posting \"\" is a\n // legitimate request). The `?? \"\"` right-hand side is defensive and\n // unreachable: hasAttribute() just returned true, so getAttribute() cannot be\n // null here. It exists only to satisfy the `string | null` return type — do\n // not chase coverage on it (the DOM contract makes the null branch impossible).\n if (triggerElement.hasAttribute(TEXT_ATTRIBUTE)) {\n return triggerElement.getAttribute(TEXT_ATTRIBUTE) ?? \"\";\n }\n const selector = triggerElement.getAttribute(FROM_ATTRIBUTE);\n if (!selector) return null;\n // A user-authored selector can be syntactically invalid (e.g. `[data-*` or a\n // bare `:not()`), which makes querySelector throw a SyntaxError. Swallow it\n // and treat the source as unresolvable — the same \"nothing to post\" path as a\n // selector that matches no element — so one bad attribute never crashes the\n // document-level click handler and kills autoTrigger for the whole tab.\n let source: Element | null;\n try {\n source = document.querySelector(selector);\n } catch {\n return null;\n }\n if (!source) return null;\n // Read a form control's `value`; fall back to text content. A bare\n // `\"value\" in source` check is too broad — it also matches <button>,\n // <li value>, <progress>, etc. (which carry an unrelated `value`), posting the\n // wrong thing. Narrow to the text-bearing controls a user actually points\n // `data-broadcast-from` at; everything else falls through to textContent.\n if (\n source instanceof HTMLInputElement ||\n source instanceof HTMLTextAreaElement ||\n source instanceof HTMLSelectElement\n ) {\n return source.value;\n }\n // `?? \"\"` is defensive: per the DOM spec only Document / DocumentType /\n // Notation nodes have a null `textContent`, and querySelector only ever\n // returns an Element (whose textContent is always a string). The branch is\n // therefore unreachable in practice and kept solely for the `string | null`\n // type — not worth a contrived test.\n return source.textContent ?? \"\";\n}\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const broadcastId = triggerElement.getAttribute(config.triggerAttribute);\n if (!broadcastId) return;\n\n // Resolve the registered constructor at call time instead of importing\n // WcsBroadcast as a value (avoids a components ⇄ autoTrigger import cycle:\n // Broadcast.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the same identity guarantee.\n const BroadcastCtor = customElements.get(config.tagNames.broadcast);\n const broadcastElement = document.getElementById(broadcastId);\n if (!BroadcastCtor || !(broadcastElement instanceof BroadcastCtor)) return;\n\n const text = resolveText(triggerElement);\n // No resolvable source: leave the click alone (do not preventDefault) so the\n // element's default action is unaffected.\n if (text === null) return;\n\n // Suppress the default action so a post can run without navigating. Intentional:\n // do not attach data-broadcast-target to an element whose default action you\n // also want (a real <a href> link). See README \"Optional DOM Triggering\".\n event.preventDefault();\n (broadcastElement as WcsBroadcast).post(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\nimport { BroadcastCore } from \"../core/BroadcastCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n// Named WcsBroadcast (not `Broadcast`) to match the <wcs-clipboard> WcsClipboard\n// / <wcs-ws> WcsWebSocket convention and avoid shadowing any global.\nexport class WcsBroadcast extends HTMLElement {\n // The channel opens synchronously in connectedCallback (no async init), so no\n // connectedCallbackPromise is needed — mirrors <wcs-ws>.\n static hasConnectedCallbackPromise = false;\n static wcBindable: IWcBindable = {\n ...BroadcastCore.wcBindable,\n // Shell-level settable surface. `name` selects the channel; `manual`\n // suppresses auto-open on connect. There is no momentary `post` property:\n // posting needs an argument (the payload), so element actions run via\n // command-token (`command.post: $command.ping`) or the DOM autoTrigger, not\n // a value-derived setter — keeping `post` a plain command keeps the\n // command-token wiring (`command.post:`) readable.\n inputs: [\n { name: \"name\", attribute: \"name\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Commands are identical to the Core's — no rename needed since the `name` /\n // `manual` attribute accessors do not collide with open/post/close.\n commands: BroadcastCore.wcBindable.commands,\n };\n static get observedAttributes(): string[] { return [\"name\"]; }\n\n private _core: BroadcastCore;\n\n constructor() {\n super();\n this._core = new BroadcastCore(this);\n }\n\n // --- Attribute accessors ---\n\n get name(): string {\n return this.getAttribute(\"name\") || \"\";\n }\n\n set name(value: string) {\n this.setAttribute(\"name\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get message(): any {\n return this._core.message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n open(): void {\n if (this.name) {\n this._core.open(this.name);\n }\n }\n\n post(data: any): void {\n this._core.post(data);\n }\n\n close(): void {\n this._core.close();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (name === \"name\" && this.isConnected && !this.manual && newValue) {\n this._core.open(newValue);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.name) {\n this._core.open(this.name);\n }\n }\n\n disconnectedCallback(): void {\n // Deliberately does NOT call unregisterAutoTrigger(). The autoTrigger click\n // listener is a single process-wide document listener (registerAutoTrigger\n // is idempotent), shared by every <wcs-broadcast> on the page — not owned by\n // this element. Tearing it down when the last element disconnects would\n // break a later-inserted trigger, so it is intentionally left installed for\n // the document's lifetime (one passive listener, negligible cost). This\n // mirrors <wcs-clipboard>, which registers but never unregisters either.\n // unregisterAutoTrigger stays exported purely as a symmetric teardown hook\n // for tests / advanced manual control; the production lifecycle never calls\n // it.\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 bootstrapBroadcast(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsBroadcast } from \"./components/Broadcast.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.broadcast)) {\n customElements.define(config.tagNames.broadcast, WcsBroadcast);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","tagNames","broadcast","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","BroadcastCore","EventTarget","static","protocol","version","properties","name","event","commands","_target","_channel","_name","_message","_error","constructor","target","super","this","message","error","_setMessage","dispatchEvent","CustomEvent","detail","bubbles","_setError","open","_hasBroadcastChannel","_unsupportedError","_closeChannel","channel","BroadcastChannel","addEventListener","_onMessage","_onMessageError","post","data","postMessage","err","_normalizeError","close","dispose","removeEventListener","Error","String","registered","TEXT_ATTRIBUTE","handleClick","Element","triggerElement","closest","broadcastId","getAttribute","BroadcastCtor","customElements","get","broadcastElement","document","getElementById","text","hasAttribute","selector","source","querySelector","HTMLInputElement","HTMLTextAreaElement","HTMLSelectElement","value","textContent","resolveText","preventDefault","WcsBroadcast","HTMLElement","wcBindable","inputs","attribute","observedAttributes","_core","setAttribute","manual","removeAttribute","attributeChangedCallback","_oldValue","newValue","isConnected","connectedCallback","style","display","disconnectedCallback","bootstrapBroadcast","userConfig","partialConfig","assign","define"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/BroadcastCore.ts","../src/autoTrigger.ts","../src/components/Broadcast.ts","../src/bootstrapBroadcast.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n broadcast: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-broadcast-target\",\n tagNames: {\n broadcast: \"wcs-broadcast\",\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// Live reference to the mutable internal config: reads always reflect the latest\n// setConfig() call. The readonly IConfig type only blocks callers from writing\n// through it — the underlying object still changes. If you need a stable,\n// frozen snapshot that won't move under you, use getConfig() instead.\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 (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\n\n/**\n * Headless cross-tab messaging primitive. A thin, framework-agnostic wrapper\n * around the BroadcastChannel API exposed through the wc-bindable protocol.\n *\n * BroadcastChannel is a same-origin pub/sub bus: every context (tab, iframe,\n * worker) that opens a channel with the same `name` receives every other\n * context's posts — but NOT its own. This self-exclusion is the whole point:\n * `post` is a `state → element` action (command-token) and an incoming\n * `message` is an `element → state` notification (event-token), but the two\n * only close the loop *across* a context boundary. Within a single tab a lone\n * `<wcs-broadcast>` never hears itself; open the page in two tabs to see the\n * round-trip.\n *\n * Unlike WebSocketCore there is no connection state, no reconnect, and no wire\n * encoding: a channel is \"open\" the moment it is constructed, and payloads ride\n * the browser's structured clone (objects pass through as-is, no JSON\n * round-trip). The only failure surfaces are a non-cloneable `post`\n * (`DataCloneError`), a `messageerror` (a peer posted something this context\n * cannot deserialize), and an absent `BroadcastChannel` constructor\n * (`unsupported`). All three flow through the `error` property — the Core never\n * throws — symmetrical with FetchCore / ClipboardCore.\n */\nexport class BroadcastCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"message\", event: \"wcs-broadcast:message\" },\n { name: \"error\", event: \"wcs-broadcast:error\" },\n ],\n commands: [\n { name: \"open\" },\n { name: \"post\" },\n { name: \"close\" },\n ],\n };\n\n private _target: EventTarget;\n private _channel: BroadcastChannel | null = null;\n private _name: string | null = null;\n private _message: any = null;\n private _error: WcsBroadcastErrorDetail | null = null;\n // Generation guard (§3.4): bumped on dispose(). An incoming message /\n // messageerror that fires after the Shell disconnected (a peer posted between\n // disconnect and the channel actually closing, or a queued event drains late)\n // has a stale `gen` and MUST NOT write state to a torn-down element. A boolean\n // flag is insufficient: dispose→reconnect would let a stale event slip through.\n private _gen = 0;\n // SSR (§3.8): a channel opens synchronously (no asynchronous probe to await),\n // 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 message(): any {\n return this._message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._error;\n }\n\n // --- Lifecycle (§3.5) ---\n\n // observe() establishes monitoring. BroadcastChannel is command-driven (the\n // Shell calls open(name) from connectedCallback / attributeChangedCallback),\n // so there is no subscription for observe() to set up here: it is an idempotent\n // no-op that resolves once ready. It exists for skeleton symmetry with the\n // monitor-style nodes so a host can uniformly await observe() == ready.\n observe(): Promise<void> {\n return this._ready;\n }\n\n // --- State setters with event dispatch ---\n\n // Deliberately NO same-value guard (unlike `error` below). A received message\n // is an event, not idempotent state: a peer posting the same value twice is\n // two distinct occurrences and must re-fire wcs-broadcast:message each time so\n // a `message:` binding and any `eventToken.message:` subscriber see both.\n private _setMessage(message: any): void {\n this._message = message;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:message\", {\n detail: message,\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsBroadcastErrorDetail | null): void {\n // Same-value guard. `error` has no derived state, so suppressing redundant\n // null→null dispatches (e.g. a successful open clearing an already-null\n // error) avoids spurious events. Reference identity is sufficient: each\n // failure builds a fresh object, and the clear path always passes null.\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-broadcast:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Join the named channel. Any previously-open channel is closed first, so\n * calling `open()` again switches channels. When the BroadcastChannel\n * constructor is unavailable this surfaces an `unsupported` error and leaves\n * the Core channel-less (a later `post()` then errors loudly rather than\n * silently dropping).\n */\n open(name: string): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n // Idempotent on the same channel: re-opening the channel we are already on\n // is pure churn (BroadcastChannel has no reconnect semantics). This also\n // absorbs the custom-element *upgrade* path — when a connected element with\n // a `name` attribute is upgraded (autoloader defines the tag after the\n // markup exists), the spec fires attributeChangedCallback (isConnected ===\n // true) *and* connectedCallback, so the Shell calls open() twice. Without\n // this guard that would create a channel and immediately tear it down.\n if (this._channel && this._name === name) return;\n this._closeChannel();\n this._setError(null);\n // Capture the generation for this channel (§3.4). The listeners below close\n // over `gen`; an event that fires after dispose() (which bumps _gen) is\n // recognised as stale and dropped without writing state to a torn-down\n // element. The handlers are stored so _closeChannel() can remove them by the\n // same reference.\n const gen = ++this._gen;\n const channel = new BroadcastChannel(name);\n this._onMessage = (event: MessageEvent): void => {\n if (gen !== this._gen) return;\n this._setMessage(event.data);\n };\n // Fired when a peer posted a value this context cannot deserialize. The event\n // carries no usable payload, so report a synthetic DataError.\n this._onMessageError = (): void => {\n if (gen !== this._gen) return;\n this._setError({\n name: \"DataError\",\n message: \"Failed to deserialize a message received on the channel.\",\n });\n };\n channel.addEventListener(\"message\", this._onMessage);\n channel.addEventListener(\"messageerror\", this._onMessageError);\n this._channel = channel;\n this._name = name;\n }\n\n /**\n * Post a structured-cloneable value to every other context on the channel.\n * The local context never receives it (self-exclusion). Never throws:\n * a non-cloneable value surfaces as a `DataCloneError` through `error`, and\n * posting with no open channel surfaces an `InvalidStateError`.\n */\n post(data: any): void {\n if (!this._hasBroadcastChannel()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (!this._channel) {\n this._setError({\n name: \"InvalidStateError\",\n message: \"Channel is not open. Call open(name) before post().\",\n });\n return;\n }\n try {\n this._channel.postMessage(data);\n } catch (err) {\n this._setError(this._normalizeError(err));\n }\n }\n\n /** Leave the channel. Idempotent — a no-op when no channel is open. */\n close(): void {\n this._closeChannel();\n }\n\n /**\n * Tear the Core down for a disconnected Shell: close the channel and reset the\n * error shadow silently (no dispatch on a torn-down element). A later\n * reconnect re-opens via the Shell's connectedCallback.\n *\n * Asymmetry by design: `_message` is deliberately NOT reset. `error` is\n * transient connection state — a stale error from a previous channel would be\n * misleading after a reconnect, so it is cleared. `message` is the last value\n * received (an event payload), not connection state; it is retained as the\n * Core's last-known datum so a binding still reads it across a disconnect/\n * reconnect, and it is naturally overwritten by the next incoming message.\n */\n dispose(): void {\n // Bump the generation first (§3.4) so any message/messageerror that drains\n // after teardown is recognised as stale, then close the channel and reset the\n // error shadow silently.\n this._gen++;\n this._closeChannel();\n this._error = null;\n }\n\n // --- Internal ---\n\n // Per-channel listeners, (re)created in open() so each closes over its own\n // generation (§3.4). null while no channel is open; the real handlers are\n // installed by open() and removed by the same reference in _closeChannel().\n private _onMessage: ((event: MessageEvent) => void) | null = null;\n private _onMessageError: (() => void) | null = null;\n\n private _closeChannel(): void {\n if (!this._channel) return;\n this._channel.removeEventListener(\"message\", this._onMessage!);\n this._channel.removeEventListener(\"messageerror\", this._onMessageError!);\n this._channel.close();\n this._channel = null;\n this._name = null;\n this._onMessage = null;\n this._onMessageError = null;\n }\n\n private _hasBroadcastChannel(): boolean {\n return typeof BroadcastChannel !== \"undefined\";\n }\n\n private _normalizeError(err: unknown): WcsBroadcastErrorDetail {\n if (err instanceof Error) {\n // DOMException is an Error subclass; its `name` (DataCloneError, etc.) is\n // the meaningful discriminator for consumers switching on failure kind.\n return { name: err.name, message: err.message };\n }\n return { name: \"Error\", message: String(err) };\n }\n\n private _unsupportedError(): WcsBroadcastErrorDetail {\n return {\n name: \"NotSupportedError\",\n message: \"BroadcastChannel is not available in this environment.\",\n };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsBroadcast } from \"./components/Broadcast.js\";\n\nlet registered = false;\n\n// Attribute names for the optional post-on-click DOM trigger (clipboard.js-style\n// DX). The element carrying `data-broadcast-target` points at a <wcs-broadcast>\n// by id; the text to post comes from either a literal `data-broadcast-text` or\n// a `data-broadcast-from` CSS selector resolving to a source element.\nconst TEXT_ATTRIBUTE = \"data-broadcast-text\";\nconst FROM_ATTRIBUTE = \"data-broadcast-from\";\n\nfunction resolveText(triggerElement: Element): string | null {\n // Literal text wins when present (including an empty string — posting \"\" is a\n // legitimate request). The `?? \"\"` right-hand side is defensive and\n // unreachable: hasAttribute() just returned true, so getAttribute() cannot be\n // null here. It exists only to satisfy the `string | null` return type — do\n // not chase coverage on it (the DOM contract makes the null branch impossible).\n if (triggerElement.hasAttribute(TEXT_ATTRIBUTE)) {\n return triggerElement.getAttribute(TEXT_ATTRIBUTE) ?? \"\";\n }\n const selector = triggerElement.getAttribute(FROM_ATTRIBUTE);\n if (!selector) return null;\n // A user-authored selector can be syntactically invalid (e.g. `[data-*` or a\n // bare `:not()`), which makes querySelector throw a SyntaxError. Swallow it\n // and treat the source as unresolvable — the same \"nothing to post\" path as a\n // selector that matches no element — so one bad attribute never crashes the\n // document-level click handler and kills autoTrigger for the whole tab.\n let source: Element | null;\n try {\n source = document.querySelector(selector);\n } catch {\n return null;\n }\n if (!source) return null;\n // Read a form control's `value`; fall back to text content. A bare\n // `\"value\" in source` check is too broad — it also matches <button>,\n // <li value>, <progress>, etc. (which carry an unrelated `value`), posting the\n // wrong thing. Narrow to the text-bearing controls a user actually points\n // `data-broadcast-from` at; everything else falls through to textContent.\n if (\n source instanceof HTMLInputElement ||\n source instanceof HTMLTextAreaElement ||\n source instanceof HTMLSelectElement\n ) {\n return source.value;\n }\n // `?? \"\"` is defensive: per the DOM spec only Document / DocumentType /\n // Notation nodes have a null `textContent`, and querySelector only ever\n // returns an Element (whose textContent is always a string). The branch is\n // therefore unreachable in practice and kept solely for the `string | null`\n // type — not worth a contrived test.\n return source.textContent ?? \"\";\n}\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const broadcastId = triggerElement.getAttribute(config.triggerAttribute);\n if (!broadcastId) return;\n\n // Resolve the registered constructor at call time instead of importing\n // WcsBroadcast as a value (avoids a components ⇄ autoTrigger import cycle:\n // Broadcast.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the same identity guarantee.\n const BroadcastCtor = customElements.get(config.tagNames.broadcast);\n const broadcastElement = document.getElementById(broadcastId);\n if (!BroadcastCtor || !(broadcastElement instanceof BroadcastCtor)) return;\n\n const text = resolveText(triggerElement);\n // No resolvable source: leave the click alone (do not preventDefault) so the\n // element's default action is unaffected.\n if (text === null) return;\n\n // Suppress the default action so a post can run without navigating. Intentional:\n // do not attach data-broadcast-target to an element whose default action you\n // also want (a real <a href> link). See README \"Optional DOM Triggering\".\n event.preventDefault();\n (broadcastElement as WcsBroadcast).post(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, WcsBroadcastErrorDetail } from \"../types.js\";\nimport { BroadcastCore } from \"../core/BroadcastCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n// Named WcsBroadcast (not `Broadcast`) to match the <wcs-clipboard> WcsClipboard\n// / <wcs-ws> WcsWebSocket convention and avoid shadowing any global.\nexport class WcsBroadcast extends HTMLElement {\n // SSR (§4.4): the channel opens synchronously in connectedCallback, so the\n // Core's observe() resolves immediately; we still expose connectedCallbackPromise\n // so a state binder can uniformly await readiness before snapshotting.\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...BroadcastCore.wcBindable,\n // Shell-level settable surface. `name` selects the channel; `manual`\n // suppresses auto-open on connect. There is no momentary `post` property:\n // posting needs an argument (the payload), so element actions run via\n // command-token (`command.post: $command.ping`) or the DOM autoTrigger, not\n // a value-derived setter — keeping `post` a plain command keeps the\n // command-token wiring (`command.post:`) readable.\n inputs: [\n { name: \"name\", attribute: \"name\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n // Commands are identical to the Core's — no rename needed since the `name` /\n // `manual` attribute accessors do not collide with open/post/close.\n commands: BroadcastCore.wcBindable.commands,\n };\n static get observedAttributes(): string[] { return [\"name\"]; }\n\n private _core: BroadcastCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new BroadcastCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get name(): string {\n return this.getAttribute(\"name\") || \"\";\n }\n\n set name(value: string) {\n this.setAttribute(\"name\", value);\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get message(): any {\n return this._core.message;\n }\n\n get error(): WcsBroadcastErrorDetail | null {\n return this._core.error;\n }\n\n // --- Commands ---\n\n open(): void {\n if (this.name) {\n this._core.open(this.name);\n }\n }\n\n post(data: any): void {\n this._core.post(data);\n }\n\n close(): void {\n this._core.close();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (name === \"name\" && this.isConnected && !this.manual && newValue) {\n this._core.open(newValue);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual && this.name) {\n this._core.open(this.name);\n }\n // SSR (§4.4): expose the Core's readiness as connectedCallbackPromise. The\n // channel opens synchronously above, so observe() resolves immediately.\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n // Deliberately does NOT call unregisterAutoTrigger(). The autoTrigger click\n // listener is a single process-wide document listener (registerAutoTrigger\n // is idempotent), shared by every <wcs-broadcast> on the page — not owned by\n // this element. Tearing it down when the last element disconnects would\n // break a later-inserted trigger, so it is intentionally left installed for\n // the document's lifetime (one passive listener, negligible cost). This\n // mirrors <wcs-clipboard>, which registers but never unregisters either.\n // unregisterAutoTrigger stays exported purely as a symmetric teardown hook\n // for tests / advanced manual control; the production lifecycle never calls\n // it.\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 bootstrapBroadcast(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsBroadcast } from \"./components/Broadcast.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.broadcast)) {\n customElements.define(config.tagNames.broadcast, WcsBroadcast);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","tagNames","broadcast","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","BroadcastCore","EventTarget","static","protocol","version","properties","name","event","commands","_target","_channel","_name","_message","_error","_gen","_ready","Promise","resolve","constructor","target","super","this","ready","message","error","observe","_setMessage","dispatchEvent","CustomEvent","detail","bubbles","_setError","open","_hasBroadcastChannel","_unsupportedError","_closeChannel","gen","channel","BroadcastChannel","_onMessage","data","_onMessageError","addEventListener","post","postMessage","err","_normalizeError","close","dispose","removeEventListener","Error","String","registered","TEXT_ATTRIBUTE","handleClick","Element","triggerElement","closest","broadcastId","getAttribute","BroadcastCtor","customElements","get","broadcastElement","document","getElementById","text","hasAttribute","selector","source","querySelector","HTMLInputElement","HTMLTextAreaElement","HTMLSelectElement","value","textContent","resolveText","preventDefault","WcsBroadcast","HTMLElement","wcBindable","inputs","attribute","observedAttributes","_core","_connectedCallbackPromise","connectedCallbackPromise","setAttribute","manual","removeAttribute","attributeChangedCallback","_oldValue","newValue","isConnected","connectedCallback","style","display","disconnectedCallback","bootstrapBroadcast","userConfig","partialConfig","assign","define"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/broadcast",
3
- "version": "1.13.1",
3
+ "version": "1.15.0",
4
4
  "description": "Declarative cross-tab messaging component for Web Components. Framework-agnostic BroadcastChannel primitive via wc-bindable-protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",