@eudi-verify/client 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.
package/dist/index.js ADDED
@@ -0,0 +1,772 @@
1
+ // src/types.ts
2
+ var TERMINAL_STATUSES = Object.freeze([
3
+ "verified",
4
+ "rejected",
5
+ "expired",
6
+ "cancelled",
7
+ "error"
8
+ ]);
9
+ function isTerminalStatus(status) {
10
+ return TERMINAL_STATUSES.includes(status);
11
+ }
12
+
13
+ // src/errors.ts
14
+ var EudiClientError = class extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = "EudiClientError";
18
+ }
19
+ };
20
+ var NetworkError = class extends EudiClientError {
21
+ cause;
22
+ constructor(message, cause) {
23
+ super(message);
24
+ this.name = "NetworkError";
25
+ this.cause = cause;
26
+ }
27
+ };
28
+ var ApiResponseError = class extends EudiClientError {
29
+ statusCode;
30
+ errorCode;
31
+ details;
32
+ constructor(statusCode, response) {
33
+ super(response.message);
34
+ this.name = "ApiResponseError";
35
+ this.statusCode = statusCode;
36
+ this.errorCode = response.error;
37
+ this.details = response.details;
38
+ }
39
+ };
40
+ var SessionNotFoundError = class extends ApiResponseError {
41
+ sessionId;
42
+ constructor(sessionId) {
43
+ super(404, { error: "not_found", message: `Session ${sessionId} not found` });
44
+ this.name = "SessionNotFoundError";
45
+ this.sessionId = sessionId;
46
+ }
47
+ };
48
+ var RateLimitError = class extends ApiResponseError {
49
+ retryAfterMs;
50
+ constructor(retryAfterMs) {
51
+ super(429, { error: "rate_limit", message: "Rate limit exceeded" });
52
+ this.name = "RateLimitError";
53
+ this.retryAfterMs = retryAfterMs;
54
+ }
55
+ };
56
+ var VerificationCancelledError = class extends EudiClientError {
57
+ constructor() {
58
+ super("Verification was cancelled");
59
+ this.name = "VerificationCancelledError";
60
+ }
61
+ };
62
+ var SessionExpiredError = class extends EudiClientError {
63
+ constructor() {
64
+ super("Session expired");
65
+ this.name = "SessionExpiredError";
66
+ }
67
+ };
68
+
69
+ // src/api.ts
70
+ function createApiClient(config) {
71
+ const { baseUrl, timeoutMs = 3e4 } = config;
72
+ const fetchFn = config.fetch ?? fetch;
73
+ const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
74
+ async function request(method, path, body) {
75
+ const url = `${normalizedBaseUrl}${path}`;
76
+ const controller = new AbortController();
77
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
78
+ try {
79
+ const response = await fetchFn(url, {
80
+ method,
81
+ headers: {
82
+ "Content-Type": "application/json",
83
+ Accept: "application/json"
84
+ },
85
+ body: body ? JSON.stringify(body) : void 0,
86
+ signal: controller.signal
87
+ });
88
+ clearTimeout(timeoutId);
89
+ if (!response.ok) {
90
+ await handleErrorResponse(response, path);
91
+ }
92
+ const data = await response.json();
93
+ return data;
94
+ } catch (error) {
95
+ clearTimeout(timeoutId);
96
+ if (error instanceof ApiResponseError) {
97
+ throw error;
98
+ }
99
+ if (error instanceof Error) {
100
+ if (error.name === "AbortError") {
101
+ throw new NetworkError(`Request timeout after ${timeoutMs}ms`);
102
+ }
103
+ throw new NetworkError(`Network request failed: ${error.message}`, error);
104
+ }
105
+ throw new NetworkError("Unknown network error");
106
+ }
107
+ }
108
+ async function handleErrorResponse(response, path) {
109
+ const { status } = response;
110
+ if (status === 404) {
111
+ const sessionIdMatch = path.match(/\/sessions\/([^/]+)/);
112
+ if (sessionIdMatch) {
113
+ throw new SessionNotFoundError(sessionIdMatch[1]);
114
+ }
115
+ }
116
+ if (status === 429) {
117
+ const retryAfter = response.headers.get("Retry-After");
118
+ const retryAfterMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : void 0;
119
+ throw new RateLimitError(retryAfterMs);
120
+ }
121
+ let errorBody;
122
+ try {
123
+ errorBody = await response.json();
124
+ } catch {
125
+ errorBody = {
126
+ error: "unknown_error",
127
+ message: `HTTP ${status}`
128
+ };
129
+ }
130
+ throw new ApiResponseError(status, errorBody);
131
+ }
132
+ return {
133
+ async createSession(verificationRequest) {
134
+ return request("POST", "/sessions", { request: verificationRequest });
135
+ },
136
+ async getSession(sessionId) {
137
+ return request("GET", `/sessions/${encodeURIComponent(sessionId)}`);
138
+ },
139
+ async cancelSession(sessionId) {
140
+ return request(
141
+ "POST",
142
+ `/sessions/${encodeURIComponent(sessionId)}/cancel`
143
+ );
144
+ }
145
+ };
146
+ }
147
+
148
+ // src/polling.ts
149
+ var DEFAULT_CONFIG = {
150
+ initialIntervalMs: 1e3,
151
+ maxIntervalMs: 1e4,
152
+ backoffMultiplier: 2
153
+ };
154
+ function createPoller(fn, config = {}) {
155
+ const { initialIntervalMs, maxIntervalMs, backoffMultiplier } = {
156
+ ...DEFAULT_CONFIG,
157
+ ...config
158
+ };
159
+ let currentInterval = initialIntervalMs;
160
+ let timeoutId = null;
161
+ let running = false;
162
+ async function poll() {
163
+ if (!running) return;
164
+ try {
165
+ const shouldStop = await fn();
166
+ if (shouldStop || !running) {
167
+ running = false;
168
+ return;
169
+ }
170
+ } catch {
171
+ }
172
+ if (!running) return;
173
+ currentInterval = Math.min(
174
+ currentInterval * backoffMultiplier,
175
+ maxIntervalMs
176
+ );
177
+ timeoutId = setTimeout(() => {
178
+ void poll();
179
+ }, currentInterval);
180
+ }
181
+ return {
182
+ start() {
183
+ if (running) return;
184
+ running = true;
185
+ currentInterval = initialIntervalMs;
186
+ void poll();
187
+ },
188
+ stop() {
189
+ running = false;
190
+ if (timeoutId !== null) {
191
+ clearTimeout(timeoutId);
192
+ timeoutId = null;
193
+ }
194
+ },
195
+ reset() {
196
+ currentInterval = initialIntervalMs;
197
+ }
198
+ };
199
+ }
200
+
201
+ // src/qr.ts
202
+ var EC_LEVELS = { L: 0, M: 1, Q: 2, H: 3 };
203
+ var VERSION_TABLE = {
204
+ L: [
205
+ { version: 1, totalCodewords: 19, ecCodewordsPerBlock: 7, numBlocks: 1 },
206
+ { version: 2, totalCodewords: 34, ecCodewordsPerBlock: 10, numBlocks: 1 },
207
+ { version: 3, totalCodewords: 55, ecCodewordsPerBlock: 15, numBlocks: 1 },
208
+ { version: 4, totalCodewords: 80, ecCodewordsPerBlock: 20, numBlocks: 1 },
209
+ { version: 5, totalCodewords: 108, ecCodewordsPerBlock: 26, numBlocks: 1 },
210
+ { version: 6, totalCodewords: 136, ecCodewordsPerBlock: 18, numBlocks: 2 },
211
+ { version: 7, totalCodewords: 156, ecCodewordsPerBlock: 20, numBlocks: 2 },
212
+ { version: 8, totalCodewords: 194, ecCodewordsPerBlock: 24, numBlocks: 2 },
213
+ { version: 9, totalCodewords: 232, ecCodewordsPerBlock: 30, numBlocks: 2 },
214
+ { version: 10, totalCodewords: 274, ecCodewordsPerBlock: 18, numBlocks: 4 },
215
+ { version: 11, totalCodewords: 401, ecCodewordsPerBlock: 20, numBlocks: 4 },
216
+ { version: 12, totalCodewords: 466, ecCodewordsPerBlock: 24, numBlocks: 4 },
217
+ { version: 13, totalCodewords: 532, ecCodewordsPerBlock: 26, numBlocks: 4 },
218
+ { version: 14, totalCodewords: 588, ecCodewordsPerBlock: 30, numBlocks: 4 }
219
+ ],
220
+ M: [
221
+ { version: 1, totalCodewords: 16, ecCodewordsPerBlock: 10, numBlocks: 1 },
222
+ { version: 2, totalCodewords: 28, ecCodewordsPerBlock: 16, numBlocks: 1 },
223
+ { version: 3, totalCodewords: 44, ecCodewordsPerBlock: 26, numBlocks: 1 },
224
+ { version: 4, totalCodewords: 64, ecCodewordsPerBlock: 18, numBlocks: 2 },
225
+ { version: 5, totalCodewords: 86, ecCodewordsPerBlock: 24, numBlocks: 2 },
226
+ { version: 6, totalCodewords: 108, ecCodewordsPerBlock: 16, numBlocks: 4 },
227
+ { version: 7, totalCodewords: 124, ecCodewordsPerBlock: 18, numBlocks: 4 },
228
+ { version: 8, totalCodewords: 154, ecCodewordsPerBlock: 22, numBlocks: 4 },
229
+ { version: 9, totalCodewords: 182, ecCodewordsPerBlock: 22, numBlocks: 5 },
230
+ { version: 10, totalCodewords: 216, ecCodewordsPerBlock: 26, numBlocks: 5 }
231
+ ],
232
+ Q: [
233
+ { version: 1, totalCodewords: 13, ecCodewordsPerBlock: 13, numBlocks: 1 },
234
+ { version: 2, totalCodewords: 22, ecCodewordsPerBlock: 22, numBlocks: 1 },
235
+ { version: 3, totalCodewords: 34, ecCodewordsPerBlock: 18, numBlocks: 2 },
236
+ { version: 4, totalCodewords: 48, ecCodewordsPerBlock: 26, numBlocks: 2 },
237
+ { version: 5, totalCodewords: 62, ecCodewordsPerBlock: 18, numBlocks: 4 },
238
+ { version: 6, totalCodewords: 76, ecCodewordsPerBlock: 24, numBlocks: 4 },
239
+ { version: 7, totalCodewords: 88, ecCodewordsPerBlock: 18, numBlocks: 6 },
240
+ { version: 8, totalCodewords: 110, ecCodewordsPerBlock: 22, numBlocks: 6 },
241
+ { version: 9, totalCodewords: 132, ecCodewordsPerBlock: 20, numBlocks: 8 },
242
+ { version: 10, totalCodewords: 154, ecCodewordsPerBlock: 24, numBlocks: 8 }
243
+ ],
244
+ H: [
245
+ { version: 1, totalCodewords: 9, ecCodewordsPerBlock: 17, numBlocks: 1 },
246
+ { version: 2, totalCodewords: 16, ecCodewordsPerBlock: 28, numBlocks: 1 },
247
+ { version: 3, totalCodewords: 26, ecCodewordsPerBlock: 22, numBlocks: 2 },
248
+ { version: 4, totalCodewords: 36, ecCodewordsPerBlock: 16, numBlocks: 4 },
249
+ { version: 5, totalCodewords: 46, ecCodewordsPerBlock: 22, numBlocks: 4 },
250
+ { version: 6, totalCodewords: 60, ecCodewordsPerBlock: 28, numBlocks: 4 },
251
+ { version: 7, totalCodewords: 66, ecCodewordsPerBlock: 26, numBlocks: 5 },
252
+ { version: 8, totalCodewords: 86, ecCodewordsPerBlock: 26, numBlocks: 6 },
253
+ { version: 9, totalCodewords: 100, ecCodewordsPerBlock: 24, numBlocks: 8 },
254
+ { version: 10, totalCodewords: 122, ecCodewordsPerBlock: 28, numBlocks: 8 }
255
+ ]
256
+ };
257
+ var FORMAT_INFO_STRINGS = {
258
+ 0: [1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
259
+ 1: [1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1],
260
+ 2: [1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0],
261
+ 3: [1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1],
262
+ 4: [1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1],
263
+ 5: [1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0],
264
+ 6: [1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
265
+ 7: [1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0],
266
+ 8: [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
267
+ 9: [1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1],
268
+ 10: [1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0],
269
+ 11: [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1],
270
+ 12: [1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
271
+ 13: [1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0],
272
+ 14: [1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1],
273
+ 15: [1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0],
274
+ 16: [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1],
275
+ 17: [0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0],
276
+ 18: [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1],
277
+ 19: [0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0],
278
+ 20: [0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0],
279
+ 21: [0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1],
280
+ 22: [0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0],
281
+ 23: [0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1],
282
+ 24: [0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
283
+ 25: [0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0],
284
+ 26: [0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1],
285
+ 27: [0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0],
286
+ 28: [0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0],
287
+ 29: [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
288
+ 30: [0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0],
289
+ 31: [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1]
290
+ };
291
+ var ALIGNMENT_PATTERNS = [
292
+ [],
293
+ [],
294
+ [6, 18],
295
+ [6, 22],
296
+ [6, 26],
297
+ [6, 30],
298
+ [6, 34],
299
+ [6, 22, 38],
300
+ [6, 24, 42],
301
+ [6, 26, 46],
302
+ [6, 28, 50],
303
+ [6, 30, 54],
304
+ [6, 32, 58],
305
+ [6, 34, 62],
306
+ [6, 26, 46, 66]
307
+ ];
308
+ function getVersionInfo(dataLength, ecLevel) {
309
+ const table = VERSION_TABLE[ecLevel];
310
+ for (const info of table) {
311
+ const dataCodewords = info.totalCodewords - info.ecCodewordsPerBlock * info.numBlocks;
312
+ const maxBytes = dataCodewords - (info.version < 10 ? 2 : 3);
313
+ if (maxBytes >= dataLength) {
314
+ return info;
315
+ }
316
+ }
317
+ throw new Error(`Data too long for QR code (max ~270 bytes with ${ecLevel} EC)`);
318
+ }
319
+ function createByteData(data, version) {
320
+ const bytes = new TextEncoder().encode(data);
321
+ const bits = [];
322
+ bits.push(0, 1, 0, 0);
323
+ const countBits = version < 10 ? 8 : 16;
324
+ for (let i = countBits - 1; i >= 0; i--) {
325
+ bits.push(bytes.length >> i & 1);
326
+ }
327
+ for (const byte of bytes) {
328
+ for (let i = 7; i >= 0; i--) {
329
+ bits.push(byte >> i & 1);
330
+ }
331
+ }
332
+ bits.push(0, 0, 0, 0);
333
+ while (bits.length % 8 !== 0) {
334
+ bits.push(0);
335
+ }
336
+ const codewords = [];
337
+ for (let i = 0; i < bits.length; i += 8) {
338
+ let byte = 0;
339
+ for (let j = 0; j < 8; j++) {
340
+ byte = byte << 1 | (bits[i + j] ?? 0);
341
+ }
342
+ codewords.push(byte);
343
+ }
344
+ return codewords;
345
+ }
346
+ var GF_EXP = new Uint8Array(512);
347
+ var GF_LOG = new Uint8Array(256);
348
+ (function initGaloisField() {
349
+ let x = 1;
350
+ for (let i = 0; i < 255; i++) {
351
+ GF_EXP[i] = x;
352
+ GF_LOG[x] = i;
353
+ x <<= 1;
354
+ if (x & 256) x ^= 285;
355
+ }
356
+ for (let i = 255; i < 512; i++) {
357
+ GF_EXP[i] = GF_EXP[i - 255];
358
+ }
359
+ })();
360
+ function gfMul(a, b) {
361
+ if (a === 0 || b === 0) return 0;
362
+ return GF_EXP[GF_LOG[a] + GF_LOG[b]];
363
+ }
364
+ function generateECCodewords(data, numECCodewords) {
365
+ const gen = [1];
366
+ for (let i = 0; i < numECCodewords; i++) {
367
+ const newGen = new Array(gen.length + 1).fill(0);
368
+ for (let j = 0; j < gen.length; j++) {
369
+ newGen[j] ^= gen[j];
370
+ newGen[j + 1] ^= gfMul(gen[j], GF_EXP[i]);
371
+ }
372
+ gen.length = newGen.length;
373
+ for (let j = 0; j < newGen.length; j++) gen[j] = newGen[j];
374
+ }
375
+ const msg = [...data, ...new Array(numECCodewords).fill(0)];
376
+ for (let i = 0; i < data.length; i++) {
377
+ const coef = msg[i];
378
+ if (coef !== 0) {
379
+ for (let j = 0; j < gen.length; j++) {
380
+ msg[i + j] ^= gfMul(gen[j], coef);
381
+ }
382
+ }
383
+ }
384
+ return msg.slice(data.length);
385
+ }
386
+ function interleaveBlocks(dataCodewords, info) {
387
+ const { numBlocks, ecCodewordsPerBlock, totalCodewords } = info;
388
+ const totalDataCodewords = totalCodewords - numBlocks * ecCodewordsPerBlock;
389
+ const smallBlockSize = Math.floor(totalDataCodewords / numBlocks);
390
+ const largeBlockCount = totalDataCodewords % numBlocks;
391
+ const dataBlocks = [];
392
+ const ecBlocks = [];
393
+ let offset = 0;
394
+ for (let i = 0; i < numBlocks; i++) {
395
+ const blockSize = smallBlockSize + (i >= numBlocks - largeBlockCount ? 1 : 0);
396
+ const block = dataCodewords.slice(offset, offset + blockSize);
397
+ offset += blockSize;
398
+ dataBlocks.push(block);
399
+ ecBlocks.push(generateECCodewords(block, ecCodewordsPerBlock));
400
+ }
401
+ const result = [];
402
+ const maxDataLen = Math.max(...dataBlocks.map((b) => b.length));
403
+ for (let i = 0; i < maxDataLen; i++) {
404
+ for (const block of dataBlocks) {
405
+ if (i < block.length) result.push(block[i]);
406
+ }
407
+ }
408
+ for (let i = 0; i < ecCodewordsPerBlock; i++) {
409
+ for (const block of ecBlocks) {
410
+ result.push(block[i]);
411
+ }
412
+ }
413
+ return result;
414
+ }
415
+ function createMatrix(version) {
416
+ const size = version * 4 + 17;
417
+ const matrix = Array.from(
418
+ { length: size },
419
+ () => new Array(size).fill(null)
420
+ );
421
+ function setFinderPattern(row, col) {
422
+ for (let r = -1; r <= 7; r++) {
423
+ for (let c = -1; c <= 7; c++) {
424
+ const nr = row + r;
425
+ const nc = col + c;
426
+ if (nr < 0 || nr >= size || nc < 0 || nc >= size) continue;
427
+ const isOuter = r === -1 || r === 7 || c === -1 || c === 7;
428
+ const isInner = r >= 1 && r <= 5 && c >= 1 && c <= 5;
429
+ const isCore = r >= 2 && r <= 4 && c >= 2 && c <= 4;
430
+ matrix[nr][nc] = isOuter ? 0 : isCore ? 1 : isInner ? 0 : 1;
431
+ }
432
+ }
433
+ }
434
+ setFinderPattern(0, 0);
435
+ setFinderPattern(0, size - 7);
436
+ setFinderPattern(size - 7, 0);
437
+ for (let i = 8; i < size - 8; i++) {
438
+ matrix[6][i] = i % 2 === 0 ? 1 : 0;
439
+ matrix[i][6] = i % 2 === 0 ? 1 : 0;
440
+ }
441
+ matrix[size - 8][8] = 1;
442
+ if (version >= 2) {
443
+ const positions = ALIGNMENT_PATTERNS[version];
444
+ for (const row of positions) {
445
+ for (const col of positions) {
446
+ if (matrix[row][col] !== null) continue;
447
+ for (let r = -2; r <= 2; r++) {
448
+ for (let c = -2; c <= 2; c++) {
449
+ const isOuter = Math.abs(r) === 2 || Math.abs(c) === 2;
450
+ const isCore = r === 0 && c === 0;
451
+ matrix[row + r][col + c] = isOuter || isCore ? 1 : 0;
452
+ }
453
+ }
454
+ }
455
+ }
456
+ }
457
+ return matrix;
458
+ }
459
+ function placeData(matrix, codewords) {
460
+ const size = matrix.length;
461
+ let bitIndex = 0;
462
+ const bits = [];
463
+ for (const cw of codewords) {
464
+ for (let i = 7; i >= 0; i--) {
465
+ bits.push(cw >> i & 1);
466
+ }
467
+ }
468
+ let col = size - 1;
469
+ let goingUp = true;
470
+ while (col > 0) {
471
+ if (col === 6) col--;
472
+ for (let row = goingUp ? size - 1 : 0; goingUp ? row >= 0 : row < size; row += goingUp ? -1 : 1) {
473
+ for (const offset of [0, -1]) {
474
+ const c = col + offset;
475
+ if (c < 0 || matrix[row][c] !== null) continue;
476
+ matrix[row][c] = bitIndex < bits.length ? bits[bitIndex++] : 0;
477
+ }
478
+ }
479
+ col -= 2;
480
+ goingUp = !goingUp;
481
+ }
482
+ }
483
+ function applyMask(matrix, maskPattern) {
484
+ const size = matrix.length;
485
+ const maskFns = [
486
+ (r, c) => (r + c) % 2 === 0,
487
+ (r) => r % 2 === 0,
488
+ (_, c) => c % 3 === 0,
489
+ (r, c) => (r + c) % 3 === 0,
490
+ (r, c) => (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0,
491
+ (r, c) => r * c % 2 + r * c % 3 === 0,
492
+ (r, c) => (r * c % 2 + r * c % 3) % 2 === 0,
493
+ (r, c) => ((r + c) % 2 + r * c % 3) % 2 === 0
494
+ ];
495
+ const maskFn = maskFns[maskPattern];
496
+ const reserved = createReservedMask(size);
497
+ for (let r = 0; r < size; r++) {
498
+ for (let c = 0; c < size; c++) {
499
+ if (!reserved[r][c] && maskFn(r, c)) {
500
+ matrix[r][c] ^= 1;
501
+ }
502
+ }
503
+ }
504
+ }
505
+ function createReservedMask(size) {
506
+ const reserved = Array.from(
507
+ { length: size },
508
+ () => new Array(size).fill(false)
509
+ );
510
+ for (let i = 0; i < 8; i++) {
511
+ for (let j = 0; j < 8; j++) {
512
+ reserved[i][j] = true;
513
+ reserved[i][size - 8 + j] = true;
514
+ reserved[size - 8 + i][j] = true;
515
+ }
516
+ }
517
+ for (let i = 0; i < 9; i++) {
518
+ reserved[i][8] = true;
519
+ reserved[8][i] = true;
520
+ reserved[size - 8 + i - 1][8] = true;
521
+ reserved[8][size - 8 + i - 1] = true;
522
+ }
523
+ for (let i = 0; i < size; i++) {
524
+ reserved[6][i] = true;
525
+ reserved[i][6] = true;
526
+ }
527
+ return reserved;
528
+ }
529
+ function addFormatInfo(matrix, ecLevel, maskPattern) {
530
+ const formatIndex = EC_LEVELS[ecLevel] * 8 + maskPattern;
531
+ const bits = FORMAT_INFO_STRINGS[formatIndex];
532
+ const size = matrix.length;
533
+ for (let i = 0; i < 6; i++) {
534
+ matrix[8][i] = bits[i];
535
+ matrix[i][8] = bits[14 - i];
536
+ }
537
+ matrix[8][7] = bits[6];
538
+ matrix[8][8] = bits[7];
539
+ matrix[7][8] = bits[8];
540
+ for (let i = 0; i < 7; i++) {
541
+ matrix[8][size - 1 - i] = bits[14 - i];
542
+ matrix[size - 1 - i][8] = bits[i];
543
+ }
544
+ matrix[size - 8][8] = bits[7];
545
+ }
546
+ function evaluatePenalty(matrix) {
547
+ const size = matrix.length;
548
+ let penalty = 0;
549
+ for (let r = 0; r < size; r++) {
550
+ let count = 1;
551
+ for (let c = 1; c < size; c++) {
552
+ if (matrix[r][c] === matrix[r][c - 1]) {
553
+ count++;
554
+ if (count === 5) penalty += 3;
555
+ else if (count > 5) penalty++;
556
+ } else {
557
+ count = 1;
558
+ }
559
+ }
560
+ }
561
+ for (let c = 0; c < size; c++) {
562
+ let count = 1;
563
+ for (let r = 1; r < size; r++) {
564
+ if (matrix[r][c] === matrix[r - 1][c]) {
565
+ count++;
566
+ if (count === 5) penalty += 3;
567
+ else if (count > 5) penalty++;
568
+ } else {
569
+ count = 1;
570
+ }
571
+ }
572
+ }
573
+ return penalty;
574
+ }
575
+ function generateQRMatrix(data, ecLevel) {
576
+ const info = getVersionInfo(data.length, ecLevel);
577
+ const dataCodewords = createByteData(data, info.version);
578
+ const totalDataCodewords = info.totalCodewords - info.numBlocks * info.ecCodewordsPerBlock;
579
+ while (dataCodewords.length < totalDataCodewords) {
580
+ dataCodewords.push(dataCodewords.length % 2 === 0 ? 236 : 17);
581
+ }
582
+ const codewords = interleaveBlocks(dataCodewords, info);
583
+ const baseMatrix = createMatrix(info.version);
584
+ placeData(baseMatrix, codewords);
585
+ let bestMatrix = null;
586
+ let bestPenalty = Infinity;
587
+ for (let mask = 0; mask < 8; mask++) {
588
+ const matrix = baseMatrix.map(
589
+ (row) => row.map((cell) => cell === null ? 0 : cell)
590
+ );
591
+ applyMask(matrix, mask);
592
+ addFormatInfo(matrix, ecLevel, mask);
593
+ const penalty = evaluatePenalty(matrix);
594
+ if (penalty < bestPenalty) {
595
+ bestPenalty = penalty;
596
+ bestMatrix = matrix;
597
+ }
598
+ }
599
+ return bestMatrix;
600
+ }
601
+ function generateQRSvg(data, options = {}) {
602
+ const { size = 200, errorCorrection = "L", quietZone = 4 } = options;
603
+ const matrix = generateQRMatrix(data, errorCorrection);
604
+ const moduleCount = matrix.length;
605
+ const totalModules = moduleCount + quietZone * 2;
606
+ const moduleSize = size / totalModules;
607
+ let paths = "";
608
+ for (let r = 0; r < moduleCount; r++) {
609
+ for (let c = 0; c < moduleCount; c++) {
610
+ if (matrix[r][c] === 1) {
611
+ const x = (c + quietZone) * moduleSize;
612
+ const y = (r + quietZone) * moduleSize;
613
+ paths += `M${x},${y}h${moduleSize}v${moduleSize}h-${moduleSize}z`;
614
+ }
615
+ }
616
+ }
617
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}"><rect width="100%" height="100%" fill="white"/><path d="${paths}" fill="black"/></svg>`;
618
+ }
619
+ function generateQRDataUrl(data, options = {}) {
620
+ const svg = generateQRSvg(data, options);
621
+ const encoded = encodeURIComponent(svg).replace(/'/g, "%27").replace(/"/g, "%22");
622
+ return `data:image/svg+xml,${encoded}`;
623
+ }
624
+
625
+ // src/verification.ts
626
+ function createVerification(config) {
627
+ const { apiUrl, polling, qr, fetch: fetchFn } = config;
628
+ const client = createApiClient({ baseUrl: apiUrl, fetch: fetchFn });
629
+ const subscribers = /* @__PURE__ */ new Set();
630
+ let currentState = { status: "idle" };
631
+ let currentSessionId = null;
632
+ let poller = null;
633
+ function setState(newState) {
634
+ currentState = newState;
635
+ for (const callback of subscribers) {
636
+ try {
637
+ callback(newState);
638
+ } catch {
639
+ }
640
+ }
641
+ }
642
+ function mapSessionToState(session) {
643
+ switch (session.status) {
644
+ case "pending":
645
+ if (session.qrUrl) {
646
+ return {
647
+ status: "showQR",
648
+ qrDataUrl: generateQRDataUrl(session.qrUrl, qr),
649
+ qrUrl: session.qrUrl,
650
+ sessionId: session.id
651
+ };
652
+ }
653
+ return { status: "loading" };
654
+ case "waiting_for_wallet":
655
+ return { status: "waitingForWallet", sessionId: session.id };
656
+ case "verified":
657
+ return {
658
+ status: "verified",
659
+ token: session.token,
660
+ claims: session.claims
661
+ };
662
+ case "rejected":
663
+ return { status: "rejected", error: session.error };
664
+ case "expired":
665
+ return { status: "expired" };
666
+ case "cancelled":
667
+ return { status: "idle" };
668
+ case "error":
669
+ return { status: "error", error: session.error ?? "Verification failed" };
670
+ default:
671
+ return { status: "error", error: "Unknown session status" };
672
+ }
673
+ }
674
+ async function pollSession() {
675
+ if (!currentSessionId) return true;
676
+ try {
677
+ const session = await client.getSession(currentSessionId);
678
+ const newState = mapSessionToState(session);
679
+ if (newState.status !== currentState.status || newState.status === "showQR" && currentState.status === "showQR" && newState.sessionId !== currentState.sessionId) {
680
+ setState(newState);
681
+ }
682
+ if (isTerminalStatus(session.status)) {
683
+ return true;
684
+ }
685
+ return false;
686
+ } catch (error) {
687
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
688
+ setState({ status: "error", error: errorMessage });
689
+ return true;
690
+ }
691
+ }
692
+ function stopPolling() {
693
+ if (poller) {
694
+ poller.stop();
695
+ poller = null;
696
+ }
697
+ }
698
+ function startPolling() {
699
+ stopPolling();
700
+ poller = createPoller(pollSession, polling);
701
+ poller.start();
702
+ }
703
+ return {
704
+ get state() {
705
+ return currentState;
706
+ },
707
+ async start(request) {
708
+ stopPolling();
709
+ currentSessionId = null;
710
+ setState({ status: "loading" });
711
+ try {
712
+ const session = await client.createSession(request);
713
+ currentSessionId = session.id;
714
+ const newState = mapSessionToState(session);
715
+ setState(newState);
716
+ if (!isTerminalStatus(session.status)) {
717
+ startPolling();
718
+ }
719
+ } catch (error) {
720
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
721
+ setState({ status: "error", error: errorMessage });
722
+ }
723
+ },
724
+ async cancel() {
725
+ if (!currentSessionId) return;
726
+ stopPolling();
727
+ try {
728
+ await client.cancelSession(currentSessionId);
729
+ currentSessionId = null;
730
+ setState({ status: "idle" });
731
+ } catch (error) {
732
+ const errorMessage = error instanceof Error ? error.message : "Failed to cancel";
733
+ setState({ status: "error", error: errorMessage });
734
+ }
735
+ },
736
+ destroy() {
737
+ stopPolling();
738
+ currentSessionId = null;
739
+ subscribers.clear();
740
+ },
741
+ subscribe(callback) {
742
+ subscribers.add(callback);
743
+ try {
744
+ callback(currentState);
745
+ } catch {
746
+ }
747
+ return () => {
748
+ subscribers.delete(callback);
749
+ };
750
+ }
751
+ };
752
+ }
753
+
754
+ // src/index.ts
755
+ var VERSION = "0.1.0";
756
+ export {
757
+ ApiResponseError,
758
+ EudiClientError,
759
+ NetworkError,
760
+ RateLimitError,
761
+ SessionExpiredError,
762
+ SessionNotFoundError,
763
+ TERMINAL_STATUSES,
764
+ VERSION,
765
+ VerificationCancelledError,
766
+ createApiClient,
767
+ createPoller,
768
+ createVerification,
769
+ generateQRDataUrl,
770
+ generateQRSvg,
771
+ isTerminalStatus
772
+ };