@glideidentity/web-client-sdk 5.0.1 → 5.1.1-beta.1

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.
Files changed (97) hide show
  1. package/README.md +8 -108
  2. package/dist/adapters/angular/index.js +1 -0
  3. package/dist/adapters/angular/phone-auth.service.d.ts +18 -0
  4. package/dist/adapters/angular/phone-auth.service.js +26 -0
  5. package/dist/adapters/react/index.js +3 -0
  6. package/dist/adapters/react/useClient.js +1 -0
  7. package/dist/adapters/react/usePhoneAuth.js +16 -1
  8. package/dist/adapters/vanilla/client.js +1 -0
  9. package/dist/adapters/vanilla/index.js +1 -0
  10. package/dist/adapters/vanilla/phone-auth.js +31 -0
  11. package/dist/adapters/vue/index.js +4 -0
  12. package/dist/adapters/vue/useClient.js +5 -0
  13. package/dist/adapters/vue/usePhoneAuth.js +20 -1
  14. package/dist/browser/web-client-sdk.min.js +1 -2
  15. package/dist/browser.js +6 -0
  16. package/dist/core/client.js +12 -0
  17. package/dist/core/logger.js +81 -1
  18. package/dist/core/phone-auth/api-types.d.ts +1 -4
  19. package/dist/core/phone-auth/api-types.js +83 -0
  20. package/dist/core/phone-auth/client.js +374 -38
  21. package/dist/core/phone-auth/error-utils.js +83 -1
  22. package/dist/core/phone-auth/index.d.ts +1 -1
  23. package/dist/core/phone-auth/index.js +2 -2
  24. package/dist/core/phone-auth/status-types.d.ts +78 -0
  25. package/dist/core/phone-auth/status-types.js +17 -0
  26. package/dist/core/phone-auth/strategies/desktop.d.ts +2 -0
  27. package/dist/core/phone-auth/strategies/desktop.js +136 -13
  28. package/dist/core/phone-auth/strategies/index.d.ts +4 -0
  29. package/dist/core/phone-auth/strategies/index.js +4 -0
  30. package/dist/core/phone-auth/strategies/link.d.ts +2 -0
  31. package/dist/core/phone-auth/strategies/link.js +97 -13
  32. package/dist/core/phone-auth/strategies/ts43.d.ts +19 -0
  33. package/dist/core/phone-auth/strategies/ts43.js +33 -2
  34. package/dist/core/phone-auth/strategies/types.js +4 -0
  35. package/dist/core/phone-auth/type-guards.js +131 -0
  36. package/dist/core/phone-auth/types.d.ts +5 -0
  37. package/dist/core/phone-auth/types.js +32 -0
  38. package/dist/core/phone-auth/ui/mobile-debug-console.js +28 -2
  39. package/dist/core/phone-auth/ui/modal.d.ts +55 -33
  40. package/dist/core/phone-auth/ui/modal.js +422 -889
  41. package/dist/core/phone-auth/validation-utils.d.ts +0 -9
  42. package/dist/core/phone-auth/validation-utils.js +34 -25
  43. package/dist/core/version.js +2 -1
  44. package/dist/esm/adapters/angular/index.js +1 -0
  45. package/dist/esm/adapters/angular/phone-auth.service.d.ts +18 -0
  46. package/dist/esm/adapters/angular/phone-auth.service.js +26 -0
  47. package/dist/esm/adapters/react/index.js +3 -0
  48. package/dist/esm/adapters/react/useClient.js +1 -0
  49. package/dist/esm/adapters/react/usePhoneAuth.js +16 -1
  50. package/dist/esm/adapters/vanilla/client.js +1 -0
  51. package/dist/esm/adapters/vanilla/index.js +1 -0
  52. package/dist/esm/adapters/vanilla/phone-auth.d.ts +24 -0
  53. package/dist/esm/adapters/vanilla/phone-auth.js +31 -0
  54. package/dist/esm/adapters/vue/index.js +4 -0
  55. package/dist/esm/adapters/vue/useClient.js +5 -0
  56. package/dist/esm/adapters/vue/usePhoneAuth.js +20 -1
  57. package/dist/esm/browser.js +6 -0
  58. package/dist/esm/core/client.d.ts +10 -0
  59. package/dist/esm/core/client.js +12 -0
  60. package/dist/esm/core/logger.d.ts +53 -0
  61. package/dist/esm/core/logger.js +81 -1
  62. package/dist/esm/core/phone-auth/api-types.d.ts +313 -1
  63. package/dist/esm/core/phone-auth/api-types.js +83 -0
  64. package/dist/esm/core/phone-auth/client.d.ts +144 -0
  65. package/dist/esm/core/phone-auth/client.js +375 -39
  66. package/dist/esm/core/phone-auth/error-utils.d.ts +29 -0
  67. package/dist/esm/core/phone-auth/error-utils.js +83 -1
  68. package/dist/esm/core/phone-auth/index.d.ts +1 -1
  69. package/dist/esm/core/phone-auth/index.js +4 -2
  70. package/dist/esm/core/phone-auth/status-types.d.ts +78 -0
  71. package/dist/esm/core/phone-auth/status-types.js +17 -0
  72. package/dist/esm/core/phone-auth/strategies/desktop.d.ts +65 -0
  73. package/dist/esm/core/phone-auth/strategies/desktop.js +136 -13
  74. package/dist/esm/core/phone-auth/strategies/index.d.ts +4 -0
  75. package/dist/esm/core/phone-auth/strategies/index.js +4 -0
  76. package/dist/esm/core/phone-auth/strategies/link.d.ts +50 -0
  77. package/dist/esm/core/phone-auth/strategies/link.js +97 -13
  78. package/dist/esm/core/phone-auth/strategies/ts43.d.ts +19 -0
  79. package/dist/esm/core/phone-auth/strategies/ts43.js +33 -2
  80. package/dist/esm/core/phone-auth/strategies/types.d.ts +13 -0
  81. package/dist/esm/core/phone-auth/strategies/types.js +4 -0
  82. package/dist/esm/core/phone-auth/type-guards.d.ts +128 -0
  83. package/dist/esm/core/phone-auth/type-guards.js +131 -0
  84. package/dist/esm/core/phone-auth/types.d.ts +113 -0
  85. package/dist/esm/core/phone-auth/types.js +32 -0
  86. package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +4 -0
  87. package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +28 -2
  88. package/dist/esm/core/phone-auth/ui/modal.d.ts +68 -27
  89. package/dist/esm/core/phone-auth/ui/modal.js +422 -889
  90. package/dist/esm/core/phone-auth/validation-utils.d.ts +26 -4
  91. package/dist/esm/core/phone-auth/validation-utils.js +34 -24
  92. package/dist/esm/core/types.d.ts +35 -0
  93. package/dist/esm/core/version.js +2 -1
  94. package/dist/esm/index.js +9 -1
  95. package/dist/index.js +7 -0
  96. package/package.json +1 -1
  97. package/dist/browser/web-client-sdk.min.js.LICENSE.txt +0 -1
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Desktop Strategy Handler
3
+ * Handles QR code-based authentication for desktop browsers
4
+ * Manages QR code display and polling for authentication status
5
+ */
1
6
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
7
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
8
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -13,33 +18,49 @@ export class DesktopHandler {
13
18
  this.isCancelled = false;
14
19
  this.isPollingInProgress = false;
15
20
  }
21
+ /**
22
+ * Maps backend HTTP status codes to client status
23
+ * @param httpStatus HTTP status code from backend
24
+ * @param bodyStatus Optional status from response body (for 200 OK responses)
25
+ * @returns Mapped status string for client use
26
+ */
16
27
  mapBackendStatus(httpStatus, bodyStatus) {
17
28
  switch (httpStatus) {
18
29
  case 200:
30
+ // For 200 OK, check the body status
19
31
  return bodyStatus === 'completed' ? 'authenticated' : 'pending';
20
32
  case 410:
21
33
  return 'expired';
22
34
  case 422:
23
- return 'error';
35
+ return 'error'; // Failed or cancelled
24
36
  case 404:
25
- return 'error';
37
+ return 'error'; // Session not found
26
38
  case 400:
27
- return 'error';
39
+ return 'error'; // Invalid session key
28
40
  default:
29
41
  return 'error';
30
42
  }
31
43
  }
44
+ /**
45
+ * Invoke desktop authentication with QR code
46
+ * Returns QR code data for display and starts polling if endpoint is provided
47
+ */
32
48
  invoke(data, options) {
33
49
  return __awaiter(this, void 0, void 0, function* () {
34
50
  const desktopData = data.data;
51
+ // Extract QR code from nested or flat structure, checking multiple field names
35
52
  let qrCode;
36
53
  let sessionId;
37
54
  let pollingEndpoint;
38
55
  let pollingInterval;
39
56
  let expiresIn;
57
+ // Check nested structure first (data.data.qr_code_image or data.data.qr_code)
40
58
  if (desktopData && desktopData.data && typeof desktopData.data === 'object') {
41
59
  const innerData = desktopData.data;
60
+ // Try to extract from inner data
61
+ // Support both single QR (qr_code_image) and dual-platform QR (ios/android)
42
62
  if (innerData.ios_qr_image && innerData.android_qr_image) {
63
+ // Dual-platform QR format - both must be present
43
64
  qrCode = {
44
65
  iosQRCode: innerData.ios_qr_image,
45
66
  androidQRCode: innerData.android_qr_image,
@@ -48,9 +69,11 @@ export class DesktopHandler {
48
69
  };
49
70
  }
50
71
  else if (innerData.ios_qr_image || innerData.android_qr_image) {
72
+ // Only one platform QR - use as single QR format
51
73
  qrCode = innerData.ios_qr_image || innerData.android_qr_image;
52
74
  }
53
75
  else {
76
+ // Single QR format (legacy)
54
77
  qrCode = innerData.qr_code_image || innerData.qr_code;
55
78
  }
56
79
  sessionId = innerData.session_id;
@@ -58,6 +81,7 @@ export class DesktopHandler {
58
81
  pollingInterval = innerData.polling_interval;
59
82
  expiresIn = innerData.expires_in;
60
83
  }
84
+ // Fall back to flat structure if no QR code found
61
85
  if (!qrCode && desktopData) {
62
86
  qrCode = desktopData.qr_code_image || desktopData.qr_code;
63
87
  sessionId = sessionId || desktopData.session_id;
@@ -65,47 +89,65 @@ export class DesktopHandler {
65
89
  pollingInterval = pollingInterval || desktopData.polling_interval;
66
90
  expiresIn = expiresIn || desktopData.expires_in;
67
91
  }
92
+ // Validate QR code exists
68
93
  if (!qrCode) {
69
94
  throw new Error('Invalid desktop authentication data: missing QR code');
70
95
  }
96
+ // Validate session ID exists
71
97
  if (!sessionId) {
72
98
  throw new Error('Invalid desktop authentication data: missing session ID');
73
99
  }
100
+ // Notify that QR code is ready
74
101
  if (options === null || options === void 0 ? void 0 : options.onQRCodeReady) {
75
102
  options.onQRCodeReady(qrCode);
76
103
  }
104
+ // Use polling endpoint with this priority:
105
+ // 1. Developer-provided endpoint from options (highest priority)
106
+ // 2. Backend-provided endpoint
107
+ // 3. Hardcoded fallback
77
108
  console.log('[Desktop QR] Polling endpoint selection:');
78
109
  console.log(' - options?.pollingEndpoint:', options === null || options === void 0 ? void 0 : options.pollingEndpoint);
79
110
  console.log(' - backend pollingEndpoint:', pollingEndpoint);
80
111
  let finalPollingEndpoint = options === null || options === void 0 ? void 0 : options.pollingEndpoint;
81
112
  let endpointSource = 'options';
82
113
  if (!finalPollingEndpoint) {
83
- finalPollingEndpoint = pollingEndpoint;
114
+ finalPollingEndpoint = pollingEndpoint; // Backend-provided status_url
84
115
  endpointSource = 'backend';
85
116
  }
86
117
  if (!finalPollingEndpoint) {
118
+ // Use hardcoded fallback - this will be constructed in the polling function
87
119
  console.log('[Desktop QR] No polling endpoint provided, will use hardcoded fallback');
88
120
  endpointSource = 'fallback';
89
121
  }
90
122
  console.log('[Desktop QR] Selected endpoint:', finalPollingEndpoint, 'from source:', endpointSource);
123
+ // Start polling for authentication status
91
124
  const finalPollingInterval = (options === null || options === void 0 ? void 0 : options.pollingInterval) || pollingInterval || 2000;
92
- const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 30;
125
+ const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 30; // Default to 1 minute
93
126
  console.log(`[Desktop QR] Starting polling - endpoint source: ${endpointSource}, interval: ${finalPollingInterval}ms, max attempts: ${maxAttempts}`);
94
- return this.startPolling(finalPollingEndpoint || '', sessionId, finalPollingInterval, maxAttempts, expiresIn || 300, options, endpointSource);
127
+ return this.startPolling(finalPollingEndpoint || '', // Pass empty string if undefined, will use fallback
128
+ sessionId, finalPollingInterval, maxAttempts, expiresIn || 300, // Default 5 minutes expiry
129
+ options, endpointSource // Pass the endpoint source for logging
130
+ );
95
131
  });
96
132
  }
133
+ /**
134
+ * Start polling for authentication status
135
+ */
97
136
  startPolling(endpoint_1, sessionId_1, interval_1, maxAttempts_1, expiresIn_1, options_1) {
98
137
  return __awaiter(this, arguments, void 0, function* (endpoint, sessionId, interval, maxAttempts, expiresIn, options, endpointSource = 'unknown') {
99
138
  const startTime = Date.now();
100
139
  const expiryTime = startTime + (expiresIn * 1000);
101
140
  return new Promise((resolve, reject) => {
141
+ // Store the reject function so we can call it from cancel()
102
142
  this.pollingReject = reject;
103
143
  const poll = () => __awaiter(this, void 0, void 0, function* () {
144
+ // Skip if another poll is already in progress
104
145
  if (this.isPollingInProgress) {
105
146
  return;
106
147
  }
107
148
  try {
108
149
  this.isPollingInProgress = true;
150
+ // Check if cancelled
109
151
  if (this.isCancelled) {
110
152
  this.stopPolling();
111
153
  if (options === null || options === void 0 ? void 0 : options.onCancel) {
@@ -117,12 +159,14 @@ export class DesktopHandler {
117
159
  message: 'Authentication cancelled'
118
160
  });
119
161
  }
162
+ // Reject the promise with a cancellation error
120
163
  reject({
121
164
  code: 'USER_DENIED',
122
165
  message: 'Authentication cancelled by user'
123
166
  });
124
167
  return;
125
168
  }
169
+ // Check if expired
126
170
  if (Date.now() > expiryTime) {
127
171
  this.stopPolling();
128
172
  if (options === null || options === void 0 ? void 0 : options.onExpired) {
@@ -140,6 +184,7 @@ export class DesktopHandler {
140
184
  });
141
185
  return;
142
186
  }
187
+ // Check max attempts
143
188
  if (this.pollingAttempts >= maxAttempts) {
144
189
  this.stopPolling();
145
190
  if (options === null || options === void 0 ? void 0 : options.onTimeout) {
@@ -157,35 +202,58 @@ export class DesktopHandler {
157
202
  });
158
203
  return;
159
204
  }
205
+ // Build public status endpoint URL - use relative path for proxied requests
160
206
  let statusUrl;
207
+ // Extract base URL and construct public endpoint
161
208
  if (endpoint && (endpoint.startsWith('http://') || endpoint.startsWith('https://'))) {
162
- const url = new URL(endpoint);
163
- statusUrl = `${url.protocol}//${url.host}/public/public/status/${sessionId}`;
209
+ // Full URL provided - use as is
210
+ statusUrl = endpoint;
164
211
  }
165
212
  else if (endpoint && endpoint !== '') {
213
+ // Relative path provided (e.g. '/api/phone-auth/status')
214
+ // Append session ID to the provided endpoint
166
215
  statusUrl = `${endpoint}/${sessionId}`;
167
216
  }
168
217
  else {
218
+ // No endpoint provided - use hardcoded fallback
219
+ // This ensures it goes through the same domain and uses the app's API configuration
169
220
  statusUrl = `/api/phone-auth/status/${sessionId}`;
170
221
  }
222
+ // Poll the public endpoint - no authentication required
171
223
  console.log(`[Desktop QR] Polling status (attempt ${this.pollingAttempts}/${maxAttempts})`);
172
224
  console.log(`[Desktop QR] Using ${endpointSource} endpoint: ${statusUrl}`);
225
+ // Build headers
226
+ const headers = {
227
+ 'Accept': 'application/json'
228
+ };
229
+ // Add developer header if devEnv is set
230
+ if (options === null || options === void 0 ? void 0 : options.devEnv) {
231
+ headers['developer'] = options.devEnv;
232
+ console.log(`[Desktop QR] Adding developer header: ${options.devEnv}`);
233
+ }
173
234
  const response = yield fetch(statusUrl, {
174
235
  method: 'GET',
175
- headers: {
176
- 'Accept': 'application/json'
177
- }
236
+ headers
178
237
  });
179
238
  console.log(`[Desktop QR] Status response: ${response.status} ${response.statusText}`);
239
+ // Backend Status Code Mapping:
240
+ // - 200 OK: Session exists (body has 'pending' or 'completed' status)
241
+ // - 410 Gone: Session expired after timeout
242
+ // - 422 Unprocessable Entity: Authentication failed or cancelled
243
+ // - 404 Not Found: Session doesn't exist
244
+ // - 400 Bad Request: Invalid session key format
245
+ // Handle response based on HTTP status code
180
246
  if (response.status === 200) {
181
247
  const result = yield response.json();
182
248
  if (result.status === 'completed') {
249
+ // Authentication completed successfully
183
250
  this.stopPolling();
251
+ // Close the modal if it exists
184
252
  const modal = document.querySelector('[style*="position: fixed"]');
185
253
  if (modal && modal.querySelector('#desktop-auth-status')) {
186
254
  setTimeout(() => {
187
255
  modal.remove();
188
- }, 500);
256
+ }, 500); // Brief delay to show success message
189
257
  }
190
258
  if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
191
259
  options.onStatusUpdate({
@@ -194,7 +262,8 @@ export class DesktopHandler {
194
262
  data: result
195
263
  });
196
264
  }
197
- this.pollingReject = undefined;
265
+ // Return the session data for next steps
266
+ this.pollingReject = undefined; // Clear the reject function on success
198
267
  resolve({
199
268
  authenticated: true,
200
269
  credential: result.credential || result.session_key || sessionId,
@@ -208,6 +277,7 @@ export class DesktopHandler {
208
277
  });
209
278
  }
210
279
  else if (result.status === 'pending') {
280
+ // Continue polling
211
281
  this.pollingAttempts++;
212
282
  if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
213
283
  options.onStatusUpdate({
@@ -218,8 +288,10 @@ export class DesktopHandler {
218
288
  }
219
289
  }
220
290
  else if (response.status === 410) {
291
+ // Session expired
221
292
  this.stopPolling();
222
293
  const errorData = yield response.json().catch(() => ({ message: 'Session expired' }));
294
+ // Close the modal on error
223
295
  const modal = document.querySelector('[style*="position: fixed"]');
224
296
  if (modal && modal.querySelector('#desktop-auth-status')) {
225
297
  setTimeout(() => {
@@ -241,8 +313,10 @@ export class DesktopHandler {
241
313
  });
242
314
  }
243
315
  else if (response.status === 422) {
316
+ // Authentication failed
244
317
  this.stopPolling();
245
318
  const errorData = yield response.json().catch(() => ({ message: 'Authentication failed' }));
319
+ // Close the modal on error
246
320
  const modal = document.querySelector('[style*="position: fixed"]');
247
321
  if (modal && modal.querySelector('#desktop-auth-status')) {
248
322
  setTimeout(() => {
@@ -264,6 +338,7 @@ export class DesktopHandler {
264
338
  });
265
339
  }
266
340
  else if (response.status === 404) {
341
+ // Session not found
267
342
  this.stopPolling();
268
343
  if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
269
344
  options.onStatusUpdate({
@@ -277,6 +352,7 @@ export class DesktopHandler {
277
352
  });
278
353
  }
279
354
  else if (response.status === 400) {
355
+ // Invalid session key
280
356
  this.stopPolling();
281
357
  const errorData = yield response.json().catch(() => ({ message: 'Invalid session key' }));
282
358
  resolve({
@@ -285,10 +361,12 @@ export class DesktopHandler {
285
361
  });
286
362
  }
287
363
  else {
364
+ // Unexpected status - continue polling
288
365
  this.pollingAttempts++;
289
366
  }
290
367
  }
291
368
  catch (error) {
369
+ // Network or other error - continue polling
292
370
  this.pollingAttempts++;
293
371
  if (options === null || options === void 0 ? void 0 : options.onStatusUpdate) {
294
372
  options.onStatusUpdate({
@@ -298,14 +376,20 @@ export class DesktopHandler {
298
376
  }
299
377
  }
300
378
  finally {
379
+ // Always clear the polling flag when done
301
380
  this.isPollingInProgress = false;
302
381
  }
303
382
  });
383
+ // Start initial poll
304
384
  poll();
385
+ // Set up interval for subsequent polls
305
386
  this.pollingIntervalId = setInterval(poll, interval);
306
387
  });
307
388
  });
308
389
  }
390
+ /**
391
+ * Stop polling
392
+ */
309
393
  stopPolling() {
310
394
  if (this.pollingIntervalId) {
311
395
  console.log('[Desktop QR] Stopping polling');
@@ -314,10 +398,18 @@ export class DesktopHandler {
314
398
  }
315
399
  this.pollingAttempts = 0;
316
400
  this.isPollingInProgress = false;
401
+ // Don't clear pollingReject here - it's needed by cancel()
317
402
  }
403
+ /**
404
+ * Check if polling is currently active
405
+ */
318
406
  isPolling() {
319
407
  return this.pollingIntervalId !== undefined;
320
408
  }
409
+ /**
410
+ * Format response for backend processing
411
+ * Desktop strategy typically returns the credential from mobile authentication
412
+ */
321
413
  formatResponse(response) {
322
414
  if (!response.authenticated || !response.credential) {
323
415
  throw new Error('Authentication not completed');
@@ -327,23 +419,35 @@ export class DesktopHandler {
327
419
  session: response.session
328
420
  };
329
421
  }
422
+ /**
423
+ * Check if desktop authentication is supported
424
+ * Desktop auth with QR codes works in any modern browser
425
+ */
330
426
  isSupported() {
427
+ // Check for required browser APIs
331
428
  return typeof window !== 'undefined' &&
332
429
  typeof fetch !== 'undefined' &&
333
430
  typeof Promise !== 'undefined';
334
431
  }
432
+ /**
433
+ * Clean up resources (stop polling if active)
434
+ */
335
435
  cleanup() {
336
436
  this.stopPolling();
337
437
  this.isCancelled = false;
338
438
  this.onCancel = undefined;
339
439
  this.pollingReject = undefined;
340
440
  }
441
+ /**
442
+ * Cancel the ongoing authentication
443
+ */
341
444
  cancel() {
342
445
  var _a;
343
446
  console.log('[Desktop QR] Cancelling authentication');
344
447
  this.isCancelled = true;
345
448
  this.stopPolling();
346
449
  (_a = this.onCancel) === null || _a === void 0 ? void 0 : _a.call(this);
450
+ // Immediately reject the polling promise
347
451
  if (this.pollingReject) {
348
452
  this.pollingReject({
349
453
  code: 'USER_DENIED',
@@ -353,14 +457,20 @@ export class DesktopHandler {
353
457
  }
354
458
  }
355
459
  }
460
+ /**
461
+ * Helper function to display QR code in a modal or inline
462
+ */
356
463
  export function createQRCodeDisplay(qrCodeData, options) {
357
464
  const container = (options === null || options === void 0 ? void 0 : options.container) || document.createElement('div');
358
465
  container.className = 'phone-auth-qr-container';
466
+ // Determine QR code to display
359
467
  let qrCodeSrc;
360
468
  if (typeof qrCodeData === 'string') {
469
+ // Simple string QR code
361
470
  qrCodeSrc = qrCodeData;
362
471
  }
363
472
  else if (qrCodeData && typeof qrCodeData === 'object') {
473
+ // QRCodeData object - prefer iOS QR if available
364
474
  if (qrCodeData.iosQRCode) {
365
475
  qrCodeSrc = qrCodeData.iosQRCode;
366
476
  }
@@ -374,6 +484,7 @@ export function createQRCodeDisplay(qrCodeData, options) {
374
484
  else {
375
485
  throw new Error('Invalid qrCodeData: must be string or QRCodeData object');
376
486
  }
487
+ // Create QR code image
377
488
  const img = document.createElement('img');
378
489
  img.src = qrCodeSrc;
379
490
  img.alt = 'QR Code for authentication';
@@ -381,6 +492,7 @@ export function createQRCodeDisplay(qrCodeData, options) {
381
492
  img.style.height = `${(options === null || options === void 0 ? void 0 : options.size) || 256}px`;
382
493
  img.style.display = 'block';
383
494
  img.style.margin = '0 auto';
495
+ // Add title if provided
384
496
  if (options === null || options === void 0 ? void 0 : options.title) {
385
497
  const title = document.createElement('h3');
386
498
  title.textContent = options.title;
@@ -389,6 +501,7 @@ export function createQRCodeDisplay(qrCodeData, options) {
389
501
  container.appendChild(title);
390
502
  }
391
503
  container.appendChild(img);
504
+ // Add description if provided
392
505
  if (options === null || options === void 0 ? void 0 : options.description) {
393
506
  const desc = document.createElement('p');
394
507
  desc.textContent = options.description;
@@ -399,7 +512,12 @@ export function createQRCodeDisplay(qrCodeData, options) {
399
512
  }
400
513
  return container;
401
514
  }
515
+ /**
516
+ * Helper function to create a modal for QR code display
517
+ * Supports both string QR codes and QRCodeData objects for dual-platform support
518
+ */
402
519
  export function showQRCodeModal(qrCodeData, options) {
520
+ // Create modal overlay
403
521
  const overlay = document.createElement('div');
404
522
  overlay.style.cssText = `
405
523
  position: fixed;
@@ -413,6 +531,7 @@ export function showQRCodeModal(qrCodeData, options) {
413
531
  justify-content: center;
414
532
  z-index: 9999;
415
533
  `;
534
+ // Create modal content
416
535
  const modal = document.createElement('div');
417
536
  modal.style.cssText = `
418
537
  background: white;
@@ -421,6 +540,7 @@ export function showQRCodeModal(qrCodeData, options) {
421
540
  max-width: 400px;
422
541
  position: relative;
423
542
  `;
543
+ // Add close button
424
544
  const closeBtn = document.createElement('button');
425
545
  closeBtn.textContent = '×';
426
546
  closeBtn.style.cssText = `
@@ -440,11 +560,13 @@ export function showQRCodeModal(qrCodeData, options) {
440
560
  }
441
561
  };
442
562
  modal.appendChild(closeBtn);
563
+ // Add QR code display
443
564
  const qrDisplay = createQRCodeDisplay(qrCodeData, {
444
565
  title: (options === null || options === void 0 ? void 0 : options.title) || 'Scan QR Code to Authenticate',
445
566
  description: (options === null || options === void 0 ? void 0 : options.description) || 'Use your mobile device to scan this QR code'
446
567
  });
447
568
  modal.appendChild(qrDisplay);
569
+ // Add loading spinner placeholder
448
570
  const statusDiv = document.createElement('div');
449
571
  statusDiv.id = 'desktop-auth-status';
450
572
  statusDiv.style.cssText = `
@@ -456,6 +578,7 @@ export function showQRCodeModal(qrCodeData, options) {
456
578
  modal.appendChild(statusDiv);
457
579
  overlay.appendChild(modal);
458
580
  document.body.appendChild(overlay);
581
+ // Close modal on overlay click
459
582
  overlay.onclick = (e) => {
460
583
  if (e.target === overlay) {
461
584
  document.body.removeChild(overlay);
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Strategy Handlers Export
3
+ * Provides implementations for TS43, Link, and Desktop authentication strategies
4
+ */
1
5
  export type { StrategyHandler } from './types';
2
6
  export { TS43Handler } from './ts43';
3
7
  export { LinkHandler } from './link';
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Strategy Handlers Export
3
+ * Provides implementations for TS43, Link, and Desktop authentication strategies
4
+ */
1
5
  export { TS43Handler } from './ts43';
2
6
  export { LinkHandler } from './link';
3
7
  export { DesktopHandler, createQRCodeDisplay, showQRCodeModal } from './desktop';
@@ -1,23 +1,44 @@
1
+ /**
2
+ * Link Strategy Handler
3
+ * Handles authentication via app links (iOS and Android)
4
+ * Opens authentication app while keeping user on current page
5
+ */
1
6
  import type { StrategyHandler } from './types';
2
7
  import type { PrepareResponse } from '../types';
3
8
  export interface LinkAuthOptions {
9
+ /** Fixed polling interval in milliseconds (default: 2000) */
4
10
  pollingInterval?: number;
11
+ /** Maximum polling attempts before timeout (default: 30 = 1 minute with 2s interval) */
5
12
  maxPollingAttempts?: number;
13
+ /** Custom polling endpoint (overrides backend-provided or configured endpoint) */
6
14
  pollingEndpoint?: string;
15
+ /** Developer environment (adds 'developer' header to requests) */
16
+ devEnv?: string;
17
+ /** Callback when link is opened */
7
18
  onLinkOpened?: () => void;
19
+ /** Callback for polling status updates */
8
20
  onStatusUpdate?: (status: PollingStatus) => void;
21
+ /** Callback when authentication times out */
9
22
  onTimeout?: () => void;
23
+ /** Callback when authentication is cancelled by user */
10
24
  onCancel?: () => void;
11
25
  }
12
26
  export interface PollingStatus {
27
+ /** Current status of the authentication */
13
28
  status: 'pending' | 'authenticated' | 'expired' | 'cancelled' | 'error';
29
+ /** Optional message */
14
30
  message?: string;
31
+ /** Authentication result data if status is 'authenticated' */
15
32
  data?: any;
16
33
  }
17
34
  export interface LinkAuthResult {
35
+ /** Whether authentication was successful */
18
36
  authenticated: boolean;
37
+ /** Authentication credential if successful */
19
38
  credential?: string;
39
+ /** Session info for subsequent requests */
20
40
  session?: any;
41
+ /** Error message if authentication failed */
21
42
  error?: string;
22
43
  }
23
44
  export declare class LinkHandler implements StrategyHandler {
@@ -27,13 +48,42 @@ export declare class LinkHandler implements StrategyHandler {
27
48
  private onCancel?;
28
49
  private pollingReject?;
29
50
  private isPollingInProgress;
51
+ /**
52
+ * Invoke link-based authentication
53
+ * Opens authentication app while keeping user on current page
54
+ */
30
55
  invoke(data: PrepareResponse, options?: LinkAuthOptions): Promise<LinkAuthResult>;
56
+ /**
57
+ * Open authentication link without navigating away
58
+ */
31
59
  private openAuthenticationLink;
60
+ /**
61
+ * Start polling for authentication status with constant interval
62
+ */
32
63
  private startPolling;
64
+ /**
65
+ * Stop polling
66
+ */
33
67
  private stopPolling;
68
+ /**
69
+ * Format response for backend processing
70
+ */
34
71
  formatResponse(response: LinkAuthResult): any;
72
+ /**
73
+ * Check if link strategy is supported
74
+ * Returns true for mobile devices (iOS and Android)
75
+ */
35
76
  isSupported(): boolean;
77
+ /**
78
+ * Clean up resources (stop polling if active)
79
+ */
36
80
  cleanup(): void;
81
+ /**
82
+ * Check if polling is currently active
83
+ */
37
84
  isPolling(): boolean;
85
+ /**
86
+ * Cancel the ongoing authentication
87
+ */
38
88
  cancel(): void;
39
89
  }