@glideidentity/web-client-sdk 4.4.10 → 5.0.1-beta.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.
Files changed (102) hide show
  1. package/README.md +167 -19
  2. package/dist/adapters/angular/index.js +0 -1
  3. package/dist/adapters/angular/phone-auth.service.d.ts +0 -18
  4. package/dist/adapters/angular/phone-auth.service.js +0 -26
  5. package/dist/adapters/react/index.d.ts +1 -1
  6. package/dist/adapters/react/index.js +0 -3
  7. package/dist/adapters/react/useClient.js +0 -1
  8. package/dist/adapters/react/usePhoneAuth.js +1 -16
  9. package/dist/adapters/vanilla/client.js +0 -1
  10. package/dist/adapters/vanilla/index.js +0 -1
  11. package/dist/adapters/vanilla/phone-auth.js +0 -31
  12. package/dist/adapters/vue/index.d.ts +1 -1
  13. package/dist/adapters/vue/index.js +0 -4
  14. package/dist/adapters/vue/useClient.js +0 -5
  15. package/dist/adapters/vue/usePhoneAuth.js +1 -20
  16. package/dist/browser/web-client-sdk.min.js +1 -1
  17. package/dist/browser/web-client-sdk.min.js.LICENSE.txt +1 -1
  18. package/dist/browser.d.ts +1 -1
  19. package/dist/browser.js +0 -6
  20. package/dist/core/client.js +0 -12
  21. package/dist/core/logger.js +1 -81
  22. package/dist/core/phone-auth/api-types.d.ts +0 -13
  23. package/dist/core/phone-auth/api-types.js +0 -83
  24. package/dist/core/phone-auth/client.js +27 -374
  25. package/dist/core/phone-auth/error-utils.js +1 -83
  26. package/dist/core/phone-auth/index.d.ts +1 -1
  27. package/dist/core/phone-auth/index.js +1 -3
  28. package/dist/core/phone-auth/status-types.d.ts +0 -78
  29. package/dist/core/phone-auth/status-types.js +0 -17
  30. package/dist/core/phone-auth/strategies/desktop.js +8 -126
  31. package/dist/core/phone-auth/strategies/index.d.ts +0 -4
  32. package/dist/core/phone-auth/strategies/index.js +0 -4
  33. package/dist/core/phone-auth/strategies/link.js +10 -88
  34. package/dist/core/phone-auth/strategies/ts43.d.ts +0 -19
  35. package/dist/core/phone-auth/strategies/ts43.js +2 -33
  36. package/dist/core/phone-auth/strategies/types.js +0 -4
  37. package/dist/core/phone-auth/type-guards.js +0 -131
  38. package/dist/core/phone-auth/types.js +0 -32
  39. package/dist/core/phone-auth/ui/mobile-debug-console.js +2 -28
  40. package/dist/core/phone-auth/ui/modal.d.ts +33 -55
  41. package/dist/core/phone-auth/ui/modal.js +889 -422
  42. package/dist/core/phone-auth/validation-utils.d.ts +0 -13
  43. package/dist/core/phone-auth/validation-utils.js +2 -81
  44. package/dist/core/version.js +1 -2
  45. package/dist/esm/adapters/angular/index.js +0 -1
  46. package/dist/esm/adapters/angular/phone-auth.service.d.ts +0 -18
  47. package/dist/esm/adapters/angular/phone-auth.service.js +0 -26
  48. package/dist/esm/adapters/react/index.d.ts +1 -1
  49. package/dist/esm/adapters/react/index.js +0 -3
  50. package/dist/esm/adapters/react/useClient.js +0 -1
  51. package/dist/esm/adapters/react/usePhoneAuth.js +1 -16
  52. package/dist/esm/adapters/vanilla/client.js +0 -1
  53. package/dist/esm/adapters/vanilla/index.js +0 -1
  54. package/dist/esm/adapters/vanilla/phone-auth.d.ts +0 -24
  55. package/dist/esm/adapters/vanilla/phone-auth.js +0 -31
  56. package/dist/esm/adapters/vue/index.d.ts +1 -1
  57. package/dist/esm/adapters/vue/index.js +0 -4
  58. package/dist/esm/adapters/vue/useClient.js +0 -5
  59. package/dist/esm/adapters/vue/usePhoneAuth.js +1 -20
  60. package/dist/esm/browser.d.ts +1 -1
  61. package/dist/esm/browser.js +0 -6
  62. package/dist/esm/core/client.d.ts +0 -10
  63. package/dist/esm/core/client.js +0 -12
  64. package/dist/esm/core/logger.d.ts +0 -53
  65. package/dist/esm/core/logger.js +1 -81
  66. package/dist/esm/core/phone-auth/api-types.d.ts +0 -328
  67. package/dist/esm/core/phone-auth/api-types.js +0 -83
  68. package/dist/esm/core/phone-auth/client.d.ts +0 -144
  69. package/dist/esm/core/phone-auth/client.js +28 -375
  70. package/dist/esm/core/phone-auth/error-utils.d.ts +0 -29
  71. package/dist/esm/core/phone-auth/error-utils.js +1 -83
  72. package/dist/esm/core/phone-auth/index.d.ts +1 -1
  73. package/dist/esm/core/phone-auth/index.js +2 -4
  74. package/dist/esm/core/phone-auth/status-types.d.ts +0 -78
  75. package/dist/esm/core/phone-auth/status-types.js +0 -17
  76. package/dist/esm/core/phone-auth/strategies/desktop.d.ts +0 -63
  77. package/dist/esm/core/phone-auth/strategies/desktop.js +8 -126
  78. package/dist/esm/core/phone-auth/strategies/index.d.ts +0 -4
  79. package/dist/esm/core/phone-auth/strategies/index.js +0 -4
  80. package/dist/esm/core/phone-auth/strategies/link.d.ts +0 -48
  81. package/dist/esm/core/phone-auth/strategies/link.js +10 -88
  82. package/dist/esm/core/phone-auth/strategies/ts43.d.ts +0 -19
  83. package/dist/esm/core/phone-auth/strategies/ts43.js +2 -33
  84. package/dist/esm/core/phone-auth/strategies/types.d.ts +0 -13
  85. package/dist/esm/core/phone-auth/strategies/types.js +0 -4
  86. package/dist/esm/core/phone-auth/type-guards.d.ts +0 -128
  87. package/dist/esm/core/phone-auth/type-guards.js +0 -131
  88. package/dist/esm/core/phone-auth/types.d.ts +0 -108
  89. package/dist/esm/core/phone-auth/types.js +0 -32
  90. package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +0 -4
  91. package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +2 -28
  92. package/dist/esm/core/phone-auth/ui/modal.d.ts +27 -68
  93. package/dist/esm/core/phone-auth/ui/modal.js +889 -422
  94. package/dist/esm/core/phone-auth/validation-utils.d.ts +0 -44
  95. package/dist/esm/core/phone-auth/validation-utils.js +2 -80
  96. package/dist/esm/core/types.d.ts +0 -35
  97. package/dist/esm/core/version.js +1 -2
  98. package/dist/esm/index.d.ts +1 -1
  99. package/dist/esm/index.js +1 -9
  100. package/dist/index.d.ts +1 -1
  101. package/dist/index.js +0 -7
  102. package/package.json +2 -2
@@ -43,7 +43,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.PhoneAuthClient = void 0;
46
- // Import API types for API communication
47
46
  const API = __importStar(require("./api-types"));
48
47
  const types_1 = require("./types");
49
48
  const error_utils_1 = require("./error-utils");
@@ -58,36 +57,31 @@ class PhoneAuthClient {
58
57
  this.crossDeviceActive = false;
59
58
  this.retryCount = 0;
60
59
  this.sessionCache = new Map();
61
- // Store base timeout for normal operations
62
60
  this.baseTimeout = config.timeout || 30000;
63
- // Default configuration with cross-device support
64
61
  this.config = {
65
62
  endpoints: {
66
63
  prepare: ((_a = config.endpoints) === null || _a === void 0 ? void 0 : _a.prepare) || '/api/magic-auth/prepare',
67
64
  process: ((_b = config.endpoints) === null || _b === void 0 ? void 0 : _b.process) || '/api/magic-auth/process',
68
- polling: (_c = config.endpoints) === null || _c === void 0 ? void 0 : _c.polling // Pass through the polling endpoint
65
+ polling: (_c = config.endpoints) === null || _c === void 0 ? void 0 : _c.polling
69
66
  },
70
67
  timeout: config.timeout || 30000,
71
- pollingInterval: config.pollingInterval || 2000, // Default 2 seconds
72
- maxPollingAttempts: config.maxPollingAttempts || 30, // Changed from 150 to 30 (1 minute total)
68
+ pollingInterval: config.pollingInterval || 2000,
69
+ maxPollingAttempts: config.maxPollingAttempts || 30,
73
70
  debug: config.debug || false,
74
71
  aggregatorId: config.aggregatorId || 'default',
75
72
  devtools: config.devtools
76
73
  };
77
74
  this.debug = this.config.debug;
78
- // Store callbacks
79
75
  this.callbacks = {
80
76
  onCrossDeviceDetected: config.onCrossDeviceDetected,
81
77
  onRetryAttempt: config.onRetryAttempt
82
78
  };
83
- // Initialize logger based on config
84
79
  this.logger = logger_1.LoggerFactory.create({
85
80
  level: config.logLevel,
86
81
  prefix: '[PhoneAuth]',
87
82
  remote: config.remoteLogging,
88
83
  custom: config.logger
89
84
  });
90
- // Initialize developer tools if configured
91
85
  if (((_d = config.devtools) === null || _d === void 0 ? void 0 : _d.showMobileConsole) && typeof window !== 'undefined') {
92
86
  Promise.resolve().then(() => __importStar(require('./ui/mobile-debug-console'))).then(({ MobileDebugConsole }) => {
93
87
  MobileDebugConsole.init();
@@ -96,32 +90,21 @@ class PhoneAuthClient {
96
90
  console.error('[PhoneAuth] Failed to load mobile debug console:', err);
97
91
  });
98
92
  }
99
- // Set up session cache cleanup
100
93
  this.setupCacheCleanup();
101
94
  }
102
- /**
103
- * Get user-friendly error message using error utilities
104
- */
105
95
  getUserFriendlyMessage(error) {
106
96
  if (typeof error === 'string') {
107
- // For legacy string error codes
108
97
  return (0, error_utils_1.getUserMessage)({ code: error });
109
98
  }
110
99
  return (0, error_utils_1.getUserMessage)(error);
111
100
  }
112
- /**
113
- * Log error with proper context and sanitization
114
- */
115
101
  logError(error, context) {
116
- // Create breadcrumb for error tracking
117
102
  const breadcrumb = (0, error_utils_1.createErrorBreadcrumb)(error);
118
- // Serialize error for logging (sanitized)
119
103
  const serialized = (0, error_utils_1.serializeError)(error);
120
104
  if (this.debug || !(0, error_utils_1.isUserError)(error)) {
121
105
  console.error('[PhoneAuth] Error:', Object.assign(Object.assign({}, serialized), { breadcrumb,
122
106
  context }));
123
107
  }
124
- // Log trace context for distributed tracing (if available)
125
108
  if (error.traceId) {
126
109
  console.debug('[PhoneAuth] Trace Context:', {
127
110
  traceId: error.traceId,
@@ -130,20 +113,11 @@ class PhoneAuthClient {
130
113
  });
131
114
  }
132
115
  }
133
- /**
134
- * Check if the browser supports secure phone authentication
135
- */
136
116
  isSupported() {
137
- // Only check on client side
138
117
  if (typeof window === 'undefined')
139
118
  return false;
140
- // Check for the DigitalCredential constructor specifically
141
- // This is more accurate than checking credentials.get which exists for other credential types
142
119
  return 'DigitalCredential' in window;
143
120
  }
144
- /**
145
- * Get detailed browser support information
146
- */
147
121
  getBrowserSupportInfo() {
148
122
  if (typeof window === 'undefined') {
149
123
  return {
@@ -162,7 +136,6 @@ class PhoneAuthClient {
162
136
  browser: isChrome ? types_1.BrowserName.CHROME : isEdge ? types_1.BrowserName.EDGE : types_1.BrowserName.OTHER
163
137
  };
164
138
  }
165
- // Provide specific guidance based on browser
166
139
  if (isChrome || isEdge) {
167
140
  return {
168
141
  supported: false,
@@ -179,16 +152,11 @@ class PhoneAuthClient {
179
152
  message: 'Your browser doesn\'t support the Digital Credentials API. Please use Chrome or Edge with the #web-identity-digital-credentials flag enabled.'
180
153
  };
181
154
  }
182
- /**
183
- * Main verification method with silent retry support
184
- */
185
155
  verify(options) {
186
156
  return __awaiter(this, void 0, void 0, function* () {
187
- // Reset retry count for new verification
188
157
  this.retryCount = 0;
189
158
  this.lastRequest = options;
190
- const maxRetries = 2; // Default max retries
191
- // Try verification with silent retries
159
+ const maxRetries = 2;
192
160
  return this.verifyWithRetry(options, maxRetries);
193
161
  });
194
162
  }
@@ -196,55 +164,38 @@ class PhoneAuthClient {
196
164
  return __awaiter(this, void 0, void 0, function* () {
197
165
  var _a, _b;
198
166
  try {
199
- // Step 1: Prepare the phone verification request
200
167
  const preparedRequest = yield this.preparePhoneRequest(options);
201
- // Step 2: Invoke secure prompt for user consent (always in UI mode for high-level API)
202
168
  const credentialResponse = yield this.invokeSecurePrompt(preparedRequest);
203
- // Check if headless result was returned (this shouldn't happen in high-level API)
204
169
  if (credentialResponse && typeof credentialResponse === 'object' && 'strategy' in credentialResponse) {
205
170
  throw this.createError(error_utils_1.PhoneAuthErrorCode.INVALID_RESPONSE, 'Headless mode is not supported in authenticatePhoneNumber. Use preparePhoneRequest and invokeSecurePrompt directly for headless mode.');
206
171
  }
207
- // Step 3: Process the response through appropriate endpoint
208
172
  const credential = credentialResponse;
209
- // Validate use_case is provided for endpoint selection
210
173
  if (!options.use_case) {
211
174
  throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
212
175
  }
213
176
  const result = options.use_case === API.USE_CASE.GET_PHONE_NUMBER
214
177
  ? yield this.getPhoneNumber(credential, preparedRequest.session)
215
178
  : yield this.verifyPhoneNumber(credential, preparedRequest.session);
216
- // Return the result directly - it's already the correct type
217
- // Cache successful result with session info for later use
218
179
  this.cacheSession(options, result);
219
180
  return result;
220
181
  }
221
182
  catch (error) {
222
183
  const authError = (0, error_utils_1.isPhoneAuthError)(error) ? error : (0, error_utils_1.parseBackendError)(error);
223
- // Check if we should retry (silent retry - don't throw yet)
224
- // Note: We cannot automatically retry USER_DENIED errors because the Digital Credentials API
225
- // requires user interaction (transient activation). Automatic retries would fail with
226
- // "The 'digital-credentials-get' feature requires transient activation" error.
227
184
  if (this.shouldRetry(authError) && this.retryCount < maxRetries) {
228
185
  this.retryCount++;
229
- // Notify about retry attempt (but don't show error to user)
230
186
  (_b = (_a = this.callbacks).onRetryAttempt) === null || _b === void 0 ? void 0 : _b.call(_a, this.retryCount, maxRetries);
231
187
  if (this.debug) {
232
188
  console.log(`[PhoneAuth] Retrying verification (attempt ${this.retryCount + 1}/${maxRetries + 1})`);
233
189
  }
234
- // Wait before retry
235
- yield this.delay(Math.min(1000 * Math.pow(2, this.retryCount - 1), 5000)); // Exponential backoff
236
- // Check cache for recent successful session
190
+ yield this.delay(Math.min(1000 * Math.pow(2, this.retryCount - 1), 5000));
237
191
  const cachedResult = this.getCachedSession(options);
238
192
  if (cachedResult) {
239
193
  if (this.debug)
240
194
  console.log('[PhoneAuth] Using cached session result');
241
195
  return cachedResult;
242
196
  }
243
- // Retry the verification
244
197
  return this.verifyWithRetry(options, maxRetries);
245
198
  }
246
- // All retries exhausted or non-retryable error - now throw
247
- // Add context
248
199
  authError.context = {
249
200
  step: 'complete',
250
201
  useCase: options.use_case,
@@ -253,15 +204,11 @@ class PhoneAuthClient {
253
204
  attemptNumber: this.retryCount + 1,
254
205
  maxAttempts: maxRetries + 1
255
206
  };
256
- // Log error with proper sanitization
257
207
  this.logError(authError, { options });
258
- // Re-throw the structured error
259
208
  if ((0, error_utils_1.isPhoneAuthError)(error)) {
260
- // If it already has context, throw as-is
261
209
  if (error.context) {
262
210
  throw error;
263
211
  }
264
- // Otherwise, create a new error with context
265
212
  const enhancedError = {
266
213
  code: authError.code,
267
214
  message: error.message,
@@ -286,67 +233,35 @@ class PhoneAuthClient {
286
233
  }
287
234
  });
288
235
  }
289
- /**
290
- * High-level method to get phone number (complete flow)
291
- * Handles prepare, credential prompt, and get phone number in one call
292
- */
293
236
  getPhoneNumberComplete(options) {
294
237
  return __awaiter(this, void 0, void 0, function* () {
295
238
  return this.verify(Object.assign({ use_case: API.USE_CASE.GET_PHONE_NUMBER }, options));
296
239
  });
297
240
  }
298
- /**
299
- * High-level method to verify phone number (complete flow)
300
- * Handles prepare, credential prompt, and verification in one call
301
- */
302
241
  verifyPhoneNumberComplete(phoneNumber, options) {
303
242
  return __awaiter(this, void 0, void 0, function* () {
304
243
  return this.verify(Object.assign({ use_case: API.USE_CASE.VERIFY_PHONE_NUMBER, phone_number: phoneNumber }, options));
305
244
  });
306
245
  }
307
- /**
308
- * Step 1: Prepare phone verification request
309
- *
310
- * This method prepares a secure request for phone verification.
311
- * You can use this with your own backend or the glide-sdk-node.
312
- *
313
- * @example
314
- * ```typescript
315
- * const request = await phoneAuthClient.preparePhoneRequest({ useCase: 'GetPhoneNumber' });
316
- * // Handle the request with custom logic
317
- * ```
318
- */
319
246
  preparePhoneRequest(options) {
320
247
  return __awaiter(this, void 0, void 0, function* () {
321
248
  var _a, _b, _c;
322
- // Validate phone number if provided
323
249
  if (options.phone_number) {
324
250
  const phoneValidation = (0, validation_utils_1.validatePhoneNumber)(options.phone_number);
325
251
  if (!phoneValidation.valid) {
326
252
  throw this.createError(error_utils_1.PhoneAuthErrorCode.INVALID_PHONE_NUMBER, phoneValidation.error, { field: 'phone_number' });
327
253
  }
328
254
  }
329
- // Validate PLMN if provided
330
255
  if (options.plmn) {
331
256
  const plmnValidation = (0, validation_utils_1.validatePlmn)(options.plmn);
332
257
  if (!plmnValidation.valid) {
333
258
  throw this.createError(error_utils_1.PhoneAuthErrorCode.BAD_REQUEST, plmnValidation.error, { field: 'plmn' });
334
259
  }
335
260
  }
336
- // Validate consent data if provided
337
- if (options.consent_data) {
338
- const consentValidation = (0, validation_utils_1.validateConsentData)(options.consent_data);
339
- if (!consentValidation.valid) {
340
- throw this.createError(error_utils_1.PhoneAuthErrorCode.BAD_REQUEST, consentValidation.error, { field: 'consent_data' });
341
- }
342
- }
343
- // Validate use_case is provided (unless only parent_session_id is given)
344
261
  if (!options.use_case && !(((_a = options.options) === null || _a === void 0 ? void 0 : _a.parent_session_id) && !options.phone_number && !options.plmn)) {
345
262
  throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'use_case is required', { field: 'use_case' });
346
263
  }
347
- // Validate required parameters based on use case
348
264
  if (!options.phone_number && !options.plmn && !((_b = options.options) === null || _b === void 0 ? void 0 : _b.parent_session_id)) {
349
- // Provide specific error message based on use case
350
265
  if (options.use_case === API.USE_CASE.GET_PHONE_NUMBER) {
351
266
  throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'PLMN (MCC/MNC) is required for GetPhoneNumber. Please provide carrier network information.', { field: 'plmn', useCase: 'GetPhoneNumber' });
352
267
  }
@@ -354,49 +269,36 @@ class PhoneAuthClient {
354
269
  throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'Phone number is required for VerifyPhoneNumber', { field: 'phoneNumber', useCase: 'VerifyPhoneNumber' });
355
270
  }
356
271
  else {
357
- // Fallback for other use cases
358
272
  throw this.createError(error_utils_1.PhoneAuthErrorCode.MISSING_PARAMETERS, 'Either phone number or PLMN (MCC/MNC) must be provided', { field: 'phoneNumber,plmn' });
359
273
  }
360
274
  }
361
- // Log parent session usage
362
275
  if (((_c = options.options) === null || _c === void 0 ? void 0 : _c.parent_session_id) && !options.phone_number && !options.plmn) {
363
276
  if (this.debug) {
364
277
  console.log('[PhoneAuth] Using parent_session_id: %s, use_case: %s', options.options.parent_session_id, options.use_case || 'not provided');
365
278
  }
366
279
  }
367
280
  const requestId = `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
368
- // Generate cryptographically secure nonce
369
281
  const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
370
282
  .replace(/\+/g, '-')
371
283
  .replace(/\//g, '_')
372
284
  .replace(/=/g, '');
373
- // Validate generated nonce
374
285
  const nonceValidation = (0, validation_utils_1.validateNonce)(nonce);
375
286
  if (!nonceValidation.valid) {
376
287
  throw this.createError(error_utils_1.PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, 'Failed to generate valid nonce', { field: 'nonce' });
377
288
  }
378
- // Build properly typed request body according to API specification
379
- // Be permissive - backend will ignore extra fields if not needed
380
289
  const requestBody = {
381
- // Include use_case if provided (optional when parent_session_id is given)
382
290
  use_case: options.use_case,
383
- // Include phone_number if provided (backend ignores if not needed)
384
291
  phone_number: options.phone_number,
385
- // Include PLMN if provided
386
292
  plmn: options.plmn ? {
387
293
  mcc: options.plmn.mcc,
388
294
  mnc: options.plmn.mnc
389
295
  } : undefined,
390
- // Auto-generated fields (SDK always provides these)
391
296
  nonce: nonce,
392
297
  id: requestId,
393
- // Optional fields
394
- consent_data: options.consent_data,
395
298
  client_info: {
396
299
  user_agent: navigator.userAgent,
397
300
  platform: navigator.platform
398
301
  },
399
- // Advanced options (for desktop-mobile binding and future features)
400
302
  options: options.options
401
303
  };
402
304
  this.log('Preparing phone verification request', requestBody);
@@ -407,20 +309,15 @@ class PhoneAuthClient {
407
309
  body: JSON.stringify(requestBody)
408
310
  });
409
311
  if (!response.ok) {
410
- // Try to get error details from response body
411
312
  let errorDetails = null;
412
313
  try {
413
314
  errorDetails = yield response.json();
414
- // Always include the HTTP status from the response
415
315
  errorDetails = Object.assign(Object.assign({}, errorDetails), { status: response.status });
416
316
  }
417
317
  catch (_d) {
418
- // If JSON parsing fails, use status text
419
318
  errorDetails = { status: response.status, statusText: response.statusText };
420
319
  }
421
- // Parse the backend error response (handles both structured and unstructured errors)
422
320
  const parsedError = (0, error_utils_1.parseBackendError)(errorDetails);
423
- // Enhance with additional context
424
321
  parsedError.context = {
425
322
  step: 'prepare',
426
323
  useCase: options.use_case,
@@ -428,7 +325,6 @@ class PhoneAuthClient {
428
325
  userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
429
326
  url: typeof window !== 'undefined' ? window.location.href : undefined
430
327
  };
431
- // Add endpoint info
432
328
  parsedError.details = Object.assign(Object.assign({}, parsedError.details), { endpoint: 'prepare', status: response.status });
433
329
  throw parsedError;
434
330
  }
@@ -437,16 +333,12 @@ class PhoneAuthClient {
437
333
  if (!data.authentication_strategy || !data.data || !data.session) {
438
334
  throw this.createError(error_utils_1.PhoneAuthErrorCode.INVALID_RESPONSE, 'Invalid response format from backend');
439
335
  }
440
- // Return the full response as-is
441
- // The invoke method will handle it based on authentication_strategy
442
336
  return data;
443
337
  }
444
338
  catch (error) {
445
- // If it's already an AuthError, re-throw it
446
339
  if (this.isAuthError(error)) {
447
340
  throw error;
448
341
  }
449
- // Otherwise, wrap it as a network error
450
342
  throw this.createError(error_utils_1.PhoneAuthErrorCode.NETWORK_ERROR, 'Failed to prepare verification request', {
451
343
  originalError: error,
452
344
  context: {
@@ -460,68 +352,16 @@ class PhoneAuthClient {
460
352
  }
461
353
  });
462
354
  }
463
- /**
464
- * Step 2: Invoke secure prompt for user consent
465
- *
466
- * This method can work in two modes:
467
- * 1. **UI Mode (default)**: Shows built-in UI components (modals/buttons)
468
- * 2. **Headless Mode**: Returns raw data for custom UI implementation
469
- *
470
- * **Important**: This method automatically handles reactive objects from frameworks
471
- * like Vue.js and React by deep cloning the input. This ensures compatibility with
472
- * browser APIs that expect plain objects.
473
- *
474
- * @example UI Mode (shows modal/button)
475
- * ```typescript
476
- * // Shows SDK's built-in UI
477
- * const credential = await phoneAuth.invokeSecurePrompt(prepareResult);
478
- *
479
- * // Customize the UI
480
- * const credential = await phoneAuth.invokeSecurePrompt(prepareResult, {
481
- * modalOptions: {
482
- * title: 'Verify Your Identity',
483
- * buttonText: 'Continue with Verizon'
484
- * }
485
- * });
486
- * ```
487
- *
488
- * @example Extended Mode (returns control methods)
489
- * ```typescript
490
- * // Get control methods for custom implementation
491
- * const result = await phoneAuth.invokeSecurePrompt(prepareResult, {
492
- * executionMode: 'extended',
493
- * preventDefaultUI: true // Desktop: no modal
494
- * });
495
- *
496
- * if (result.strategy === 'desktop') {
497
- * // Show custom QR UI
498
- * showCustomQR(result.qr_code_data);
499
- * // Start polling
500
- * await result.start_polling();
501
- * }
502
- * ```
503
- *
504
- * @param prepareResponse - Response from prepare() with strategy and data
505
- * @param options - Control UI behavior and response type
506
- * @returns Credential or ExtendedResponse based on executionMode
507
- */
508
355
  invokeSecurePrompt(prepareResponse, options) {
509
356
  return __awaiter(this, void 0, void 0, function* () {
510
- // Deep clone to avoid issues with reactive objects (Vue/React)
511
- // This ensures we work with plain objects for browser APIs
512
- // Vue's reactivity system wraps objects in Proxies which can interfere
513
- // with browser APIs like Digital Credentials API that expect plain objects
514
357
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
515
- // Try structuredClone first (modern browsers), but catch errors and fallback to JSON method
516
358
  let plainResponse;
517
359
  try {
518
- // structuredClone might throw if object contains non-cloneable properties
519
360
  plainResponse = typeof structuredClone !== 'undefined'
520
361
  ? structuredClone(prepareResponse)
521
362
  : JSON.parse(JSON.stringify(prepareResponse));
522
363
  }
523
364
  catch (cloneError) {
524
- // Fallback to JSON method if structuredClone fails
525
365
  if (this.debug) {
526
366
  console.log('[PhoneAuth] structuredClone failed, using JSON fallback:', cloneError);
527
367
  }
@@ -531,16 +371,11 @@ class PhoneAuthClient {
531
371
  console.log('[PhoneAuth] Session cache size:', this.sessionCache.size);
532
372
  console.log('[PhoneAuth] Retry count:', this.retryCount);
533
373
  console.log('[PhoneAuth] PrepareResponse received:', JSON.stringify(plainResponse, null, 2));
534
- // Treat options as InvokeOptions (the modern format)
535
- // Legacy DesktopAuthOptions properties will still work through property access
536
374
  const opts = options;
537
- // Get configuration from options - access properties directly
538
375
  const strategy = plainResponse.authentication_strategy;
539
376
  const preventDefaultUI = (_a = opts === null || opts === void 0 ? void 0 : opts.preventDefaultUI) !== null && _a !== void 0 ? _a : false;
540
377
  const executionMode = (_b = opts === null || opts === void 0 ? void 0 : opts.executionMode) !== null && _b !== void 0 ? _b : 'standard';
541
- // Handle based on authentication strategy
542
378
  if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.TS43) {
543
- // Check browser support for TS43 strategy which requires Digital Credentials API
544
379
  if (!this.isSupported()) {
545
380
  throw this.createError(error_utils_1.PhoneAuthErrorCode.BROWSER_NOT_SUPPORTED, 'Your browser does not support the Digital Credentials API required for TS43 authentication');
546
381
  }
@@ -554,18 +389,13 @@ class PhoneAuthClient {
554
389
  }
555
390
  };
556
391
  this.log('Invoking TS43 secure authentication prompt', secureCredentialRequest);
557
- // Function to trigger TS43 authentication
558
392
  const triggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
559
393
  var _a, _b;
560
394
  try {
561
- // This is the browser API call for TS43
562
- // Cast to CredentialRequestOptions with digital field (TS43 specific)
563
395
  const credentialOptions = secureCredentialRequest;
564
396
  const credentialResponse = yield navigator.credentials.get(credentialOptions);
565
- // Type guard for Digital Credential response
566
397
  const digitalResponse = credentialResponse;
567
398
  if (!digitalResponse || !('data' in digitalResponse) || !digitalResponse.data) {
568
- // Check if this is likely due to the browser flag being disabled
569
399
  const supportInfo = this.getBrowserSupportInfo();
570
400
  if (supportInfo.browser === types_1.BrowserName.CHROME || supportInfo.browser === types_1.BrowserName.EDGE) {
571
401
  throw new Error(`Digital Credentials API returned no response. This usually means the browser feature flag is not enabled. Please ensure the ${supportInfo.helpUrl || '#web-identity-digital-credentials flag'} is set to "Enabled" (not "Default" or "Disabled") and restart your browser.`);
@@ -577,7 +407,6 @@ class PhoneAuthClient {
577
407
  return credentialData.vp_token;
578
408
  }
579
409
  catch (error) {
580
- // Capture detailed browser error information
581
410
  const errorObj = error;
582
411
  const browserErrorDetails = {
583
412
  name: errorObj.name || 'UnknownError',
@@ -590,11 +419,9 @@ class PhoneAuthClient {
590
419
  timestamp: new Date().toISOString(),
591
420
  userAgent: navigator.userAgent,
592
421
  url: window.location.href,
593
- // Include request details for debugging
594
422
  authentication_strategy: plainResponse.authentication_strategy,
595
423
  hasSession: !!plainResponse.session
596
424
  };
597
- // Handle specific browser errors
598
425
  if (errorObj.name === types_1.BrowserError.NOT_ALLOWED) {
599
426
  throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, 'User denied the credential request or the request timed out', {
600
427
  originalError: error,
@@ -602,7 +429,6 @@ class PhoneAuthClient {
602
429
  context: errorContext
603
430
  });
604
431
  }
605
- // NetworkError with code 19 specifically indicates user cancellation in Digital Credentials API
606
432
  if (errorObj.name === types_1.BrowserError.NETWORK && errorObj.code === types_1.BrowserErrorCode.USER_CANCELLED_DC_API) {
607
433
  throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled by user', {
608
434
  originalError: error,
@@ -610,7 +436,6 @@ class PhoneAuthClient {
610
436
  context: errorContext
611
437
  });
612
438
  }
613
- // NetworkError without code 19 is a real network error
614
439
  if (errorObj.name === types_1.BrowserError.NETWORK) {
615
440
  throw this.createError(error_utils_1.PhoneAuthErrorCode.NETWORK_ERROR, 'Network error occurred while retrieving credentials', {
616
441
  originalError: error,
@@ -632,7 +457,6 @@ class PhoneAuthClient {
632
457
  context: errorContext
633
458
  });
634
459
  }
635
- // Check for other cancellation patterns
636
460
  if (errorObj.name === types_1.BrowserError.ABORT ||
637
461
  ((_a = browserErrorDetails.message) === null || _a === void 0 ? void 0 : _a.includes('The operation was aborted')) ||
638
462
  ((_b = browserErrorDetails.message) === null || _b === void 0 ? void 0 : _b.includes('User cancelled'))) {
@@ -642,7 +466,6 @@ class PhoneAuthClient {
642
466
  context: errorContext
643
467
  });
644
468
  }
645
- // For any other errors, capture all details
646
469
  throw this.createError(error_utils_1.PhoneAuthErrorCode.INTERNAL_SERVER_ERROR, `Digital Credentials API error: ${errorObj.message || 'Unknown error'}`, {
647
470
  originalError: error,
648
471
  browserError: browserErrorDetails,
@@ -650,11 +473,6 @@ class PhoneAuthClient {
650
473
  });
651
474
  }
652
475
  });
653
- // IMPORTANT: For TS43, we ALWAYS call the API directly without any modal
654
- // The Digital Credentials API provides its own OS-level UI (drawer/bottom sheet)
655
- // Adding our own modal would be redundant and confusing for users
656
- // Unlike Link (which needs a button for iOS App Clips), TS43 just needs direct invocation
657
- // Enhanced trigger function with callback support
658
476
  const enhancedTriggerTS43 = () => __awaiter(this, void 0, void 0, function* () {
659
477
  var _a, _b;
660
478
  try {
@@ -674,16 +492,11 @@ class PhoneAuthClient {
674
492
  throw error;
675
493
  }
676
494
  });
677
- // TS43 always auto-triggers (no SDK UI ever)
678
- // The Digital Credentials API provides its own OS-level UI
679
- // Use a wrapper object to allow updating the promise reference
680
495
  const credentialWrapper = {
681
496
  promise: null
682
497
  };
683
498
  try {
684
- // Always try to trigger immediately
685
499
  const vpToken = yield enhancedTriggerTS43();
686
- // Convert to AuthCredential format
687
500
  credentialWrapper.promise = Promise.resolve({
688
501
  credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
689
502
  session: plainResponse.session,
@@ -691,34 +504,25 @@ class PhoneAuthClient {
691
504
  });
692
505
  }
693
506
  catch (error) {
694
- // If auto-trigger fails, create a rejected promise
695
507
  credentialWrapper.promise = Promise.reject(error);
696
508
  }
697
- // Handle based on execution mode
698
509
  if (executionMode === 'extended') {
699
- // Extended mode - return control methods
700
510
  const response = {
701
511
  strategy: 'ts43',
702
512
  session: plainResponse.session,
703
- credential: credentialWrapper.promise, // Initial value
704
- // Re-trigger credential request
513
+ credential: credentialWrapper.promise,
705
514
  trigger: () => __awaiter(this, void 0, void 0, function* () {
706
515
  const vpToken = yield enhancedTriggerTS43();
707
- // Update the credential promise in the wrapper
708
516
  credentialWrapper.promise = Promise.resolve({
709
517
  credential: typeof vpToken === 'string' ? vpToken : Object.values(vpToken)[0],
710
518
  session: plainResponse.session,
711
519
  authenticated: true
712
520
  });
713
- // Return void as per interface
714
521
  }),
715
522
  cancel: () => {
716
- // TS43 doesn't have a way to cancel once triggered
717
- // but we can reject the promise
718
523
  credentialWrapper.promise = Promise.reject(this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, 'Authentication cancelled'));
719
524
  }
720
525
  };
721
- // Define credential as a getter that always returns the current promise
722
526
  Object.defineProperty(response, 'credential', {
723
527
  get() {
724
528
  return credentialWrapper.promise;
@@ -729,20 +533,15 @@ class PhoneAuthClient {
729
533
  return response;
730
534
  }
731
535
  else {
732
- // Standard mode - just return credential
733
- // Wait for and return the credential
734
536
  const credential = yield credentialWrapper.promise;
735
- // Return in standard format
736
537
  return {
737
538
  [plainResponse.session.session_key]: credential.credential
738
539
  };
739
540
  }
740
541
  }
741
542
  else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.DESKTOP) {
742
- // Desktop strategy - QR code based authentication
743
543
  const desktopData = plainResponse.data;
744
544
  const handler = new desktop_1.DesktopHandler();
745
- // Extract QR code data - convert to QRCodeData format for modal
746
545
  const qrCodeData = {
747
546
  iosQRCode: ((_c = desktopData.data) === null || _c === void 0 ? void 0 : _c.ios_qr_image) || desktopData.ios_qr_image ||
748
547
  ((_d = desktopData.data) === null || _d === void 0 ? void 0 : _d.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code || '',
@@ -750,18 +549,15 @@ class PhoneAuthClient {
750
549
  iosUrl: ((_f = desktopData.data) === null || _f === void 0 ? void 0 : _f.ios_url) || desktopData.ios_url,
751
550
  androidUrl: ((_g = desktopData.data) === null || _g === void 0 ? void 0 : _g.android_url) || desktopData.android_url
752
551
  };
753
- // Also keep snake_case format for extended response
754
552
  const qrCodeDataSnakeCase = {
755
553
  ios_qr_image: ((_h = desktopData.data) === null || _h === void 0 ? void 0 : _h.ios_qr_image) || desktopData.ios_qr_image,
756
554
  android_qr_image: ((_j = desktopData.data) === null || _j === void 0 ? void 0 : _j.android_qr_image) || desktopData.android_qr_image,
757
555
  qr_code: ((_k = desktopData.data) === null || _k === void 0 ? void 0 : _k.qr_code_image) || desktopData.qr_code_image || desktopData.qr_code,
758
556
  challenge: (_l = desktopData.data) === null || _l === void 0 ? void 0 : _l.challenge
759
557
  };
760
- // Polling options - gather from any options format
761
558
  const pollingEndpointToUse = (opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint) ||
762
559
  ((_m = this.config.endpoints) === null || _m === void 0 ? void 0 : _m.polling);
763
560
  const pollingOptions = Object.assign(Object.assign({}, opts), { pollingEndpoint: pollingEndpointToUse, pollingInterval: (opts === null || opts === void 0 ? void 0 : opts.pollingInterval) || this.config.pollingInterval || 2000, maxPollingAttempts: (opts === null || opts === void 0 ? void 0 : opts.maxPollingAttempts) || this.config.maxPollingAttempts || 30, onQRCodeReady: undefined, onStatusUpdate: undefined });
764
- // Decide whether to show modal based on preventDefaultUI
765
561
  const showModal = !preventDefaultUI;
766
562
  let modal;
767
563
  let modalRef = undefined;
@@ -773,40 +569,27 @@ class PhoneAuthClient {
773
569
  });
774
570
  if (showModal) {
775
571
  console.log('[Desktop] Creating modal with QR data:', qrCodeData);
776
- // Create and setup modal
777
572
  modal = new modal_1.AuthModal(opts === null || opts === void 0 ? void 0 : opts.modalOptions, opts === null || opts === void 0 ? void 0 : opts.callbacks);
778
573
  modal.setCloseCallback(() => {
779
574
  this.log('Desktop QR modal closed by user, cancelling polling');
780
575
  handler.cancel();
781
576
  });
782
- // Add UI callbacks to polling options
783
577
  pollingOptions.onQRCodeReady = (qrData) => {
784
578
  console.log('[Desktop] onQRCodeReady callback triggered:', qrData);
785
579
  modal.showQRCode(qrData, 'Scan with your mobile device');
786
580
  };
787
581
  pollingOptions.onStatusUpdate = (status) => {
788
- if (status.status === 'pending') {
789
- modal.updateStatus('Waiting for authentication...');
790
- }
791
- else if (status.status === 'authenticated') {
792
- modal.updateStatus('Authentication successful!');
793
- setTimeout(() => modal.close(), 1500);
794
- }
795
- else if (status.status === 'expired') {
796
- modal.updateStatus('QR code expired', true);
797
- }
798
- else if (status.status === 'error') {
799
- modal.updateStatus('Authentication failed', true);
582
+ if (status.status === 'authenticated' ||
583
+ status.status === 'error' ||
584
+ status.status === 'expired') {
585
+ modal.close();
800
586
  }
801
587
  };
802
- // Note: We don't show the QR code here. It will be shown by the onQRCodeReady callback
803
- // that gets triggered immediately when handler.invoke() is called
804
588
  modalRef = modal;
805
589
  }
806
590
  else {
807
591
  console.log('[Desktop] Modal not shown - preventDefaultUI is true');
808
592
  }
809
- // Create credential promise
810
593
  const startPolling = () => handler.invoke(plainResponse, pollingOptions).then(result => {
811
594
  if (result.authenticated && result.credential) {
812
595
  return {
@@ -819,22 +602,13 @@ class PhoneAuthClient {
819
602
  throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Desktop authentication failed');
820
603
  }
821
604
  });
822
- // Handle based on execution mode
823
605
  if (executionMode === 'extended') {
824
- // Extended mode - return control methods
825
- // Create a promise and always start polling immediately
826
606
  const credentialPromise = new Promise((resolve, reject) => {
827
- // Always start polling immediately in extended mode (consistent with Link)
828
- // This prevents the "unresolved promise trap" where developers might await
829
- // the credential before calling start_polling()
830
607
  startPolling()
831
608
  .then(resolve)
832
609
  .catch(reject);
833
610
  });
834
- // Create wrapped functions
835
611
  const wrappedStartPolling = () => __awaiter(this, void 0, void 0, function* () {
836
- // Polling has already started automatically in extended mode
837
- // This method just returns the existing credential promise for consistency
838
612
  return credentialPromise;
839
613
  });
840
614
  const wrappedStopPolling = () => {
@@ -847,9 +621,7 @@ class PhoneAuthClient {
847
621
  handler.cleanup();
848
622
  if (modal)
849
623
  modal.close();
850
- // The credential promise will be rejected by the handler.cancel() call
851
624
  };
852
- // Create the response object with a getter for is_polling
853
625
  const response = {
854
626
  strategy: 'desktop',
855
627
  session: plainResponse.session,
@@ -859,10 +631,8 @@ class PhoneAuthClient {
859
631
  start_polling: wrappedStartPolling,
860
632
  stop_polling: wrappedStopPolling,
861
633
  cancel: wrappedCancel,
862
- // This will be replaced with a getter
863
- is_polling: false // Initial value, will be overridden by getter
634
+ is_polling: false
864
635
  };
865
- // Define is_polling as a getter that returns current state
866
636
  Object.defineProperty(response, 'is_polling', {
867
637
  get() {
868
638
  return handler.isPolling();
@@ -873,11 +643,8 @@ class PhoneAuthClient {
873
643
  return response;
874
644
  }
875
645
  else {
876
- // Standard mode - return credential when complete
877
- // Start polling and wait for result
878
646
  try {
879
647
  const credential = yield startPolling();
880
- // Extract session ID for compatibility
881
648
  let sessionId = 'default';
882
649
  if (desktopData && typeof desktopData === 'object') {
883
650
  if (desktopData.data && typeof desktopData.data === 'object') {
@@ -897,10 +664,8 @@ class PhoneAuthClient {
897
664
  }
898
665
  }
899
666
  else if (plainResponse.authentication_strategy === API.AUTHENTICATION_STRATEGY.LINK) {
900
- // Link strategy - app-based authentication (iOS/Android)
901
667
  const linkData = plainResponse.data;
902
668
  const handler = new link_1.LinkHandler();
903
- // Create reusable trigger function that ONLY opens the App Clip
904
669
  const triggerLink = () => {
905
670
  var _a, _b;
906
671
  try {
@@ -920,12 +685,9 @@ class PhoneAuthClient {
920
685
  });
921
686
  }
922
687
  };
923
- // Link always auto-opens the app (no SDK UI by default)
924
- // Open immediately to preserve user gesture context
925
688
  if ((opts === null || opts === void 0 ? void 0 : opts.autoTrigger) !== false) {
926
689
  triggerLink();
927
690
  }
928
- // Start polling in the background
929
691
  console.log('[PhoneAuth Client] Link polling config:', {
930
692
  fromOptions: opts === null || opts === void 0 ? void 0 : opts.pollingEndpoint,
931
693
  fromClientConfig: (_o = this.config.endpoints) === null || _o === void 0 ? void 0 : _o.polling,
@@ -939,15 +701,11 @@ class PhoneAuthClient {
939
701
  onStatusUpdate: undefined
940
702
  };
941
703
  console.log('[PhoneAuth Client] Final Link polling options:', pollingOptions);
942
- // Handle based on execution mode
943
704
  if (executionMode === 'extended') {
944
- // Extended mode - return control methods and start polling immediately
945
705
  let pollingStarted = false;
946
706
  let pollingPromise = null;
947
- // Start polling immediately (Link always polls automatically unlike Desktop)
948
707
  pollingPromise = handler.invoke(plainResponse, pollingOptions);
949
708
  pollingStarted = true;
950
- // Create credential promise from the polling result
951
709
  const credentialPromise = pollingPromise.then(result => {
952
710
  if (result.authenticated && result.credential) {
953
711
  return {
@@ -960,10 +718,8 @@ class PhoneAuthClient {
960
718
  throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
961
719
  }
962
720
  });
963
- // Function to restart polling (for retry scenarios)
964
721
  const startPolling = () => __awaiter(this, void 0, void 0, function* () {
965
722
  if (!pollingStarted) {
966
- // This is here for API consistency, but for Link it's already polling
967
723
  pollingStarted = true;
968
724
  if (!pollingPromise) {
969
725
  pollingPromise = handler.invoke(plainResponse, pollingOptions);
@@ -980,7 +736,6 @@ class PhoneAuthClient {
980
736
  throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
981
737
  }
982
738
  }
983
- // If already polling, just return the existing promise result
984
739
  const result = yield pollingPromise;
985
740
  if (result.authenticated && result.credential) {
986
741
  return {
@@ -1000,21 +755,15 @@ class PhoneAuthClient {
1000
755
  data: {
1001
756
  app_url: linkData.url
1002
757
  },
1003
- // Re-open the app link
1004
758
  trigger: triggerLink,
1005
- // Polling control - now prevents double polling
1006
759
  start_polling: startPolling,
1007
760
  stop_polling: () => handler.cleanup(),
1008
761
  cancel: () => {
1009
762
  handler.cancel();
1010
763
  handler.cleanup();
1011
- // Note: For Link, polling is already started, so the promise
1012
- // will be rejected by the handler.cancel() call
1013
764
  },
1014
- // This will be replaced with a getter
1015
- is_polling: false // Initial value, will be overridden by getter
765
+ is_polling: false
1016
766
  };
1017
- // Define is_polling as a getter that returns current state
1018
767
  Object.defineProperty(response, 'is_polling', {
1019
768
  get() {
1020
769
  return handler.isPolling();
@@ -1025,7 +774,6 @@ class PhoneAuthClient {
1025
774
  return response;
1026
775
  }
1027
776
  else {
1028
- // Standard mode - start polling immediately and wait for completion
1029
777
  const credentialPromise = handler.invoke(plainResponse, pollingOptions).then(result => {
1030
778
  if (result.authenticated && result.credential) {
1031
779
  return {
@@ -1038,44 +786,28 @@ class PhoneAuthClient {
1038
786
  throw this.createError(error_utils_1.PhoneAuthErrorCode.USER_DENIED, result.error || 'Link authentication failed');
1039
787
  }
1040
788
  });
1041
- // Wait for credential and return in standard format
1042
789
  const credential = yield credentialPromise;
1043
790
  const aggregatorId = this.config.aggregatorId || 'default';
1044
791
  return { [aggregatorId]: credential.credential };
1045
792
  }
1046
793
  }
1047
794
  else {
1048
- // Unknown strategy
1049
795
  throw this.createError(error_utils_1.PhoneAuthErrorCode.UNSUPPORTED_STRATEGY, `Unknown authentication strategy: ${plainResponse.authentication_strategy}`);
1050
796
  }
1051
797
  });
1052
798
  }
1053
- /**
1054
- * Step 3A: Get phone number from credential
1055
- *
1056
- * @example
1057
- * ```typescript
1058
- * const prepareResp = await phoneAuthClient.preparePhoneRequest({ useCase: 'GetPhoneNumber', plmn: {...} });
1059
- * const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
1060
- * const result = await phoneAuthClient.getPhoneNumber(credential, prepareResp.session);
1061
- * console.log(result.phone_number); // +1234567890
1062
- * ```
1063
- */
1064
799
  getPhoneNumber(credentialResponse, session) {
1065
800
  return __awaiter(this, void 0, void 0, function* () {
1066
- // Extract credential string
1067
801
  const credentialString = this.extractCredentialString(credentialResponse);
1068
- // Build request body for GetPhoneNumber
1069
802
  const requestBody = {
1070
803
  session: session,
1071
804
  credential: credentialString,
1072
- use_case: API.USE_CASE.GET_PHONE_NUMBER // Required for server routing
805
+ use_case: API.USE_CASE.GET_PHONE_NUMBER
1073
806
  };
1074
- // Only show full details in debug mode, mask sensitive data otherwise
1075
807
  if (this.config.debug) {
1076
808
  this.log('Getting phone number from credential', {
1077
809
  session: session,
1078
- credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined', // Show partial for debugging
810
+ credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
1079
811
  endpoint: this.config.endpoints.process || '/api/phone-auth/process'
1080
812
  });
1081
813
  }
@@ -1119,35 +851,18 @@ class PhoneAuthClient {
1119
851
  }
1120
852
  });
1121
853
  }
1122
- /**
1123
- * Step 3B: Verify phone number with credential
1124
- *
1125
- * @example
1126
- * ```typescript
1127
- * const prepareResp = await phoneAuthClient.preparePhoneRequest({
1128
- * useCase: 'VerifyPhoneNumber',
1129
- * phoneNumber: '+1234567890'
1130
- * });
1131
- * const credential = await phoneAuthClient.invokeSecurePrompt(prepareResp);
1132
- * const result = await phoneAuthClient.verifyPhoneNumber(credential, prepareResp.session);
1133
- * console.log(result.verified); // true
1134
- * ```
1135
- */
1136
854
  verifyPhoneNumber(credentialResponse, session) {
1137
855
  return __awaiter(this, void 0, void 0, function* () {
1138
- // Extract credential string
1139
856
  const credentialString = this.extractCredentialString(credentialResponse);
1140
- // Build request body for VerifyPhoneNumber
1141
857
  const requestBody = {
1142
858
  session: session,
1143
859
  credential: credentialString,
1144
- use_case: API.USE_CASE.VERIFY_PHONE_NUMBER // Required for server routing
860
+ use_case: API.USE_CASE.VERIFY_PHONE_NUMBER
1145
861
  };
1146
- // Only show full details in debug mode, mask sensitive data otherwise
1147
862
  if (this.config.debug) {
1148
863
  this.log('Verifying phone number with credential', {
1149
864
  session: session,
1150
- credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined', // Show partial for debugging
865
+ credential: credentialString ? credentialString.substring(0, 50) + '...' : 'undefined',
1151
866
  endpoint: this.config.endpoints.process || '/api/phone-auth/process'
1152
867
  });
1153
868
  }
@@ -1194,53 +909,36 @@ class PhoneAuthClient {
1194
909
  }
1195
910
  });
1196
911
  }
1197
- /**
1198
- * Helper to extract credential string from various formats
1199
- */
1200
912
  extractCredentialString(credentialResponse) {
1201
- // If already a string, return it
1202
913
  if (typeof credentialResponse === 'string') {
1203
914
  return credentialResponse;
1204
915
  }
1205
- // Extract from vp_token object - try configured aggregatorId first, then 'glide', then 'default'
1206
916
  let credential = credentialResponse[this.config.aggregatorId || 'glide'];
1207
- // Fallback to 'glide' if not found
1208
917
  if (!credential && credentialResponse['glide']) {
1209
918
  credential = credentialResponse['glide'];
1210
919
  }
1211
- // Fallback to 'default' if still not found
1212
920
  if (!credential && credentialResponse['default']) {
1213
921
  credential = credentialResponse['default'];
1214
922
  }
1215
- // If still not found, try to get the first available key
1216
923
  if (!credential) {
1217
924
  const keys = Object.keys(credentialResponse);
1218
925
  if (keys.length > 0) {
1219
926
  credential = credentialResponse[keys[0]];
1220
927
  }
1221
928
  }
1222
- // Convert array to string if needed
1223
929
  return Array.isArray(credential) ? credential[0] : credential;
1224
930
  }
1225
- /**
1226
- * Helper to extract error details from response
1227
- */
1228
931
  extractErrorDetails(response) {
1229
932
  return __awaiter(this, void 0, void 0, function* () {
1230
933
  try {
1231
934
  const errorData = yield response.json();
1232
- // Always include the HTTP status from the response
1233
- return Object.assign(Object.assign({}, errorData), { status: response.status // Ensure HTTP status is included
1234
- });
935
+ return Object.assign(Object.assign({}, errorData), { status: response.status });
1235
936
  }
1236
937
  catch (_a) {
1237
938
  return { status: response.status, statusText: response.statusText };
1238
939
  }
1239
940
  });
1240
941
  }
1241
- /**
1242
- * Fetch with timeout
1243
- */
1244
942
  fetchWithTimeout(url, options) {
1245
943
  return __awaiter(this, void 0, void 0, function* () {
1246
944
  const controller = new AbortController();
@@ -1253,25 +951,18 @@ class PhoneAuthClient {
1253
951
  }
1254
952
  });
1255
953
  }
1256
- /**
1257
- * Create an AuthError
1258
- */
1259
954
  createError(code, message, details) {
1260
955
  const error = {
1261
956
  code,
1262
957
  message,
1263
- details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined // Remove the original error object
1264
- }) : details
958
+ details: (details === null || details === void 0 ? void 0 : details.originalError) ? Object.assign(Object.assign({}, details), { originalError: undefined }) : details
1265
959
  };
1266
- // Add browser error details if present
1267
960
  if (details === null || details === void 0 ? void 0 : details.browserError) {
1268
961
  error.browserError = details.browserError;
1269
962
  }
1270
- // Add context if present
1271
963
  if (details === null || details === void 0 ? void 0 : details.context) {
1272
964
  error.context = details.context;
1273
965
  }
1274
- // Add other specific fields
1275
966
  if (details === null || details === void 0 ? void 0 : details.status) {
1276
967
  error.status = details.status;
1277
968
  }
@@ -1286,51 +977,37 @@ class PhoneAuthClient {
1286
977
  }
1287
978
  return error;
1288
979
  }
1289
- /**
1290
- * Type guard for AuthError
1291
- */
1292
980
  isAuthError(error) {
1293
981
  return error && typeof error.code === 'string' && typeof error.message === 'string';
1294
982
  }
1295
- /**
1296
- * Debug logging
1297
- */
1298
983
  log(...args) {
1299
984
  if (this.debug) {
1300
985
  console.log('[PhoneAuth]', ...args);
1301
986
  }
1302
987
  }
1303
- /**
1304
- * Determine if an error should trigger a retry
1305
- */
1306
988
  shouldRetry(error) {
1307
989
  var _a, _b, _c;
1308
- // Don't retry on 4xx client errors (these are not transient)
1309
990
  if (error.status && error.status >= 400 && error.status < 500) {
1310
991
  return false;
1311
992
  }
1312
- // Don't retry on explicit user denial or unsupported browser
1313
- // USER_DENIED cannot be retried automatically because the Digital Credentials API
1314
- // requires user interaction (transient activation)
1315
993
  const nonRetryableCodes = [
1316
994
  'USER_DENIED',
1317
995
  'BROWSER_NOT_SUPPORTED',
1318
996
  'INVALID_PHONE_NUMBER',
1319
997
  'INVALID_PARAMETERS',
1320
998
  'MISSING_PARAMETERS',
1321
- 'UNPROCESSABLE_ENTITY', // 422 errors
1322
- 'USE_CASE_MISMATCH', // Use case validation errors
1323
- 'PHONE_NUMBER_MISMATCH', // Phone verification failures
1324
- 'VERIFICATION_FAILED', // Verification failures
1325
- 'INVALID_CREDENTIAL', // Bad credentials
1326
- 'CARRIER_NOT_ELIGIBLE', // Carrier not supported
1327
- 'SESSION_EXPIRED', // Session expired
1328
- 'INVALID_SESSION' // Invalid session
999
+ 'UNPROCESSABLE_ENTITY',
1000
+ 'USE_CASE_MISMATCH',
1001
+ 'PHONE_NUMBER_MISMATCH',
1002
+ 'VERIFICATION_FAILED',
1003
+ 'INVALID_CREDENTIAL',
1004
+ 'CARRIER_NOT_ELIGIBLE',
1005
+ 'SESSION_EXPIRED',
1006
+ 'INVALID_SESSION'
1329
1007
  ];
1330
1008
  if (nonRetryableCodes.includes(error.code)) {
1331
1009
  return false;
1332
1010
  }
1333
- // Only retry on network and server errors (5xx)
1334
1011
  const retryableCodes = [
1335
1012
  'NETWORK_ERROR',
1336
1013
  'REQUEST_TIMEOUT',
@@ -1339,7 +1016,6 @@ class PhoneAuthClient {
1339
1016
  'BAD_GATEWAY',
1340
1017
  'INTERNAL_SERVER_ERROR'
1341
1018
  ];
1342
- // Also check for cross-device error types in details
1343
1019
  const isCrossDeviceError = ((_a = error.details) === null || _a === void 0 ? void 0 : _a.errorType) && [
1344
1020
  'CROSS_DEVICE_TIMEOUT',
1345
1021
  'CROSS_DEVICE_CONNECTION_LOST',
@@ -1349,17 +1025,13 @@ class PhoneAuthClient {
1349
1025
  isCrossDeviceError ||
1350
1026
  (((_b = error.browserError) === null || _b === void 0 ? void 0 : _b.name) === 'NetworkError' && ((_c = error.browserError) === null || _c === void 0 ? void 0 : _c.code) !== 19);
1351
1027
  }
1352
- /**
1353
- * Analyze and enhance errors specific to cross-device flows
1354
- */
1355
1028
  analyzeCrossDeviceError(error, prepareResponse) {
1356
1029
  var _a;
1357
1030
  const errorObj = error;
1358
- // Check for specific cross-device error patterns
1359
1031
  const isCrossDeviceTimeout = (errorObj.name === 'AbortError' && this.crossDeviceActive) ||
1360
1032
  (((_a = errorObj.message) === null || _a === void 0 ? void 0 : _a.includes('timeout')) && this.crossDeviceActive);
1361
1033
  const isCrossDeviceNetworkIssue = errorObj.name === 'NetworkError' &&
1362
- errorObj.code !== 19 && // Not user cancellation
1034
+ errorObj.code !== 19 &&
1363
1035
  this.crossDeviceActive;
1364
1036
  if (isCrossDeviceTimeout) {
1365
1037
  return {
@@ -1387,12 +1059,8 @@ class PhoneAuthClient {
1387
1059
  browserError: error.browserError
1388
1060
  };
1389
1061
  }
1390
- // Return the original error if not cross-device specific
1391
1062
  return error;
1392
1063
  }
1393
- /**
1394
- * Cache successful session for retry scenarios
1395
- */
1396
1064
  cacheSession(options, result) {
1397
1065
  const cacheKey = this.getCacheKey(options);
1398
1066
  this.sessionCache.set(cacheKey, {
@@ -1400,15 +1068,11 @@ class PhoneAuthClient {
1400
1068
  data: result
1401
1069
  });
1402
1070
  }
1403
- /**
1404
- * Retrieve cached session if available and recent
1405
- */
1406
1071
  getCachedSession(options) {
1407
1072
  const cacheKey = this.getCacheKey(options);
1408
1073
  const cached = this.sessionCache.get(cacheKey);
1409
1074
  if (!cached)
1410
1075
  return null;
1411
- // Cache valid for 5 minutes
1412
1076
  const cacheValidMs = 5 * 60 * 1000;
1413
1077
  if (Date.now() - cached.timestamp > cacheValidMs) {
1414
1078
  this.sessionCache.delete(cacheKey);
@@ -1416,22 +1080,14 @@ class PhoneAuthClient {
1416
1080
  }
1417
1081
  return cached.data;
1418
1082
  }
1419
- /**
1420
- * Generate cache key for session storage
1421
- */
1422
1083
  getCacheKey(options) {
1423
1084
  var _a, _b;
1424
1085
  return `${options.use_case}-${options.phone_number || 'no-phone'}-${((_a = options.plmn) === null || _a === void 0 ? void 0 : _a.mcc) || ''}-${((_b = options.plmn) === null || _b === void 0 ? void 0 : _b.mnc) || ''}`;
1425
1086
  }
1426
- /**
1427
- * Set up periodic cache cleanup
1428
- */
1429
1087
  setupCacheCleanup() {
1430
- // Only run cleanup in browser environment (not during SSR)
1431
1088
  if (typeof window === 'undefined') {
1432
1089
  return;
1433
1090
  }
1434
- // Clean up expired cache entries every minute
1435
1091
  setInterval(() => {
1436
1092
  const now = Date.now();
1437
1093
  const cacheValidMs = 5 * 60 * 1000;
@@ -1442,9 +1098,6 @@ class PhoneAuthClient {
1442
1098
  }
1443
1099
  }, 60000);
1444
1100
  }
1445
- /**
1446
- * Utility delay function
1447
- */
1448
1101
  delay(ms) {
1449
1102
  return new Promise(resolve => setTimeout(resolve, ms));
1450
1103
  }