@glideidentity/web-client-sdk 4.4.8-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 (136) hide show
  1. package/README.md +938 -0
  2. package/dist/adapters/angular/client.service.d.ts +7 -0
  3. package/dist/adapters/angular/client.service.js +30 -0
  4. package/dist/adapters/angular/index.d.ts +3 -0
  5. package/dist/adapters/angular/index.js +18 -0
  6. package/dist/adapters/angular/phone-auth.service.d.ts +38 -0
  7. package/dist/adapters/angular/phone-auth.service.js +130 -0
  8. package/dist/adapters/react/index.d.ts +9 -0
  9. package/dist/adapters/react/index.js +28 -0
  10. package/dist/adapters/react/useClient.d.ts +26 -0
  11. package/dist/adapters/react/useClient.js +121 -0
  12. package/dist/adapters/react/usePhoneAuth.d.ts +23 -0
  13. package/dist/adapters/react/usePhoneAuth.js +95 -0
  14. package/dist/adapters/vanilla/client.d.ts +8 -0
  15. package/dist/adapters/vanilla/client.js +33 -0
  16. package/dist/adapters/vanilla/index.d.ts +3 -0
  17. package/dist/adapters/vanilla/index.js +18 -0
  18. package/dist/adapters/vanilla/phone-auth.d.ts +46 -0
  19. package/dist/adapters/vanilla/phone-auth.js +138 -0
  20. package/dist/adapters/vue/index.d.ts +10 -0
  21. package/dist/adapters/vue/index.js +36 -0
  22. package/dist/adapters/vue/useClient.d.ts +115 -0
  23. package/dist/adapters/vue/useClient.js +131 -0
  24. package/dist/adapters/vue/usePhoneAuth.d.ts +94 -0
  25. package/dist/adapters/vue/usePhoneAuth.js +103 -0
  26. package/dist/browser/web-client-sdk.min.js +2 -0
  27. package/dist/browser/web-client-sdk.min.js.LICENSE.txt +1 -0
  28. package/dist/browser.d.ts +7 -0
  29. package/dist/browser.js +31 -0
  30. package/dist/core/client.d.ts +22 -0
  31. package/dist/core/client.js +77 -0
  32. package/dist/core/logger.d.ts +130 -0
  33. package/dist/core/logger.js +370 -0
  34. package/dist/core/phone-auth/api-types.d.ts +525 -0
  35. package/dist/core/phone-auth/api-types.js +215 -0
  36. package/dist/core/phone-auth/client.d.ts +187 -0
  37. package/dist/core/phone-auth/client.js +1353 -0
  38. package/dist/core/phone-auth/error-utils.d.ts +110 -0
  39. package/dist/core/phone-auth/error-utils.js +350 -0
  40. package/dist/core/phone-auth/index.d.ts +7 -0
  41. package/dist/core/phone-auth/index.js +47 -0
  42. package/dist/core/phone-auth/status-types.d.ts +107 -0
  43. package/dist/core/phone-auth/status-types.js +31 -0
  44. package/dist/core/phone-auth/strategies/desktop.d.ts +113 -0
  45. package/dist/core/phone-auth/strategies/desktop.js +502 -0
  46. package/dist/core/phone-auth/strategies/index.d.ts +11 -0
  47. package/dist/core/phone-auth/strategies/index.js +15 -0
  48. package/dist/core/phone-auth/strategies/link.d.ts +81 -0
  49. package/dist/core/phone-auth/strategies/link.js +265 -0
  50. package/dist/core/phone-auth/strategies/ts43.d.ts +32 -0
  51. package/dist/core/phone-auth/strategies/ts43.js +146 -0
  52. package/dist/core/phone-auth/strategies/types.d.ts +18 -0
  53. package/dist/core/phone-auth/strategies/types.js +6 -0
  54. package/dist/core/phone-auth/type-guards.d.ts +125 -0
  55. package/dist/core/phone-auth/type-guards.js +160 -0
  56. package/dist/core/phone-auth/types.d.ts +232 -0
  57. package/dist/core/phone-auth/types.js +93 -0
  58. package/dist/core/phone-auth/ui/mobile-debug-console.d.ts +25 -0
  59. package/dist/core/phone-auth/ui/mobile-debug-console.js +288 -0
  60. package/dist/core/phone-auth/ui/modal.d.ts +84 -0
  61. package/dist/core/phone-auth/ui/modal.js +574 -0
  62. package/dist/core/phone-auth/validation-utils.d.ts +66 -0
  63. package/dist/core/phone-auth/validation-utils.js +182 -0
  64. package/dist/core/types.d.ts +62 -0
  65. package/dist/core/types.js +2 -0
  66. package/dist/core/version.d.ts +1 -0
  67. package/dist/core/version.js +5 -0
  68. package/dist/esm/adapters/angular/client.service.d.ts +7 -0
  69. package/dist/esm/adapters/angular/client.service.js +27 -0
  70. package/dist/esm/adapters/angular/index.d.ts +3 -0
  71. package/dist/esm/adapters/angular/index.js +4 -0
  72. package/dist/esm/adapters/angular/phone-auth.service.d.ts +38 -0
  73. package/dist/esm/adapters/angular/phone-auth.service.js +127 -0
  74. package/dist/esm/adapters/react/index.d.ts +9 -0
  75. package/dist/esm/adapters/react/index.js +8 -0
  76. package/dist/esm/adapters/react/useClient.d.ts +26 -0
  77. package/dist/esm/adapters/react/useClient.js +116 -0
  78. package/dist/esm/adapters/react/usePhoneAuth.d.ts +23 -0
  79. package/dist/esm/adapters/react/usePhoneAuth.js +92 -0
  80. package/dist/esm/adapters/vanilla/client.d.ts +8 -0
  81. package/dist/esm/adapters/vanilla/client.js +29 -0
  82. package/dist/esm/adapters/vanilla/index.d.ts +3 -0
  83. package/dist/esm/adapters/vanilla/index.js +4 -0
  84. package/dist/esm/adapters/vanilla/phone-auth.d.ts +46 -0
  85. package/dist/esm/adapters/vanilla/phone-auth.js +134 -0
  86. package/dist/esm/adapters/vue/index.d.ts +10 -0
  87. package/dist/esm/adapters/vue/index.js +11 -0
  88. package/dist/esm/adapters/vue/useClient.d.ts +115 -0
  89. package/dist/esm/adapters/vue/useClient.js +127 -0
  90. package/dist/esm/adapters/vue/usePhoneAuth.d.ts +94 -0
  91. package/dist/esm/adapters/vue/usePhoneAuth.js +100 -0
  92. package/dist/esm/browser.d.ts +7 -0
  93. package/dist/esm/browser.js +11 -0
  94. package/dist/esm/core/client.d.ts +22 -0
  95. package/dist/esm/core/client.js +70 -0
  96. package/dist/esm/core/logger.d.ts +130 -0
  97. package/dist/esm/core/logger.js +359 -0
  98. package/dist/esm/core/phone-auth/api-types.d.ts +525 -0
  99. package/dist/esm/core/phone-auth/api-types.js +203 -0
  100. package/dist/esm/core/phone-auth/client.d.ts +187 -0
  101. package/dist/esm/core/phone-auth/client.js +1316 -0
  102. package/dist/esm/core/phone-auth/error-utils.d.ts +110 -0
  103. package/dist/esm/core/phone-auth/error-utils.js +338 -0
  104. package/dist/esm/core/phone-auth/index.d.ts +7 -0
  105. package/dist/esm/core/phone-auth/index.js +6 -0
  106. package/dist/esm/core/phone-auth/status-types.d.ts +107 -0
  107. package/dist/esm/core/phone-auth/status-types.js +26 -0
  108. package/dist/esm/core/phone-auth/strategies/desktop.d.ts +113 -0
  109. package/dist/esm/core/phone-auth/strategies/desktop.js +496 -0
  110. package/dist/esm/core/phone-auth/strategies/index.d.ts +11 -0
  111. package/dist/esm/core/phone-auth/strategies/index.js +7 -0
  112. package/dist/esm/core/phone-auth/strategies/link.d.ts +81 -0
  113. package/dist/esm/core/phone-auth/strategies/link.js +261 -0
  114. package/dist/esm/core/phone-auth/strategies/ts43.d.ts +32 -0
  115. package/dist/esm/core/phone-auth/strategies/ts43.js +142 -0
  116. package/dist/esm/core/phone-auth/strategies/types.d.ts +18 -0
  117. package/dist/esm/core/phone-auth/strategies/types.js +5 -0
  118. package/dist/esm/core/phone-auth/type-guards.d.ts +125 -0
  119. package/dist/esm/core/phone-auth/type-guards.js +150 -0
  120. package/dist/esm/core/phone-auth/types.d.ts +232 -0
  121. package/dist/esm/core/phone-auth/types.js +76 -0
  122. package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +25 -0
  123. package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +284 -0
  124. package/dist/esm/core/phone-auth/ui/modal.d.ts +84 -0
  125. package/dist/esm/core/phone-auth/ui/modal.js +570 -0
  126. package/dist/esm/core/phone-auth/validation-utils.d.ts +66 -0
  127. package/dist/esm/core/phone-auth/validation-utils.js +174 -0
  128. package/dist/esm/core/types.d.ts +62 -0
  129. package/dist/esm/core/types.js +1 -0
  130. package/dist/esm/core/version.d.ts +1 -0
  131. package/dist/esm/core/version.js +2 -0
  132. package/dist/esm/index.d.ts +12 -0
  133. package/dist/esm/index.js +15 -0
  134. package/dist/index.d.ts +12 -0
  135. package/dist/index.js +52 -0
  136. package/package.json +92 -0
package/README.md ADDED
@@ -0,0 +1,938 @@
1
+ # Glide Web Client SDK
2
+
3
+ The official web SDK for integrating Glide's carrier-grade phone verification into your web applications. Supports React, Vue, Angular, and vanilla JavaScript/TypeScript.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Instant Verification**: Direct carrier verification without SMS
8
+ - 🔐 **Fraud Resistant**: Can't be intercepted or spoofed like SMS codes
9
+ - ⚡ **Real-time Progress**: Track verification steps with detailed state management
10
+ - 📱 **Cross-Device Support**: Desktop QR code, mobile App Clips, and Android deep links
11
+ - 🤖 **Smart Defaults**: Automatic headless/UI mode selection based on strategy
12
+ - 🔄 **Unified Trigger Pattern**: Consistent API for Link and TS43 authentication
13
+ - 🌐 **Framework Support**: React, Vue 3, Angular, and vanilla JS/TS
14
+ - 🛡️ **Type Safe**: Full TypeScript support with type guards and IntelliSense
15
+ - 🎯 **Developer Friendly**: Clear error messages and browser compatibility checks
16
+
17
+ ## What's New in v4.4
18
+
19
+ ### 🎉 Smart Defaults & Better DX
20
+ - **Smart Mode Selection**: Link/TS43 default to headless, Desktop defaults to UI mode
21
+ - **Type Guards**: Helper functions for type-safe result handling
22
+ - **Unified Trigger Pattern**: Consistent `trigger()` function for all strategies
23
+ - **Auto-Trigger Support**: Automatic App Clip opening with retry capability
24
+ - **TypeScript Enhancements**: Full IntelliSense and type safety
25
+ - **Cleaner API**: Removed non-functional options (iframe, redundant callbacks)
26
+
27
+ ## Table of Contents
28
+
29
+ - [Installation](#installation)
30
+ - [Quick Start](#quick-start)
31
+ - [Smart Defaults & Type Guards](#smart-defaults--type-guards)
32
+ - [Cross-Device Support](#cross-device-support)
33
+ - [Framework Examples](#framework-examples)
34
+ - [Error Handling](#error-handling)
35
+ - [Advanced Features](#advanced-features)
36
+ - [Browser Support](#browser-support)
37
+ - [API Reference](#api-reference)
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install glide-web-client-sdk
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ### React
48
+
49
+ ```jsx
50
+ import { usePhoneAuth } from 'glide-web-client-sdk/react'
51
+
52
+ function PhoneVerification() {
53
+ const {
54
+ getPhoneNumber,
55
+ verifyPhoneNumber,
56
+ retryLastRequest, // NEW: Manual retry capability
57
+ isLoading,
58
+ error,
59
+ result,
60
+ currentStep,
61
+ isSupported
62
+ } = usePhoneAuth({
63
+ endpoints: {
64
+ prepare: '/api/phone-auth/prepare',
65
+ process: '/api/phone-auth/process'
66
+ },
67
+ // Optional callbacks for monitoring
68
+ onCrossDeviceDetected: () => {
69
+ console.log('QR code detected - user completing on phone');
70
+ },
71
+ onRetryAttempt: (attempt, maxAttempts) => {
72
+ console.log(`Silent retry ${attempt}/${maxAttempts}`);
73
+ }
74
+ })
75
+
76
+ const handleVerify = async () => {
77
+ try {
78
+ // Verify a specific phone number
79
+ await verifyPhoneNumber('+1234567890')
80
+ } catch (error) {
81
+ // Error only thrown after all retries exhausted
82
+ console.error('Verification failed:', error)
83
+ }
84
+ }
85
+
86
+ return (
87
+ <div>
88
+ <button onClick={handleVerify} disabled={isLoading}>
89
+ Verify My Phone
90
+ </button>
91
+
92
+ {/* Manual retry button (NEW) */}
93
+ {error && !isLoading && (
94
+ <button onClick={retryLastRequest}>
95
+ Retry
96
+ </button>
97
+ )}
98
+
99
+ {result && <p>Phone verified: {result.phone_number}</p>}
100
+ </div>
101
+ )
102
+ }
103
+ ```
104
+
105
+ ## 🎨 Dual-Mode API: UI Mode vs Headless Mode
106
+
107
+ The SDK now supports two distinct modes for authentication, giving you complete control over the user experience:
108
+
109
+ ### **UI Mode (Default)** - Ready-to-Use Components
110
+ The SDK provides built-in modals and buttons for authentication. Perfect for quick integration.
111
+
112
+ ### **Headless Mode** - Complete Control
113
+ Returns raw data without any UI, allowing you to build completely custom experiences.
114
+
115
+ ### When to Use Each Mode
116
+
117
+ | Use Case | Mode | Why |
118
+ |----------|------|-----|
119
+ | Quick integration | UI Mode | Get running in minutes with pre-built components |
120
+ | Custom design requirements | Headless | Match your exact brand guidelines |
121
+ | Mobile apps (React Native, etc) | Headless | Native UI components |
122
+ | Testing/Automation | Headless | Direct control over flow |
123
+ | Standard web apps | UI Mode | Optimized UX out of the box |
124
+
125
+ ### UI Mode Example (Default)
126
+
127
+ ```javascript
128
+ // Shows SDK's built-in UI based on authentication strategy
129
+ const prepareResult = await phoneAuth.preparePhoneRequest({
130
+ use_case: 'VerifyPhoneNumber',
131
+ phone_number: '+14155551234'
132
+ });
133
+
134
+ // Behavior varies by strategy:
135
+ // - Desktop: Shows QR code modal
136
+ // - Link: Shows button modal to open app
137
+ // - TS43: Directly invokes Digital Credentials API (no modal - OS provides UI)
138
+ const credential = await phoneAuth.invokeSecurePrompt(prepareResult);
139
+
140
+ // Or customize the built-in UI
141
+ const credential = await phoneAuth.invokeSecurePrompt(prepareResult, {
142
+ modalOptions: {
143
+ title: 'Verify Your Identity',
144
+ description: 'Complete verification to continue',
145
+ buttonText: 'Verify with Verizon',
146
+ className: 'my-custom-modal'
147
+ },
148
+ callbacks: {
149
+ onOpen: () => console.log('Modal opened'),
150
+ onAuthComplete: (result) => console.log('Success!', result)
151
+ }
152
+ });
153
+ ```
154
+
155
+ ### Headless Mode Example
156
+
157
+ ```javascript
158
+ // Get raw data without any UI
159
+ const prepareResult = await phoneAuth.preparePhoneRequest({
160
+ use_case: 'VerifyPhoneNumber',
161
+ phone_number: '+14155551234'
162
+ });
163
+
164
+ // Returns data instead of showing UI
165
+ const result = await phoneAuth.invokeSecurePrompt(prepareResult, {
166
+ headless: true
167
+ });
168
+
169
+ // Handle each strategy with your custom UI
170
+ if (result.strategy === 'link') {
171
+ // iOS/Android - App Clip or native app
172
+ console.log('URL to open:', result.url);
173
+
174
+ // Open in your custom way
175
+ window.open(result.url, '_blank');
176
+
177
+ // Wait for completion
178
+ const credential = await result.pollingPromise;
179
+
180
+ } else if (result.strategy === 'desktop') {
181
+ // Desktop - QR code
182
+ console.log('QR code image:', result.qrCode);
183
+
184
+ // Display QR in your custom UI
185
+ document.getElementById('qr').src = result.qrCode;
186
+
187
+ // Wait for mobile scan completion
188
+ const credential = await result.pollingPromise;
189
+
190
+ } else if (result.strategy === 'ts43') {
191
+ // Android/Chrome - Digital Credentials
192
+ console.log('Ready to invoke Digital Credentials');
193
+
194
+ // Trigger when user clicks your custom button
195
+ const credential = await result.trigger();
196
+
197
+ // Note: TS43 never shows a modal in UI mode - the OS provides its own UI
198
+ // The Digital Credentials API shows a native drawer/bottom sheet
199
+ }
200
+ ```
201
+
202
+ ### Smart Defaults & Type Guards (NEW in v4.4+)
203
+
204
+ The SDK now uses **smart defaults** that match the most common use case for each authentication strategy:
205
+
206
+ | Strategy | Default Mode | Returns | Why |
207
+ |----------|-------------|---------|-----|
208
+ | **Link** | Headless | `HeadlessResult` with `trigger()` | Developers want custom "Open App" buttons |
209
+ | **TS43** | Headless | `HeadlessResult` with `trigger()` | Browser provides native credential UI |
210
+ | **Desktop** | UI Mode | `Credential` directly | QR modal is complex to build from scratch |
211
+
212
+ #### Using Type Guards
213
+
214
+ Instead of manual type checking, use the SDK's built-in type guards for cleaner, type-safe code:
215
+
216
+ ```typescript
217
+ import {
218
+ invokeSecurePrompt,
219
+ isHeadlessResult,
220
+ isLinkStrategy,
221
+ isTS43Strategy,
222
+ isDesktopStrategy
223
+ } from 'glide-web-client-sdk';
224
+
225
+ // No need to specify headless for Link/TS43 - it's the default!
226
+ const result = await invokeSecurePrompt(prepareResult);
227
+
228
+ // Use type guards for type-safe handling
229
+ if (isHeadlessResult(result)) {
230
+ // TypeScript knows this is HeadlessResult
231
+ console.log('Strategy:', result.strategy);
232
+
233
+ if (isLinkStrategy(result)) {
234
+ // Handle Link: custom button to open app
235
+ myButton.onclick = () => result.trigger();
236
+ await result.pollingPromise;
237
+ }
238
+ else if (isTS43Strategy(result)) {
239
+ // Handle TS43: invoke immediately or on button
240
+ const credential = await result.trigger();
241
+ }
242
+ else if (isDesktopStrategy(result)) {
243
+ // Desktop in headless mode (explicitly requested)
244
+ showCustomQR(result.qrCodeUrl);
245
+ await result.pollingPromise;
246
+ }
247
+ } else {
248
+ // Got credential directly (Desktop UI mode by default)
249
+ const processedResult = await verifyPhoneNumberCredential(result, session);
250
+ }
251
+ ```
252
+
253
+ #### Available Type Guards
254
+
255
+ ```typescript
256
+ // Check if result is headless or UI mode
257
+ isHeadlessResult(result) // true if headless mode
258
+ isCredential(result) // true if UI mode (got credential)
259
+
260
+ // Strategy-specific guards (for HeadlessResult)
261
+ isLinkStrategy(result) // true if Link strategy
262
+ isTS43Strategy(result) // true if TS43 strategy
263
+ isDesktopStrategy(result) // true if Desktop strategy
264
+
265
+ // Helper functions
266
+ getStrategy(result) // Returns: 'link' | 'ts43' | 'desktop' | undefined
267
+ requiresPolling(result) // Returns: true for Link/Desktop, false for TS43
268
+ requiresUserAction(result)// Returns: true if user interaction needed
269
+ ```
270
+
271
+ #### Override Defaults When Needed
272
+
273
+ You can still override the defaults when you have specific requirements:
274
+
275
+ ```javascript
276
+ // Force UI mode for Link/TS43 (rarely needed)
277
+ const credential = await invokeSecurePrompt(prepareResult, {
278
+ headless: false // Show SDK modal instead of using custom UI
279
+ });
280
+
281
+ // Force headless mode for Desktop (for custom QR display)
282
+ const result = await invokeSecurePrompt(prepareResult, {
283
+ headless: true // Get QR data instead of showing modal
284
+ });
285
+ ```
286
+
287
+ ### React Example with Custom UI
288
+
289
+ ```jsx
290
+ function CustomPhoneAuth() {
291
+ const [qrCode, setQrCode] = useState(null);
292
+ const [appUrl, setAppUrl] = useState(null);
293
+
294
+ const handleVerify = async () => {
295
+ const prepareResult = await phoneAuth.preparePhoneRequest({
296
+ use_case: 'VerifyPhoneNumber',
297
+ phone_number: phoneNumber
298
+ });
299
+
300
+ // No need for headless: true for Link/TS43 - it's the default!
301
+ const result = await phoneAuth.invokeSecurePrompt(prepareResult);
302
+
303
+ // Use type guards for clean code
304
+ if (isHeadlessResult(result)) {
305
+ if (isDesktopStrategy(result)) {
306
+ // Desktop in headless (explicitly requested)
307
+ setQrCode(result.qrCode);
308
+ } else if (isLinkStrategy(result)) {
309
+ // Link defaults to headless
310
+ setAppUrl(result.url);
311
+ }
312
+
313
+ // Handle completion
314
+ const credential = await result.pollingPromise;
315
+ console.log('Verified!');
316
+ };
317
+
318
+ return (
319
+ <div className="my-custom-design">
320
+ {qrCode && (
321
+ <div className="qr-container">
322
+ <img src={qrCode} alt="Scan to verify" />
323
+ <p>Scan with your phone</p>
324
+ </div>
325
+ )}
326
+
327
+ {appUrl && (
328
+ <button onClick={() => window.open(appUrl)}>
329
+ Open Verification App
330
+ </button>
331
+ )}
332
+ </div>
333
+ );
334
+ }
335
+ ```
336
+
337
+ ### Vue Example with Both Modes
338
+
339
+ ```vue
340
+ <template>
341
+ <div>
342
+ <!-- Use SDK UI for quick setup -->
343
+ <button @click="verifyWithUI">
344
+ Verify (SDK UI)
345
+ </button>
346
+
347
+ <!-- Or build custom UI -->
348
+ <button @click="verifyHeadless">
349
+ Verify (Custom UI)
350
+ </button>
351
+
352
+ <!-- Custom QR display -->
353
+ <div v-if="qrCode" class="custom-qr">
354
+ <img :src="qrCode" />
355
+ </div>
356
+ </div>
357
+ </template>
358
+
359
+ <script setup>
360
+ import { usePhoneAuth } from 'glide-web-client-sdk/vue'
361
+
362
+ const phoneAuth = usePhoneAuth({
363
+ endpoints: {
364
+ prepare: '/api/phone-auth/prepare',
365
+ process: '/api/phone-auth/process'
366
+ }
367
+ });
368
+
369
+ const qrCode = ref(null);
370
+
371
+ // Using built-in UI
372
+ async function verifyWithUI() {
373
+ const prepared = await phoneAuth.preparePhoneRequest({
374
+ use_case: 'VerifyPhoneNumber',
375
+ phone_number: '+14155551234'
376
+ });
377
+
378
+ // Shows SDK modal
379
+ const credential = await phoneAuth.invokeSecurePrompt(prepared);
380
+ }
381
+
382
+ // Using headless mode
383
+ async function verifyHeadless() {
384
+ const prepared = await phoneAuth.preparePhoneRequest({
385
+ use_case: 'VerifyPhoneNumber',
386
+ phone_number: '+14155551234'
387
+ });
388
+
389
+ // Get raw data
390
+ const result = await phoneAuth.invokeSecurePrompt(prepared, {
391
+ headless: true
392
+ });
393
+
394
+ if (result.strategy === 'desktop') {
395
+ qrCode.value = result.qrCode;
396
+ await result.pollingPromise;
397
+ }
398
+ }
399
+ </script>
400
+ ```
401
+
402
+ ### TypeScript Types
403
+
404
+ ```typescript
405
+ interface InvokeOptions {
406
+ // Enable headless mode (no UI)
407
+ headless?: boolean;
408
+
409
+ // Customize built-in UI (only for UI mode)
410
+ modalOptions?: {
411
+ className?: string;
412
+ title?: string;
413
+ description?: string;
414
+ buttonText?: string;
415
+ showCloseButton?: boolean;
416
+ zIndex?: number;
417
+ };
418
+
419
+ // UI event callbacks (only for UI mode)
420
+ callbacks?: {
421
+ onOpen?: () => void;
422
+ onClose?: () => void;
423
+ onAuthStart?: () => void;
424
+ onAuthComplete?: (result: any) => void;
425
+ onError?: (error: Error) => void;
426
+ };
427
+ }
428
+
429
+ interface HeadlessResult {
430
+ strategy: 'ts43' | 'link' | 'desktop';
431
+ url?: string; // For Link strategy
432
+ qrCode?: string; // For Desktop strategy
433
+ pollingPromise?: Promise<any>; // Resolves when complete
434
+ trigger?: () => Promise<any>; // For TS43 strategy
435
+ session: SessionInfo;
436
+ }
437
+ ```
438
+
439
+ ## Smart Defaults & Type Guards
440
+
441
+ ### Smart Mode Selection
442
+
443
+ The SDK automatically chooses the optimal mode based on the authentication strategy:
444
+
445
+ ```javascript
446
+ // Link Strategy (App Clips) - Defaults to HEADLESS
447
+ const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
448
+ // No need to specify headless: true - it's automatic!
449
+
450
+ // TS43 Strategy (Android) - Defaults to HEADLESS
451
+ const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
452
+ // Automatically headless for custom UI control
453
+
454
+ // Desktop Strategy (QR Code) - Defaults to UI MODE
455
+ const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
456
+ // Shows built-in QR modal automatically
457
+
458
+ // Override defaults if needed
459
+ const result = await phoneAuth.invokeSecurePrompt(prepareResponse, {
460
+ headless: false // Force UI mode for any strategy
461
+ });
462
+ ```
463
+
464
+ ### Type Guards for Safe Result Handling
465
+
466
+ Use built-in type guards for type-safe result handling:
467
+
468
+ ```typescript
469
+ import {
470
+ isHeadlessResult,
471
+ isCredential,
472
+ isLinkStrategy,
473
+ isTS43Strategy,
474
+ isDesktopStrategy,
475
+ requiresPolling,
476
+ requiresUserAction
477
+ } from 'glide-web-client-sdk';
478
+
479
+ const result = await phoneAuth.invokeSecurePrompt(prepareResponse);
480
+
481
+ // Check result type
482
+ if (isHeadlessResult(result)) {
483
+ // TypeScript knows this is HeadlessResult
484
+ console.log('Strategy:', result.strategy);
485
+
486
+ // Check specific strategies
487
+ if (isLinkStrategy(result)) {
488
+ // Handle Link (App Clip) flow
489
+ await result.trigger(); // Opens App Clip
490
+ const credential = await result.pollingPromise;
491
+ } else if (isTS43Strategy(result)) {
492
+ // Handle TS43 (Android) flow
493
+ const credential = await result.trigger(); // Invokes credential API
494
+ }
495
+
496
+ // Check capabilities
497
+ if (requiresPolling(result)) {
498
+ // Link or Desktop - needs polling
499
+ const credential = await result.pollingPromise;
500
+ }
501
+
502
+ if (requiresUserAction(result)) {
503
+ // Link or TS43 - needs user interaction
504
+ button.onclick = () => result.trigger();
505
+ }
506
+ } else if (isCredential(result)) {
507
+ // Direct credential (UI mode handled everything)
508
+ const verified = await phoneAuth.processCredential(result);
509
+ }
510
+ ```
511
+
512
+ ### Unified Trigger Pattern
513
+
514
+ All headless strategies use the same `trigger()` pattern:
515
+
516
+ ```javascript
517
+ // Link Strategy - trigger opens App Clip
518
+ const linkResult = await phoneAuth.invokeSecurePrompt(prepareResponse, {
519
+ autoTrigger: false // Don't auto-open, let user click
520
+ });
521
+ button.onclick = () => linkResult.trigger(); // User controls when to open
522
+
523
+ // TS43 Strategy - trigger invokes credential API
524
+ const ts43Result = await phoneAuth.invokeSecurePrompt(prepareResponse, {
525
+ autoTrigger: false // Preserve user gesture
526
+ });
527
+ button.onclick = async () => {
528
+ const credential = await ts43Result.trigger(); // Must be in click handler
529
+ };
530
+
531
+ // Auto-trigger (default for Link)
532
+ const autoResult = await phoneAuth.invokeSecurePrompt(prepareResponse);
533
+ // App Clip opens immediately, retry available
534
+ retryButton.onclick = () => autoResult.trigger(); // Retry if needed
535
+ ```
536
+
537
+ ## Cross-Device Support
538
+
539
+ The SDK automatically handles cross-device authentication flows (QR code scenarios) without any configuration needed.
540
+
541
+ ### How It Works
542
+
543
+ 1. **Detection**: After 3 seconds with credential prompt open, the SDK detects likely QR code flow
544
+ 2. **Timeout Extension**: Automatically extends timeout from 30 seconds to 2 minutes
545
+ 3. **User Experience**: Users have ample time to scan QR and complete on their phone
546
+
547
+ ### Monitoring Cross-Device Flows
548
+
549
+ ```javascript
550
+ const { getPhoneNumber } = usePhoneAuth({
551
+ endpoints: { /* ... */ },
552
+
553
+ // Optional: Track when QR codes are shown
554
+ onCrossDeviceDetected: () => {
555
+ // Analytics: Track QR code usage
556
+ analytics.track('phone_auth_qr_code_shown');
557
+ }
558
+ });
559
+ ```
560
+
561
+ ## Silent Retry Pattern
562
+
563
+ The SDK automatically retries transient failures without showing errors to users.
564
+
565
+ ### Default Behavior
566
+
567
+ - **2 automatic retries** on network/timeout errors
568
+ - **Exponential backoff** between attempts (1s, 2s)
569
+ - **Error only shown** after all retries are exhausted
570
+ - **Session caching** to recover from interruptions
571
+
572
+ ### Example Flow
573
+
574
+ ```
575
+ User clicks verify →
576
+ Try 1 fails (network glitch) →
577
+ [Silent retry - no error shown] →
578
+ Try 2 succeeds →
579
+ ✓ Success shown to user
580
+
581
+ User never sees the transient failure!
582
+ ```
583
+
584
+ ### Monitoring Retries
585
+
586
+ ```javascript
587
+ const { verifyPhoneNumber } = usePhoneAuth({
588
+ endpoints: { /* ... */ },
589
+
590
+ // Optional: Track retry patterns
591
+ onRetryAttempt: (attempt, maxAttempts) => {
592
+ // Log to monitoring service
593
+ logger.info('Phone auth retry', {
594
+ attempt,
595
+ maxAttempts
596
+ });
597
+ }
598
+ });
599
+ ```
600
+
601
+ ## Advanced Features
602
+
603
+ ### Manual Retry
604
+
605
+ Users can manually retry failed verifications:
606
+
607
+ ```javascript
608
+ const { retryLastRequest, error } = usePhoneAuth(config);
609
+
610
+ // Show retry button after error
611
+ if (error) {
612
+ <button onClick={retryLastRequest}>Try Again</button>
613
+ }
614
+ ```
615
+
616
+ ### Callbacks
617
+
618
+ Both callbacks are optional and have no performance impact when not used:
619
+
620
+ | Callback | Purpose | Use Case |
621
+ |----------|---------|----------|
622
+ | `onCrossDeviceDetected` | Fires when QR code flow detected | Analytics, custom UI |
623
+ | `onRetryAttempt` | Fires on each retry attempt | Monitoring, debugging |
624
+
625
+ ### TypeScript Support
626
+
627
+ Full TypeScript support with exported types:
628
+
629
+ ```typescript
630
+ import {
631
+ usePhoneAuth,
632
+ PhoneAuthResult,
633
+ AuthError,
634
+ UsePhoneAuthOptions
635
+ } from 'glide-web-client-sdk/react';
636
+
637
+ const config: UsePhoneAuthOptions = {
638
+ endpoints: {
639
+ prepare: '/api/prepare',
640
+ process: '/api/process'
641
+ },
642
+ onCrossDeviceDetected: () => void,
643
+ onRetryAttempt: (attempt: number, max: number) => void
644
+ };
645
+ ```
646
+
647
+ ## Error Handling
648
+
649
+ ### Enhanced Error Context
650
+
651
+ Errors now include additional context for debugging:
652
+
653
+ ```javascript
654
+ catch (error) {
655
+ console.log(error.code); // 'REQUEST_TIMEOUT'
656
+ console.log(error.message); // User-friendly message
657
+ console.log(error.context); // { attemptNumber: 3, maxAttempts: 3 }
658
+ console.log(error.browserError); // Browser-specific error details
659
+ }
660
+ ```
661
+
662
+ ### Cross-Device Error Types
663
+
664
+ The SDK provides specific error information for cross-device issues:
665
+
666
+ ```javascript
667
+ if (error.details?.errorType === 'CROSS_DEVICE_TIMEOUT') {
668
+ // QR code expired
669
+ }
670
+ if (error.details?.errorType === 'CROSS_DEVICE_CONNECTION_LOST') {
671
+ // Connection lost during phone verification
672
+ }
673
+ ```
674
+
675
+ ## Browser Support
676
+
677
+ ### Requirements
678
+
679
+ - Chrome 118+ or Edge 118+
680
+ - Digital Credentials API flag enabled
681
+
682
+ ### Checking Support
683
+
684
+ ```javascript
685
+ const { isSupported, browserSupportInfo } = usePhoneAuth();
686
+
687
+ if (!isSupported) {
688
+ console.log(browserSupportInfo.message);
689
+ // "Please enable chrome://flags/#web-identity-digital-credentials"
690
+ }
691
+ ```
692
+
693
+ ## Vue 3 Example
694
+
695
+ ```vue
696
+ <template>
697
+ <div>
698
+ <button @click="verify" :disabled="isLoading">
699
+ Verify Phone
700
+ </button>
701
+
702
+ <!-- Retry button -->
703
+ <button v-if="error" @click="retryLastRequest">
704
+ Retry
705
+ </button>
706
+
707
+ <div v-if="result">
708
+ Phone: {{ result.phone_number }}
709
+ </div>
710
+ </div>
711
+ </template>
712
+
713
+ <script setup>
714
+ import { usePhoneAuth } from 'glide-web-client-sdk/vue'
715
+
716
+ const {
717
+ verifyPhoneNumber,
718
+ retryLastRequest,
719
+ isLoading,
720
+ error,
721
+ result
722
+ } = usePhoneAuth({
723
+ endpoints: {
724
+ prepare: '/api/phone-auth/prepare',
725
+ process: '/api/phone-auth/process'
726
+ },
727
+ onCrossDeviceDetected: () => {
728
+ console.log('QR code shown');
729
+ }
730
+ })
731
+
732
+ const verify = async () => {
733
+ await verifyPhoneNumber('+1234567890')
734
+ }
735
+ </script>
736
+ ```
737
+
738
+ ## Angular Example
739
+
740
+ ```typescript
741
+ import { Component } from '@angular/core';
742
+ import { PhoneAuthService } from 'glide-web-client-sdk/angular';
743
+
744
+ @Component({
745
+ selector: 'app-phone-verify',
746
+ template: `
747
+ <button (click)="verify()" [disabled]="isLoading$ | async">
748
+ Verify
749
+ </button>
750
+
751
+ <button *ngIf="error$ | async" (click)="retry()">
752
+ Retry
753
+ </button>
754
+
755
+ <div *ngIf="result$ | async as result">
756
+ Phone: {{ result.phone_number }}
757
+ </div>
758
+ `
759
+ })
760
+ export class PhoneVerifyComponent {
761
+ isLoading$ = this.phoneAuth.isLoading$;
762
+ error$ = this.phoneAuth.error$;
763
+ result$ = this.phoneAuth.result$;
764
+
765
+ constructor(private phoneAuth: PhoneAuthService) {
766
+ this.phoneAuth.configure({
767
+ endpoints: {
768
+ prepare: '/api/phone-auth/prepare',
769
+ process: '/api/phone-auth/process'
770
+ },
771
+ onCrossDeviceDetected: () => {
772
+ console.log('QR code detected');
773
+ }
774
+ });
775
+ }
776
+
777
+ async verify() {
778
+ await this.phoneAuth.verifyPhoneNumber('+1234567890');
779
+ }
780
+
781
+ async retry() {
782
+ await this.phoneAuth.retryLastRequest();
783
+ }
784
+ }
785
+ ```
786
+
787
+ ## Vanilla JavaScript
788
+
789
+ ```javascript
790
+ import { PhoneAuthClient } from 'glide-web-client-sdk';
791
+
792
+ const client = new PhoneAuthClient({
793
+ endpoints: {
794
+ prepare: '/api/phone-auth/prepare',
795
+ process: '/api/phone-auth/process'
796
+ },
797
+ onCrossDeviceDetected: () => {
798
+ console.log('QR code detected');
799
+ },
800
+ onRetryAttempt: (attempt, max) => {
801
+ console.log(`Retry ${attempt}/${max}`);
802
+ }
803
+ });
804
+
805
+ // Verify phone number with automatic retry
806
+ try {
807
+ const result = await client.verifyPhoneNumber('+1234567890');
808
+ console.log('Verified:', result.verified);
809
+ } catch (error) {
810
+ // Only thrown after all retries exhausted
811
+ console.error('Failed after retries:', error);
812
+ }
813
+ ```
814
+
815
+ ## Migration from v3
816
+
817
+ No breaking changes! The SDK is fully backward compatible. New features are additive:
818
+
819
+ ```javascript
820
+ // v3 code continues to work
821
+ const { getPhoneNumber } = usePhoneAuth(config);
822
+
823
+ // v4 adds new optional features
824
+ const { getPhoneNumber, retryLastRequest } = usePhoneAuth({
825
+ ...config,
826
+ onCrossDeviceDetected: () => {}, // Optional
827
+ onRetryAttempt: () => {} // Optional
828
+ });
829
+ ```
830
+
831
+ ## Debug Tools
832
+
833
+ For development and testing, debug components are available in the `/examples/debug-components/` folder:
834
+
835
+ - `PhoneAuthDebugger.jsx` - Comprehensive debugging interface
836
+ - `AppEnhanced.jsx` - Production app with debug mode
837
+
838
+ These are not included in the main bundle. Copy them to your project if needed.
839
+
840
+ ## API Reference
841
+
842
+ ### Configuration Options
843
+
844
+ ```typescript
845
+ interface AuthConfig {
846
+ // Required endpoints
847
+ endpoints: {
848
+ prepare: string; // Your backend prepare endpoint
849
+ process: string; // Your backend process endpoint
850
+ polling?: string; // Optional custom polling endpoint
851
+ };
852
+
853
+ // Optional settings
854
+ pollingInterval?: number; // Polling interval in ms (default: 2000)
855
+ maxPollingAttempts?: number; // Max polling attempts (default: 150)
856
+ timeout?: number; // API timeout in ms (default: 30000)
857
+ debug?: boolean; // Enable debug logging (default: false)
858
+
859
+ // Optional callbacks
860
+ onCrossDeviceDetected?: () => void; // QR code shown
861
+ onRetryAttempt?: (attempt: number, maxAttempts: number) => void;
862
+ onTimeout?: () => void;
863
+ onCancel?: () => void;
864
+ }
865
+ ```
866
+
867
+ ### Modal Customization (UI Mode)
868
+
869
+ ```typescript
870
+ interface ModalOptions {
871
+ className?: string; // Custom CSS class
872
+ title?: string; // Modal title
873
+ description?: string; // Modal description
874
+ buttonText?: string; // Button label
875
+ showCloseButton?: boolean; // Show close button (default: true)
876
+ }
877
+
878
+ // Usage
879
+ const result = await phoneAuth.invokeSecurePrompt(prepareResponse, {
880
+ headless: false,
881
+ modalOptions: {
882
+ title: 'Verify Your Identity',
883
+ buttonText: 'Continue'
884
+ }
885
+ });
886
+ ```
887
+
888
+ ### Methods
889
+
890
+ ```typescript
891
+ // High-level methods (handle everything)
892
+ getPhoneNumber(): Promise<GetPhoneNumberResponse>
893
+ verifyPhoneNumber(phoneNumber?: string): Promise<VerifyPhoneNumberResponse>
894
+
895
+ // Low-level methods (granular control)
896
+ preparePhoneRequest(options: PrepareRequest): Promise<PrepareResponse>
897
+ invokeSecurePrompt(response: PrepareResponse, options?: InvokeOptions): Promise<HeadlessResult | Credential>
898
+ getPhoneNumberCredential(credential: any, session: SessionInfo): Promise<GetPhoneNumberResponse>
899
+ verifyPhoneNumberCredential(credential: any, session: SessionInfo): Promise<VerifyPhoneNumberResponse>
900
+
901
+ // Utility methods
902
+ retryLastRequest(): Promise<any>
903
+ cancelCurrentRequest(): void
904
+ ```
905
+
906
+ ### Response Types
907
+
908
+ ```typescript
909
+ interface GetPhoneNumberResponse {
910
+ phone_number: string;
911
+ aud?: string;
912
+ }
913
+
914
+ interface VerifyPhoneNumberResponse {
915
+ phone_number: string;
916
+ verified: boolean;
917
+ aud?: string;
918
+ }
919
+
920
+ interface HeadlessResult {
921
+ strategy: 'link' | 'ts43' | 'desktop';
922
+ trigger?: () => void | Promise<any>;
923
+ pollingPromise?: Promise<any>;
924
+ session: SessionInfo;
925
+ url?: string; // For Link strategy
926
+ qrCode?: string; // For Desktop strategy
927
+ }
928
+ ```
929
+
930
+ ## Support
931
+
932
+ - [Documentation](https://docs.glideidentity.com)
933
+ - [API Reference](https://api.glideidentity.com/docs)
934
+ - [Support](https://support.glideidentity.com)
935
+
936
+ ## License
937
+
938
+ MIT