@eudi-verify/embed 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,46 +19,47 @@ Or include directly via CDN:
19
19
  ```html
20
20
  <!DOCTYPE html>
21
21
  <html>
22
- <head>
23
- <script type="module">
24
- import '@eudi-verify/embed';
25
- </script>
26
- </head>
27
- <body>
28
- <eudi-verify
29
- api-url="/api/eudi"
30
- request='{"age_over_18":true}'
31
- ></eudi-verify>
32
-
33
- <script>
34
- document.querySelector('eudi-verify')
35
- .addEventListener('verified', (e) => {
36
- console.log('Token:', e.detail.token);
37
- console.log('Claims:', e.detail.claims);
38
- });
39
- </script>
40
- </body>
22
+ <head>
23
+ <script type="module">
24
+ import "@eudi-verify/embed";
25
+ </script>
26
+ </head>
27
+ <body>
28
+ <eudi-verify
29
+ api-url="/api/eudi"
30
+ request='{"age_over_18":true}'
31
+ ></eudi-verify>
32
+
33
+ <script>
34
+ document
35
+ .querySelector("eudi-verify")
36
+ .addEventListener("verified", (e) => {
37
+ console.log("Token:", e.detail.token);
38
+ console.log("Claims:", e.detail.claims);
39
+ });
40
+ </script>
41
+ </body>
41
42
  </html>
42
43
  ```
43
44
 
44
45
  ## Attributes
45
46
 
46
- | Attribute | Type | Required | Description |
47
- |-----------|------|----------|-------------|
48
- | `api-url` | string | Yes | Base URL for your verifier API |
49
- | `request` | JSON string | Yes | Verification request (e.g., `'{"age_over_18":true}'`) |
50
- | `auto-start` | boolean | No | Start verification automatically on page load |
47
+ | Attribute | Type | Required | Description |
48
+ | ------------ | ----------- | -------- | ----------------------------------------------------- |
49
+ | `api-url` | string | Yes | Base URL for your verifier API |
50
+ | `request` | JSON string | Yes | Verification request (e.g., `'{"age_over_18":true}'`) |
51
+ | `auto-start` | boolean | No | Start verification automatically on page load |
51
52
 
52
53
  ## Methods
53
54
 
54
- | Method | Description |
55
- |--------|-------------|
56
- | `start()` | Start the verification flow |
55
+ | Method | Description |
56
+ | ---------- | ------------------------------- |
57
+ | `start()` | Start the verification flow |
57
58
  | `cancel()` | Cancel the current verification |
58
- | `reset()` | Reset to idle state |
59
+ | `reset()` | Reset to idle state |
59
60
 
60
61
  ```js
61
- const widget = document.querySelector('eudi-verify');
62
+ const widget = document.querySelector("eudi-verify");
62
63
 
63
64
  // Programmatically start
64
65
  widget.start();
@@ -72,34 +73,34 @@ widget.reset();
72
73
 
73
74
  ## Events
74
75
 
75
- | Event | Detail | Description |
76
- |-------|--------|-------------|
77
- | `verified` | `{ token: string, claims: object }` | Verification succeeded |
78
- | `rejected` | `{ error?: string }` | User rejected in wallet |
79
- | `expired` | `{}` | Session expired |
80
- | `error` | `{ error: string }` | Error occurred |
81
- | `state-change` | `{ state: VerificationState }` | Any state change |
76
+ | Event | Detail | Description |
77
+ | -------------- | ----------------------------------- | ----------------------- |
78
+ | `verified` | `{ token: string, claims: object }` | Verification succeeded |
79
+ | `rejected` | `{ error?: string }` | User rejected in wallet |
80
+ | `expired` | `{}` | Session expired |
81
+ | `error` | `{ error: string }` | Error occurred |
82
+ | `state-change` | `{ state: VerificationState }` | Any state change |
82
83
 
83
84
  ```js
84
- widget.addEventListener('verified', (e) => {
85
+ widget.addEventListener("verified", (e) => {
85
86
  // Send token to your backend for validation
86
- fetch('/checkout', {
87
- method: 'POST',
88
- headers: { 'Content-Type': 'application/json' },
87
+ fetch("/checkout", {
88
+ method: "POST",
89
+ headers: { "Content-Type": "application/json" },
89
90
  body: JSON.stringify({ eudiToken: e.detail.token }),
90
91
  });
91
92
  });
92
93
 
93
- widget.addEventListener('rejected', () => {
94
- alert('Verification was declined');
94
+ widget.addEventListener("rejected", () => {
95
+ alert("Verification was declined");
95
96
  });
96
97
 
97
- widget.addEventListener('error', (e) => {
98
- console.error('Error:', e.detail.error);
98
+ widget.addEventListener("error", (e) => {
99
+ console.error("Error:", e.detail.error);
99
100
  });
100
101
 
101
- widget.addEventListener('state-change', (e) => {
102
- console.log('State:', e.detail.state.status);
102
+ widget.addEventListener("state-change", (e) => {
103
+ console.log("State:", e.detail.state.status);
103
104
  });
104
105
  ```
105
106
 
@@ -107,27 +108,27 @@ widget.addEventListener('state-change', (e) => {
107
108
 
108
109
  The widget wraps `@eudi-verify/client`'s state machine. **DOM events are your integration boundary** — there is no separate error-hook API.
109
110
 
110
- | Event | When | Typical handling |
111
- |-------|------|------------------|
112
- | `rejected` | User declined in wallet | Retry prompt; not usually an ops alert |
113
- | `expired` | Session timed out | Offer to restart |
114
- | `error` | Network/API failure, invalid config | Show message; report to error tracking |
115
- | `state-change` | Any transition | Escape hatch — full `VerificationState` in `e.detail.state` |
111
+ | Event | When | Typical handling |
112
+ | -------------- | ----------------------------------- | ----------------------------------------------------------- |
113
+ | `rejected` | User declined in wallet | Retry prompt; not usually an ops alert |
114
+ | `expired` | Session timed out | Offer to restart |
115
+ | `error` | Network/API failure, invalid config | Show message; report to error tracking |
116
+ | `state-change` | Any transition | Escape hatch — full `VerificationState` in `e.detail.state` |
116
117
 
117
118
  ```js
118
119
  // Error reporting (Sentry, Datadog browser SDK, etc.)
119
- widget.addEventListener('error', (e) => {
120
- reportError({ source: 'eudi-verify', message: e.detail.error });
120
+ widget.addEventListener("error", (e) => {
121
+ reportError({ source: "eudi-verify", message: e.detail.error });
121
122
  });
122
123
 
123
- widget.addEventListener('rejected', (e) => {
124
- reportEvent({ type: 'verification_rejected', detail: e.detail.error });
124
+ widget.addEventListener("rejected", (e) => {
125
+ reportEvent({ type: "verification_rejected", detail: e.detail.error });
125
126
  });
126
127
 
127
128
  // Or handle everything via state-change
128
- widget.addEventListener('state-change', (e) => {
129
+ widget.addEventListener("state-change", (e) => {
129
130
  const { state } = e.detail;
130
- if (state.status === 'error') reportError({ message: state.error });
131
+ if (state.status === "error") reportError({ message: state.error });
131
132
  });
132
133
  ```
133
134
 
@@ -143,12 +144,12 @@ Style the widget using CSS custom properties:
143
144
 
144
145
  ```css
145
146
  eudi-verify {
146
- --eudi-primary: #003399; /* EU blue - buttons, success */
147
- --eudi-text: #1a1a1a; /* Body text */
148
- --eudi-background: #ffffff; /* Widget surface */
149
- --eudi-border-radius: 8px; /* Corner rounding */
147
+ --eudi-primary: #003399; /* EU blue - buttons, success */
148
+ --eudi-text: #1a1a1a; /* Body text */
149
+ --eudi-background: #ffffff; /* Widget surface */
150
+ --eudi-border-radius: 8px; /* Corner rounding */
150
151
  --eudi-font-family: system-ui, sans-serif;
151
- --eudi-error: #d32f2f; /* Error/rejected state */
152
+ --eudi-error: #d32f2f; /* Error/rejected state */
152
153
  }
153
154
  ```
154
155
 
@@ -158,7 +159,7 @@ Example with custom branding:
158
159
  eudi-verify {
159
160
  --eudi-primary: #0052b4;
160
161
  --eudi-border-radius: 12px;
161
- --eudi-font-family: 'Inter', sans-serif;
162
+ --eudi-font-family: "Inter", sans-serif;
162
163
  }
163
164
  ```
164
165
 
@@ -179,10 +180,12 @@ The widget uses open Shadow DOM for style encapsulation. Styles don't leak in or
179
180
  ```html
180
181
  <eudi-verify>
181
182
  #shadow-root (open)
182
- <style>/* Internal styles */</style>
183
- <div class="eudi-widget" role="region" aria-label="Identity verification">
184
- <!-- State containers -->
185
- </div>
183
+ <style>
184
+ /* Internal styles */
185
+ </style>
186
+ <div class="eudi-widget" role="region" aria-label="Identity verification">
187
+ <!-- State containers -->
188
+ </div>
186
189
  </eudi-verify>
187
190
  ```
188
191
 
@@ -199,16 +202,16 @@ idle → loading → showQR → waitingForWallet → verified
199
202
 
200
203
  Each state renders different UI:
201
204
 
202
- | State | UI |
203
- |-------|-----|
204
- | `idle` | "Verify with EU Wallet" button |
205
- | `loading` | Spinner |
206
- | `showQR` | QR code with cancel button |
205
+ | State | UI |
206
+ | ------------------ | -------------------------------- |
207
+ | `idle` | "Verify with EU Wallet" button |
208
+ | `loading` | Spinner |
209
+ | `showQR` | QR code with cancel button |
207
210
  | `waitingForWallet` | "Waiting for wallet approval..." |
208
- | `verified` | Success checkmark |
209
- | `rejected` | Error with retry button |
210
- | `expired` | Expired message with retry |
211
- | `error` | Error message with retry |
211
+ | `verified` | Success checkmark |
212
+ | `rejected` | Error with retry button |
213
+ | `expired` | Expired message with retry |
214
+ | `error` | Error message with retry |
212
215
 
213
216
  ## Claims You Can Request
214
217
 
@@ -236,12 +239,12 @@ Each state renders different UI:
236
239
 
237
240
  ## Build Outputs
238
241
 
239
- | File | Format | Usage |
240
- |------|--------|-------|
241
- | `dist/eudi-verify.js` | ES Module | `import '@eudi-verify/embed'` |
242
- | `dist/eudi-verify.cjs` | CommonJS | `require('@eudi-verify/embed')` |
243
- | `dist/eudi-verify.iife.js` | IIFE | `<script src="...">` |
244
- | `dist/index.d.ts` | TypeScript | Type definitions |
242
+ | File | Format | Usage |
243
+ | -------------------------- | ---------- | ------------------------------- |
244
+ | `dist/eudi-verify.js` | ES Module | `import '@eudi-verify/embed'` |
245
+ | `dist/eudi-verify.cjs` | CommonJS | `require('@eudi-verify/embed')` |
246
+ | `dist/eudi-verify.iife.js` | IIFE | `<script src="...">` |
247
+ | `dist/index.d.ts` | TypeScript | Type definitions |
245
248
 
246
249
  ## Development
247
250
 
@@ -481,7 +481,7 @@ function renderRejected() {
481
481
  <div class="eudi-error">
482
482
  ${ERROR_ICON}
483
483
  <p class="eudi-error-text">Verification declined</p>
484
- <p class="eudi-error-detail"></p>
484
+ <p id="eudi-rejected-detail" class="eudi-error-detail"></p>
485
485
  <button class="eudi-retry-btn" type="button" data-action="reset">Try again</button>
486
486
  </div>
487
487
  </div>
@@ -510,7 +510,7 @@ function renderError() {
510
510
  <div class="eudi-error">
511
511
  ${ERROR_ICON}
512
512
  <p class="eudi-error-text">Verification failed</p>
513
- <p class="eudi-error-detail"></p>
513
+ <p id="eudi-error-state-detail" class="eudi-error-detail"></p>
514
514
  <button class="eudi-retry-btn" type="button" data-action="reset">Try again</button>
515
515
  </div>
516
516
  </div>
@@ -563,16 +563,42 @@ function updateWidgetState(container, state) {
563
563
  }
564
564
  }
565
565
  if (state.status === "rejected" && "error" in state && state.error) {
566
- const detail = container.querySelector(`#${getStateId("rejected")} .eudi-error-detail`);
566
+ const detail = container.querySelector(
567
+ `#${getStateId("rejected")} #eudi-rejected-detail`
568
+ );
567
569
  if (detail) {
568
570
  detail.textContent = state.error;
569
571
  }
572
+ wireRetryDescribedBy(
573
+ container,
574
+ getStateId("rejected"),
575
+ "eudi-rejected-detail"
576
+ );
570
577
  }
571
578
  if (state.status === "error" && "error" in state) {
572
- const detail = container.querySelector(`#${getStateId("error")} .eudi-error-detail`);
579
+ const detail = container.querySelector(
580
+ `#${getStateId("error")} #eudi-error-state-detail`
581
+ );
573
582
  if (detail) {
574
583
  detail.textContent = state.error;
575
584
  }
585
+ wireRetryDescribedBy(
586
+ container,
587
+ getStateId("error"),
588
+ "eudi-error-state-detail"
589
+ );
590
+ }
591
+ }
592
+ function wireRetryDescribedBy(container, stateId, detailId) {
593
+ const detail = container.querySelector(`#${detailId}`);
594
+ const retryBtn = container.querySelector(
595
+ `#${stateId} .eudi-retry-btn`
596
+ );
597
+ if (!retryBtn) return;
598
+ if (detail?.textContent) {
599
+ retryBtn.setAttribute("aria-describedby", detailId);
600
+ } else {
601
+ retryBtn.removeAttribute("aria-describedby");
576
602
  }
577
603
  }
578
604
 
@@ -584,7 +610,9 @@ var FOCUSABLE_SELECTORS = [
584
610
  "input:not([disabled])"
585
611
  ].join(", ");
586
612
  function getFocusableElements(container) {
587
- return Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS));
613
+ return Array.from(
614
+ container.querySelectorAll(FOCUSABLE_SELECTORS)
615
+ );
588
616
  }
589
617
  function createFocusTrap(container) {
590
618
  let previouslyFocused = null;
@@ -848,6 +876,11 @@ var EudiVerifyElement = class extends HTMLElement {
848
876
  #updateState(state) {
849
877
  if (!this.#container) return;
850
878
  updateWidgetState(this.#container, state);
879
+ if (state.status === "loading") {
880
+ this.#container.setAttribute("aria-busy", "true");
881
+ } else {
882
+ this.#container.removeAttribute("aria-busy");
883
+ }
851
884
  if (this.#liveRegion && state.status !== this.#lastStatus) {
852
885
  const message = STATE_MESSAGES[state.status];
853
886
  if (message) {
@@ -861,12 +894,14 @@ var EudiVerifyElement = class extends HTMLElement {
861
894
  #manageFocus(state) {
862
895
  if (!this.#container) return;
863
896
  switch (state.status) {
864
- case "showQR": {
865
- const cancelBtn = this.#container.querySelector("#eudi-state-showQR .eudi-cancel-btn");
897
+ case "showQR":
898
+ case "waitingForWallet": {
899
+ const cancelBtn = this.#container.querySelector(
900
+ `#eudi-state-${state.status} .eudi-cancel-btn`
901
+ );
866
902
  cancelBtn?.focus();
867
903
  break;
868
904
  }
869
- case "verified":
870
905
  case "rejected":
871
906
  case "expired":
872
907
  case "error": {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/element.ts","../src/styles.ts","../src/render.ts","../src/a11y.ts"],"sourcesContent":["/**\n * @eudi-verify/embed\n *\n * <eudi-verify> Custom Element for EUDI Wallet verification.\n *\n * @example\n * ```html\n * <script type=\"module\">\n * import '@eudi-verify/embed';\n * </script>\n *\n * <eudi-verify\n * api-url=\"/api/eudi\"\n * request='{\"age_over_18\":true}'\n * ></eudi-verify>\n * ```\n */\n\nexport const VERSION = '0.1.0';\n\nexport { EudiVerifyElement, type EudiVerifyEventMap } from './element.js';\nexport { createStyles, CSS_VARIABLES } from './styles.js';\nexport {\n announce,\n clearAnnouncement,\n createFocusTrap,\n getFocusableElements,\n STATE_MESSAGES,\n} from './a11y.js';\nexport { renderWidget, updateWidgetState, getStateId } from './render.js';\n\nimport { EudiVerifyElement } from './element.js';\n\nif (typeof customElements !== 'undefined' && !customElements.get('eudi-verify')) {\n customElements.define('eudi-verify', EudiVerifyElement);\n}\n","/**\n * @eudi-verify/embed - Custom Element\n *\n * <eudi-verify> vanilla Custom Element for EUDI Wallet verification.\n * Uses open Shadow DOM with CSS custom property theming.\n */\n\nimport {\n createVerification,\n type Verification,\n type VerificationState,\n type VerificationRequest,\n} from '@eudi-verify/client';\nimport { createStyles } from './styles.js';\nimport { renderWidget, updateWidgetState } from './render.js';\nimport {\n announce,\n clearAnnouncement,\n STATE_MESSAGES,\n getAnnouncementPriority,\n} from './a11y.js';\n\n/**\n * Events dispatched by EudiVerifyElement.\n */\nexport interface EudiVerifyEventMap {\n verified: CustomEvent<{ token: string; claims: Record<string, unknown> }>;\n rejected: CustomEvent<{ error?: string }>;\n expired: CustomEvent<Record<string, never>>;\n error: CustomEvent<{ error: string }>;\n 'state-change': CustomEvent<{ state: VerificationState }>;\n}\n\n/**\n * Observed attributes for the custom element.\n */\nconst OBSERVED_ATTRIBUTES = ['api-url', 'request', 'auto-start'] as const;\ntype ObservedAttribute = (typeof OBSERVED_ATTRIBUTES)[number];\n\n/**\n * <eudi-verify> Custom Element\n *\n * @example\n * ```html\n * <eudi-verify\n * api-url=\"/api/eudi\"\n * request='{\"age_over_18\":true}'\n * ></eudi-verify>\n * ```\n *\n * @fires verified - Verification succeeded\n * @fires rejected - User rejected in wallet\n * @fires expired - Session expired\n * @fires error - Error occurred\n * @fires state-change - Any state change\n */\nexport class EudiVerifyElement extends HTMLElement {\n static get observedAttributes(): readonly string[] {\n return OBSERVED_ATTRIBUTES;\n }\n\n #shadow: ShadowRoot;\n #verification: Verification | null = null;\n #unsubscribe: (() => void) | null = null;\n #container: HTMLElement | null = null;\n #liveRegion: HTMLElement | null = null;\n #lastStatus: VerificationState['status'] | null = null;\n #isDemo: boolean | null = null;\n #demoBanner: HTMLElement | null = null;\n\n constructor() {\n super();\n this.#shadow = this.attachShadow({ mode: 'open' });\n }\n\n /**\n * Get the API URL attribute.\n */\n get apiUrl(): string {\n return this.getAttribute('api-url') ?? '';\n }\n\n /**\n * Set the API URL attribute.\n */\n set apiUrl(value: string) {\n this.setAttribute('api-url', value);\n }\n\n /**\n * Get the request attribute (JSON string).\n */\n get request(): string {\n return this.getAttribute('request') ?? '';\n }\n\n /**\n * Set the request attribute (JSON string).\n */\n set request(value: string) {\n this.setAttribute('request', value);\n }\n\n /**\n * Check if auto-start is enabled.\n */\n get autoStart(): boolean {\n return this.hasAttribute('auto-start');\n }\n\n /**\n * Set auto-start attribute.\n */\n set autoStart(value: boolean) {\n if (value) {\n this.setAttribute('auto-start', '');\n } else {\n this.removeAttribute('auto-start');\n }\n }\n\n /**\n * Current verification state (read-only).\n */\n get state(): VerificationState | null {\n return this.#verification?.state ?? null;\n }\n\n connectedCallback(): void {\n this.#render();\n this.#setupEventListeners();\n\n if (this.autoStart && this.apiUrl && this.request) {\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this.#cleanup();\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null\n ): void {\n if (oldValue === newValue) return;\n\n if (name === 'api-url' && this.#verification) {\n this.#cleanup();\n }\n }\n\n /**\n * Start the verification flow.\n */\n start(): void {\n if (!this.apiUrl) {\n console.error('[eudi-verify] api-url attribute is required');\n return;\n }\n\n let requestObj: VerificationRequest;\n try {\n requestObj = this.request ? JSON.parse(this.request) : {};\n } catch {\n console.error('[eudi-verify] Invalid JSON in request attribute');\n this.#dispatchError('Invalid verification request');\n return;\n }\n\n this.#ensureVerification();\n this.#verification!.start(requestObj);\n }\n\n /**\n * Cancel the current verification.\n */\n cancel(): void {\n this.#verification?.cancel();\n }\n\n /**\n * Reset to idle state.\n */\n reset(): void {\n this.#cleanup();\n this.#updateState({ status: 'idle' });\n }\n\n #render(): void {\n this.#shadow.innerHTML = `\n <style>${createStyles()}</style>\n ${renderWidget()}\n `;\n\n this.#container = this.#shadow.querySelector('.eudi-widget');\n this.#liveRegion = this.#shadow.querySelector('[aria-live]');\n this.#demoBanner = this.#shadow.querySelector('.eudi-demo-banner');\n }\n\n #setupEventListeners(): void {\n this.#shadow.addEventListener('click', (event) => {\n const target = event.target as HTMLElement;\n const button = target.closest<HTMLElement>('[data-action]');\n if (!button) return;\n\n const action = button.dataset.action;\n switch (action) {\n case 'start':\n this.start();\n break;\n case 'cancel':\n this.cancel();\n break;\n case 'reset':\n this.reset();\n this.start();\n break;\n }\n });\n }\n\n #ensureVerification(): void {\n if (this.#verification) return;\n\n this.#verification = createVerification({\n apiUrl: this.apiUrl,\n });\n\n this.#unsubscribe = this.#verification.subscribe((state) => {\n this.#handleStateChange(state);\n });\n\n // Detect demo mode on first API interaction\n this.#detectDemoMode();\n }\n\n async #detectDemoMode(): Promise<void> {\n if (this.#isDemo !== null || !this.apiUrl) return;\n\n try {\n const response = await fetch(`${this.apiUrl}/sessions`, {\n method: 'HEAD',\n });\n const mode = response.headers.get('X-Eudi-Mode');\n this.#isDemo = mode === 'demo';\n this.#updateDemoBanner();\n } catch {\n // If detection fails, don't show banner (fail safely)\n this.#isDemo = false;\n }\n }\n\n #updateDemoBanner(): void {\n if (!this.#demoBanner) return;\n\n if (this.#isDemo === true) {\n this.#demoBanner.removeAttribute('hidden');\n } else {\n this.#demoBanner.setAttribute('hidden', '');\n }\n }\n\n #handleStateChange(state: VerificationState): void {\n this.#updateState(state);\n this.#dispatchStateChange(state);\n\n switch (state.status) {\n case 'verified':\n if ('token' in state && 'claims' in state) {\n this.#dispatchVerified(state.token, state.claims);\n }\n break;\n case 'rejected':\n this.#dispatchRejected('error' in state ? state.error : undefined);\n break;\n case 'expired':\n this.#dispatchExpired();\n break;\n case 'error':\n if ('error' in state) {\n this.#dispatchError(state.error);\n }\n break;\n }\n }\n\n #updateState(state: VerificationState): void {\n if (!this.#container) return;\n\n updateWidgetState(this.#container, state);\n\n if (this.#liveRegion && state.status !== this.#lastStatus) {\n const message = STATE_MESSAGES[state.status];\n if (message) {\n const priority = getAnnouncementPriority(state.status);\n announce(this.#liveRegion, message, priority);\n }\n }\n\n this.#lastStatus = state.status;\n\n this.#manageFocus(state);\n }\n\n #manageFocus(state: VerificationState): void {\n if (!this.#container) return;\n\n switch (state.status) {\n case 'showQR': {\n const cancelBtn =\n this.#container.querySelector<HTMLElement>('#eudi-state-showQR .eudi-cancel-btn');\n cancelBtn?.focus();\n break;\n }\n case 'verified':\n case 'rejected':\n case 'expired':\n case 'error': {\n const retryBtn = this.#container.querySelector<HTMLElement>(\n `#eudi-state-${state.status} .eudi-retry-btn`\n );\n retryBtn?.focus();\n break;\n }\n }\n }\n\n #dispatchVerified(token: string, claims: Record<string, unknown>): void {\n this.dispatchEvent(\n new CustomEvent('verified', {\n bubbles: true,\n composed: true,\n detail: { token, claims },\n })\n );\n }\n\n #dispatchRejected(error?: string): void {\n this.dispatchEvent(\n new CustomEvent('rejected', {\n bubbles: true,\n composed: true,\n detail: { error },\n })\n );\n }\n\n #dispatchExpired(): void {\n this.dispatchEvent(\n new CustomEvent('expired', {\n bubbles: true,\n composed: true,\n detail: {},\n })\n );\n }\n\n #dispatchError(error: string): void {\n this.dispatchEvent(\n new CustomEvent('error', {\n bubbles: true,\n composed: true,\n detail: { error },\n })\n );\n }\n\n #dispatchStateChange(state: VerificationState): void {\n this.dispatchEvent(\n new CustomEvent('state-change', {\n bubbles: true,\n composed: true,\n detail: { state },\n })\n );\n }\n\n #cleanup(): void {\n if (this.#unsubscribe) {\n this.#unsubscribe();\n this.#unsubscribe = null;\n }\n\n if (this.#verification) {\n this.#verification.destroy();\n this.#verification = null;\n }\n\n if (this.#liveRegion) {\n clearAnnouncement(this.#liveRegion);\n }\n\n this.#lastStatus = null;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'eudi-verify': EudiVerifyElement;\n }\n\n interface HTMLElementEventMap extends EudiVerifyEventMap {}\n}\n","/**\n * @eudi-verify/embed - Styles\n *\n * CSS template with custom property theming.\n * Uses open Shadow DOM for style encapsulation.\n */\n\n/**\n * CSS custom properties for theming.\n * These can be set on the host element or any ancestor.\n */\nexport const CSS_VARIABLES = {\n '--eudi-primary': '#003399',\n '--eudi-text': '#1a1a1a',\n '--eudi-background': '#ffffff',\n '--eudi-border-radius': '8px',\n '--eudi-font-family': 'system-ui, sans-serif',\n '--eudi-error': '#d32f2f',\n} as const;\n\n/**\n * Generate the internal stylesheet for the shadow DOM.\n * Uses CSS custom properties with fallbacks to defaults.\n */\nexport function createStyles(): string {\n return /* css */ `\n :host {\n display: block;\n font-family: var(--eudi-font-family, ${CSS_VARIABLES['--eudi-font-family']});\n color: var(--eudi-text, ${CSS_VARIABLES['--eudi-text']});\n }\n\n :host([hidden]) {\n display: none;\n }\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n .eudi-widget {\n background: var(--eudi-background, ${CSS_VARIABLES['--eudi-background']});\n border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES['--eudi-text']}) 20%, transparent);\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES['--eudi-border-radius']});\n padding: 24px;\n text-align: center;\n min-width: 280px;\n max-width: 400px;\n margin-inline: auto;\n }\n\n /* State containers - only one visible at a time */\n .eudi-state {\n display: none;\n }\n\n .eudi-state[data-active] {\n display: block;\n }\n\n /* Start button */\n .eudi-start-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 24px;\n font-size: 16px;\n font-weight: 500;\n font-family: inherit;\n color: #ffffff;\n background: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n border: none;\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES['--eudi-border-radius']});\n cursor: pointer;\n transition: background-color 0.2s ease, transform 0.1s ease;\n }\n\n .eudi-start-btn:hover {\n background: color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']}) 85%, black);\n }\n\n .eudi-start-btn:focus-visible {\n outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n outline-offset: 2px;\n }\n\n .eudi-start-btn:active {\n transform: scale(0.98);\n }\n\n /* EU stars icon */\n .eudi-icon {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n }\n\n /* Loading state */\n .eudi-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']}) 20%, transparent);\n border-top-color: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n border-radius: 50%;\n animation: eudi-spin 0.8s linear infinite;\n }\n\n @keyframes eudi-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n /* Reduced motion */\n @media (prefers-reduced-motion: reduce) {\n .eudi-spinner {\n animation: none;\n border-top-color: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n border-right-color: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n }\n\n .eudi-start-btn {\n transition: none;\n }\n }\n\n /* QR code state */\n .eudi-qr {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n }\n\n .eudi-qr-img {\n width: 200px;\n height: 200px;\n border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES['--eudi-text']}) 15%, transparent);\n border-radius: 4px;\n }\n\n .eudi-qr-text {\n margin: 0;\n font-size: 14px;\n color: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES['--eudi-text']}) 80%, transparent);\n }\n\n .eudi-cancel-btn {\n margin-top: 8px;\n padding: 8px 16px;\n font-size: 14px;\n font-family: inherit;\n color: var(--eudi-text, ${CSS_VARIABLES['--eudi-text']});\n background: transparent;\n border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES['--eudi-text']}) 30%, transparent);\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES['--eudi-border-radius']});\n cursor: pointer;\n transition: background-color 0.2s ease;\n }\n\n .eudi-cancel-btn:hover {\n background: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES['--eudi-text']}) 5%, transparent);\n }\n\n .eudi-cancel-btn:focus-visible {\n outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n outline-offset: 2px;\n }\n\n /* Waiting for wallet */\n .eudi-waiting {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-waiting-icon {\n width: 48px;\n height: 48px;\n color: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n }\n\n .eudi-waiting-text {\n margin: 0;\n font-size: 16px;\n }\n\n /* Success state */\n .eudi-success {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-success-icon {\n width: 48px;\n height: 48px;\n color: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n }\n\n .eudi-success-text {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n }\n\n /* Error/rejected/expired states */\n .eudi-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-error-icon {\n width: 48px;\n height: 48px;\n color: var(--eudi-error, ${CSS_VARIABLES['--eudi-error']});\n }\n\n .eudi-error-text {\n margin: 0;\n font-size: 16px;\n color: var(--eudi-error, ${CSS_VARIABLES['--eudi-error']});\n }\n\n .eudi-error-detail {\n margin: 0;\n font-size: 14px;\n color: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES['--eudi-text']}) 70%, transparent);\n }\n\n .eudi-retry-btn {\n margin-top: 8px;\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n color: #ffffff;\n background: var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n border: none;\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES['--eudi-border-radius']});\n cursor: pointer;\n transition: background-color 0.2s ease;\n }\n\n .eudi-retry-btn:hover {\n background: color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']}) 85%, black);\n }\n\n .eudi-retry-btn:focus-visible {\n outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES['--eudi-primary']});\n outline-offset: 2px;\n }\n\n /* Screen reader only */\n .eudi-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* Demo mode banner */\n .eudi-demo-banner {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px 12px;\n margin-bottom: 16px;\n font-size: 13px;\n color: #7c5c00;\n background: #fef3cd;\n border: 1px solid #ffe69c;\n border-radius: 4px;\n }\n\n .eudi-demo-banner[hidden] {\n display: none;\n }\n\n .eudi-warning-icon {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n color: #7c5c00;\n }\n `;\n}\n","/**\n * @eudi-verify/embed - State-based Rendering\n *\n * Renders the widget UI based on verification state.\n */\n\nimport type { VerificationState } from '@eudi-verify/client';\n\n/**\n * SVG icon: EU stars (simplified)\n */\nconst EU_STARS_ICON = /* html */ `\n<svg class=\"eudi-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <g fill=\"currentColor\">\n <polygon points=\"12,4 12.5,5.5 14,5.5 12.75,6.5 13.25,8 12,7 10.75,8 11.25,6.5 10,5.5 11.5,5.5\"/>\n <polygon points=\"17,6.5 17.35,7.6 18.5,7.6 17.6,8.3 17.9,9.4 17,8.7 16.1,9.4 16.4,8.3 15.5,7.6 16.65,7.6\"/>\n <polygon points=\"19,11 19.35,12.1 20.5,12.1 19.6,12.8 19.9,13.9 19,13.2 18.1,13.9 18.4,12.8 17.5,12.1 18.65,12.1\"/>\n <polygon points=\"17,15.5 17.35,16.6 18.5,16.6 17.6,17.3 17.9,18.4 17,17.7 16.1,18.4 16.4,17.3 15.5,16.6 16.65,16.6\"/>\n <polygon points=\"12,18 12.35,19.1 13.5,19.1 12.6,19.8 12.9,20.9 12,20.2 11.1,20.9 11.4,19.8 10.5,19.1 11.65,19.1\"/>\n <polygon points=\"7,15.5 7.35,16.6 8.5,16.6 7.6,17.3 7.9,18.4 7,17.7 6.1,18.4 6.4,17.3 5.5,16.6 6.65,16.6\"/>\n <polygon points=\"5,11 5.35,12.1 6.5,12.1 5.6,12.8 5.9,13.9 5,13.2 4.1,13.9 4.4,12.8 3.5,12.1 4.65,12.1\"/>\n <polygon points=\"7,6.5 7.35,7.6 8.5,7.6 7.6,8.3 7.9,9.4 7,8.7 6.1,9.4 6.4,8.3 5.5,7.6 6.65,7.6\"/>\n </g>\n</svg>\n`;\n\n/**\n * SVG icon: checkmark (success)\n */\nconst SUCCESS_ICON = /* html */ `\n<svg class=\"eudi-success-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M8 12l3 3 5-6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n`;\n\n/**\n * SVG icon: X (error)\n */\nconst ERROR_ICON = /* html */ `\n<svg class=\"eudi-error-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M15 9l-6 6M9 9l6 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>\n`;\n\n/**\n * SVG icon: clock (expired)\n */\nconst EXPIRED_ICON = /* html */ `\n<svg class=\"eudi-error-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M12 6v6l4 2\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>\n`;\n\n/**\n * SVG icon: wallet (waiting)\n */\nconst WALLET_ICON = /* html */ `\n<svg class=\"eudi-waiting-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"6\" width=\"20\" height=\"14\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M2 10h20\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"17\" cy=\"14\" r=\"1.5\" fill=\"currentColor\"/>\n</svg>\n`;\n\n/**\n * SVG icon: warning (demo banner)\n */\nconst WARNING_ICON = /* html */ `\n<svg class=\"eudi-warning-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n`;\n\n/**\n * Map of status to state container ID.\n */\ntype RenderableStatus = VerificationState['status'];\n\n/**\n * Get the state container ID for a status.\n */\nexport function getStateId(status: RenderableStatus): string {\n return `eudi-state-${status}`;\n}\n\n/**\n * Render the idle state (start button).\n */\nfunction renderIdle(): string {\n return /* html */ `\n <div id=\"${getStateId('idle')}\" class=\"eudi-state\" data-active>\n <button class=\"eudi-start-btn\" type=\"button\" data-action=\"start\">\n ${EU_STARS_ICON}\n <span>Verify with EU Wallet</span>\n </button>\n </div>\n `;\n}\n\n/**\n * Render the loading state.\n */\nfunction renderLoading(): string {\n return /* html */ `\n <div id=\"${getStateId('loading')}\" class=\"eudi-state\">\n <div class=\"eudi-loading\">\n <div class=\"eudi-spinner\" aria-hidden=\"true\"></div>\n <p>Loading...</p>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the QR code state.\n */\nfunction renderShowQR(): string {\n return /* html */ `\n <div id=\"${getStateId('showQR')}\" class=\"eudi-state\">\n <div class=\"eudi-qr\">\n <img class=\"eudi-qr-img\" src=\"\" alt=\"Scan with EUDI Wallet\" />\n <p class=\"eudi-qr-text\">Scan with your EU Digital Identity Wallet</p>\n <button class=\"eudi-cancel-btn\" type=\"button\" data-action=\"cancel\">Cancel</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the waiting for wallet state.\n */\nfunction renderWaitingForWallet(): string {\n return /* html */ `\n <div id=\"${getStateId('waitingForWallet')}\" class=\"eudi-state\">\n <div class=\"eudi-waiting\">\n ${WALLET_ICON}\n <p class=\"eudi-waiting-text\">Waiting for wallet approval...</p>\n <button class=\"eudi-cancel-btn\" type=\"button\" data-action=\"cancel\">Cancel</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the verified state.\n */\nfunction renderVerified(): string {\n return /* html */ `\n <div id=\"${getStateId('verified')}\" class=\"eudi-state\">\n <div class=\"eudi-success\">\n ${SUCCESS_ICON}\n <p class=\"eudi-success-text\">Verified</p>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the rejected state.\n */\nfunction renderRejected(): string {\n return /* html */ `\n <div id=\"${getStateId('rejected')}\" class=\"eudi-state\">\n <div class=\"eudi-error\">\n ${ERROR_ICON}\n <p class=\"eudi-error-text\">Verification declined</p>\n <p class=\"eudi-error-detail\"></p>\n <button class=\"eudi-retry-btn\" type=\"button\" data-action=\"reset\">Try again</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the expired state.\n */\nfunction renderExpired(): string {\n return /* html */ `\n <div id=\"${getStateId('expired')}\" class=\"eudi-state\">\n <div class=\"eudi-error\">\n ${EXPIRED_ICON}\n <p class=\"eudi-error-text\">Session expired</p>\n <button class=\"eudi-retry-btn\" type=\"button\" data-action=\"reset\">Try again</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the error state.\n */\nfunction renderError(): string {\n return /* html */ `\n <div id=\"${getStateId('error')}\" class=\"eudi-state\">\n <div class=\"eudi-error\">\n ${ERROR_ICON}\n <p class=\"eudi-error-text\">Verification failed</p>\n <p class=\"eudi-error-detail\"></p>\n <button class=\"eudi-retry-btn\" type=\"button\" data-action=\"reset\">Try again</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the demo mode banner.\n */\nfunction renderDemoBanner(): string {\n return /* html */ `\n <div class=\"eudi-demo-banner\" role=\"status\" aria-live=\"polite\" hidden>\n ${WARNING_ICON}\n <span>Demo mode — credentials are simulated</span>\n </div>\n `;\n}\n\n/**\n * Render the complete widget structure.\n * All states are rendered, only one is visible at a time.\n */\nexport function renderWidget(): string {\n return /* html */ `\n <div class=\"eudi-widget\" role=\"region\" aria-label=\"Identity verification\">\n <div class=\"eudi-sr-only\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n ${renderDemoBanner()}\n ${renderIdle()}\n ${renderLoading()}\n ${renderShowQR()}\n ${renderWaitingForWallet()}\n ${renderVerified()}\n ${renderRejected()}\n ${renderExpired()}\n ${renderError()}\n </div>\n `;\n}\n\n/**\n * Update the visible state and content based on verification state.\n */\nexport function updateWidgetState(\n container: HTMLElement,\n state: VerificationState\n): void {\n const stateContainers = container.querySelectorAll<HTMLElement>('.eudi-state');\n\n for (const stateEl of stateContainers) {\n if (stateEl.id === getStateId(state.status)) {\n stateEl.setAttribute('data-active', '');\n } else {\n stateEl.removeAttribute('data-active');\n }\n }\n\n if (state.status === 'showQR') {\n const img = container.querySelector<HTMLImageElement>('.eudi-qr-img');\n if (img && 'qrDataUrl' in state) {\n img.src = state.qrDataUrl;\n }\n }\n\n if (state.status === 'rejected' && 'error' in state && state.error) {\n const detail = container.querySelector(`#${getStateId('rejected')} .eudi-error-detail`);\n if (detail) {\n detail.textContent = state.error;\n }\n }\n\n if (state.status === 'error' && 'error' in state) {\n const detail = container.querySelector(`#${getStateId('error')} .eudi-error-detail`);\n if (detail) {\n detail.textContent = state.error;\n }\n }\n}\n","/**\n * @eudi-verify/embed - Accessibility Utilities\n *\n * WCAG 2.1 AA compliance helpers for focus management and announcements.\n */\n\n/**\n * Selectors for focusable elements within the widget.\n */\nconst FOCUSABLE_SELECTORS = [\n 'button:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n 'a[href]',\n 'input:not([disabled])',\n].join(', ');\n\n/**\n * Get all focusable elements within a container.\n */\nexport function getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS));\n}\n\n/**\n * Create a focus trap within a container.\n * Returns a cleanup function to remove the trap.\n */\nexport function createFocusTrap(container: HTMLElement): () => void {\n let previouslyFocused: HTMLElement | null = null;\n\n function handleKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Tab') return;\n\n const focusable = getFocusableElements(container);\n if (focusable.length === 0) return;\n\n const firstElement = focusable[0]!;\n const lastElement = focusable[focusable.length - 1]!;\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n\n function activate(): void {\n previouslyFocused = document.activeElement as HTMLElement | null;\n container.addEventListener('keydown', handleKeyDown);\n\n const focusable = getFocusableElements(container);\n if (focusable.length > 0) {\n focusable[0]!.focus();\n }\n }\n\n function deactivate(): void {\n container.removeEventListener('keydown', handleKeyDown);\n if (previouslyFocused && typeof previouslyFocused.focus === 'function') {\n previouslyFocused.focus();\n }\n }\n\n activate();\n return deactivate;\n}\n\n/**\n * Announce a message to screen readers via aria-live region.\n */\nexport function announce(\n liveRegion: HTMLElement,\n message: string,\n priority: 'polite' | 'assertive' = 'polite'\n): void {\n liveRegion.setAttribute('aria-live', priority);\n liveRegion.textContent = '';\n requestAnimationFrame(() => {\n liveRegion.textContent = message;\n });\n}\n\n/**\n * Clear the announcement from a live region.\n */\nexport function clearAnnouncement(liveRegion: HTMLElement): void {\n liveRegion.textContent = '';\n}\n\n/**\n * Messages for each verification state (for screen reader announcements).\n */\nexport const STATE_MESSAGES = {\n idle: '',\n loading: 'Loading verification session...',\n showQR: 'QR code ready. Scan with your EU Digital Identity Wallet.',\n waitingForWallet: 'Waiting for wallet approval...',\n verified: 'Identity verified successfully.',\n rejected: 'Verification was declined.',\n expired: 'Verification session expired.',\n error: 'Verification error occurred.',\n} as const;\n\n/**\n * Get aria-live priority for a state change.\n * Terminal states use assertive, others use polite.\n */\nexport function getAnnouncementPriority(\n status: keyof typeof STATE_MESSAGES\n): 'polite' | 'assertive' {\n switch (status) {\n case 'verified':\n case 'rejected':\n case 'expired':\n case 'error':\n return 'assertive';\n default:\n return 'polite';\n }\n}\n\n/**\n * Check if reduced motion is preferred.\n */\nexport function prefersReducedMotion(): boolean {\n return (\n typeof window !== 'undefined' &&\n window.matchMedia?.('(prefers-reduced-motion: reduce)').matches\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/element.ts","../src/styles.ts","../src/render.ts","../src/a11y.ts"],"sourcesContent":["/**\n * @eudi-verify/embed\n *\n * <eudi-verify> Custom Element for EUDI Wallet verification.\n *\n * @example\n * ```html\n * <script type=\"module\">\n * import '@eudi-verify/embed';\n * </script>\n *\n * <eudi-verify\n * api-url=\"/api/eudi\"\n * request='{\"age_over_18\":true}'\n * ></eudi-verify>\n * ```\n */\n\nexport const VERSION = \"0.1.0\";\n\nexport { EudiVerifyElement, type EudiVerifyEventMap } from \"./element.js\";\nexport { createStyles, CSS_VARIABLES } from \"./styles.js\";\nexport {\n announce,\n clearAnnouncement,\n createFocusTrap,\n getFocusableElements,\n STATE_MESSAGES,\n} from \"./a11y.js\";\nexport { renderWidget, updateWidgetState, getStateId } from \"./render.js\";\n\nimport { EudiVerifyElement } from \"./element.js\";\n\nif (\n typeof customElements !== \"undefined\" &&\n !customElements.get(\"eudi-verify\")\n) {\n customElements.define(\"eudi-verify\", EudiVerifyElement);\n}\n","/**\n * @eudi-verify/embed - Custom Element\n *\n * <eudi-verify> vanilla Custom Element for EUDI Wallet verification.\n * Uses open Shadow DOM with CSS custom property theming.\n */\n\nimport {\n createVerification,\n type Verification,\n type VerificationState,\n type VerificationRequest,\n} from \"@eudi-verify/client\";\nimport { createStyles } from \"./styles.js\";\nimport { renderWidget, updateWidgetState } from \"./render.js\";\nimport {\n announce,\n clearAnnouncement,\n STATE_MESSAGES,\n getAnnouncementPriority,\n} from \"./a11y.js\";\n\n/**\n * Events dispatched by EudiVerifyElement.\n */\nexport interface EudiVerifyEventMap {\n verified: CustomEvent<{ token: string; claims: Record<string, unknown> }>;\n rejected: CustomEvent<{ error?: string }>;\n expired: CustomEvent<Record<string, never>>;\n error: CustomEvent<{ error: string }>;\n \"state-change\": CustomEvent<{ state: VerificationState }>;\n}\n\n/**\n * Observed attributes for the custom element.\n */\nconst OBSERVED_ATTRIBUTES = [\"api-url\", \"request\", \"auto-start\"] as const;\ntype ObservedAttribute = (typeof OBSERVED_ATTRIBUTES)[number];\n\n/**\n * <eudi-verify> Custom Element\n *\n * @example\n * ```html\n * <eudi-verify\n * api-url=\"/api/eudi\"\n * request='{\"age_over_18\":true}'\n * ></eudi-verify>\n * ```\n *\n * @fires verified - Verification succeeded\n * @fires rejected - User rejected in wallet\n * @fires expired - Session expired\n * @fires error - Error occurred\n * @fires state-change - Any state change\n */\nexport class EudiVerifyElement extends HTMLElement {\n static get observedAttributes(): readonly string[] {\n return OBSERVED_ATTRIBUTES;\n }\n\n #shadow: ShadowRoot;\n #verification: Verification | null = null;\n #unsubscribe: (() => void) | null = null;\n #container: HTMLElement | null = null;\n #liveRegion: HTMLElement | null = null;\n #lastStatus: VerificationState[\"status\"] | null = null;\n #isDemo: boolean | null = null;\n #demoBanner: HTMLElement | null = null;\n\n constructor() {\n super();\n this.#shadow = this.attachShadow({ mode: \"open\" });\n }\n\n /**\n * Get the API URL attribute.\n */\n get apiUrl(): string {\n return this.getAttribute(\"api-url\") ?? \"\";\n }\n\n /**\n * Set the API URL attribute.\n */\n set apiUrl(value: string) {\n this.setAttribute(\"api-url\", value);\n }\n\n /**\n * Get the request attribute (JSON string).\n */\n get request(): string {\n return this.getAttribute(\"request\") ?? \"\";\n }\n\n /**\n * Set the request attribute (JSON string).\n */\n set request(value: string) {\n this.setAttribute(\"request\", value);\n }\n\n /**\n * Check if auto-start is enabled.\n */\n get autoStart(): boolean {\n return this.hasAttribute(\"auto-start\");\n }\n\n /**\n * Set auto-start attribute.\n */\n set autoStart(value: boolean) {\n if (value) {\n this.setAttribute(\"auto-start\", \"\");\n } else {\n this.removeAttribute(\"auto-start\");\n }\n }\n\n /**\n * Current verification state (read-only).\n */\n get state(): VerificationState | null {\n return this.#verification?.state ?? null;\n }\n\n connectedCallback(): void {\n this.#render();\n this.#setupEventListeners();\n\n if (this.autoStart && this.apiUrl && this.request) {\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this.#cleanup();\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null,\n ): void {\n if (oldValue === newValue) return;\n\n if (name === \"api-url\" && this.#verification) {\n this.#cleanup();\n }\n }\n\n /**\n * Start the verification flow.\n */\n start(): void {\n if (!this.apiUrl) {\n console.error(\"[eudi-verify] api-url attribute is required\");\n return;\n }\n\n let requestObj: VerificationRequest;\n try {\n requestObj = this.request ? JSON.parse(this.request) : {};\n } catch {\n console.error(\"[eudi-verify] Invalid JSON in request attribute\");\n this.#dispatchError(\"Invalid verification request\");\n return;\n }\n\n this.#ensureVerification();\n this.#verification!.start(requestObj);\n }\n\n /**\n * Cancel the current verification.\n */\n cancel(): void {\n this.#verification?.cancel();\n }\n\n /**\n * Reset to idle state.\n */\n reset(): void {\n this.#cleanup();\n this.#updateState({ status: \"idle\" });\n }\n\n #render(): void {\n this.#shadow.innerHTML = `\n <style>${createStyles()}</style>\n ${renderWidget()}\n `;\n\n this.#container = this.#shadow.querySelector(\".eudi-widget\");\n this.#liveRegion = this.#shadow.querySelector(\"[aria-live]\");\n this.#demoBanner = this.#shadow.querySelector(\".eudi-demo-banner\");\n }\n\n #setupEventListeners(): void {\n this.#shadow.addEventListener(\"click\", (event) => {\n const target = event.target as HTMLElement;\n const button = target.closest<HTMLElement>(\"[data-action]\");\n if (!button) return;\n\n const action = button.dataset.action;\n switch (action) {\n case \"start\":\n this.start();\n break;\n case \"cancel\":\n this.cancel();\n break;\n case \"reset\":\n this.reset();\n this.start();\n break;\n }\n });\n }\n\n #ensureVerification(): void {\n if (this.#verification) return;\n\n this.#verification = createVerification({\n apiUrl: this.apiUrl,\n });\n\n this.#unsubscribe = this.#verification.subscribe((state) => {\n this.#handleStateChange(state);\n });\n\n // Detect demo mode on first API interaction\n this.#detectDemoMode();\n }\n\n async #detectDemoMode(): Promise<void> {\n if (this.#isDemo !== null || !this.apiUrl) return;\n\n try {\n const response = await fetch(`${this.apiUrl}/sessions`, {\n method: \"HEAD\",\n });\n const mode = response.headers.get(\"X-Eudi-Mode\");\n this.#isDemo = mode === \"demo\";\n this.#updateDemoBanner();\n } catch {\n // If detection fails, don't show banner (fail safely)\n this.#isDemo = false;\n }\n }\n\n #updateDemoBanner(): void {\n if (!this.#demoBanner) return;\n\n if (this.#isDemo === true) {\n this.#demoBanner.removeAttribute(\"hidden\");\n } else {\n this.#demoBanner.setAttribute(\"hidden\", \"\");\n }\n }\n\n #handleStateChange(state: VerificationState): void {\n this.#updateState(state);\n this.#dispatchStateChange(state);\n\n switch (state.status) {\n case \"verified\":\n if (\"token\" in state && \"claims\" in state) {\n this.#dispatchVerified(state.token, state.claims);\n }\n break;\n case \"rejected\":\n this.#dispatchRejected(\"error\" in state ? state.error : undefined);\n break;\n case \"expired\":\n this.#dispatchExpired();\n break;\n case \"error\":\n if (\"error\" in state) {\n this.#dispatchError(state.error);\n }\n break;\n }\n }\n\n #updateState(state: VerificationState): void {\n if (!this.#container) return;\n\n updateWidgetState(this.#container, state);\n\n if (state.status === \"loading\") {\n this.#container.setAttribute(\"aria-busy\", \"true\");\n } else {\n this.#container.removeAttribute(\"aria-busy\");\n }\n\n if (this.#liveRegion && state.status !== this.#lastStatus) {\n const message = STATE_MESSAGES[state.status];\n if (message) {\n const priority = getAnnouncementPriority(state.status);\n announce(this.#liveRegion, message, priority);\n }\n }\n\n this.#lastStatus = state.status;\n\n this.#manageFocus(state);\n }\n\n #manageFocus(state: VerificationState): void {\n if (!this.#container) return;\n\n switch (state.status) {\n case \"showQR\":\n case \"waitingForWallet\": {\n const cancelBtn = this.#container.querySelector<HTMLElement>(\n `#eudi-state-${state.status} .eudi-cancel-btn`,\n );\n cancelBtn?.focus();\n break;\n }\n case \"rejected\":\n case \"expired\":\n case \"error\": {\n const retryBtn = this.#container.querySelector<HTMLElement>(\n `#eudi-state-${state.status} .eudi-retry-btn`,\n );\n retryBtn?.focus();\n break;\n }\n }\n }\n\n #dispatchVerified(token: string, claims: Record<string, unknown>): void {\n this.dispatchEvent(\n new CustomEvent(\"verified\", {\n bubbles: true,\n composed: true,\n detail: { token, claims },\n }),\n );\n }\n\n #dispatchRejected(error?: string): void {\n this.dispatchEvent(\n new CustomEvent(\"rejected\", {\n bubbles: true,\n composed: true,\n detail: { error },\n }),\n );\n }\n\n #dispatchExpired(): void {\n this.dispatchEvent(\n new CustomEvent(\"expired\", {\n bubbles: true,\n composed: true,\n detail: {},\n }),\n );\n }\n\n #dispatchError(error: string): void {\n this.dispatchEvent(\n new CustomEvent(\"error\", {\n bubbles: true,\n composed: true,\n detail: { error },\n }),\n );\n }\n\n #dispatchStateChange(state: VerificationState): void {\n this.dispatchEvent(\n new CustomEvent(\"state-change\", {\n bubbles: true,\n composed: true,\n detail: { state },\n }),\n );\n }\n\n #cleanup(): void {\n if (this.#unsubscribe) {\n this.#unsubscribe();\n this.#unsubscribe = null;\n }\n\n if (this.#verification) {\n this.#verification.destroy();\n this.#verification = null;\n }\n\n if (this.#liveRegion) {\n clearAnnouncement(this.#liveRegion);\n }\n\n this.#lastStatus = null;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"eudi-verify\": EudiVerifyElement;\n }\n\n interface HTMLElementEventMap extends EudiVerifyEventMap {}\n}\n","/**\n * @eudi-verify/embed - Styles\n *\n * CSS template with custom property theming.\n * Uses open Shadow DOM for style encapsulation.\n */\n\n/**\n * CSS custom properties for theming.\n * These can be set on the host element or any ancestor.\n */\nexport const CSS_VARIABLES = {\n \"--eudi-primary\": \"#003399\",\n \"--eudi-text\": \"#1a1a1a\",\n \"--eudi-background\": \"#ffffff\",\n \"--eudi-border-radius\": \"8px\",\n \"--eudi-font-family\": \"system-ui, sans-serif\",\n \"--eudi-error\": \"#d32f2f\",\n} as const;\n\n/**\n * Generate the internal stylesheet for the shadow DOM.\n * Uses CSS custom properties with fallbacks to defaults.\n */\nexport function createStyles(): string {\n return /* css */ `\n :host {\n display: block;\n font-family: var(--eudi-font-family, ${CSS_VARIABLES[\"--eudi-font-family\"]});\n color: var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]});\n }\n\n :host([hidden]) {\n display: none;\n }\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n .eudi-widget {\n background: var(--eudi-background, ${CSS_VARIABLES[\"--eudi-background\"]});\n border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]}) 20%, transparent);\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES[\"--eudi-border-radius\"]});\n padding: 24px;\n text-align: center;\n min-width: 280px;\n max-width: 400px;\n margin-inline: auto;\n }\n\n /* State containers - only one visible at a time */\n .eudi-state {\n display: none;\n }\n\n .eudi-state[data-active] {\n display: block;\n }\n\n /* Start button */\n .eudi-start-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 24px;\n font-size: 16px;\n font-weight: 500;\n font-family: inherit;\n color: #ffffff;\n background: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n border: none;\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES[\"--eudi-border-radius\"]});\n cursor: pointer;\n transition: background-color 0.2s ease, transform 0.1s ease;\n }\n\n .eudi-start-btn:hover {\n background: color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]}) 85%, black);\n }\n\n .eudi-start-btn:focus-visible {\n outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n outline-offset: 2px;\n }\n\n .eudi-start-btn:active {\n transform: scale(0.98);\n }\n\n /* EU stars icon */\n .eudi-icon {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n }\n\n /* Loading state */\n .eudi-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]}) 20%, transparent);\n border-top-color: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n border-radius: 50%;\n animation: eudi-spin 0.8s linear infinite;\n }\n\n @keyframes eudi-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n /* Reduced motion */\n @media (prefers-reduced-motion: reduce) {\n .eudi-spinner {\n animation: none;\n border-top-color: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n border-right-color: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n }\n\n .eudi-start-btn {\n transition: none;\n }\n }\n\n /* QR code state */\n .eudi-qr {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n }\n\n .eudi-qr-img {\n width: 200px;\n height: 200px;\n border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]}) 15%, transparent);\n border-radius: 4px;\n }\n\n .eudi-qr-text {\n margin: 0;\n font-size: 14px;\n color: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]}) 80%, transparent);\n }\n\n .eudi-cancel-btn {\n margin-top: 8px;\n padding: 8px 16px;\n font-size: 14px;\n font-family: inherit;\n color: var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]});\n background: transparent;\n border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]}) 30%, transparent);\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES[\"--eudi-border-radius\"]});\n cursor: pointer;\n transition: background-color 0.2s ease;\n }\n\n .eudi-cancel-btn:hover {\n background: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]}) 5%, transparent);\n }\n\n .eudi-cancel-btn:focus-visible {\n outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n outline-offset: 2px;\n }\n\n /* Waiting for wallet */\n .eudi-waiting {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-waiting-icon {\n width: 48px;\n height: 48px;\n color: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n }\n\n .eudi-waiting-text {\n margin: 0;\n font-size: 16px;\n }\n\n /* Success state */\n .eudi-success {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-success-icon {\n width: 48px;\n height: 48px;\n color: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n }\n\n .eudi-success-text {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n }\n\n /* Error/rejected/expired states */\n .eudi-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n }\n\n .eudi-error-icon {\n width: 48px;\n height: 48px;\n color: var(--eudi-error, ${CSS_VARIABLES[\"--eudi-error\"]});\n }\n\n .eudi-error-text {\n margin: 0;\n font-size: 16px;\n color: var(--eudi-error, ${CSS_VARIABLES[\"--eudi-error\"]});\n }\n\n .eudi-error-detail {\n margin: 0;\n font-size: 14px;\n color: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES[\"--eudi-text\"]}) 70%, transparent);\n }\n\n .eudi-retry-btn {\n margin-top: 8px;\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n color: #ffffff;\n background: var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n border: none;\n border-radius: var(--eudi-border-radius, ${CSS_VARIABLES[\"--eudi-border-radius\"]});\n cursor: pointer;\n transition: background-color 0.2s ease;\n }\n\n .eudi-retry-btn:hover {\n background: color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]}) 85%, black);\n }\n\n .eudi-retry-btn:focus-visible {\n outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES[\"--eudi-primary\"]});\n outline-offset: 2px;\n }\n\n /* Screen reader only */\n .eudi-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* Demo mode banner */\n .eudi-demo-banner {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px 12px;\n margin-bottom: 16px;\n font-size: 13px;\n color: #7c5c00;\n background: #fef3cd;\n border: 1px solid #ffe69c;\n border-radius: 4px;\n }\n\n .eudi-demo-banner[hidden] {\n display: none;\n }\n\n .eudi-warning-icon {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n color: #7c5c00;\n }\n `;\n}\n","/**\n * @eudi-verify/embed - State-based Rendering\n *\n * Renders the widget UI based on verification state.\n */\n\nimport type { VerificationState } from \"@eudi-verify/client\";\n\n/**\n * SVG icon: EU stars (simplified)\n */\nconst EU_STARS_ICON = /* html */ `\n<svg class=\"eudi-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <g fill=\"currentColor\">\n <polygon points=\"12,4 12.5,5.5 14,5.5 12.75,6.5 13.25,8 12,7 10.75,8 11.25,6.5 10,5.5 11.5,5.5\"/>\n <polygon points=\"17,6.5 17.35,7.6 18.5,7.6 17.6,8.3 17.9,9.4 17,8.7 16.1,9.4 16.4,8.3 15.5,7.6 16.65,7.6\"/>\n <polygon points=\"19,11 19.35,12.1 20.5,12.1 19.6,12.8 19.9,13.9 19,13.2 18.1,13.9 18.4,12.8 17.5,12.1 18.65,12.1\"/>\n <polygon points=\"17,15.5 17.35,16.6 18.5,16.6 17.6,17.3 17.9,18.4 17,17.7 16.1,18.4 16.4,17.3 15.5,16.6 16.65,16.6\"/>\n <polygon points=\"12,18 12.35,19.1 13.5,19.1 12.6,19.8 12.9,20.9 12,20.2 11.1,20.9 11.4,19.8 10.5,19.1 11.65,19.1\"/>\n <polygon points=\"7,15.5 7.35,16.6 8.5,16.6 7.6,17.3 7.9,18.4 7,17.7 6.1,18.4 6.4,17.3 5.5,16.6 6.65,16.6\"/>\n <polygon points=\"5,11 5.35,12.1 6.5,12.1 5.6,12.8 5.9,13.9 5,13.2 4.1,13.9 4.4,12.8 3.5,12.1 4.65,12.1\"/>\n <polygon points=\"7,6.5 7.35,7.6 8.5,7.6 7.6,8.3 7.9,9.4 7,8.7 6.1,9.4 6.4,8.3 5.5,7.6 6.65,7.6\"/>\n </g>\n</svg>\n`;\n\n/**\n * SVG icon: checkmark (success)\n */\nconst SUCCESS_ICON = /* html */ `\n<svg class=\"eudi-success-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M8 12l3 3 5-6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n`;\n\n/**\n * SVG icon: X (error)\n */\nconst ERROR_ICON = /* html */ `\n<svg class=\"eudi-error-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M15 9l-6 6M9 9l6 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>\n`;\n\n/**\n * SVG icon: clock (expired)\n */\nconst EXPIRED_ICON = /* html */ `\n<svg class=\"eudi-error-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M12 6v6l4 2\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>\n`;\n\n/**\n * SVG icon: wallet (waiting)\n */\nconst WALLET_ICON = /* html */ `\n<svg class=\"eudi-waiting-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"6\" width=\"20\" height=\"14\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M2 10h20\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"17\" cy=\"14\" r=\"1.5\" fill=\"currentColor\"/>\n</svg>\n`;\n\n/**\n * SVG icon: warning (demo banner)\n */\nconst WARNING_ICON = /* html */ `\n<svg class=\"eudi-warning-icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n`;\n\n/**\n * Map of status to state container ID.\n */\ntype RenderableStatus = VerificationState[\"status\"];\n\n/**\n * Get the state container ID for a status.\n */\nexport function getStateId(status: RenderableStatus): string {\n return `eudi-state-${status}`;\n}\n\n/**\n * Render the idle state (start button).\n */\nfunction renderIdle(): string {\n return /* html */ `\n <div id=\"${getStateId(\"idle\")}\" class=\"eudi-state\" data-active>\n <button class=\"eudi-start-btn\" type=\"button\" data-action=\"start\">\n ${EU_STARS_ICON}\n <span>Verify with EU Wallet</span>\n </button>\n </div>\n `;\n}\n\n/**\n * Render the loading state.\n */\nfunction renderLoading(): string {\n return /* html */ `\n <div id=\"${getStateId(\"loading\")}\" class=\"eudi-state\">\n <div class=\"eudi-loading\">\n <div class=\"eudi-spinner\" aria-hidden=\"true\"></div>\n <p>Loading...</p>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the QR code state.\n */\nfunction renderShowQR(): string {\n return /* html */ `\n <div id=\"${getStateId(\"showQR\")}\" class=\"eudi-state\">\n <div class=\"eudi-qr\">\n <img class=\"eudi-qr-img\" src=\"\" alt=\"Scan with EUDI Wallet\" />\n <p class=\"eudi-qr-text\">Scan with your EU Digital Identity Wallet</p>\n <button class=\"eudi-cancel-btn\" type=\"button\" data-action=\"cancel\">Cancel</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the waiting for wallet state.\n */\nfunction renderWaitingForWallet(): string {\n return /* html */ `\n <div id=\"${getStateId(\"waitingForWallet\")}\" class=\"eudi-state\">\n <div class=\"eudi-waiting\">\n ${WALLET_ICON}\n <p class=\"eudi-waiting-text\">Waiting for wallet approval...</p>\n <button class=\"eudi-cancel-btn\" type=\"button\" data-action=\"cancel\">Cancel</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the verified state.\n */\nfunction renderVerified(): string {\n return /* html */ `\n <div id=\"${getStateId(\"verified\")}\" class=\"eudi-state\">\n <div class=\"eudi-success\">\n ${SUCCESS_ICON}\n <p class=\"eudi-success-text\">Verified</p>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the rejected state.\n */\nfunction renderRejected(): string {\n return /* html */ `\n <div id=\"${getStateId(\"rejected\")}\" class=\"eudi-state\">\n <div class=\"eudi-error\">\n ${ERROR_ICON}\n <p class=\"eudi-error-text\">Verification declined</p>\n <p id=\"eudi-rejected-detail\" class=\"eudi-error-detail\"></p>\n <button class=\"eudi-retry-btn\" type=\"button\" data-action=\"reset\">Try again</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the expired state.\n */\nfunction renderExpired(): string {\n return /* html */ `\n <div id=\"${getStateId(\"expired\")}\" class=\"eudi-state\">\n <div class=\"eudi-error\">\n ${EXPIRED_ICON}\n <p class=\"eudi-error-text\">Session expired</p>\n <button class=\"eudi-retry-btn\" type=\"button\" data-action=\"reset\">Try again</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the error state.\n */\nfunction renderError(): string {\n return /* html */ `\n <div id=\"${getStateId(\"error\")}\" class=\"eudi-state\">\n <div class=\"eudi-error\">\n ${ERROR_ICON}\n <p class=\"eudi-error-text\">Verification failed</p>\n <p id=\"eudi-error-state-detail\" class=\"eudi-error-detail\"></p>\n <button class=\"eudi-retry-btn\" type=\"button\" data-action=\"reset\">Try again</button>\n </div>\n </div>\n `;\n}\n\n/**\n * Render the demo mode banner.\n */\nfunction renderDemoBanner(): string {\n return /* html */ `\n <div class=\"eudi-demo-banner\" role=\"status\" aria-live=\"polite\" hidden>\n ${WARNING_ICON}\n <span>Demo mode — credentials are simulated</span>\n </div>\n `;\n}\n\n/**\n * Render the complete widget structure.\n * All states are rendered, only one is visible at a time.\n */\nexport function renderWidget(): string {\n return /* html */ `\n <div class=\"eudi-widget\" role=\"region\" aria-label=\"Identity verification\">\n <div class=\"eudi-sr-only\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n ${renderDemoBanner()}\n ${renderIdle()}\n ${renderLoading()}\n ${renderShowQR()}\n ${renderWaitingForWallet()}\n ${renderVerified()}\n ${renderRejected()}\n ${renderExpired()}\n ${renderError()}\n </div>\n `;\n}\n\n/**\n * Update the visible state and content based on verification state.\n */\nexport function updateWidgetState(\n container: HTMLElement,\n state: VerificationState,\n): void {\n const stateContainers =\n container.querySelectorAll<HTMLElement>(\".eudi-state\");\n\n for (const stateEl of stateContainers) {\n if (stateEl.id === getStateId(state.status)) {\n stateEl.setAttribute(\"data-active\", \"\");\n } else {\n stateEl.removeAttribute(\"data-active\");\n }\n }\n\n if (state.status === \"showQR\") {\n const img = container.querySelector<HTMLImageElement>(\".eudi-qr-img\");\n if (img && \"qrDataUrl\" in state) {\n img.src = state.qrDataUrl;\n }\n }\n\n if (state.status === \"rejected\" && \"error\" in state && state.error) {\n const detail = container.querySelector(\n `#${getStateId(\"rejected\")} #eudi-rejected-detail`,\n );\n if (detail) {\n detail.textContent = state.error;\n }\n wireRetryDescribedBy(\n container,\n getStateId(\"rejected\"),\n \"eudi-rejected-detail\",\n );\n }\n\n if (state.status === \"error\" && \"error\" in state) {\n const detail = container.querySelector(\n `#${getStateId(\"error\")} #eudi-error-state-detail`,\n );\n if (detail) {\n detail.textContent = state.error;\n }\n wireRetryDescribedBy(\n container,\n getStateId(\"error\"),\n \"eudi-error-state-detail\",\n );\n }\n}\n\nfunction wireRetryDescribedBy(\n container: HTMLElement,\n stateId: string,\n detailId: string,\n): void {\n const detail = container.querySelector(`#${detailId}`);\n const retryBtn = container.querySelector<HTMLButtonElement>(\n `#${stateId} .eudi-retry-btn`,\n );\n if (!retryBtn) return;\n\n if (detail?.textContent) {\n retryBtn.setAttribute(\"aria-describedby\", detailId);\n } else {\n retryBtn.removeAttribute(\"aria-describedby\");\n }\n}\n","/**\n * @eudi-verify/embed - Accessibility Utilities\n *\n * WCAG 2.1 AA compliance helpers for focus management and announcements.\n */\n\n/**\n * Selectors for focusable elements within the widget.\n */\nconst FOCUSABLE_SELECTORS = [\n \"button:not([disabled])\",\n '[tabindex]:not([tabindex=\"-1\"])',\n \"a[href]\",\n \"input:not([disabled])\",\n].join(\", \");\n\n/**\n * Get all focusable elements within a container.\n */\nexport function getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n );\n}\n\n/**\n * Create a focus trap within a container.\n * Returns a cleanup function to remove the trap.\n *\n * ponytail: not used for the default inline widget — trapping focus would block\n * tabbing to surrounding page content (demo links, logs). Reserved for future\n * modal/dialog embedding.\n */\nexport function createFocusTrap(container: HTMLElement): () => void {\n let previouslyFocused: HTMLElement | null = null;\n\n function handleKeyDown(event: KeyboardEvent): void {\n if (event.key !== \"Tab\") return;\n\n const focusable = getFocusableElements(container);\n if (focusable.length === 0) return;\n\n const firstElement = focusable[0]!;\n const lastElement = focusable[focusable.length - 1]!;\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n\n function activate(): void {\n previouslyFocused = document.activeElement as HTMLElement | null;\n container.addEventListener(\"keydown\", handleKeyDown);\n\n const focusable = getFocusableElements(container);\n if (focusable.length > 0) {\n focusable[0]!.focus();\n }\n }\n\n function deactivate(): void {\n container.removeEventListener(\"keydown\", handleKeyDown);\n if (previouslyFocused && typeof previouslyFocused.focus === \"function\") {\n previouslyFocused.focus();\n }\n }\n\n activate();\n return deactivate;\n}\n\n/**\n * Announce a message to screen readers via aria-live region.\n */\nexport function announce(\n liveRegion: HTMLElement,\n message: string,\n priority: \"polite\" | \"assertive\" = \"polite\",\n): void {\n liveRegion.setAttribute(\"aria-live\", priority);\n liveRegion.textContent = \"\";\n requestAnimationFrame(() => {\n liveRegion.textContent = message;\n });\n}\n\n/**\n * Clear the announcement from a live region.\n */\nexport function clearAnnouncement(liveRegion: HTMLElement): void {\n liveRegion.textContent = \"\";\n}\n\n/**\n * Messages for each verification state (for screen reader announcements).\n */\nexport const STATE_MESSAGES = {\n idle: \"\",\n loading: \"Loading verification session...\",\n showQR: \"QR code ready. Scan with your EU Digital Identity Wallet.\",\n waitingForWallet: \"Waiting for wallet approval...\",\n verified: \"Identity verified successfully.\",\n rejected: \"Verification was declined.\",\n expired: \"Verification session expired.\",\n error: \"Verification error occurred.\",\n} as const;\n\n/**\n * Get aria-live priority for a state change.\n * Terminal states use assertive, others use polite.\n */\nexport function getAnnouncementPriority(\n status: keyof typeof STATE_MESSAGES,\n): \"polite\" | \"assertive\" {\n switch (status) {\n case \"verified\":\n case \"rejected\":\n case \"expired\":\n case \"error\":\n return \"assertive\";\n default:\n return \"polite\";\n }\n}\n\n/**\n * Check if reduced motion is preferred.\n */\nexport function prefersReducedMotion(): boolean {\n return (\n typeof window !== \"undefined\" &&\n window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches\n );\n}\n"],"mappings":"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","names":[]}