@glideidentity/web-client-sdk 4.4.8-beta.1 → 4.4.8-beta.2

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 (34) hide show
  1. package/dist/adapters/react/usePhoneAuth.d.ts +1 -1
  2. package/dist/adapters/vue/useClient.d.ts +3 -3
  3. package/dist/adapters/vue/usePhoneAuth.d.ts +1 -1
  4. package/dist/browser/web-client-sdk.min.js +1 -1
  5. package/dist/core/phone-auth/api-types.d.ts +112 -27
  6. package/dist/core/phone-auth/client.d.ts +13 -11
  7. package/dist/core/phone-auth/client.js +257 -248
  8. package/dist/core/phone-auth/index.d.ts +1 -1
  9. package/dist/core/phone-auth/index.js +7 -2
  10. package/dist/core/phone-auth/strategies/desktop.d.ts +1 -0
  11. package/dist/core/phone-auth/strategies/desktop.js +64 -18
  12. package/dist/core/phone-auth/type-guards.d.ts +61 -43
  13. package/dist/core/phone-auth/type-guards.js +82 -44
  14. package/dist/core/phone-auth/ui/modal.js +14 -1
  15. package/dist/core/version.js +1 -1
  16. package/dist/esm/adapters/react/usePhoneAuth.d.ts +1 -1
  17. package/dist/esm/adapters/vue/useClient.d.ts +3 -3
  18. package/dist/esm/adapters/vue/usePhoneAuth.d.ts +1 -1
  19. package/dist/esm/core/phone-auth/api-types.d.ts +112 -27
  20. package/dist/esm/core/phone-auth/client.d.ts +13 -11
  21. package/dist/esm/core/phone-auth/client.js +257 -248
  22. package/dist/esm/core/phone-auth/index.d.ts +1 -1
  23. package/dist/esm/core/phone-auth/index.js +3 -1
  24. package/dist/esm/core/phone-auth/strategies/desktop.d.ts +1 -0
  25. package/dist/esm/core/phone-auth/strategies/desktop.js +64 -18
  26. package/dist/esm/core/phone-auth/type-guards.d.ts +61 -43
  27. package/dist/esm/core/phone-auth/type-guards.js +76 -41
  28. package/dist/esm/core/phone-auth/ui/modal.js +14 -1
  29. package/dist/esm/core/version.js +1 -1
  30. package/dist/esm/index.d.ts +2 -2
  31. package/dist/esm/index.js +3 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +7 -2
  34. package/package.json +1 -1
@@ -3,4 +3,6 @@ export * from './types';
3
3
  export { PhoneAuthErrorCode, isPhoneAuthError, isUserError, getUserMessage, isErrorCode, getRetryDelay, isRetryableError, serializeError, createErrorBreadcrumb } from './error-utils';
4
4
  export { validatePhoneNumber, validatePlmn, validateUseCaseRequirements, validateConsentData, validateNonce } from './validation-utils';
5
5
  export { MobileDebugConsole } from './ui/mobile-debug-console';
6
- export { isHeadlessResult, isCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, requiresPolling, requiresUserAction } from './type-guards';
6
+ export { isExtendedResponse, isCredential, isAuthCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, hasPollingControls, hasTrigger,
7
+ // Deprecated aliases
8
+ isHeadlessResult, requiresPolling, requiresUserAction } from './type-guards';
@@ -55,6 +55,7 @@ export declare class DesktopHandler implements StrategyHandler {
55
55
  private pollingAttempts;
56
56
  private isCancelled;
57
57
  private onCancel?;
58
+ private pollingReject?;
58
59
  /**
59
60
  * Maps backend HTTP status codes to client status
60
61
  * @param httpStatus HTTP status code from backend
@@ -57,7 +57,20 @@ export class DesktopHandler {
57
57
  if (desktopData && desktopData.data && typeof desktopData.data === 'object') {
58
58
  const innerData = desktopData.data;
59
59
  // Try to extract from inner data
60
- qrCode = innerData.qr_code_image || innerData.qr_code;
60
+ // Support both single QR (qr_code_image) and dual-platform QR (ios/android)
61
+ if (innerData.ios_qr_image || innerData.android_qr_image) {
62
+ // Dual-platform QR format
63
+ qrCode = {
64
+ iosQRCode: innerData.ios_qr_image,
65
+ androidQRCode: innerData.android_qr_image,
66
+ iosUrl: innerData.ios_url,
67
+ androidUrl: innerData.android_url
68
+ };
69
+ }
70
+ else {
71
+ // Single QR format (legacy)
72
+ qrCode = innerData.qr_code_image || innerData.qr_code;
73
+ }
61
74
  sessionId = innerData.session_id;
62
75
  pollingEndpoint = innerData.status_url;
63
76
  pollingInterval = innerData.polling_interval;
@@ -84,33 +97,44 @@ export class DesktopHandler {
84
97
  options.onQRCodeReady(qrCode);
85
98
  }
86
99
  // Use polling endpoint with this priority:
87
- // 1. Developer-provided endpoint from options
100
+ // 1. Developer-provided endpoint from options (highest priority)
88
101
  // 2. Backend-provided endpoint
89
- // 3. Error if none available
90
- const finalPollingEndpoint = (options === null || options === void 0 ? void 0 : options.pollingEndpoint) || pollingEndpoint;
91
- // If no polling endpoint available, return QR code data and let the app handle the rest
102
+ // 3. Hardcoded fallback
103
+ console.log('[Desktop QR] Polling endpoint selection:');
104
+ console.log(' - options?.pollingEndpoint:', options === null || options === void 0 ? void 0 : options.pollingEndpoint);
105
+ console.log(' - backend pollingEndpoint:', pollingEndpoint);
106
+ let finalPollingEndpoint = options === null || options === void 0 ? void 0 : options.pollingEndpoint;
107
+ let endpointSource = 'options';
92
108
  if (!finalPollingEndpoint) {
93
- return {
94
- authenticated: false,
95
- session: data.session,
96
- error: 'No polling endpoint provided - configure endpoints.polling or ensure backend provides status_url'
97
- };
109
+ finalPollingEndpoint = pollingEndpoint; // Backend-provided status_url
110
+ endpointSource = 'backend';
98
111
  }
112
+ if (!finalPollingEndpoint) {
113
+ // Use hardcoded fallback - this will be constructed in the polling function
114
+ console.log('[Desktop QR] No polling endpoint provided, will use hardcoded fallback');
115
+ endpointSource = 'fallback';
116
+ }
117
+ console.log('[Desktop QR] Selected endpoint:', finalPollingEndpoint, 'from source:', endpointSource);
99
118
  // Start polling for authentication status
100
119
  const finalPollingInterval = (options === null || options === void 0 ? void 0 : options.pollingInterval) || pollingInterval || 2000;
101
120
  const maxAttempts = (options === null || options === void 0 ? void 0 : options.maxPollingAttempts) || 150; // Default to 5 minutes
102
- return this.startPolling(finalPollingEndpoint, sessionId, finalPollingInterval, maxAttempts, expiresIn || 300, // Default 5 minutes expiry
103
- options);
121
+ console.log(`[Desktop QR] Starting polling - endpoint source: ${endpointSource}, interval: ${finalPollingInterval}ms, max attempts: ${maxAttempts}`);
122
+ return this.startPolling(finalPollingEndpoint || '', // Pass empty string if undefined, will use fallback
123
+ sessionId, finalPollingInterval, maxAttempts, expiresIn || 300, // Default 5 minutes expiry
124
+ options, endpointSource // Pass the endpoint source for logging
125
+ );
104
126
  });
105
127
  }
106
128
  /**
107
129
  * Start polling for authentication status
108
130
  */
109
- startPolling(endpoint, sessionId, interval, maxAttempts, expiresIn, options) {
110
- return __awaiter(this, void 0, void 0, function* () {
131
+ startPolling(endpoint_1, sessionId_1, interval_1, maxAttempts_1, expiresIn_1, options_1) {
132
+ return __awaiter(this, arguments, void 0, function* (endpoint, sessionId, interval, maxAttempts, expiresIn, options, endpointSource = 'unknown') {
111
133
  const startTime = Date.now();
112
134
  const expiryTime = startTime + (expiresIn * 1000);
113
135
  return new Promise((resolve, reject) => {
136
+ // Store the reject function so we can call it from cancel()
137
+ this.pollingReject = reject;
114
138
  const poll = () => __awaiter(this, void 0, void 0, function* () {
115
139
  try {
116
140
  // Check if cancelled
@@ -125,9 +149,10 @@ export class DesktopHandler {
125
149
  message: 'Authentication cancelled'
126
150
  });
127
151
  }
128
- resolve({
129
- authenticated: false,
130
- error: 'Authentication cancelled'
152
+ // Reject the promise with a cancellation error
153
+ reject({
154
+ code: 'USER_DENIED',
155
+ message: 'Authentication cancelled by user'
131
156
  });
132
157
  return;
133
158
  }
@@ -175,12 +200,19 @@ export class DesktopHandler {
175
200
  const url = new URL(endpoint);
176
201
  statusUrl = `${url.protocol}//${url.host}/public/public/status/${sessionId}`;
177
202
  }
203
+ else if (endpoint && endpoint !== '') {
204
+ // Relative path provided (e.g. '/api/phone-auth/status')
205
+ // Append session ID to the provided endpoint
206
+ statusUrl = `${endpoint}/${sessionId}`;
207
+ }
178
208
  else {
179
- // Use relative path that will be proxied through the app's server
209
+ // No endpoint provided - use hardcoded fallback
180
210
  // This ensures it goes through the same domain and uses the app's API configuration
181
211
  statusUrl = `/api/phone-auth/status/${sessionId}`;
182
212
  }
183
213
  // Poll the public endpoint - no authentication required
214
+ console.log(`[Desktop QR] Polling status (attempt ${this.pollingAttempts}/${maxAttempts})`);
215
+ console.log(`[Desktop QR] Using ${endpointSource} endpoint: ${statusUrl}`);
184
216
  const response = yield fetch(statusUrl, {
185
217
  method: 'GET',
186
218
  headers: {
@@ -188,6 +220,7 @@ export class DesktopHandler {
188
220
  // No Authorization header needed for public endpoint
189
221
  }
190
222
  });
223
+ console.log(`[Desktop QR] Status response: ${response.status} ${response.statusText}`);
191
224
  // Backend Status Code Mapping:
192
225
  // - 200 OK: Session exists (body has 'pending' or 'completed' status)
193
226
  // - 410 Gone: Session expired after timeout
@@ -215,6 +248,7 @@ export class DesktopHandler {
215
248
  });
216
249
  }
217
250
  // Return the session data for next steps
251
+ this.pollingReject = undefined; // Clear the reject function on success
218
252
  resolve({
219
253
  authenticated: true,
220
254
  credential: result.credential || result.session_key || sessionId,
@@ -339,10 +373,12 @@ export class DesktopHandler {
339
373
  */
340
374
  stopPolling() {
341
375
  if (this.pollingIntervalId) {
376
+ console.log('[Desktop QR] Stopping polling');
342
377
  clearInterval(this.pollingIntervalId);
343
378
  this.pollingIntervalId = undefined;
344
379
  }
345
380
  this.pollingAttempts = 0;
381
+ // Don't clear pollingReject here - it's needed by cancel()
346
382
  }
347
383
  /**
348
384
  * Format response for backend processing
@@ -374,15 +410,25 @@ export class DesktopHandler {
374
410
  this.stopPolling();
375
411
  this.isCancelled = false;
376
412
  this.onCancel = undefined;
413
+ this.pollingReject = undefined;
377
414
  }
378
415
  /**
379
416
  * Cancel the ongoing authentication
380
417
  */
381
418
  cancel() {
382
419
  var _a;
420
+ console.log('[Desktop QR] Cancelling authentication');
383
421
  this.isCancelled = true;
384
422
  this.stopPolling();
385
423
  (_a = this.onCancel) === null || _a === void 0 ? void 0 : _a.call(this);
424
+ // Immediately reject the polling promise
425
+ if (this.pollingReject) {
426
+ this.pollingReject({
427
+ code: 'USER_DENIED',
428
+ message: 'Authentication cancelled by user'
429
+ });
430
+ this.pollingReject = undefined;
431
+ }
386
432
  }
387
433
  }
388
434
  /**
@@ -4,29 +4,29 @@
4
4
  * These utilities help developers work with the SDK responses in a type-safe way
5
5
  * without having to write their own type checking logic.
6
6
  */
7
- import type { HeadlessResult } from './api-types';
7
+ import type { AnyExtendedResponse, DesktopExtendedResponse, LinkExtendedResponse, TS43ExtendedResponse, AuthCredential } from './api-types';
8
8
  /**
9
- * Type guard to check if the result is a HeadlessResult (headless mode)
10
- * or a Credential (UI mode).
9
+ * Type guard to check if the result is an ExtendedResponse (extended mode)
10
+ * or a Credential (standard mode).
11
11
  *
12
12
  * @example
13
13
  * ```typescript
14
- * const result = await invokeSecurePrompt(sdkRequest);
14
+ * const result = await invokeSecurePrompt(sdkRequest, { executionMode: 'extended' });
15
15
  *
16
- * if (isHeadlessResult(result)) {
17
- * // TypeScript knows this is HeadlessResult
16
+ * if (isExtendedResponse(result)) {
17
+ * // TypeScript knows this is ExtendedResponse
18
18
  * console.log(result.strategy);
19
- * await result.trigger();
19
+ * await result.cancel();
20
20
  * } else {
21
21
  * // TypeScript knows this is a Credential
22
22
  * const processedResult = await verifyPhoneNumberCredential(result, session);
23
23
  * }
24
24
  * ```
25
25
  */
26
- export declare function isHeadlessResult(result: any): result is HeadlessResult;
26
+ export declare function isExtendedResponse(result: any): result is AnyExtendedResponse;
27
27
  /**
28
- * Type guard to check if the result is a Credential (UI mode response).
29
- * A credential is either a string token or an object without HeadlessResult properties.
28
+ * Type guard to check if the result is a Credential (standard mode response).
29
+ * A credential is either a string token or an object without ExtendedResponse properties.
30
30
  *
31
31
  * @example
32
32
  * ```typescript
@@ -40,55 +40,61 @@ export declare function isCredential(result: any): result is string | {
40
40
  [aggregator_id: string]: string | string[];
41
41
  };
42
42
  /**
43
- * Type guard to check if a HeadlessResult is using the Link strategy.
43
+ * Type guard to check if the result is an AuthCredential object.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * if (isAuthCredential(result)) {
48
+ * console.log(result.credential);
49
+ * console.log(result.authenticated);
50
+ * }
51
+ * ```
52
+ */
53
+ export declare function isAuthCredential(result: any): result is AuthCredential;
54
+ /**
55
+ * Type guard to check if an ExtendedResponse is using the Link strategy.
44
56
  * Link strategy involves opening an app link (App Clip on iOS, app on Android).
45
57
  *
46
58
  * @example
47
59
  * ```typescript
48
- * if (isHeadlessResult(result) && isLinkStrategy(result)) {
49
- * // Show custom button for opening app
50
- * myButton.onclick = () => result.trigger();
51
- * await result.pollingPromise;
60
+ * if (isExtendedResponse(result) && isLinkStrategy(result)) {
61
+ * // Re-trigger app opening if needed
62
+ * result.trigger();
63
+ * await result.credential;
52
64
  * }
53
65
  * ```
54
66
  */
55
- export declare function isLinkStrategy(result: HeadlessResult): result is HeadlessResult & {
56
- strategy: 'link';
57
- };
67
+ export declare function isLinkStrategy(result: AnyExtendedResponse): result is LinkExtendedResponse;
58
68
  /**
59
- * Type guard to check if a HeadlessResult is using the TS43 strategy.
69
+ * Type guard to check if an ExtendedResponse is using the TS43 strategy.
60
70
  * TS43 strategy uses the browser's Digital Credentials API.
61
71
  *
62
72
  * @example
63
73
  * ```typescript
64
- * if (isHeadlessResult(result) && isTS43Strategy(result)) {
65
- * // Invoke credential API immediately or on button click
66
- * const credential = await result.trigger();
74
+ * if (isExtendedResponse(result) && isTS43Strategy(result)) {
75
+ * // Re-trigger credential request if needed
76
+ * await result.trigger();
67
77
  * }
68
78
  * ```
69
79
  */
70
- export declare function isTS43Strategy(result: HeadlessResult): result is HeadlessResult & {
71
- strategy: 'ts43';
72
- };
80
+ export declare function isTS43Strategy(result: AnyExtendedResponse): result is TS43ExtendedResponse;
73
81
  /**
74
- * Type guard to check if a HeadlessResult is using the Desktop strategy.
82
+ * Type guard to check if an ExtendedResponse is using the Desktop strategy.
75
83
  * Desktop strategy involves QR codes for cross-device authentication.
76
84
  *
77
85
  * @example
78
86
  * ```typescript
79
- * if (isHeadlessResult(result) && isDesktopStrategy(result)) {
87
+ * if (isExtendedResponse(result) && isDesktopStrategy(result)) {
80
88
  * // Show custom QR code UI
81
- * displayQRCode(result.qrCodeUrl);
82
- * await result.pollingPromise;
89
+ * displayQRCode(result.qr_code_data);
90
+ * await result.start_polling();
83
91
  * }
84
92
  * ```
85
93
  */
86
- export declare function isDesktopStrategy(result: HeadlessResult): result is HeadlessResult & {
87
- strategy: 'desktop';
88
- };
94
+ export declare function isDesktopStrategy(result: AnyExtendedResponse): result is DesktopExtendedResponse;
89
95
  /**
90
96
  * Helper function to safely get the authentication strategy from any result.
91
- * Returns undefined if the result is not a HeadlessResult.
97
+ * Returns undefined if the result is not an ExtendedResponse.
92
98
  *
93
99
  * @example
94
100
  * ```typescript
@@ -100,26 +106,38 @@ export declare function isDesktopStrategy(result: HeadlessResult): result is Hea
100
106
  */
101
107
  export declare function getStrategy(result: any): 'link' | 'ts43' | 'desktop' | undefined;
102
108
  /**
103
- * Helper function to determine if a result requires polling.
104
- * Link and Desktop strategies require polling, TS43 does not.
109
+ * Helper function to determine if a result has polling controls.
110
+ * Link and Desktop strategies have polling controls in extended mode.
105
111
  *
106
112
  * @example
107
113
  * ```typescript
108
- * if (requiresPolling(result)) {
109
- * await result.pollingPromise;
114
+ * if (hasPollingControls(result)) {
115
+ * await result.start_polling();
110
116
  * }
111
117
  * ```
112
118
  */
113
- export declare function requiresPolling(result: any): boolean;
119
+ export declare function hasPollingControls(result: any): boolean;
114
120
  /**
115
- * Helper function to determine if a result requires user interaction.
116
- * All headless strategies require some form of user interaction.
121
+ * Helper function to determine if a result has a trigger method.
122
+ * Link and TS43 strategies have trigger methods in extended mode.
117
123
  *
118
124
  * @example
119
125
  * ```typescript
120
- * if (requiresUserAction(result)) {
121
- * showActionButton();
126
+ * if (hasTrigger(result)) {
127
+ * result.trigger();
122
128
  * }
123
129
  * ```
124
130
  */
125
- export declare function requiresUserAction(result: any): boolean;
131
+ export declare function hasTrigger(result: any): boolean;
132
+ /**
133
+ * @deprecated Use isExtendedResponse instead
134
+ */
135
+ export declare const isHeadlessResult: typeof isExtendedResponse;
136
+ /**
137
+ * @deprecated Use hasPollingControls instead
138
+ */
139
+ export declare const requiresPolling: typeof hasPollingControls;
140
+ /**
141
+ * @deprecated This function is no longer needed as extended mode handles user actions differently
142
+ */
143
+ export declare const requiresUserAction: (result: any) => boolean;
@@ -5,33 +5,34 @@
5
5
  * without having to write their own type checking logic.
6
6
  */
7
7
  /**
8
- * Type guard to check if the result is a HeadlessResult (headless mode)
9
- * or a Credential (UI mode).
8
+ * Type guard to check if the result is an ExtendedResponse (extended mode)
9
+ * or a Credential (standard mode).
10
10
  *
11
11
  * @example
12
12
  * ```typescript
13
- * const result = await invokeSecurePrompt(sdkRequest);
13
+ * const result = await invokeSecurePrompt(sdkRequest, { executionMode: 'extended' });
14
14
  *
15
- * if (isHeadlessResult(result)) {
16
- * // TypeScript knows this is HeadlessResult
15
+ * if (isExtendedResponse(result)) {
16
+ * // TypeScript knows this is ExtendedResponse
17
17
  * console.log(result.strategy);
18
- * await result.trigger();
18
+ * await result.cancel();
19
19
  * } else {
20
20
  * // TypeScript knows this is a Credential
21
21
  * const processedResult = await verifyPhoneNumberCredential(result, session);
22
22
  * }
23
23
  * ```
24
24
  */
25
- export function isHeadlessResult(result) {
25
+ export function isExtendedResponse(result) {
26
26
  return result &&
27
27
  typeof result === 'object' &&
28
28
  'strategy' in result &&
29
- 'trigger' in result &&
30
- typeof result.trigger === 'function';
29
+ 'credential' in result &&
30
+ 'cancel' in result &&
31
+ typeof result.cancel === 'function';
31
32
  }
32
33
  /**
33
- * Type guard to check if the result is a Credential (UI mode response).
34
- * A credential is either a string token or an object without HeadlessResult properties.
34
+ * Type guard to check if the result is a Credential (standard mode response).
35
+ * A credential is either a string token or an object without ExtendedResponse properties.
35
36
  *
36
37
  * @example
37
38
  * ```typescript
@@ -47,22 +48,40 @@ export function isCredential(result) {
47
48
  // String credentials are valid
48
49
  if (typeof result === 'string')
49
50
  return true;
50
- // Object credentials should NOT have HeadlessResult properties
51
+ // Object credentials should NOT have ExtendedResponse properties
51
52
  if (typeof result === 'object') {
52
- return !('strategy' in result) && !('trigger' in result);
53
+ return !('strategy' in result) && !('credential' in result) && !('cancel' in result);
53
54
  }
54
55
  return false;
55
56
  }
56
57
  /**
57
- * Type guard to check if a HeadlessResult is using the Link strategy.
58
+ * Type guard to check if the result is an AuthCredential object.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * if (isAuthCredential(result)) {
63
+ * console.log(result.credential);
64
+ * console.log(result.authenticated);
65
+ * }
66
+ * ```
67
+ */
68
+ export function isAuthCredential(result) {
69
+ return result &&
70
+ typeof result === 'object' &&
71
+ 'credential' in result &&
72
+ 'authenticated' in result &&
73
+ 'session' in result;
74
+ }
75
+ /**
76
+ * Type guard to check if an ExtendedResponse is using the Link strategy.
58
77
  * Link strategy involves opening an app link (App Clip on iOS, app on Android).
59
78
  *
60
79
  * @example
61
80
  * ```typescript
62
- * if (isHeadlessResult(result) && isLinkStrategy(result)) {
63
- * // Show custom button for opening app
64
- * myButton.onclick = () => result.trigger();
65
- * await result.pollingPromise;
81
+ * if (isExtendedResponse(result) && isLinkStrategy(result)) {
82
+ * // Re-trigger app opening if needed
83
+ * result.trigger();
84
+ * await result.credential;
66
85
  * }
67
86
  * ```
68
87
  */
@@ -70,14 +89,14 @@ export function isLinkStrategy(result) {
70
89
  return result.strategy === 'link';
71
90
  }
72
91
  /**
73
- * Type guard to check if a HeadlessResult is using the TS43 strategy.
92
+ * Type guard to check if an ExtendedResponse is using the TS43 strategy.
74
93
  * TS43 strategy uses the browser's Digital Credentials API.
75
94
  *
76
95
  * @example
77
96
  * ```typescript
78
- * if (isHeadlessResult(result) && isTS43Strategy(result)) {
79
- * // Invoke credential API immediately or on button click
80
- * const credential = await result.trigger();
97
+ * if (isExtendedResponse(result) && isTS43Strategy(result)) {
98
+ * // Re-trigger credential request if needed
99
+ * await result.trigger();
81
100
  * }
82
101
  * ```
83
102
  */
@@ -85,15 +104,15 @@ export function isTS43Strategy(result) {
85
104
  return result.strategy === 'ts43';
86
105
  }
87
106
  /**
88
- * Type guard to check if a HeadlessResult is using the Desktop strategy.
107
+ * Type guard to check if an ExtendedResponse is using the Desktop strategy.
89
108
  * Desktop strategy involves QR codes for cross-device authentication.
90
109
  *
91
110
  * @example
92
111
  * ```typescript
93
- * if (isHeadlessResult(result) && isDesktopStrategy(result)) {
112
+ * if (isExtendedResponse(result) && isDesktopStrategy(result)) {
94
113
  * // Show custom QR code UI
95
- * displayQRCode(result.qrCodeUrl);
96
- * await result.pollingPromise;
114
+ * displayQRCode(result.qr_code_data);
115
+ * await result.start_polling();
97
116
  * }
98
117
  * ```
99
118
  */
@@ -102,7 +121,7 @@ export function isDesktopStrategy(result) {
102
121
  }
103
122
  /**
104
123
  * Helper function to safely get the authentication strategy from any result.
105
- * Returns undefined if the result is not a HeadlessResult.
124
+ * Returns undefined if the result is not an ExtendedResponse.
106
125
  *
107
126
  * @example
108
127
  * ```typescript
@@ -113,38 +132,54 @@ export function isDesktopStrategy(result) {
113
132
  * ```
114
133
  */
115
134
  export function getStrategy(result) {
116
- if (isHeadlessResult(result)) {
135
+ if (isExtendedResponse(result)) {
117
136
  return result.strategy;
118
137
  }
119
138
  return undefined;
120
139
  }
121
140
  /**
122
- * Helper function to determine if a result requires polling.
123
- * Link and Desktop strategies require polling, TS43 does not.
141
+ * Helper function to determine if a result has polling controls.
142
+ * Link and Desktop strategies have polling controls in extended mode.
124
143
  *
125
144
  * @example
126
145
  * ```typescript
127
- * if (requiresPolling(result)) {
128
- * await result.pollingPromise;
146
+ * if (hasPollingControls(result)) {
147
+ * await result.start_polling();
129
148
  * }
130
149
  * ```
131
150
  */
132
- export function requiresPolling(result) {
133
- if (!isHeadlessResult(result))
151
+ export function hasPollingControls(result) {
152
+ if (!isExtendedResponse(result))
134
153
  return false;
135
- return result.strategy === 'link' || result.strategy === 'desktop';
154
+ return (result.strategy === 'link' && 'start_polling' in result) ||
155
+ (result.strategy === 'desktop' && 'start_polling' in result);
136
156
  }
137
157
  /**
138
- * Helper function to determine if a result requires user interaction.
139
- * All headless strategies require some form of user interaction.
158
+ * Helper function to determine if a result has a trigger method.
159
+ * Link and TS43 strategies have trigger methods in extended mode.
140
160
  *
141
161
  * @example
142
162
  * ```typescript
143
- * if (requiresUserAction(result)) {
144
- * showActionButton();
163
+ * if (hasTrigger(result)) {
164
+ * result.trigger();
145
165
  * }
146
166
  * ```
147
167
  */
148
- export function requiresUserAction(result) {
149
- return isHeadlessResult(result);
168
+ export function hasTrigger(result) {
169
+ if (!isExtendedResponse(result))
170
+ return false;
171
+ return 'trigger' in result && typeof result.trigger === 'function';
150
172
  }
173
+ // Export legacy function names as deprecated aliases for backward compatibility
174
+ /**
175
+ * @deprecated Use isExtendedResponse instead
176
+ */
177
+ export const isHeadlessResult = isExtendedResponse;
178
+ /**
179
+ * @deprecated Use hasPollingControls instead
180
+ */
181
+ export const requiresPolling = hasPollingControls;
182
+ /**
183
+ * @deprecated This function is no longer needed as extended mode handles user actions differently
184
+ */
185
+ export const requiresUserAction = (result) => false;
@@ -50,6 +50,11 @@ export class AuthModal {
50
50
  */
51
51
  showQRCode(qrCodeData, statusMessage = 'Scan QR code with your phone') {
52
52
  console.log('[Modal] showQRCode called with:', qrCodeData);
53
+ // If modal is already open, don't recreate it
54
+ if (this.isOpen) {
55
+ console.log('[Modal] Modal already open, skipping recreation');
56
+ return;
57
+ }
53
58
  // Check if it's the new dual-platform format with VALID Android QR code
54
59
  if (typeof qrCodeData === 'object' && qrCodeData.iosQRCode) {
55
60
  const hasValidAndroidQR = qrCodeData.androidQRCode &&
@@ -59,6 +64,8 @@ export class AuthModal {
59
64
  if (hasValidAndroidQR) {
60
65
  console.log('[Modal] Using dual-platform modal');
61
66
  this.createDualPlatformQRModal(qrCodeData, statusMessage);
67
+ // Note: createDualPlatformQRModal calls show() internally
68
+ return;
62
69
  }
63
70
  else {
64
71
  console.log('[Modal] Android QR missing/empty, using single iOS QR');
@@ -119,6 +126,8 @@ export class AuthModal {
119
126
  <p class="glide-auth-status" id="glide-platform-message">Scan with your iPhone to authenticate</p>
120
127
  </div>
121
128
  `);
129
+ // IMPORTANT: Call show() to actually display the modal!
130
+ this.show();
122
131
  }
123
132
  /**
124
133
  * Sets a callback to be called when the modal is cancelled/closed
@@ -292,7 +301,11 @@ export class AuthModal {
292
301
  // Add close button handler
293
302
  const closeBtn = this.container.querySelector('.glide-auth-close');
294
303
  if (closeBtn) {
295
- closeBtn.addEventListener('click', () => this.close());
304
+ closeBtn.addEventListener('click', () => {
305
+ var _a;
306
+ (_a = this.closeCallback) === null || _a === void 0 ? void 0 : _a.call(this); // Call cancellation callback if set
307
+ this.close();
308
+ });
296
309
  }
297
310
  // Add backdrop click handler
298
311
  this.backdrop.addEventListener('click', (e) => {
@@ -1,2 +1,2 @@
1
1
  // SDK version - injected at build time
2
- export const SDK_VERSION = '4.4.8-beta.1';
2
+ export const SDK_VERSION = '4.4.8-beta.2';
@@ -1,8 +1,8 @@
1
1
  export { PhoneAuthClient } from './core/phone-auth';
2
2
  export type { AuthConfig as PhoneAuthConfig, PhoneAuthOptions, PhoneAuthResult, AuthError as PhoneAuthError, AuthStep as PhoneAuthStep } from './core/phone-auth';
3
3
  export { PhoneAuthErrorCode, isPhoneAuthError, isUserError, getUserMessage, isErrorCode, getRetryDelay, isRetryableError, serializeError, createErrorBreadcrumb } from './core/phone-auth';
4
- export { isHeadlessResult, isCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, requiresPolling, requiresUserAction } from './core/phone-auth';
5
- export type { PhoneAuthCallbacks, PLMN, SessionInfo, HeadlessResult, InvokeOptions, PrepareRequest, PrepareResponse, GetPhoneNumberRequest, GetPhoneNumberResponse, VerifyPhoneNumberRequest, VerifyPhoneNumberResponse, SecureCredentialRequest, SecureCredentialResponse, DigitalCredential, TS43Data, LinkData, DesktopData, ClientInfo, ConsentData, BrowserErrorType, BrowserErrorCodeType, BrowserNameType } from './core/phone-auth/types';
4
+ export { isExtendedResponse, isCredential, isAuthCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, hasPollingControls, hasTrigger, isHeadlessResult, requiresPolling, requiresUserAction } from './core/phone-auth';
5
+ export type { PhoneAuthCallbacks, PLMN, SessionInfo, InvokeOptions, ExecutionMode, AuthCredential, AnyExtendedResponse, DesktopExtendedResponse, LinkExtendedResponse, TS43ExtendedResponse, PrepareRequest, PrepareResponse, GetPhoneNumberRequest, GetPhoneNumberResponse, VerifyPhoneNumberRequest, VerifyPhoneNumberResponse, SecureCredentialRequest, SecureCredentialResponse, DigitalCredential, TS43Data, LinkData, DesktopData, ClientInfo, ConsentData, BrowserErrorType, BrowserErrorCodeType, BrowserNameType } from './core/phone-auth/types';
6
6
  export { USE_CASE as UseCase, AUTHENTICATION_STRATEGY as AuthenticationStrategy, BrowserError, BrowserErrorCode, BrowserName } from './core/phone-auth/types';
7
7
  export { DesktopHandler, showQRCodeModal, createQRCodeDisplay } from './core/phone-auth/strategies/desktop';
8
8
  export type { DesktopAuthOptions, DesktopAuthResult, PollingStatus, QRCodeData } from './core/phone-auth/strategies/desktop';
package/dist/esm/index.js CHANGED
@@ -3,7 +3,9 @@ export { PhoneAuthClient } from './core/phone-auth';
3
3
  // Phone Auth Error Utilities
4
4
  export { PhoneAuthErrorCode, isPhoneAuthError, isUserError, getUserMessage, isErrorCode, getRetryDelay, isRetryableError, serializeError, createErrorBreadcrumb } from './core/phone-auth';
5
5
  // Phone Auth Type Guards and Helpers
6
- export { isHeadlessResult, isCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, requiresPolling, requiresUserAction } from './core/phone-auth';
6
+ export { isExtendedResponse, isCredential, isAuthCredential, isLinkStrategy, isTS43Strategy, isDesktopStrategy, getStrategy, hasPollingControls, hasTrigger,
7
+ // Deprecated aliases
8
+ isHeadlessResult, requiresPolling, requiresUserAction } from './core/phone-auth';
7
9
  // Export constants for use case and strategy
8
10
  export { USE_CASE as UseCase, AUTHENTICATION_STRATEGY as AuthenticationStrategy, BrowserError, BrowserErrorCode, BrowserName } from './core/phone-auth/types';
9
11
  // Desktop Strategy Exports