@sip-protocol/sdk 0.2.9 → 0.2.10

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.
package/src/browser.ts CHANGED
@@ -21,6 +21,8 @@ export * from './index'
21
21
 
22
22
  // Browser-specific exports (import directly from browser module to get WASM support)
23
23
  export { BrowserNoirProvider } from './proofs/browser'
24
+ export { ProofWorker, createWorkerBlobURL } from './proofs/worker'
25
+ export type { WorkerRequest, WorkerResponse, WorkerMessageType } from './proofs/worker'
24
26
 
25
27
  // Re-export utilities that are already in main (for convenience)
26
28
  export {
@@ -32,4 +34,25 @@ export {
32
34
  browserBytesToHex,
33
35
  } from './proofs'
34
36
 
37
+ // Mobile browser detection utilities
38
+ export {
39
+ detectMobilePlatform,
40
+ detectMobileBrowser,
41
+ getMobileDeviceInfo,
42
+ checkMobileWASMCompatibility,
43
+ getBrowserVersion,
44
+ getOSVersion,
45
+ isTablet,
46
+ supportsTouch,
47
+ supportsWASMSimd,
48
+ supportsWASMBulkMemory,
49
+ } from './proofs/browser-utils'
50
+
51
+ export type {
52
+ MobilePlatform,
53
+ MobileBrowser,
54
+ MobileDeviceInfo,
55
+ MobileWASMCompatibility,
56
+ } from './proofs/browser-utils'
57
+
35
58
  export type { BrowserNoirProviderConfig, ProofProgressCallback } from './proofs'
@@ -4,8 +4,397 @@
4
4
  * These utilities replace Node.js-specific functions (like Buffer)
5
5
  * with browser-compatible alternatives using Web APIs.
6
6
  *
7
+ * Includes mobile browser detection and compatibility checking for:
8
+ * - Safari iOS
9
+ * - Chrome Android
10
+ * - Firefox Mobile
11
+ *
7
12
  * @module proofs/browser-utils
13
+ * @see https://github.com/sip-protocol/sip-protocol/issues/142
14
+ */
15
+
16
+ // ─── Mobile Browser Detection ────────────────────────────────────────────────
17
+
18
+ /**
19
+ * Mobile platform type
20
+ */
21
+ export type MobilePlatform = 'ios' | 'android' | 'other' | 'desktop'
22
+
23
+ /**
24
+ * Mobile browser type
25
+ */
26
+ export type MobileBrowser = 'safari' | 'chrome' | 'firefox' | 'samsung' | 'opera' | 'edge' | 'other'
27
+
28
+ /**
29
+ * Mobile device information
30
+ */
31
+ export interface MobileDeviceInfo {
32
+ /** Whether the device is mobile */
33
+ isMobile: boolean
34
+ /** Mobile platform (ios, android, other, desktop) */
35
+ platform: MobilePlatform
36
+ /** Mobile browser (safari, chrome, firefox, etc.) */
37
+ browser: MobileBrowser
38
+ /** Browser version string */
39
+ browserVersion: string | null
40
+ /** OS version string */
41
+ osVersion: string | null
42
+ /** Whether the device is a tablet */
43
+ isTablet: boolean
44
+ /** Whether the device supports touch */
45
+ supportsTouch: boolean
46
+ /** Estimated device memory in GB (Chrome only) */
47
+ deviceMemoryGB: number | null
48
+ /** Hardware concurrency (CPU cores) */
49
+ hardwareConcurrency: number | null
50
+ }
51
+
52
+ /**
53
+ * Mobile WASM compatibility status
54
+ */
55
+ export interface MobileWASMCompatibility {
56
+ /** Whether WebAssembly is supported */
57
+ webAssembly: boolean
58
+ /** Whether SharedArrayBuffer is supported */
59
+ sharedArrayBuffer: boolean
60
+ /** Whether Web Workers are supported */
61
+ webWorkers: boolean
62
+ /** Whether SIMD is supported */
63
+ simd: boolean
64
+ /** Whether bulk memory operations are supported */
65
+ bulkMemory: boolean
66
+ /** Whether BigInt is supported (needed for 64-bit ops) */
67
+ bigInt: boolean
68
+ /** Overall compatibility score (0-100) */
69
+ score: number
70
+ /** Human-readable compatibility summary */
71
+ summary: string
72
+ /** Specific issues detected */
73
+ issues: string[]
74
+ /** Recommended configuration adjustments */
75
+ recommendations: string[]
76
+ }
77
+
78
+ /**
79
+ * Detect mobile platform from user agent
80
+ */
81
+ export function detectMobilePlatform(): MobilePlatform {
82
+ if (typeof navigator === 'undefined') return 'desktop'
83
+
84
+ const ua = navigator.userAgent.toLowerCase()
85
+
86
+ // iOS detection (iPhone, iPad, iPod)
87
+ if (/iphone|ipad|ipod/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) {
88
+ return 'ios'
89
+ }
90
+
91
+ // Android detection
92
+ if (/android/.test(ua)) {
93
+ return 'android'
94
+ }
95
+
96
+ // Check for other mobile indicators
97
+ if (/mobile|webos|blackberry|opera mini|opera mobi|iemobile|wpdesktop/.test(ua)) {
98
+ return 'other'
99
+ }
100
+
101
+ return 'desktop'
102
+ }
103
+
104
+ /**
105
+ * Detect mobile browser from user agent
106
+ */
107
+ export function detectMobileBrowser(): MobileBrowser {
108
+ if (typeof navigator === 'undefined') return 'other'
109
+
110
+ const ua = navigator.userAgent.toLowerCase()
111
+
112
+ // Safari on iOS (must check before Chrome as Chrome iOS reports Safari)
113
+ if (/safari/.test(ua) && /iphone|ipad|ipod/.test(ua) && !/crios|fxios/.test(ua)) {
114
+ return 'safari'
115
+ }
116
+
117
+ // Chrome (including Chrome on iOS which is CriOS)
118
+ if (/chrome|crios/.test(ua) && !/edg|opr|samsung/.test(ua)) {
119
+ return 'chrome'
120
+ }
121
+
122
+ // Firefox (including Firefox on iOS which is FxiOS)
123
+ if (/firefox|fxios/.test(ua)) {
124
+ return 'firefox'
125
+ }
126
+
127
+ // Samsung Internet
128
+ if (/samsung/.test(ua)) {
129
+ return 'samsung'
130
+ }
131
+
132
+ // Opera
133
+ if (/opr|opera/.test(ua)) {
134
+ return 'opera'
135
+ }
136
+
137
+ // Edge
138
+ if (/edg/.test(ua)) {
139
+ return 'edge'
140
+ }
141
+
142
+ return 'other'
143
+ }
144
+
145
+ /**
146
+ * Extract browser version from user agent
147
+ */
148
+ export function getBrowserVersion(): string | null {
149
+ if (typeof navigator === 'undefined') return null
150
+
151
+ const ua = navigator.userAgent
152
+ const browser = detectMobileBrowser()
153
+
154
+ const patterns: Record<string, RegExp> = {
155
+ safari: /version\/(\d+(\.\d+)*)/i,
156
+ chrome: /chrome\/(\d+(\.\d+)*)|crios\/(\d+(\.\d+)*)/i,
157
+ firefox: /firefox\/(\d+(\.\d+)*)|fxios\/(\d+(\.\d+)*)/i,
158
+ samsung: /samsungbrowser\/(\d+(\.\d+)*)/i,
159
+ opera: /opr\/(\d+(\.\d+)*)/i,
160
+ edge: /edg\/(\d+(\.\d+)*)/i,
161
+ other: /version\/(\d+(\.\d+)*)/i,
162
+ }
163
+
164
+ const pattern = patterns[browser] || patterns.other
165
+ const match = ua.match(pattern)
166
+
167
+ if (match) {
168
+ // Return the first non-undefined captured group
169
+ for (let i = 1; i < match.length; i++) {
170
+ if (match[i]) return match[i]
171
+ }
172
+ }
173
+
174
+ return null
175
+ }
176
+
177
+ /**
178
+ * Extract OS version from user agent
179
+ */
180
+ export function getOSVersion(): string | null {
181
+ if (typeof navigator === 'undefined') return null
182
+
183
+ const ua = navigator.userAgent
184
+ const platform = detectMobilePlatform()
185
+
186
+ if (platform === 'ios') {
187
+ const match = ua.match(/os (\d+[_\d]*)/i)
188
+ return match ? match[1].replace(/_/g, '.') : null
189
+ }
190
+
191
+ if (platform === 'android') {
192
+ const match = ua.match(/android (\d+(\.\d+)*)/i)
193
+ return match ? match[1] : null
194
+ }
195
+
196
+ return null
197
+ }
198
+
199
+ /**
200
+ * Check if device is a tablet
201
+ */
202
+ export function isTablet(): boolean {
203
+ if (typeof navigator === 'undefined') return false
204
+
205
+ const ua = navigator.userAgent.toLowerCase()
206
+
207
+ // iPad detection
208
+ if (/ipad/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) {
209
+ return true
210
+ }
211
+
212
+ // Android tablet detection (no "mobile" in UA)
213
+ if (/android/.test(ua) && !/mobile/.test(ua)) {
214
+ return true
215
+ }
216
+
217
+ return false
218
+ }
219
+
220
+ /**
221
+ * Check if device supports touch
222
+ */
223
+ export function supportsTouch(): boolean {
224
+ if (typeof window === 'undefined') return false
225
+
226
+ return 'ontouchstart' in window || navigator.maxTouchPoints > 0
227
+ }
228
+
229
+ /**
230
+ * Get comprehensive mobile device info
231
+ */
232
+ export function getMobileDeviceInfo(): MobileDeviceInfo {
233
+ const platform = detectMobilePlatform()
234
+
235
+ return {
236
+ isMobile: platform !== 'desktop',
237
+ platform,
238
+ browser: detectMobileBrowser(),
239
+ browserVersion: getBrowserVersion(),
240
+ osVersion: getOSVersion(),
241
+ isTablet: isTablet(),
242
+ supportsTouch: supportsTouch(),
243
+ deviceMemoryGB:
244
+ // @ts-expect-error - deviceMemory is non-standard
245
+ typeof navigator !== 'undefined' && navigator.deviceMemory
246
+ ? // @ts-expect-error - deviceMemory is non-standard
247
+ navigator.deviceMemory
248
+ : null,
249
+ hardwareConcurrency:
250
+ typeof navigator !== 'undefined' && navigator.hardwareConcurrency
251
+ ? navigator.hardwareConcurrency
252
+ : null,
253
+ }
254
+ }
255
+
256
+ // ─── Mobile WASM Compatibility ───────────────────────────────────────────────
257
+
258
+ /**
259
+ * Check if WebAssembly SIMD is supported
260
+ */
261
+ export function supportsWASMSimd(): boolean {
262
+ try {
263
+ // Test for SIMD support using a minimal SIMD module
264
+ return WebAssembly.validate(
265
+ new Uint8Array([
266
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7b,
267
+ 0x03, 0x02, 0x01, 0x00, 0x0a, 0x0a, 0x01, 0x08, 0x00, 0x41, 0x00, 0xfd, 0x0f, 0xfd, 0x62,
268
+ 0x0b,
269
+ ])
270
+ )
271
+ } catch {
272
+ return false
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Check if WebAssembly bulk memory operations are supported
8
278
  */
279
+ export function supportsWASMBulkMemory(): boolean {
280
+ try {
281
+ // Test for bulk memory using memory.fill instruction
282
+ return WebAssembly.validate(
283
+ new Uint8Array([
284
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x03,
285
+ 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x0a, 0x0d, 0x01, 0x0b, 0x00, 0x41, 0x00,
286
+ 0x41, 0x00, 0x41, 0x00, 0xfc, 0x0b, 0x00, 0x0b,
287
+ ])
288
+ )
289
+ } catch {
290
+ return false
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Check mobile WASM compatibility
296
+ */
297
+ export function checkMobileWASMCompatibility(): MobileWASMCompatibility {
298
+ const issues: string[] = []
299
+ const recommendations: string[] = []
300
+ let score = 100
301
+
302
+ // WebAssembly basic support
303
+ const webAssembly = typeof WebAssembly !== 'undefined'
304
+ if (!webAssembly) {
305
+ issues.push('WebAssembly not supported')
306
+ score -= 50
307
+ }
308
+
309
+ // SharedArrayBuffer (critical for multi-threaded WASM)
310
+ const sharedArrayBuffer = supportsSharedArrayBuffer()
311
+ if (!sharedArrayBuffer) {
312
+ issues.push('SharedArrayBuffer not available (requires COOP/COEP headers)')
313
+ recommendations.push('Server must send Cross-Origin-Opener-Policy: same-origin')
314
+ recommendations.push('Server must send Cross-Origin-Embedder-Policy: require-corp')
315
+ score -= 20
316
+ }
317
+
318
+ // Web Workers
319
+ const webWorkers = supportsWebWorkers()
320
+ if (!webWorkers) {
321
+ issues.push('Web Workers not supported')
322
+ recommendations.push('Consider using a polyfill or fallback to main-thread execution')
323
+ score -= 15
324
+ }
325
+
326
+ // WASM SIMD (optional but improves performance)
327
+ const simd = supportsWASMSimd()
328
+ if (!simd) {
329
+ recommendations.push('WASM SIMD not supported - proofs will be slower')
330
+ score -= 5
331
+ }
332
+
333
+ // WASM bulk memory
334
+ const bulkMemory = supportsWASMBulkMemory()
335
+ if (!bulkMemory) {
336
+ recommendations.push('WASM bulk memory not supported - may affect performance')
337
+ score -= 5
338
+ }
339
+
340
+ // BigInt support
341
+ const bigInt = typeof BigInt !== 'undefined'
342
+ if (!bigInt) {
343
+ issues.push('BigInt not supported (required for 64-bit operations)')
344
+ score -= 10
345
+ }
346
+
347
+ // Mobile-specific checks
348
+ const deviceInfo = getMobileDeviceInfo()
349
+ if (deviceInfo.isMobile) {
350
+ // Memory check for mobile
351
+ if (deviceInfo.deviceMemoryGB !== null && deviceInfo.deviceMemoryGB < 2) {
352
+ recommendations.push(`Low device memory (${deviceInfo.deviceMemoryGB}GB) - may experience issues with large proofs`)
353
+ score -= 5
354
+ }
355
+
356
+ // iOS Safari specific warnings
357
+ if (deviceInfo.platform === 'ios' && deviceInfo.browser === 'safari') {
358
+ if (!sharedArrayBuffer) {
359
+ recommendations.push('iOS Safari requires iOS 15.2+ for SharedArrayBuffer support')
360
+ }
361
+ }
362
+
363
+ // Android Chrome specific
364
+ if (deviceInfo.platform === 'android' && deviceInfo.browser === 'chrome') {
365
+ if (!sharedArrayBuffer) {
366
+ recommendations.push('Ensure COOP/COEP headers are set - Chrome Android requires them')
367
+ }
368
+ }
369
+ }
370
+
371
+ // Generate summary
372
+ let summary: string
373
+ if (score >= 90) {
374
+ summary = 'Excellent - Full WASM proof support'
375
+ } else if (score >= 70) {
376
+ summary = 'Good - WASM proofs supported with minor limitations'
377
+ } else if (score >= 50) {
378
+ summary = 'Limited - WASM proofs may work with reduced performance'
379
+ } else {
380
+ summary = 'Poor - WASM proofs not recommended on this device'
381
+ }
382
+
383
+ return {
384
+ webAssembly,
385
+ sharedArrayBuffer,
386
+ webWorkers,
387
+ simd,
388
+ bulkMemory,
389
+ bigInt,
390
+ score: Math.max(0, score),
391
+ summary,
392
+ issues,
393
+ recommendations,
394
+ }
395
+ }
396
+
397
+ // ─── Core Browser Utilities ──────────────────────────────────────────────────
9
398
 
10
399
  /**
11
400
  * Convert hex string to Uint8Array (browser-compatible)