@eudi-verify/embed 0.1.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.
@@ -0,0 +1,962 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ CSS_VARIABLES: () => CSS_VARIABLES,
24
+ EudiVerifyElement: () => EudiVerifyElement,
25
+ STATE_MESSAGES: () => STATE_MESSAGES,
26
+ VERSION: () => VERSION,
27
+ announce: () => announce,
28
+ clearAnnouncement: () => clearAnnouncement,
29
+ createFocusTrap: () => createFocusTrap,
30
+ createStyles: () => createStyles,
31
+ getFocusableElements: () => getFocusableElements,
32
+ getStateId: () => getStateId,
33
+ renderWidget: () => renderWidget,
34
+ updateWidgetState: () => updateWidgetState
35
+ });
36
+ module.exports = __toCommonJS(src_exports);
37
+
38
+ // src/element.ts
39
+ var import_client = require("@eudi-verify/client");
40
+
41
+ // src/styles.ts
42
+ var CSS_VARIABLES = {
43
+ "--eudi-primary": "#003399",
44
+ "--eudi-text": "#1a1a1a",
45
+ "--eudi-background": "#ffffff",
46
+ "--eudi-border-radius": "8px",
47
+ "--eudi-font-family": "system-ui, sans-serif",
48
+ "--eudi-error": "#d32f2f"
49
+ };
50
+ function createStyles() {
51
+ return (
52
+ /* css */
53
+ `
54
+ :host {
55
+ display: block;
56
+ font-family: var(--eudi-font-family, ${CSS_VARIABLES["--eudi-font-family"]});
57
+ color: var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]});
58
+ }
59
+
60
+ :host([hidden]) {
61
+ display: none;
62
+ }
63
+
64
+ *,
65
+ *::before,
66
+ *::after {
67
+ box-sizing: border-box;
68
+ }
69
+
70
+ .eudi-widget {
71
+ background: var(--eudi-background, ${CSS_VARIABLES["--eudi-background"]});
72
+ border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]}) 20%, transparent);
73
+ border-radius: var(--eudi-border-radius, ${CSS_VARIABLES["--eudi-border-radius"]});
74
+ padding: 24px;
75
+ text-align: center;
76
+ min-width: 280px;
77
+ max-width: 400px;
78
+ margin-inline: auto;
79
+ }
80
+
81
+ /* State containers - only one visible at a time */
82
+ .eudi-state {
83
+ display: none;
84
+ }
85
+
86
+ .eudi-state[data-active] {
87
+ display: block;
88
+ }
89
+
90
+ /* Start button */
91
+ .eudi-start-btn {
92
+ display: inline-flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ gap: 8px;
96
+ padding: 12px 24px;
97
+ font-size: 16px;
98
+ font-weight: 500;
99
+ font-family: inherit;
100
+ color: #ffffff;
101
+ background: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
102
+ border: none;
103
+ border-radius: var(--eudi-border-radius, ${CSS_VARIABLES["--eudi-border-radius"]});
104
+ cursor: pointer;
105
+ transition: background-color 0.2s ease, transform 0.1s ease;
106
+ }
107
+
108
+ .eudi-start-btn:hover {
109
+ background: color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]}) 85%, black);
110
+ }
111
+
112
+ .eudi-start-btn:focus-visible {
113
+ outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
114
+ outline-offset: 2px;
115
+ }
116
+
117
+ .eudi-start-btn:active {
118
+ transform: scale(0.98);
119
+ }
120
+
121
+ /* EU stars icon */
122
+ .eudi-icon {
123
+ width: 20px;
124
+ height: 20px;
125
+ flex-shrink: 0;
126
+ }
127
+
128
+ /* Loading state */
129
+ .eudi-loading {
130
+ display: flex;
131
+ flex-direction: column;
132
+ align-items: center;
133
+ gap: 12px;
134
+ padding: 16px 0;
135
+ }
136
+
137
+ .eudi-spinner {
138
+ width: 40px;
139
+ height: 40px;
140
+ border: 3px solid color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]}) 20%, transparent);
141
+ border-top-color: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
142
+ border-radius: 50%;
143
+ animation: eudi-spin 0.8s linear infinite;
144
+ }
145
+
146
+ @keyframes eudi-spin {
147
+ to {
148
+ transform: rotate(360deg);
149
+ }
150
+ }
151
+
152
+ /* Reduced motion */
153
+ @media (prefers-reduced-motion: reduce) {
154
+ .eudi-spinner {
155
+ animation: none;
156
+ border-top-color: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
157
+ border-right-color: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
158
+ }
159
+
160
+ .eudi-start-btn {
161
+ transition: none;
162
+ }
163
+ }
164
+
165
+ /* QR code state */
166
+ .eudi-qr {
167
+ display: flex;
168
+ flex-direction: column;
169
+ align-items: center;
170
+ gap: 16px;
171
+ }
172
+
173
+ .eudi-qr-img {
174
+ width: 200px;
175
+ height: 200px;
176
+ border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]}) 15%, transparent);
177
+ border-radius: 4px;
178
+ }
179
+
180
+ .eudi-qr-text {
181
+ margin: 0;
182
+ font-size: 14px;
183
+ color: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]}) 80%, transparent);
184
+ }
185
+
186
+ .eudi-cancel-btn {
187
+ margin-top: 8px;
188
+ padding: 8px 16px;
189
+ font-size: 14px;
190
+ font-family: inherit;
191
+ color: var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]});
192
+ background: transparent;
193
+ border: 1px solid color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]}) 30%, transparent);
194
+ border-radius: var(--eudi-border-radius, ${CSS_VARIABLES["--eudi-border-radius"]});
195
+ cursor: pointer;
196
+ transition: background-color 0.2s ease;
197
+ }
198
+
199
+ .eudi-cancel-btn:hover {
200
+ background: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]}) 5%, transparent);
201
+ }
202
+
203
+ .eudi-cancel-btn:focus-visible {
204
+ outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
205
+ outline-offset: 2px;
206
+ }
207
+
208
+ /* Waiting for wallet */
209
+ .eudi-waiting {
210
+ display: flex;
211
+ flex-direction: column;
212
+ align-items: center;
213
+ gap: 12px;
214
+ padding: 16px 0;
215
+ }
216
+
217
+ .eudi-waiting-icon {
218
+ width: 48px;
219
+ height: 48px;
220
+ color: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
221
+ }
222
+
223
+ .eudi-waiting-text {
224
+ margin: 0;
225
+ font-size: 16px;
226
+ }
227
+
228
+ /* Success state */
229
+ .eudi-success {
230
+ display: flex;
231
+ flex-direction: column;
232
+ align-items: center;
233
+ gap: 12px;
234
+ padding: 16px 0;
235
+ }
236
+
237
+ .eudi-success-icon {
238
+ width: 48px;
239
+ height: 48px;
240
+ color: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
241
+ }
242
+
243
+ .eudi-success-text {
244
+ margin: 0;
245
+ font-size: 18px;
246
+ font-weight: 500;
247
+ color: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
248
+ }
249
+
250
+ /* Error/rejected/expired states */
251
+ .eudi-error {
252
+ display: flex;
253
+ flex-direction: column;
254
+ align-items: center;
255
+ gap: 12px;
256
+ padding: 16px 0;
257
+ }
258
+
259
+ .eudi-error-icon {
260
+ width: 48px;
261
+ height: 48px;
262
+ color: var(--eudi-error, ${CSS_VARIABLES["--eudi-error"]});
263
+ }
264
+
265
+ .eudi-error-text {
266
+ margin: 0;
267
+ font-size: 16px;
268
+ color: var(--eudi-error, ${CSS_VARIABLES["--eudi-error"]});
269
+ }
270
+
271
+ .eudi-error-detail {
272
+ margin: 0;
273
+ font-size: 14px;
274
+ color: color-mix(in srgb, var(--eudi-text, ${CSS_VARIABLES["--eudi-text"]}) 70%, transparent);
275
+ }
276
+
277
+ .eudi-retry-btn {
278
+ margin-top: 8px;
279
+ padding: 10px 20px;
280
+ font-size: 14px;
281
+ font-weight: 500;
282
+ font-family: inherit;
283
+ color: #ffffff;
284
+ background: var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
285
+ border: none;
286
+ border-radius: var(--eudi-border-radius, ${CSS_VARIABLES["--eudi-border-radius"]});
287
+ cursor: pointer;
288
+ transition: background-color 0.2s ease;
289
+ }
290
+
291
+ .eudi-retry-btn:hover {
292
+ background: color-mix(in srgb, var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]}) 85%, black);
293
+ }
294
+
295
+ .eudi-retry-btn:focus-visible {
296
+ outline: 2px solid var(--eudi-primary, ${CSS_VARIABLES["--eudi-primary"]});
297
+ outline-offset: 2px;
298
+ }
299
+
300
+ /* Screen reader only */
301
+ .eudi-sr-only {
302
+ position: absolute;
303
+ width: 1px;
304
+ height: 1px;
305
+ padding: 0;
306
+ margin: -1px;
307
+ overflow: hidden;
308
+ clip: rect(0, 0, 0, 0);
309
+ white-space: nowrap;
310
+ border: 0;
311
+ }
312
+
313
+ /* Demo mode banner */
314
+ .eudi-demo-banner {
315
+ display: flex;
316
+ align-items: center;
317
+ justify-content: center;
318
+ gap: 8px;
319
+ padding: 8px 12px;
320
+ margin-bottom: 16px;
321
+ font-size: 13px;
322
+ color: #7c5c00;
323
+ background: #fef3cd;
324
+ border: 1px solid #ffe69c;
325
+ border-radius: 4px;
326
+ }
327
+
328
+ .eudi-demo-banner[hidden] {
329
+ display: none;
330
+ }
331
+
332
+ .eudi-warning-icon {
333
+ width: 16px;
334
+ height: 16px;
335
+ flex-shrink: 0;
336
+ color: #7c5c00;
337
+ }
338
+ `
339
+ );
340
+ }
341
+
342
+ // src/render.ts
343
+ var EU_STARS_ICON = (
344
+ /* html */
345
+ `
346
+ <svg class="eudi-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
347
+ <circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.5"/>
348
+ <g fill="currentColor">
349
+ <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"/>
350
+ <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"/>
351
+ <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"/>
352
+ <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"/>
353
+ <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"/>
354
+ <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"/>
355
+ <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"/>
356
+ <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"/>
357
+ </g>
358
+ </svg>
359
+ `
360
+ );
361
+ var SUCCESS_ICON = (
362
+ /* html */
363
+ `
364
+ <svg class="eudi-success-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
365
+ <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
366
+ <path d="M8 12l3 3 5-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
367
+ </svg>
368
+ `
369
+ );
370
+ var ERROR_ICON = (
371
+ /* html */
372
+ `
373
+ <svg class="eudi-error-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
374
+ <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
375
+ <path d="M15 9l-6 6M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
376
+ </svg>
377
+ `
378
+ );
379
+ var EXPIRED_ICON = (
380
+ /* html */
381
+ `
382
+ <svg class="eudi-error-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
383
+ <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
384
+ <path d="M12 6v6l4 2" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
385
+ </svg>
386
+ `
387
+ );
388
+ var WALLET_ICON = (
389
+ /* html */
390
+ `
391
+ <svg class="eudi-waiting-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
392
+ <rect x="2" y="6" width="20" height="14" rx="2" stroke="currentColor" stroke-width="2"/>
393
+ <path d="M2 10h20" stroke="currentColor" stroke-width="2"/>
394
+ <circle cx="17" cy="14" r="1.5" fill="currentColor"/>
395
+ </svg>
396
+ `
397
+ );
398
+ var WARNING_ICON = (
399
+ /* html */
400
+ `
401
+ <svg class="eudi-warning-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
402
+ <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"/>
403
+ </svg>
404
+ `
405
+ );
406
+ function getStateId(status) {
407
+ return `eudi-state-${status}`;
408
+ }
409
+ function renderIdle() {
410
+ return (
411
+ /* html */
412
+ `
413
+ <div id="${getStateId("idle")}" class="eudi-state" data-active>
414
+ <button class="eudi-start-btn" type="button" data-action="start">
415
+ ${EU_STARS_ICON}
416
+ <span>Verify with EU Wallet</span>
417
+ </button>
418
+ </div>
419
+ `
420
+ );
421
+ }
422
+ function renderLoading() {
423
+ return (
424
+ /* html */
425
+ `
426
+ <div id="${getStateId("loading")}" class="eudi-state">
427
+ <div class="eudi-loading">
428
+ <div class="eudi-spinner" aria-hidden="true"></div>
429
+ <p>Loading...</p>
430
+ </div>
431
+ </div>
432
+ `
433
+ );
434
+ }
435
+ function renderShowQR() {
436
+ return (
437
+ /* html */
438
+ `
439
+ <div id="${getStateId("showQR")}" class="eudi-state">
440
+ <div class="eudi-qr">
441
+ <img class="eudi-qr-img" src="" alt="Scan with EUDI Wallet" />
442
+ <p class="eudi-qr-text">Scan with your EU Digital Identity Wallet</p>
443
+ <button class="eudi-cancel-btn" type="button" data-action="cancel">Cancel</button>
444
+ </div>
445
+ </div>
446
+ `
447
+ );
448
+ }
449
+ function renderWaitingForWallet() {
450
+ return (
451
+ /* html */
452
+ `
453
+ <div id="${getStateId("waitingForWallet")}" class="eudi-state">
454
+ <div class="eudi-waiting">
455
+ ${WALLET_ICON}
456
+ <p class="eudi-waiting-text">Waiting for wallet approval...</p>
457
+ <button class="eudi-cancel-btn" type="button" data-action="cancel">Cancel</button>
458
+ </div>
459
+ </div>
460
+ `
461
+ );
462
+ }
463
+ function renderVerified() {
464
+ return (
465
+ /* html */
466
+ `
467
+ <div id="${getStateId("verified")}" class="eudi-state">
468
+ <div class="eudi-success">
469
+ ${SUCCESS_ICON}
470
+ <p class="eudi-success-text">Verified</p>
471
+ </div>
472
+ </div>
473
+ `
474
+ );
475
+ }
476
+ function renderRejected() {
477
+ return (
478
+ /* html */
479
+ `
480
+ <div id="${getStateId("rejected")}" class="eudi-state">
481
+ <div class="eudi-error">
482
+ ${ERROR_ICON}
483
+ <p class="eudi-error-text">Verification declined</p>
484
+ <p class="eudi-error-detail"></p>
485
+ <button class="eudi-retry-btn" type="button" data-action="reset">Try again</button>
486
+ </div>
487
+ </div>
488
+ `
489
+ );
490
+ }
491
+ function renderExpired() {
492
+ return (
493
+ /* html */
494
+ `
495
+ <div id="${getStateId("expired")}" class="eudi-state">
496
+ <div class="eudi-error">
497
+ ${EXPIRED_ICON}
498
+ <p class="eudi-error-text">Session expired</p>
499
+ <button class="eudi-retry-btn" type="button" data-action="reset">Try again</button>
500
+ </div>
501
+ </div>
502
+ `
503
+ );
504
+ }
505
+ function renderError() {
506
+ return (
507
+ /* html */
508
+ `
509
+ <div id="${getStateId("error")}" class="eudi-state">
510
+ <div class="eudi-error">
511
+ ${ERROR_ICON}
512
+ <p class="eudi-error-text">Verification failed</p>
513
+ <p class="eudi-error-detail"></p>
514
+ <button class="eudi-retry-btn" type="button" data-action="reset">Try again</button>
515
+ </div>
516
+ </div>
517
+ `
518
+ );
519
+ }
520
+ function renderDemoBanner() {
521
+ return (
522
+ /* html */
523
+ `
524
+ <div class="eudi-demo-banner" role="status" aria-live="polite" hidden>
525
+ ${WARNING_ICON}
526
+ <span>Demo mode \u2014 credentials are simulated</span>
527
+ </div>
528
+ `
529
+ );
530
+ }
531
+ function renderWidget() {
532
+ return (
533
+ /* html */
534
+ `
535
+ <div class="eudi-widget" role="region" aria-label="Identity verification">
536
+ <div class="eudi-sr-only" aria-live="polite" aria-atomic="true"></div>
537
+ ${renderDemoBanner()}
538
+ ${renderIdle()}
539
+ ${renderLoading()}
540
+ ${renderShowQR()}
541
+ ${renderWaitingForWallet()}
542
+ ${renderVerified()}
543
+ ${renderRejected()}
544
+ ${renderExpired()}
545
+ ${renderError()}
546
+ </div>
547
+ `
548
+ );
549
+ }
550
+ function updateWidgetState(container, state) {
551
+ const stateContainers = container.querySelectorAll(".eudi-state");
552
+ for (const stateEl of stateContainers) {
553
+ if (stateEl.id === getStateId(state.status)) {
554
+ stateEl.setAttribute("data-active", "");
555
+ } else {
556
+ stateEl.removeAttribute("data-active");
557
+ }
558
+ }
559
+ if (state.status === "showQR") {
560
+ const img = container.querySelector(".eudi-qr-img");
561
+ if (img && "qrDataUrl" in state) {
562
+ img.src = state.qrDataUrl;
563
+ }
564
+ }
565
+ if (state.status === "rejected" && "error" in state && state.error) {
566
+ const detail = container.querySelector(`#${getStateId("rejected")} .eudi-error-detail`);
567
+ if (detail) {
568
+ detail.textContent = state.error;
569
+ }
570
+ }
571
+ if (state.status === "error" && "error" in state) {
572
+ const detail = container.querySelector(`#${getStateId("error")} .eudi-error-detail`);
573
+ if (detail) {
574
+ detail.textContent = state.error;
575
+ }
576
+ }
577
+ }
578
+
579
+ // src/a11y.ts
580
+ var FOCUSABLE_SELECTORS = [
581
+ "button:not([disabled])",
582
+ '[tabindex]:not([tabindex="-1"])',
583
+ "a[href]",
584
+ "input:not([disabled])"
585
+ ].join(", ");
586
+ function getFocusableElements(container) {
587
+ return Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS));
588
+ }
589
+ function createFocusTrap(container) {
590
+ let previouslyFocused = null;
591
+ function handleKeyDown(event) {
592
+ if (event.key !== "Tab") return;
593
+ const focusable = getFocusableElements(container);
594
+ if (focusable.length === 0) return;
595
+ const firstElement = focusable[0];
596
+ const lastElement = focusable[focusable.length - 1];
597
+ if (event.shiftKey && document.activeElement === firstElement) {
598
+ event.preventDefault();
599
+ lastElement.focus();
600
+ } else if (!event.shiftKey && document.activeElement === lastElement) {
601
+ event.preventDefault();
602
+ firstElement.focus();
603
+ }
604
+ }
605
+ function activate() {
606
+ previouslyFocused = document.activeElement;
607
+ container.addEventListener("keydown", handleKeyDown);
608
+ const focusable = getFocusableElements(container);
609
+ if (focusable.length > 0) {
610
+ focusable[0].focus();
611
+ }
612
+ }
613
+ function deactivate() {
614
+ container.removeEventListener("keydown", handleKeyDown);
615
+ if (previouslyFocused && typeof previouslyFocused.focus === "function") {
616
+ previouslyFocused.focus();
617
+ }
618
+ }
619
+ activate();
620
+ return deactivate;
621
+ }
622
+ function announce(liveRegion, message, priority = "polite") {
623
+ liveRegion.setAttribute("aria-live", priority);
624
+ liveRegion.textContent = "";
625
+ requestAnimationFrame(() => {
626
+ liveRegion.textContent = message;
627
+ });
628
+ }
629
+ function clearAnnouncement(liveRegion) {
630
+ liveRegion.textContent = "";
631
+ }
632
+ var STATE_MESSAGES = {
633
+ idle: "",
634
+ loading: "Loading verification session...",
635
+ showQR: "QR code ready. Scan with your EU Digital Identity Wallet.",
636
+ waitingForWallet: "Waiting for wallet approval...",
637
+ verified: "Identity verified successfully.",
638
+ rejected: "Verification was declined.",
639
+ expired: "Verification session expired.",
640
+ error: "Verification error occurred."
641
+ };
642
+ function getAnnouncementPriority(status) {
643
+ switch (status) {
644
+ case "verified":
645
+ case "rejected":
646
+ case "expired":
647
+ case "error":
648
+ return "assertive";
649
+ default:
650
+ return "polite";
651
+ }
652
+ }
653
+
654
+ // src/element.ts
655
+ var OBSERVED_ATTRIBUTES = ["api-url", "request", "auto-start"];
656
+ var EudiVerifyElement = class extends HTMLElement {
657
+ static get observedAttributes() {
658
+ return OBSERVED_ATTRIBUTES;
659
+ }
660
+ #shadow;
661
+ #verification = null;
662
+ #unsubscribe = null;
663
+ #container = null;
664
+ #liveRegion = null;
665
+ #lastStatus = null;
666
+ #isDemo = null;
667
+ #demoBanner = null;
668
+ constructor() {
669
+ super();
670
+ this.#shadow = this.attachShadow({ mode: "open" });
671
+ }
672
+ /**
673
+ * Get the API URL attribute.
674
+ */
675
+ get apiUrl() {
676
+ return this.getAttribute("api-url") ?? "";
677
+ }
678
+ /**
679
+ * Set the API URL attribute.
680
+ */
681
+ set apiUrl(value) {
682
+ this.setAttribute("api-url", value);
683
+ }
684
+ /**
685
+ * Get the request attribute (JSON string).
686
+ */
687
+ get request() {
688
+ return this.getAttribute("request") ?? "";
689
+ }
690
+ /**
691
+ * Set the request attribute (JSON string).
692
+ */
693
+ set request(value) {
694
+ this.setAttribute("request", value);
695
+ }
696
+ /**
697
+ * Check if auto-start is enabled.
698
+ */
699
+ get autoStart() {
700
+ return this.hasAttribute("auto-start");
701
+ }
702
+ /**
703
+ * Set auto-start attribute.
704
+ */
705
+ set autoStart(value) {
706
+ if (value) {
707
+ this.setAttribute("auto-start", "");
708
+ } else {
709
+ this.removeAttribute("auto-start");
710
+ }
711
+ }
712
+ /**
713
+ * Current verification state (read-only).
714
+ */
715
+ get state() {
716
+ return this.#verification?.state ?? null;
717
+ }
718
+ connectedCallback() {
719
+ this.#render();
720
+ this.#setupEventListeners();
721
+ if (this.autoStart && this.apiUrl && this.request) {
722
+ this.start();
723
+ }
724
+ }
725
+ disconnectedCallback() {
726
+ this.#cleanup();
727
+ }
728
+ attributeChangedCallback(name, oldValue, newValue) {
729
+ if (oldValue === newValue) return;
730
+ if (name === "api-url" && this.#verification) {
731
+ this.#cleanup();
732
+ }
733
+ }
734
+ /**
735
+ * Start the verification flow.
736
+ */
737
+ start() {
738
+ if (!this.apiUrl) {
739
+ console.error("[eudi-verify] api-url attribute is required");
740
+ return;
741
+ }
742
+ let requestObj;
743
+ try {
744
+ requestObj = this.request ? JSON.parse(this.request) : {};
745
+ } catch {
746
+ console.error("[eudi-verify] Invalid JSON in request attribute");
747
+ this.#dispatchError("Invalid verification request");
748
+ return;
749
+ }
750
+ this.#ensureVerification();
751
+ this.#verification.start(requestObj);
752
+ }
753
+ /**
754
+ * Cancel the current verification.
755
+ */
756
+ cancel() {
757
+ this.#verification?.cancel();
758
+ }
759
+ /**
760
+ * Reset to idle state.
761
+ */
762
+ reset() {
763
+ this.#cleanup();
764
+ this.#updateState({ status: "idle" });
765
+ }
766
+ #render() {
767
+ this.#shadow.innerHTML = `
768
+ <style>${createStyles()}</style>
769
+ ${renderWidget()}
770
+ `;
771
+ this.#container = this.#shadow.querySelector(".eudi-widget");
772
+ this.#liveRegion = this.#shadow.querySelector("[aria-live]");
773
+ this.#demoBanner = this.#shadow.querySelector(".eudi-demo-banner");
774
+ }
775
+ #setupEventListeners() {
776
+ this.#shadow.addEventListener("click", (event) => {
777
+ const target = event.target;
778
+ const button = target.closest("[data-action]");
779
+ if (!button) return;
780
+ const action = button.dataset.action;
781
+ switch (action) {
782
+ case "start":
783
+ this.start();
784
+ break;
785
+ case "cancel":
786
+ this.cancel();
787
+ break;
788
+ case "reset":
789
+ this.reset();
790
+ this.start();
791
+ break;
792
+ }
793
+ });
794
+ }
795
+ #ensureVerification() {
796
+ if (this.#verification) return;
797
+ this.#verification = (0, import_client.createVerification)({
798
+ apiUrl: this.apiUrl
799
+ });
800
+ this.#unsubscribe = this.#verification.subscribe((state) => {
801
+ this.#handleStateChange(state);
802
+ });
803
+ this.#detectDemoMode();
804
+ }
805
+ async #detectDemoMode() {
806
+ if (this.#isDemo !== null || !this.apiUrl) return;
807
+ try {
808
+ const response = await fetch(`${this.apiUrl}/sessions`, {
809
+ method: "HEAD"
810
+ });
811
+ const mode = response.headers.get("X-Eudi-Mode");
812
+ this.#isDemo = mode === "demo";
813
+ this.#updateDemoBanner();
814
+ } catch {
815
+ this.#isDemo = false;
816
+ }
817
+ }
818
+ #updateDemoBanner() {
819
+ if (!this.#demoBanner) return;
820
+ if (this.#isDemo === true) {
821
+ this.#demoBanner.removeAttribute("hidden");
822
+ } else {
823
+ this.#demoBanner.setAttribute("hidden", "");
824
+ }
825
+ }
826
+ #handleStateChange(state) {
827
+ this.#updateState(state);
828
+ this.#dispatchStateChange(state);
829
+ switch (state.status) {
830
+ case "verified":
831
+ if ("token" in state && "claims" in state) {
832
+ this.#dispatchVerified(state.token, state.claims);
833
+ }
834
+ break;
835
+ case "rejected":
836
+ this.#dispatchRejected("error" in state ? state.error : void 0);
837
+ break;
838
+ case "expired":
839
+ this.#dispatchExpired();
840
+ break;
841
+ case "error":
842
+ if ("error" in state) {
843
+ this.#dispatchError(state.error);
844
+ }
845
+ break;
846
+ }
847
+ }
848
+ #updateState(state) {
849
+ if (!this.#container) return;
850
+ updateWidgetState(this.#container, state);
851
+ if (this.#liveRegion && state.status !== this.#lastStatus) {
852
+ const message = STATE_MESSAGES[state.status];
853
+ if (message) {
854
+ const priority = getAnnouncementPriority(state.status);
855
+ announce(this.#liveRegion, message, priority);
856
+ }
857
+ }
858
+ this.#lastStatus = state.status;
859
+ this.#manageFocus(state);
860
+ }
861
+ #manageFocus(state) {
862
+ if (!this.#container) return;
863
+ switch (state.status) {
864
+ case "showQR": {
865
+ const cancelBtn = this.#container.querySelector("#eudi-state-showQR .eudi-cancel-btn");
866
+ cancelBtn?.focus();
867
+ break;
868
+ }
869
+ case "verified":
870
+ case "rejected":
871
+ case "expired":
872
+ case "error": {
873
+ const retryBtn = this.#container.querySelector(
874
+ `#eudi-state-${state.status} .eudi-retry-btn`
875
+ );
876
+ retryBtn?.focus();
877
+ break;
878
+ }
879
+ }
880
+ }
881
+ #dispatchVerified(token, claims) {
882
+ this.dispatchEvent(
883
+ new CustomEvent("verified", {
884
+ bubbles: true,
885
+ composed: true,
886
+ detail: { token, claims }
887
+ })
888
+ );
889
+ }
890
+ #dispatchRejected(error) {
891
+ this.dispatchEvent(
892
+ new CustomEvent("rejected", {
893
+ bubbles: true,
894
+ composed: true,
895
+ detail: { error }
896
+ })
897
+ );
898
+ }
899
+ #dispatchExpired() {
900
+ this.dispatchEvent(
901
+ new CustomEvent("expired", {
902
+ bubbles: true,
903
+ composed: true,
904
+ detail: {}
905
+ })
906
+ );
907
+ }
908
+ #dispatchError(error) {
909
+ this.dispatchEvent(
910
+ new CustomEvent("error", {
911
+ bubbles: true,
912
+ composed: true,
913
+ detail: { error }
914
+ })
915
+ );
916
+ }
917
+ #dispatchStateChange(state) {
918
+ this.dispatchEvent(
919
+ new CustomEvent("state-change", {
920
+ bubbles: true,
921
+ composed: true,
922
+ detail: { state }
923
+ })
924
+ );
925
+ }
926
+ #cleanup() {
927
+ if (this.#unsubscribe) {
928
+ this.#unsubscribe();
929
+ this.#unsubscribe = null;
930
+ }
931
+ if (this.#verification) {
932
+ this.#verification.destroy();
933
+ this.#verification = null;
934
+ }
935
+ if (this.#liveRegion) {
936
+ clearAnnouncement(this.#liveRegion);
937
+ }
938
+ this.#lastStatus = null;
939
+ }
940
+ };
941
+
942
+ // src/index.ts
943
+ var VERSION = "0.1.0";
944
+ if (typeof customElements !== "undefined" && !customElements.get("eudi-verify")) {
945
+ customElements.define("eudi-verify", EudiVerifyElement);
946
+ }
947
+ // Annotate the CommonJS export names for ESM import in node:
948
+ 0 && (module.exports = {
949
+ CSS_VARIABLES,
950
+ EudiVerifyElement,
951
+ STATE_MESSAGES,
952
+ VERSION,
953
+ announce,
954
+ clearAnnouncement,
955
+ createFocusTrap,
956
+ createStyles,
957
+ getFocusableElements,
958
+ getStateId,
959
+ renderWidget,
960
+ updateWidgetState
961
+ });
962
+ //# sourceMappingURL=eudi-verify.cjs.map