@mobana/react-native-sdk 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.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +249 -0
  3. package/android/build.gradle +50 -0
  4. package/android/src/main/AndroidManifest.xml +6 -0
  5. package/android/src/main/java/ai/mobana/sdk/MobanaModule.kt +67 -0
  6. package/android/src/main/java/ai/mobana/sdk/MobanaPackage.kt +19 -0
  7. package/app.plugin.js +274 -0
  8. package/ios/Mobana.h +11 -0
  9. package/ios/Mobana.m +20 -0
  10. package/lib/commonjs/Mobana.js +676 -0
  11. package/lib/commonjs/Mobana.js.map +1 -0
  12. package/lib/commonjs/NativeMobana.js +53 -0
  13. package/lib/commonjs/NativeMobana.js.map +1 -0
  14. package/lib/commonjs/api.js +201 -0
  15. package/lib/commonjs/api.js.map +1 -0
  16. package/lib/commonjs/bridge/index.js +19 -0
  17. package/lib/commonjs/bridge/index.js.map +1 -0
  18. package/lib/commonjs/bridge/injectBridge.js +528 -0
  19. package/lib/commonjs/bridge/injectBridge.js.map +1 -0
  20. package/lib/commonjs/components/FlowWebView.js +676 -0
  21. package/lib/commonjs/components/FlowWebView.js.map +1 -0
  22. package/lib/commonjs/components/MobanaProvider.js +275 -0
  23. package/lib/commonjs/components/MobanaProvider.js.map +1 -0
  24. package/lib/commonjs/components/index.js +20 -0
  25. package/lib/commonjs/components/index.js.map +1 -0
  26. package/lib/commonjs/device.js +49 -0
  27. package/lib/commonjs/device.js.map +1 -0
  28. package/lib/commonjs/index.js +20 -0
  29. package/lib/commonjs/index.js.map +1 -0
  30. package/lib/commonjs/package.json +1 -0
  31. package/lib/commonjs/storage.js +277 -0
  32. package/lib/commonjs/storage.js.map +1 -0
  33. package/lib/commonjs/types.js +2 -0
  34. package/lib/commonjs/types.js.map +1 -0
  35. package/lib/module/Mobana.js +673 -0
  36. package/lib/module/Mobana.js.map +1 -0
  37. package/lib/module/NativeMobana.js +49 -0
  38. package/lib/module/NativeMobana.js.map +1 -0
  39. package/lib/module/api.js +194 -0
  40. package/lib/module/api.js.map +1 -0
  41. package/lib/module/bridge/index.js +4 -0
  42. package/lib/module/bridge/index.js.map +1 -0
  43. package/lib/module/bridge/injectBridge.js +523 -0
  44. package/lib/module/bridge/injectBridge.js.map +1 -0
  45. package/lib/module/components/FlowWebView.js +672 -0
  46. package/lib/module/components/FlowWebView.js.map +1 -0
  47. package/lib/module/components/MobanaProvider.js +270 -0
  48. package/lib/module/components/MobanaProvider.js.map +1 -0
  49. package/lib/module/components/index.js +5 -0
  50. package/lib/module/components/index.js.map +1 -0
  51. package/lib/module/device.js +45 -0
  52. package/lib/module/device.js.map +1 -0
  53. package/lib/module/index.js +53 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/storage.js +257 -0
  56. package/lib/module/storage.js.map +1 -0
  57. package/lib/module/types.js +2 -0
  58. package/lib/module/types.js.map +1 -0
  59. package/lib/typescript/Mobana.d.ts +209 -0
  60. package/lib/typescript/Mobana.d.ts.map +1 -0
  61. package/lib/typescript/NativeMobana.d.ts +11 -0
  62. package/lib/typescript/NativeMobana.d.ts.map +1 -0
  63. package/lib/typescript/api.d.ts +34 -0
  64. package/lib/typescript/api.d.ts.map +1 -0
  65. package/lib/typescript/bridge/index.d.ts +3 -0
  66. package/lib/typescript/bridge/index.d.ts.map +1 -0
  67. package/lib/typescript/bridge/injectBridge.d.ts +23 -0
  68. package/lib/typescript/bridge/injectBridge.d.ts.map +1 -0
  69. package/lib/typescript/components/FlowWebView.d.ts +38 -0
  70. package/lib/typescript/components/FlowWebView.d.ts.map +1 -0
  71. package/lib/typescript/components/MobanaProvider.d.ts +65 -0
  72. package/lib/typescript/components/MobanaProvider.d.ts.map +1 -0
  73. package/lib/typescript/components/index.d.ts +5 -0
  74. package/lib/typescript/components/index.d.ts.map +1 -0
  75. package/lib/typescript/device.d.ts +6 -0
  76. package/lib/typescript/device.d.ts.map +1 -0
  77. package/lib/typescript/index.d.ts +46 -0
  78. package/lib/typescript/index.d.ts.map +1 -0
  79. package/lib/typescript/storage.d.ts +68 -0
  80. package/lib/typescript/storage.d.ts.map +1 -0
  81. package/lib/typescript/types.d.ts +298 -0
  82. package/lib/typescript/types.d.ts.map +1 -0
  83. package/mobana.podspec +19 -0
  84. package/package.json +131 -0
  85. package/src/Mobana.ts +742 -0
  86. package/src/NativeMobana.ts +61 -0
  87. package/src/api.ts +259 -0
  88. package/src/bridge/index.ts +2 -0
  89. package/src/bridge/injectBridge.ts +542 -0
  90. package/src/components/FlowWebView.tsx +826 -0
  91. package/src/components/MobanaProvider.tsx +393 -0
  92. package/src/components/index.ts +4 -0
  93. package/src/device.ts +42 -0
  94. package/src/index.ts +66 -0
  95. package/src/storage.ts +262 -0
  96. package/src/types.ts +362 -0
@@ -0,0 +1,523 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Bridge context passed from native to WebView
5
+ */
6
+
7
+ /**
8
+ * Generate JavaScript code to inject into WebView
9
+ * Creates the window.Mobana bridge object
10
+ */
11
+ export function generateBridgeScript(context) {
12
+ const contextJson = JSON.stringify(context);
13
+
14
+ // This JavaScript runs inside the WebView
15
+ return `
16
+ (function() {
17
+ 'use strict';
18
+
19
+ // Bridge context from native
20
+ var __context = ${contextJson};
21
+ var __localData = __context.localData || {};
22
+
23
+ // Pending async requests (requestId -> { resolve, reject })
24
+ var __pendingRequests = {};
25
+ var __requestId = 0;
26
+
27
+ // Send message to native
28
+ function postMessage(type, payload, requestId) {
29
+ var message = {
30
+ type: type,
31
+ payload: payload,
32
+ requestId: requestId
33
+ };
34
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
35
+ }
36
+
37
+ // Make an async request to native and wait for response
38
+ function asyncRequest(type, payload) {
39
+ return new Promise(function(resolve, reject) {
40
+ var id = ++__requestId;
41
+ __pendingRequests[id] = { resolve: resolve, reject: reject };
42
+ postMessage(type, payload, id);
43
+ });
44
+ }
45
+
46
+ // Handle response from native (called via injectJavaScript)
47
+ window.__mobanaBridgeResponse = function(requestId, success, result) {
48
+ var pending = __pendingRequests[requestId];
49
+ if (pending) {
50
+ delete __pendingRequests[requestId];
51
+ if (success) {
52
+ pending.resolve(result);
53
+ } else {
54
+ pending.reject(new Error(result || 'Request failed'));
55
+ }
56
+ }
57
+ };
58
+
59
+ // Mobana bridge object
60
+ window.Mobana = {
61
+ // ============================================
62
+ // Data Access
63
+ // ============================================
64
+
65
+ /**
66
+ * Get attribution data for this install
67
+ * @returns {Object|null} Attribution object or null if not matched
68
+ */
69
+ getAttribution: function() {
70
+ return __context.attribution;
71
+ },
72
+
73
+ /**
74
+ * Get custom parameters passed to startFlow()
75
+ * @returns {Object} Parameters object
76
+ */
77
+ getParams: function() {
78
+ return __context.params || {};
79
+ },
80
+
81
+ /**
82
+ * Get the install ID
83
+ * @returns {string} Unique install identifier
84
+ */
85
+ getInstallId: function() {
86
+ return __context.installId;
87
+ },
88
+
89
+ /**
90
+ * Get the current platform
91
+ * @returns {string} 'ios' or 'android'
92
+ */
93
+ getPlatform: function() {
94
+ return __context.platform;
95
+ },
96
+
97
+ /**
98
+ * Get safe area insets for the device screen
99
+ * @returns {Object} { top, bottom, left, right, width, height }
100
+ */
101
+ getSafeArea: function() {
102
+ return __context.safeArea;
103
+ },
104
+
105
+ /**
106
+ * Get the device color scheme (light/dark mode)
107
+ * @returns {string} 'light' or 'dark'
108
+ */
109
+ getColorScheme: function() {
110
+ return __context.colorScheme;
111
+ },
112
+
113
+ /**
114
+ * Store data locally on device (persists across app sessions)
115
+ * @param {string} key - Data key
116
+ * @param {*} value - Data value
117
+ */
118
+ setLocalData: function(key, value) {
119
+ __localData[key] = value;
120
+ postMessage('setLocalData', { key: key, value: value });
121
+ },
122
+
123
+ /**
124
+ * Retrieve locally stored data
125
+ * @param {string} key - Data key
126
+ * @returns {*} Data value or undefined
127
+ */
128
+ getLocalData: function(key) {
129
+ return __localData[key];
130
+ },
131
+
132
+ // ============================================
133
+ // Flow Control
134
+ // ============================================
135
+
136
+ /**
137
+ * Complete the flow with optional data
138
+ * @param {Object} data - Optional data to return to the app
139
+ */
140
+ complete: function(data) {
141
+ postMessage('complete', { data: data });
142
+ },
143
+
144
+ /**
145
+ * Dismiss the flow
146
+ */
147
+ dismiss: function() {
148
+ postMessage('dismiss', {});
149
+ },
150
+
151
+ /**
152
+ * Track a custom event
153
+ * @param {string} name - Event name (snake_case, e.g., 'welcome_viewed')
154
+ */
155
+ trackEvent: function(name) {
156
+ postMessage('trackEvent', { name: name });
157
+ },
158
+
159
+ /**
160
+ * Request the app to perform an async action and return a result.
161
+ * The flow stays open while the app processes the request.
162
+ * Requires onCallback to be provided when starting the flow.
163
+ *
164
+ * @param {Object} data - Arbitrary data to send to the app's onCallback handler
165
+ * @param {Object} options - Optional configuration
166
+ * @param {number} options.timeout - Timeout in seconds (default: 300)
167
+ * @returns {Promise<Object>} Result returned by the app's onCallback handler
168
+ *
169
+ * @example
170
+ * // Request a purchase
171
+ * try {
172
+ * var result = await Mobana.requestCallback(
173
+ * { action: 'purchase', planId: 'premium' },
174
+ * { timeout: 120 }
175
+ * );
176
+ * if (result.success) {
177
+ * Mobana.complete({ purchased: true });
178
+ * }
179
+ * } catch (error) {
180
+ * // Timeout, no handler, or handler threw an error
181
+ * }
182
+ */
183
+ requestCallback: function(data, options) {
184
+ var opts = options || {};
185
+ var timeout = typeof opts.timeout === 'number' ? opts.timeout : 300;
186
+
187
+ var promise = asyncRequest('requestCallback', { data: data || {} });
188
+
189
+ // Wrap with timeout
190
+ var timeoutMs = timeout * 1000;
191
+ var timer;
192
+ var timeoutPromise = new Promise(function(_, reject) {
193
+ timer = setTimeout(function() {
194
+ reject(new Error('requestCallback timed out after ' + timeout + 's'));
195
+ }, timeoutMs);
196
+ });
197
+
198
+ return Promise.race([promise, timeoutPromise]).then(
199
+ function(result) { clearTimeout(timer); return result; },
200
+ function(error) { clearTimeout(timer); throw error; }
201
+ );
202
+ },
203
+
204
+ // ============================================
205
+ // Permissions
206
+ // ============================================
207
+
208
+ /**
209
+ * Request notification permission
210
+ * @returns {Promise<boolean>} True if granted
211
+ */
212
+ requestNotificationPermission: function() {
213
+ return asyncRequest('requestNotificationPermission', {});
214
+ },
215
+
216
+ /**
217
+ * Check notification permission status without requesting
218
+ * @returns {Promise<Object>} { status: string, granted: boolean, settings?: Object }
219
+ */
220
+ checkNotificationPermission: function() {
221
+ return asyncRequest('checkNotificationPermission', {});
222
+ },
223
+
224
+ /**
225
+ * Request App Tracking Transparency permission (iOS only)
226
+ * @returns {Promise<string>} 'authorized', 'denied', 'not-determined', or 'restricted'
227
+ */
228
+ requestATTPermission: function() {
229
+ return asyncRequest('requestATTPermission', {});
230
+ },
231
+
232
+ /**
233
+ * Check App Tracking Transparency status without requesting (iOS only)
234
+ * @returns {Promise<string>} 'authorized', 'denied', 'not-determined', or 'restricted'
235
+ */
236
+ checkATTPermission: function() {
237
+ return asyncRequest('checkATTPermission', {});
238
+ },
239
+
240
+ /**
241
+ * Request location permission
242
+ * @param {Object} options - Optional configuration
243
+ * @param {string} options.precision - 'precise' (default) or 'coarse'. On Android, this determines
244
+ * whether to request ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION. On iOS, precision is
245
+ * controlled by the user in the permission dialog.
246
+ * @returns {Promise<string>} Permission result ('granted', 'denied', 'blocked', 'unavailable')
247
+ */
248
+ requestLocationPermission: function(options) {
249
+ var opts = options || {};
250
+ return asyncRequest('requestLocationPermission', { precision: opts.precision || 'precise' });
251
+ },
252
+
253
+ /**
254
+ * Request background location permission
255
+ * @returns {Promise<string>} Permission result ('granted', 'denied', 'blocked', 'unavailable')
256
+ */
257
+ requestBackgroundLocationPermission: function() {
258
+ return asyncRequest('requestBackgroundLocationPermission', {});
259
+ },
260
+
261
+ /**
262
+ * Get current location permission status
263
+ * @returns {Promise<Object>} Location permission status object:
264
+ * - foreground: 'granted' | 'denied' | 'blocked' | 'not_requested'
265
+ * - background: 'granted' | 'denied' | 'blocked' | 'not_requested'
266
+ * - precision: 'precise' | 'coarse' | 'unknown'
267
+ */
268
+ getLocationPermissionStatus: function() {
269
+ return asyncRequest('getLocationPermissionStatus', {});
270
+ },
271
+
272
+ /**
273
+ * Get current location
274
+ * @returns {Promise<Object>} Location coordinates
275
+ */
276
+ getCurrentLocation: function() {
277
+ return asyncRequest('getCurrentLocation', {});
278
+ },
279
+
280
+ // ============================================
281
+ // Native Utilities
282
+ // ============================================
283
+
284
+ /**
285
+ * Request app store review
286
+ * Note: This will complete the flow and show the review dialog after the flow closes.
287
+ * Due to iOS StoreKit limitations, reviews cannot be shown while a modal is visible.
288
+ * Use this as the final action in your flow.
289
+ */
290
+ requestAppReview: function() {
291
+ postMessage('requestAppReview', {});
292
+ },
293
+
294
+ /**
295
+ * Trigger haptic feedback
296
+ * @param {string} style - 'light', 'medium', 'heavy', 'success', 'warning', 'error', 'selection'
297
+ */
298
+ haptic: function(style) {
299
+ postMessage('haptic', { style: style || 'medium' });
300
+ },
301
+
302
+ /**
303
+ * Open a URL in the browser
304
+ * @param {string} url - URL to open
305
+ */
306
+ openURL: function(url) {
307
+ postMessage('openURL', { url: url });
308
+ },
309
+
310
+ /**
311
+ * Open app settings
312
+ */
313
+ openSettings: function() {
314
+ postMessage('openSettings', {});
315
+ },
316
+
317
+ /**
318
+ * Play a sound from a URL (external or base64 data URL)
319
+ * @param {string} url - Sound URL (https:// or data:audio/...)
320
+ * @param {Object} options - Optional playback options
321
+ * @param {number} options.volume - Volume level (0.0 - 1.0, default 1.0)
322
+ * @param {boolean} options.loop - Whether to loop the sound (default false)
323
+ * @param {function} options.onEnd - Callback when sound finishes playing
324
+ * @param {function} options.onError - Callback when an error occurs
325
+ * @returns {Object} Controller with { isPlaying, stop() }
326
+ */
327
+ playSound: function(url, options) {
328
+ var opts = options || {};
329
+ var volume = typeof opts.volume === 'number' ? Math.max(0, Math.min(1, opts.volume)) : 1.0;
330
+ var loop = opts.loop === true;
331
+ var onEnd = typeof opts.onEnd === 'function' ? opts.onEnd : null;
332
+ var onError = typeof opts.onError === 'function' ? opts.onError : null;
333
+
334
+ var controller = {
335
+ isPlaying: false,
336
+ stop: function() {}
337
+ };
338
+
339
+ try {
340
+ var audio = new Audio(url);
341
+ audio.volume = volume;
342
+ audio.loop = loop;
343
+
344
+ audio.onplay = function() {
345
+ controller.isPlaying = true;
346
+ };
347
+
348
+ audio.onended = function() {
349
+ controller.isPlaying = false;
350
+ if (onEnd) {
351
+ try { onEnd(); } catch (e) { console.warn('playSound onEnd error:', e); }
352
+ }
353
+ };
354
+
355
+ audio.onerror = function(e) {
356
+ controller.isPlaying = false;
357
+ console.warn('playSound error: Failed to load or play sound');
358
+ if (onError) {
359
+ try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
360
+ }
361
+ };
362
+
363
+ audio.onpause = function() {
364
+ if (!audio.ended) {
365
+ controller.isPlaying = false;
366
+ }
367
+ };
368
+
369
+ controller.stop = function() {
370
+ try {
371
+ audio.pause();
372
+ audio.currentTime = 0;
373
+ controller.isPlaying = false;
374
+ } catch (e) {
375
+ // Audio may have been garbage collected
376
+ }
377
+ };
378
+
379
+ audio.play().catch(function(e) {
380
+ controller.isPlaying = false;
381
+ console.warn('playSound error: ' + e.message);
382
+ if (onError) {
383
+ try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
384
+ }
385
+ });
386
+
387
+ controller.isPlaying = true;
388
+ } catch (e) {
389
+ console.warn('playSound error: ' + e.message);
390
+ if (onError) {
391
+ try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
392
+ }
393
+ }
394
+
395
+ return controller;
396
+ }
397
+ };
398
+
399
+ // Mark bridge as ready
400
+ window.__mobanaBridgeReady = true;
401
+
402
+ // Dispatch ready event for flows that want to wait
403
+ if (typeof document !== 'undefined') {
404
+ document.dispatchEvent(new Event('mobana:ready'));
405
+ }
406
+ })();
407
+ `;
408
+ }
409
+
410
+ /**
411
+ * Build complete HTML with injected bridge, styles, and safe area CSS variables
412
+ */
413
+ export function buildFlowHtml(html, css, js, bridgeScript, safeArea, colorScheme) {
414
+ let fullHtml = html;
415
+
416
+ // 0. Ensure viewport meta tag has viewport-fit=cover (required for edge-to-edge rendering on iOS)
417
+ const viewportMetaRegex = /<meta\s+[^>]*name=["']viewport["'][^>]*>/i;
418
+ const viewportMatch = fullHtml.match(viewportMetaRegex);
419
+ if (viewportMatch) {
420
+ const existingTag = viewportMatch[0];
421
+ if (!existingTag.includes('viewport-fit=cover')) {
422
+ // Append viewport-fit=cover to existing content attribute
423
+ const updatedTag = existingTag.replace(/content=["']([^"']*)["']/i, (match, content) => `content="${content}, viewport-fit=cover"`);
424
+ fullHtml = fullHtml.replace(existingTag, updatedTag);
425
+ }
426
+ } else {
427
+ // No viewport meta tag — inject a sensible default
428
+ const defaultViewport = '<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">';
429
+ if (fullHtml.includes('</head>')) {
430
+ fullHtml = fullHtml.replace(/<head([^>]*)>/i, `<head$1>${defaultViewport}`);
431
+ } else if (fullHtml.includes('<body')) {
432
+ fullHtml = fullHtml.replace('<body', `<head>${defaultViewport}</head><body`);
433
+ } else {
434
+ fullHtml = `<head>${defaultViewport}</head>` + fullHtml;
435
+ }
436
+ }
437
+
438
+ // 1. Inject SDK base resets FIRST (before user CSS, so flows can override)
439
+ const resetStyle = `<style data-mobana="reset">
440
+ /* Mobana SDK base resets — flows can override any of these */
441
+ *, *::before, *::after {
442
+ margin: 0;
443
+ padding: 0;
444
+ box-sizing: border-box;
445
+ -webkit-tap-highlight-color: transparent;
446
+ }
447
+ body {
448
+ -webkit-font-smoothing: antialiased;
449
+ -webkit-user-select: none;
450
+ user-select: none;
451
+ -webkit-touch-callout: none;
452
+ overflow: hidden;
453
+ }
454
+ </style>`;
455
+ if (fullHtml.includes('</head>')) {
456
+ // Insert at the START of <head> so it comes before any flow styles
457
+ fullHtml = fullHtml.replace(/<head([^>]*)>/i, `<head$1>${resetStyle}`);
458
+ } else if (fullHtml.includes('<body')) {
459
+ fullHtml = fullHtml.replace('<body', `<head>${resetStyle}</head><body`);
460
+ } else {
461
+ fullHtml = resetStyle + fullHtml;
462
+ }
463
+
464
+ // 2. Inject user CSS (if separate) — after resets, before env vars
465
+ if (css) {
466
+ const styleTag = `<style>${css}</style>`;
467
+ if (fullHtml.includes('</head>')) {
468
+ fullHtml = fullHtml.replace('</head>', `${styleTag}</head>`);
469
+ } else if (fullHtml.includes('<body')) {
470
+ fullHtml = fullHtml.replace('<body', `<head>${styleTag}</head><body`);
471
+ } else {
472
+ fullHtml = styleTag + fullHtml;
473
+ }
474
+ }
475
+
476
+ // 3. Inject CSS environment variables AFTER user CSS (SDK values take precedence)
477
+ // Note: We use values from react-native-safe-area-context (not CSS env()) for reliable
478
+ // cross-platform insets. Step 0 ensures viewport-fit=cover for edge-to-edge rendering.
479
+ const envVarsStyle = `<style data-mobana="env">
480
+ :root {
481
+ /* Color scheme - enables light-dark() CSS function */
482
+ color-scheme: ${colorScheme || 'light'};
483
+ --color-scheme: ${colorScheme || 'light'};
484
+ /* Safe area insets - from react-native-safe-area-context */
485
+ --safe-area-top: ${safeArea?.top ?? 0}px;
486
+ --safe-area-right: ${safeArea?.right ?? 0}px;
487
+ --safe-area-bottom: ${safeArea?.bottom ?? 0}px;
488
+ --safe-area-left: ${safeArea?.left ?? 0}px;
489
+ /* Screen dimensions */
490
+ --screen-width: ${safeArea?.width ?? 0}px;
491
+ --screen-height: ${safeArea?.height ?? 0}px;
492
+ }
493
+ </style>`;
494
+ if (fullHtml.includes('</head>')) {
495
+ fullHtml = fullHtml.replace('</head>', `${envVarsStyle}</head>`);
496
+ } else if (fullHtml.includes('<body')) {
497
+ fullHtml = fullHtml.replace('<body', `<head>${envVarsStyle}</head><body`);
498
+ } else {
499
+ fullHtml = envVarsStyle + fullHtml;
500
+ }
501
+
502
+ // Inject JS if separate
503
+ if (js) {
504
+ const scriptTag = `<script>${js}</script>`;
505
+ if (fullHtml.includes('</body>')) {
506
+ fullHtml = fullHtml.replace('</body>', `${scriptTag}</body>`);
507
+ } else {
508
+ fullHtml = fullHtml + scriptTag;
509
+ }
510
+ }
511
+
512
+ // Inject bridge script (must come before user JS)
513
+ const bridgeTag = `<script>${bridgeScript}</script>`;
514
+ if (fullHtml.includes('</head>')) {
515
+ fullHtml = fullHtml.replace('</head>', `${bridgeTag}</head>`);
516
+ } else if (fullHtml.includes('<body')) {
517
+ fullHtml = fullHtml.replace('<body', `<head>${bridgeTag}</head><body`);
518
+ } else {
519
+ fullHtml = bridgeTag + fullHtml;
520
+ }
521
+ return fullHtml;
522
+ }
523
+ //# sourceMappingURL=injectBridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["generateBridgeScript","context","contextJson","JSON","stringify","buildFlowHtml","html","css","js","bridgeScript","safeArea","colorScheme","fullHtml","viewportMetaRegex","viewportMatch","match","existingTag","includes","updatedTag","replace","content","defaultViewport","resetStyle","styleTag","envVarsStyle","top","right","bottom","left","width","height","scriptTag","bridgeTag"],"sourceRoot":"../../../src","sources":["bridge/injectBridge.ts"],"mappings":";;AAEA;AACA;AACA;;AAWA;AACA;AACA;AACA;AACA,OAAO,SAASA,oBAAoBA,CAACC,OAAsB,EAAU;EACnE,MAAMC,WAAW,GAAGC,IAAI,CAACC,SAAS,CAACH,OAAO,CAAC;;EAE3C;EACA,OAAO;AACT;AACA;AACA;AACA;AACA,oBAAoBC,WAAW;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;;AAEA;AACA;AACA;AACA,OAAO,SAASG,aAAaA,CAC3BC,IAAY,EACZC,GAAuB,EACvBC,EAAsB,EACtBC,YAAoB,EACpBC,QAAmB,EACnBC,WAAyB,EACjB;EACR,IAAIC,QAAQ,GAAGN,IAAI;;EAEnB;EACA,MAAMO,iBAAiB,GAAG,2CAA2C;EACrE,MAAMC,aAAa,GAAGF,QAAQ,CAACG,KAAK,CAACF,iBAAiB,CAAC;EACvD,IAAIC,aAAa,EAAE;IA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","ignoreList":[]}