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