@polyguard/sdk 1.4.2 → 1.5.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,157 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for src/ticketService.js</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../prettify.css" />
9
+ <link rel="stylesheet" href="../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> ticketService.js</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">100% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>9/9</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">100% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>6/6</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">100% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>1/1</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">100% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>9/9</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line high'></div>
65
+ <pre><table class="coverage">
66
+ <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
+ <a name='L2'></a><a href='#L2'>2</a>
68
+ <a name='L3'></a><a href='#L3'>3</a>
69
+ <a name='L4'></a><a href='#L4'>4</a>
70
+ <a name='L5'></a><a href='#L5'>5</a>
71
+ <a name='L6'></a><a href='#L6'>6</a>
72
+ <a name='L7'></a><a href='#L7'>7</a>
73
+ <a name='L8'></a><a href='#L8'>8</a>
74
+ <a name='L9'></a><a href='#L9'>9</a>
75
+ <a name='L10'></a><a href='#L10'>10</a>
76
+ <a name='L11'></a><a href='#L11'>11</a>
77
+ <a name='L12'></a><a href='#L12'>12</a>
78
+ <a name='L13'></a><a href='#L13'>13</a>
79
+ <a name='L14'></a><a href='#L14'>14</a>
80
+ <a name='L15'></a><a href='#L15'>15</a>
81
+ <a name='L16'></a><a href='#L16'>16</a>
82
+ <a name='L17'></a><a href='#L17'>17</a>
83
+ <a name='L18'></a><a href='#L18'>18</a>
84
+ <a name='L19'></a><a href='#L19'>19</a>
85
+ <a name='L20'></a><a href='#L20'>20</a>
86
+ <a name='L21'></a><a href='#L21'>21</a>
87
+ <a name='L22'></a><a href='#L22'>22</a>
88
+ <a name='L23'></a><a href='#L23'>23</a>
89
+ <a name='L24'></a><a href='#L24'>24</a>
90
+ <a name='L25'></a><a href='#L25'>25</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
91
+ <span class="cline-any cline-yes">36x</span>
92
+ <span class="cline-any cline-neutral">&nbsp;</span>
93
+ <span class="cline-any cline-neutral">&nbsp;</span>
94
+ <span class="cline-any cline-neutral">&nbsp;</span>
95
+ <span class="cline-any cline-yes">36x</span>
96
+ <span class="cline-any cline-neutral">&nbsp;</span>
97
+ <span class="cline-any cline-neutral">&nbsp;</span>
98
+ <span class="cline-any cline-neutral">&nbsp;</span>
99
+ <span class="cline-any cline-neutral">&nbsp;</span>
100
+ <span class="cline-any cline-neutral">&nbsp;</span>
101
+ <span class="cline-any cline-yes">35x</span>
102
+ <span class="cline-any cline-yes">1x</span>
103
+ <span class="cline-any cline-neutral">&nbsp;</span>
104
+ <span class="cline-any cline-neutral">&nbsp;</span>
105
+ <span class="cline-any cline-yes">34x</span>
106
+ <span class="cline-any cline-yes">34x</span>
107
+ <span class="cline-any cline-neutral">&nbsp;</span>
108
+ <span class="cline-any cline-yes">34x</span>
109
+ <span class="cline-any cline-yes">1x</span>
110
+ <span class="cline-any cline-neutral">&nbsp;</span>
111
+ <span class="cline-any cline-neutral">&nbsp;</span>
112
+ <span class="cline-any cline-yes">33x</span>
113
+ <span class="cline-any cline-neutral">&nbsp;</span>
114
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">export async function fetchTicket({ apiServer, appId, link_uuid, requiredProofs, scanType }) {
115
+ const ticketUrl = link_uuid
116
+ ? `https://${apiServer}/v2/ticket/${appId}/${link_uuid}`
117
+ : `https://${apiServer}/v2/ticket/${appId}`;
118
+ &nbsp;
119
+ const ticketRes = await fetch(ticketUrl, {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ body: JSON.stringify({ requiredProofs, scanType }),
123
+ });
124
+ &nbsp;
125
+ if (!ticketRes.ok) {
126
+ throw new Error('Failed to get ticket');
127
+ }
128
+ &nbsp;
129
+ const ticketData = await ticketRes.json();
130
+ const ticket = ticketData.ticket;
131
+ &nbsp;
132
+ if (!ticket) {
133
+ throw new Error('No ticket returned from server');
134
+ }
135
+ &nbsp;
136
+ return ticket;
137
+ }
138
+ &nbsp;</pre></td></tr></table></pre>
139
+
140
+ <div class='push'></div><!-- for sticky footer -->
141
+ </div><!-- /wrapper -->
142
+ <div class='footer quiet pad2 space-top1 center small'>
143
+ Code coverage generated by
144
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
145
+ at 2026-05-17T02:18:23.420Z
146
+ </div>
147
+ <script src="../prettify.js"></script>
148
+ <script>
149
+ window.onload = function () {
150
+ prettyPrint();
151
+ };
152
+ </script>
153
+ <script src="../sorter.js"></script>
154
+ <script src="../block-navigation.js"></script>
155
+ </body>
156
+ </html>
157
+
@@ -0,0 +1,349 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for src/ui.js</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../prettify.css" />
9
+ <link rel="stylesheet" href="../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> ui.js</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">100% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>34/34</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">60% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>6/10</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">100% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>6/6</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">100% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>32/32</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line high'></div>
65
+ <pre><table class="coverage">
66
+ <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
+ <a name='L2'></a><a href='#L2'>2</a>
68
+ <a name='L3'></a><a href='#L3'>3</a>
69
+ <a name='L4'></a><a href='#L4'>4</a>
70
+ <a name='L5'></a><a href='#L5'>5</a>
71
+ <a name='L6'></a><a href='#L6'>6</a>
72
+ <a name='L7'></a><a href='#L7'>7</a>
73
+ <a name='L8'></a><a href='#L8'>8</a>
74
+ <a name='L9'></a><a href='#L9'>9</a>
75
+ <a name='L10'></a><a href='#L10'>10</a>
76
+ <a name='L11'></a><a href='#L11'>11</a>
77
+ <a name='L12'></a><a href='#L12'>12</a>
78
+ <a name='L13'></a><a href='#L13'>13</a>
79
+ <a name='L14'></a><a href='#L14'>14</a>
80
+ <a name='L15'></a><a href='#L15'>15</a>
81
+ <a name='L16'></a><a href='#L16'>16</a>
82
+ <a name='L17'></a><a href='#L17'>17</a>
83
+ <a name='L18'></a><a href='#L18'>18</a>
84
+ <a name='L19'></a><a href='#L19'>19</a>
85
+ <a name='L20'></a><a href='#L20'>20</a>
86
+ <a name='L21'></a><a href='#L21'>21</a>
87
+ <a name='L22'></a><a href='#L22'>22</a>
88
+ <a name='L23'></a><a href='#L23'>23</a>
89
+ <a name='L24'></a><a href='#L24'>24</a>
90
+ <a name='L25'></a><a href='#L25'>25</a>
91
+ <a name='L26'></a><a href='#L26'>26</a>
92
+ <a name='L27'></a><a href='#L27'>27</a>
93
+ <a name='L28'></a><a href='#L28'>28</a>
94
+ <a name='L29'></a><a href='#L29'>29</a>
95
+ <a name='L30'></a><a href='#L30'>30</a>
96
+ <a name='L31'></a><a href='#L31'>31</a>
97
+ <a name='L32'></a><a href='#L32'>32</a>
98
+ <a name='L33'></a><a href='#L33'>33</a>
99
+ <a name='L34'></a><a href='#L34'>34</a>
100
+ <a name='L35'></a><a href='#L35'>35</a>
101
+ <a name='L36'></a><a href='#L36'>36</a>
102
+ <a name='L37'></a><a href='#L37'>37</a>
103
+ <a name='L38'></a><a href='#L38'>38</a>
104
+ <a name='L39'></a><a href='#L39'>39</a>
105
+ <a name='L40'></a><a href='#L40'>40</a>
106
+ <a name='L41'></a><a href='#L41'>41</a>
107
+ <a name='L42'></a><a href='#L42'>42</a>
108
+ <a name='L43'></a><a href='#L43'>43</a>
109
+ <a name='L44'></a><a href='#L44'>44</a>
110
+ <a name='L45'></a><a href='#L45'>45</a>
111
+ <a name='L46'></a><a href='#L46'>46</a>
112
+ <a name='L47'></a><a href='#L47'>47</a>
113
+ <a name='L48'></a><a href='#L48'>48</a>
114
+ <a name='L49'></a><a href='#L49'>49</a>
115
+ <a name='L50'></a><a href='#L50'>50</a>
116
+ <a name='L51'></a><a href='#L51'>51</a>
117
+ <a name='L52'></a><a href='#L52'>52</a>
118
+ <a name='L53'></a><a href='#L53'>53</a>
119
+ <a name='L54'></a><a href='#L54'>54</a>
120
+ <a name='L55'></a><a href='#L55'>55</a>
121
+ <a name='L56'></a><a href='#L56'>56</a>
122
+ <a name='L57'></a><a href='#L57'>57</a>
123
+ <a name='L58'></a><a href='#L58'>58</a>
124
+ <a name='L59'></a><a href='#L59'>59</a>
125
+ <a name='L60'></a><a href='#L60'>60</a>
126
+ <a name='L61'></a><a href='#L61'>61</a>
127
+ <a name='L62'></a><a href='#L62'>62</a>
128
+ <a name='L63'></a><a href='#L63'>63</a>
129
+ <a name='L64'></a><a href='#L64'>64</a>
130
+ <a name='L65'></a><a href='#L65'>65</a>
131
+ <a name='L66'></a><a href='#L66'>66</a>
132
+ <a name='L67'></a><a href='#L67'>67</a>
133
+ <a name='L68'></a><a href='#L68'>68</a>
134
+ <a name='L69'></a><a href='#L69'>69</a>
135
+ <a name='L70'></a><a href='#L70'>70</a>
136
+ <a name='L71'></a><a href='#L71'>71</a>
137
+ <a name='L72'></a><a href='#L72'>72</a>
138
+ <a name='L73'></a><a href='#L73'>73</a>
139
+ <a name='L74'></a><a href='#L74'>74</a>
140
+ <a name='L75'></a><a href='#L75'>75</a>
141
+ <a name='L76'></a><a href='#L76'>76</a>
142
+ <a name='L77'></a><a href='#L77'>77</a>
143
+ <a name='L78'></a><a href='#L78'>78</a>
144
+ <a name='L79'></a><a href='#L79'>79</a>
145
+ <a name='L80'></a><a href='#L80'>80</a>
146
+ <a name='L81'></a><a href='#L81'>81</a>
147
+ <a name='L82'></a><a href='#L82'>82</a>
148
+ <a name='L83'></a><a href='#L83'>83</a>
149
+ <a name='L84'></a><a href='#L84'>84</a>
150
+ <a name='L85'></a><a href='#L85'>85</a>
151
+ <a name='L86'></a><a href='#L86'>86</a>
152
+ <a name='L87'></a><a href='#L87'>87</a>
153
+ <a name='L88'></a><a href='#L88'>88</a>
154
+ <a name='L89'></a><a href='#L89'>89</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
155
+ <span class="cline-any cline-neutral">&nbsp;</span>
156
+ <span class="cline-any cline-neutral">&nbsp;</span>
157
+ <span class="cline-any cline-yes">2x</span>
158
+ <span class="cline-any cline-neutral">&nbsp;</span>
159
+ <span class="cline-any cline-neutral">&nbsp;</span>
160
+ <span class="cline-any cline-neutral">&nbsp;</span>
161
+ <span class="cline-any cline-neutral">&nbsp;</span>
162
+ <span class="cline-any cline-neutral">&nbsp;</span>
163
+ <span class="cline-any cline-neutral">&nbsp;</span>
164
+ <span class="cline-any cline-neutral">&nbsp;</span>
165
+ <span class="cline-any cline-neutral">&nbsp;</span>
166
+ <span class="cline-any cline-neutral">&nbsp;</span>
167
+ <span class="cline-any cline-neutral">&nbsp;</span>
168
+ <span class="cline-any cline-neutral">&nbsp;</span>
169
+ <span class="cline-any cline-neutral">&nbsp;</span>
170
+ <span class="cline-any cline-neutral">&nbsp;</span>
171
+ <span class="cline-any cline-neutral">&nbsp;</span>
172
+ <span class="cline-any cline-neutral">&nbsp;</span>
173
+ <span class="cline-any cline-neutral">&nbsp;</span>
174
+ <span class="cline-any cline-neutral">&nbsp;</span>
175
+ <span class="cline-any cline-yes">31x</span>
176
+ <span class="cline-any cline-yes">31x</span>
177
+ <span class="cline-any cline-yes">31x</span>
178
+ <span class="cline-any cline-yes">31x</span>
179
+ <span class="cline-any cline-yes">31x</span>
180
+ <span class="cline-any cline-yes">31x</span>
181
+ <span class="cline-any cline-yes">31x</span>
182
+ <span class="cline-any cline-yes">31x</span>
183
+ <span class="cline-any cline-yes">31x</span>
184
+ <span class="cline-any cline-yes">31x</span>
185
+ <span class="cline-any cline-yes">31x</span>
186
+ <span class="cline-any cline-yes">31x</span>
187
+ <span class="cline-any cline-yes">31x</span>
188
+ <span class="cline-any cline-yes">31x</span>
189
+ <span class="cline-any cline-neutral">&nbsp;</span>
190
+ <span class="cline-any cline-neutral">&nbsp;</span>
191
+ <span class="cline-any cline-neutral">&nbsp;</span>
192
+ <span class="cline-any cline-neutral">&nbsp;</span>
193
+ <span class="cline-any cline-neutral">&nbsp;</span>
194
+ <span class="cline-any cline-neutral">&nbsp;</span>
195
+ <span class="cline-any cline-neutral">&nbsp;</span>
196
+ <span class="cline-any cline-neutral">&nbsp;</span>
197
+ <span class="cline-any cline-neutral">&nbsp;</span>
198
+ <span class="cline-any cline-neutral">&nbsp;</span>
199
+ <span class="cline-any cline-neutral">&nbsp;</span>
200
+ <span class="cline-any cline-neutral">&nbsp;</span>
201
+ <span class="cline-any cline-neutral">&nbsp;</span>
202
+ <span class="cline-any cline-neutral">&nbsp;</span>
203
+ <span class="cline-any cline-neutral">&nbsp;</span>
204
+ <span class="cline-any cline-neutral">&nbsp;</span>
205
+ <span class="cline-any cline-neutral">&nbsp;</span>
206
+ <span class="cline-any cline-neutral">&nbsp;</span>
207
+ <span class="cline-any cline-neutral">&nbsp;</span>
208
+ <span class="cline-any cline-neutral">&nbsp;</span>
209
+ <span class="cline-any cline-yes">31x</span>
210
+ <span class="cline-any cline-neutral">&nbsp;</span>
211
+ <span class="cline-any cline-neutral">&nbsp;</span>
212
+ <span class="cline-any cline-neutral">&nbsp;</span>
213
+ <span class="cline-any cline-yes">37x</span>
214
+ <span class="cline-any cline-neutral">&nbsp;</span>
215
+ <span class="cline-any cline-neutral">&nbsp;</span>
216
+ <span class="cline-any cline-neutral">&nbsp;</span>
217
+ <span class="cline-any cline-yes">3x</span>
218
+ <span class="cline-any cline-yes">3x</span>
219
+ <span class="cline-any cline-yes">3x</span>
220
+ <span class="cline-any cline-neutral">&nbsp;</span>
221
+ <span class="cline-any cline-neutral">&nbsp;</span>
222
+ <span class="cline-any cline-yes">3x</span>
223
+ <span class="cline-any cline-yes">3x</span>
224
+ <span class="cline-any cline-yes">3x</span>
225
+ <span class="cline-any cline-yes">3x</span>
226
+ <span class="cline-any cline-neutral">&nbsp;</span>
227
+ <span class="cline-any cline-yes">3x</span>
228
+ <span class="cline-any cline-yes">3x</span>
229
+ <span class="cline-any cline-yes">3x</span>
230
+ <span class="cline-any cline-neutral">&nbsp;</span>
231
+ <span class="cline-any cline-neutral">&nbsp;</span>
232
+ <span class="cline-any cline-neutral">&nbsp;</span>
233
+ <span class="cline-any cline-neutral">&nbsp;</span>
234
+ <span class="cline-any cline-neutral">&nbsp;</span>
235
+ <span class="cline-any cline-yes">3x</span>
236
+ <span class="cline-any cline-yes">3x</span>
237
+ <span class="cline-any cline-yes">3x</span>
238
+ <span class="cline-any cline-yes">3x</span>
239
+ <span class="cline-any cline-neutral">&nbsp;</span>
240
+ <span class="cline-any cline-yes">3x</span>
241
+ <span class="cline-any cline-neutral">&nbsp;</span>
242
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import QRCode from 'qrcode';
243
+ &nbsp;
244
+ // Animated spinner SVG
245
+ export const LOADING_SPINNER = `&lt;svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200" fill="none"&gt;
246
+ &lt;style&gt;
247
+ .spinner-circle {
248
+ animation: spin 1s linear infinite;
249
+ transform-origin: 100px 100px;
250
+ }
251
+ @keyframes spin {
252
+ from { transform: rotate(0deg); }
253
+ to { transform: rotate(360deg); }
254
+ }
255
+ &lt;/style&gt;
256
+ &lt;circle cx="100" cy="100" r="80" stroke="#e0e0e0" stroke-width="8" fill="none" /&gt;
257
+ &lt;circle cx="100" cy="100" r="80" stroke="#407796" stroke-width="8" fill="none"
258
+ stroke-dasharray="251.2" stroke-dashoffset="188.4"
259
+ class="spinner-circle" stroke-linecap="round" /&gt;
260
+ &lt;/svg&gt;`;
261
+ &nbsp;
262
+ export function buildModal() {
263
+ const modal = document.createElement('div');
264
+ modal.style.position = 'fixed';
265
+ modal.style.top = '0';
266
+ modal.style.left = '0';
267
+ modal.style.width = '100vw';
268
+ modal.style.height = '100vh';
269
+ modal.style.background = 'rgba(0,0,0,0.45)';
270
+ modal.style.display = 'flex';
271
+ modal.style.alignItems = 'flex-start';
272
+ modal.style.justifyContent = 'center';
273
+ modal.style.overflowY = 'auto';
274
+ modal.style.paddingTop = '24px';
275
+ modal.style.zIndex = '2147483647';
276
+ modal.innerHTML = `
277
+ &lt;div id="polyguard-modal-content" style="background: #fff; color: #222; border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.18); padding: 26px 19px 19px 19px; max-width: 312px; width: 100%; text-align: center; position: relative; z-index: 2147483647; font-family: 'IBM Plex Sans', 'Inter', 'Helvetica', 'Arial', sans-serif; margin: 0 auto; box-sizing: border-box;"&gt;
278
+ &lt;button id="polyguard-modal-close" style="position: absolute; top: 10px; right: 10px; background: none; border: none; font-size: 18px; color: #222; cursor: pointer; z-index: 2;"&gt;&amp;times;&lt;/button&gt;
279
+ &lt;h2 style="margin-top: 0; font-size: 1.04rem; font-weight: 700; color: #222;"&gt;Quick Identity Verification&lt;/h2&gt;
280
+ &lt;div style="font-size: 10px; color: #888; margin-bottom: 10px; font-weight: 500;"&gt;Powered by Polyguard&lt;/div&gt;
281
+ &lt;div id="polyguard-qr" style="width: 200px; height: 200px; margin: 0 auto 13px auto; display: flex; align-items: center; justify-content: center; background: #f4f8fb; border-radius: 10px;"&gt;&lt;/div&gt;
282
+ &lt;div style="margin-bottom: 10px; font-weight: 600; font-size: 13px; color: #222;"&gt;Scan this QR code to verify your identity.&lt;/div&gt;
283
+ &lt;ul style="text-align: left; margin: 0 0 13px 0; padding-left: 16px; font-size: 11px; color: #444;"&gt;
284
+ &lt;li&gt;We use the Polyguard service to verify your identity.&lt;/li&gt;
285
+ &lt;li&gt;If you do not have the Polyguard app, the QR code will redirect you to download it from the App Store or Google Play.&lt;/li&gt;
286
+ &lt;li&gt;Your credentials remain private on your device.&lt;/li&gt;
287
+ &lt;/ul&gt;
288
+ &lt;div style="display: flex; justify-content: center; gap: 10px; font-size: 10px; color: #5a6c7d; margin-bottom: 10px; flex-wrap: wrap;"&gt;
289
+ &lt;span&gt;&amp;#128274; Encrypted&lt;/span&gt;
290
+ &lt;span&gt;&amp;#128241; On-device&lt;/span&gt;
291
+ &lt;span&gt;&amp;#9202; Expires&lt;/span&gt;
292
+ &lt;/div&gt;
293
+ &lt;div id="polyguard-error" style="color: #b31d28; font-size: 11px; margin-bottom: 6px; display: none;"&gt;&lt;/div&gt;
294
+ &lt;button id="polyguard-modal-cancel" style="background: #407796; color: #fff; font-weight: 600; border-radius: 8px; border: none; padding: 8px 26px; font-size: 13px; cursor: pointer; margin-top: 6px; width: 100%; max-width: 192px;"&gt;Cancel&lt;/button&gt;
295
+ &lt;/div&gt;
296
+ `;
297
+ return modal;
298
+ }
299
+ &nbsp;
300
+ export function showSpinner(qrDiv) {
301
+ qrDiv.innerHTML = LOADING_SPINNER;
302
+ }
303
+ &nbsp;
304
+ export function renderMobileButton(qrDiv, qrUrl, isTargetMode) {
305
+ qrDiv.innerHTML = `&lt;button id="polyguard-open-app-button" style="background: #407796; color: #fff; font-weight: 600; border-radius: 8px; border: none; padding: 10px 32px; font-size: 16px; cursor: pointer;"&gt;Open Polyguard App&lt;/button&gt;`;
306
+ qrDiv.style.background = 'transparent';
307
+ qrDiv.querySelector('#polyguard-open-app-button').onclick = () =&gt; window.location.assign(qrUrl);
308
+ &nbsp;
309
+ // Update surrounding text only in modal mode
310
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!isTargetMode) {
311
+ const instructionText = qrDiv.nextElementSibling;
312
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (instructionText) {
313
+ instructionText.textContent = 'Tap the button to verify with the Polyguard app.';
314
+ }
315
+ const instructionList = instructionText.nextElementSibling;
316
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (instructionList &amp;&amp; instructionList.children[1]) {
317
+ instructionList.children[1].textContent = 'If you do not have the Polyguard app, you will be redirected to download it.';
318
+ }
319
+ }
320
+ }
321
+ &nbsp;
322
+ export function renderQRCode(qrDiv, qrUrl) {
323
+ const startTime = Date.now();
324
+ console.log('time before qr code', startTime);
325
+ QRCode.toString(qrUrl, { type: 'svg' }, (err, svg) =&gt; {
326
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!err) qrDiv.innerHTML = svg;
327
+ });
328
+ console.log('time to generate qr code', Date.now() - startTime);
329
+ }
330
+ &nbsp;</pre></td></tr></table></pre>
331
+
332
+ <div class='push'></div><!-- for sticky footer -->
333
+ </div><!-- /wrapper -->
334
+ <div class='footer quiet pad2 space-top1 center small'>
335
+ Code coverage generated by
336
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
337
+ at 2026-05-17T02:18:23.420Z
338
+ </div>
339
+ <script src="../prettify.js"></script>
340
+ <script>
341
+ window.onload = function () {
342
+ prettyPrint();
343
+ };
344
+ </script>
345
+ <script src="../sorter.js"></script>
346
+ <script src="../block-navigation.js"></script>
347
+ </body>
348
+ </html>
349
+
package/dist/sdk.esm.js CHANGED
@@ -10920,8 +10920,9 @@ function renderQRCode(qrDiv, qrUrl) {
10920
10920
  }
10921
10921
 
10922
10922
  // src/ticketService.js
10923
- async function fetchTicket({ apiServer, appId, link_uuid, requiredProofs, scanType }) {
10924
- const ticketUrl = link_uuid ? `https://${apiServer}/v2/ticket/${appId}/${link_uuid}` : `https://${apiServer}/v2/ticket/${appId}`;
10923
+ async function fetchTicket({ apiServer, proxyUrl, appId, link_uuid, requiredProofs, scanType }) {
10924
+ const base = proxyUrl ? proxyUrl.replace(/\/$/, "") : `https://${apiServer}`;
10925
+ const ticketUrl = link_uuid ? `${base}/v2/ticket/${appId}/${link_uuid}` : `${base}/v2/ticket/${appId}`;
10925
10926
  const ticketRes = await fetch(ticketUrl, {
10926
10927
  method: "POST",
10927
10928
  headers: { "Content-Type": "application/json" },
@@ -11033,15 +11034,24 @@ function handleWebSocketMessage(event, ctx) {
11033
11034
  var PolyguardWebsocketClientImpl = class {
11034
11035
  constructor(params = {}) {
11035
11036
  this.apiClient = new ApiClient_default();
11036
- const { apiKey, baseUrl, appId, apiServer, requiredProofs = ["Full Name"], scanType = "single", redirectUrl, callbackUrl, cookieName, link_uuid, sidebarUrl } = params;
11037
+ const { apiKey, baseUrl, appId, apiServer, proxyUrl, iKnowThisIsInsecure, requiredProofs = ["Full Name"], scanType = "single", redirectUrl, callbackUrl, cookieName, link_uuid, sidebarUrl } = params;
11037
11038
  this.sidebarUrl = sidebarUrl;
11038
- if (baseUrl) {
11039
+ if (apiKey && typeof window !== "undefined" && iKnowThisIsInsecure !== true) {
11040
+ throw new Error(
11041
+ "apiKey is forbidden in browser code; use proxyUrl with a server-side proxy (see @polyguard/sdk/server). Set iKnowThisIsInsecure: true to bypass."
11042
+ );
11043
+ }
11044
+ if (proxyUrl) {
11045
+ this.apiClient.basePath = proxyUrl.replace(/\/$/, "");
11046
+ } else if (baseUrl) {
11039
11047
  this.apiClient.basePath = baseUrl;
11040
11048
  }
11041
11049
  if (apiKey) {
11042
11050
  if (this.apiClient.authentications["bearerAuth"]) {
11043
11051
  this.apiClient.authentications["bearerAuth"].apiKey = apiKey;
11044
11052
  }
11053
+ this.apiClient.defaultHeaders = this.apiClient.defaultHeaders || {};
11054
+ this.apiClient.defaultHeaders["x-pg-api-key"] = apiKey;
11045
11055
  }
11046
11056
  for (const key in src_exports) {
11047
11057
  if (key.endsWith("Api")) {
@@ -11052,6 +11062,7 @@ var PolyguardWebsocketClientImpl = class {
11052
11062
  this.integrationType = "websocket";
11053
11063
  this.appId = appId;
11054
11064
  this.apiServer = apiServer;
11065
+ this.proxyUrl = proxyUrl;
11055
11066
  this.requiredProofs = requiredProofs;
11056
11067
  this.scanType = scanType;
11057
11068
  this.redirectUrl = redirectUrl;
@@ -11132,6 +11143,7 @@ var PolyguardWebsocketClientImpl = class {
11132
11143
  const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
11133
11144
  const newTicket = await fetchTicket({
11134
11145
  apiServer: this.apiServer,
11146
+ proxyUrl: this.proxyUrl,
11135
11147
  appId: this.appId,
11136
11148
  link_uuid: this.link_uuid,
11137
11149
  requiredProofs: this.requiredProofs,
package/dist/sdk.js CHANGED
@@ -10953,8 +10953,9 @@ var Polyguard = (() => {
10953
10953
  }
10954
10954
 
10955
10955
  // src/ticketService.js
10956
- async function fetchTicket({ apiServer, appId, link_uuid, requiredProofs, scanType }) {
10957
- const ticketUrl = link_uuid ? `https://${apiServer}/v2/ticket/${appId}/${link_uuid}` : `https://${apiServer}/v2/ticket/${appId}`;
10956
+ async function fetchTicket({ apiServer, proxyUrl, appId, link_uuid, requiredProofs, scanType }) {
10957
+ const base = proxyUrl ? proxyUrl.replace(/\/$/, "") : `https://${apiServer}`;
10958
+ const ticketUrl = link_uuid ? `${base}/v2/ticket/${appId}/${link_uuid}` : `${base}/v2/ticket/${appId}`;
10958
10959
  const ticketRes = await fetch(ticketUrl, {
10959
10960
  method: "POST",
10960
10961
  headers: { "Content-Type": "application/json" },
@@ -11066,15 +11067,24 @@ var Polyguard = (() => {
11066
11067
  var PolyguardWebsocketClientImpl = class {
11067
11068
  constructor(params = {}) {
11068
11069
  this.apiClient = new ApiClient_default();
11069
- const { apiKey, baseUrl, appId, apiServer, requiredProofs = ["Full Name"], scanType = "single", redirectUrl, callbackUrl, cookieName, link_uuid, sidebarUrl } = params;
11070
+ const { apiKey, baseUrl, appId, apiServer, proxyUrl, iKnowThisIsInsecure, requiredProofs = ["Full Name"], scanType = "single", redirectUrl, callbackUrl, cookieName, link_uuid, sidebarUrl } = params;
11070
11071
  this.sidebarUrl = sidebarUrl;
11071
- if (baseUrl) {
11072
+ if (apiKey && typeof window !== "undefined" && iKnowThisIsInsecure !== true) {
11073
+ throw new Error(
11074
+ "apiKey is forbidden in browser code; use proxyUrl with a server-side proxy (see @polyguard/sdk/server). Set iKnowThisIsInsecure: true to bypass."
11075
+ );
11076
+ }
11077
+ if (proxyUrl) {
11078
+ this.apiClient.basePath = proxyUrl.replace(/\/$/, "");
11079
+ } else if (baseUrl) {
11072
11080
  this.apiClient.basePath = baseUrl;
11073
11081
  }
11074
11082
  if (apiKey) {
11075
11083
  if (this.apiClient.authentications["bearerAuth"]) {
11076
11084
  this.apiClient.authentications["bearerAuth"].apiKey = apiKey;
11077
11085
  }
11086
+ this.apiClient.defaultHeaders = this.apiClient.defaultHeaders || {};
11087
+ this.apiClient.defaultHeaders["x-pg-api-key"] = apiKey;
11078
11088
  }
11079
11089
  for (const key in src_exports) {
11080
11090
  if (key.endsWith("Api")) {
@@ -11085,6 +11095,7 @@ var Polyguard = (() => {
11085
11095
  this.integrationType = "websocket";
11086
11096
  this.appId = appId;
11087
11097
  this.apiServer = apiServer;
11098
+ this.proxyUrl = proxyUrl;
11088
11099
  this.requiredProofs = requiredProofs;
11089
11100
  this.scanType = scanType;
11090
11101
  this.redirectUrl = redirectUrl;
@@ -11165,6 +11176,7 @@ var Polyguard = (() => {
11165
11176
  const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
11166
11177
  const newTicket = await fetchTicket({
11167
11178
  apiServer: this.apiServer,
11179
+ proxyUrl: this.proxyUrl,
11168
11180
  appId: this.appId,
11169
11181
  link_uuid: this.link_uuid,
11170
11182
  requiredProofs: this.requiredProofs,