rampkit-expo-dev 0.0.30 → 0.0.32

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.
@@ -2,7 +2,6 @@ import { RampKitContext } from "./types";
2
2
  export declare const injectedHardening = "\n(function(){\n try {\n var meta = document.querySelector('meta[name=\"viewport\"]');\n if (!meta) { meta = document.createElement('meta'); meta.name = 'viewport'; document.head.appendChild(meta); }\n meta.setAttribute('content','width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover');\n var style = document.createElement('style');\n style.textContent='html,body{overflow-x:hidden!important;} html,body,*{-webkit-user-select:none!important;user-select:none!important;-webkit-touch-callout:none!important;-ms-user-select:none!important;touch-action: pan-y;} *{-webkit-tap-highlight-color: rgba(0,0,0,0)!important;} ::selection{background: transparent!important;} ::-moz-selection{background: transparent!important;} a,img{-webkit-user-drag:none!important;user-drag:none!important;-webkit-touch-callout:none!important} input,textarea{caret-color:transparent!important;-webkit-user-select:none!important;user-select:none!important}';\n document.head.appendChild(style);\n var prevent=function(e){e.preventDefault&&e.preventDefault();};\n document.addEventListener('gesturestart',prevent,{passive:false});\n document.addEventListener('gesturechange',prevent,{passive:false});\n document.addEventListener('gestureend',prevent,{passive:false});\n document.addEventListener('dblclick',prevent,{passive:false});\n document.addEventListener('wheel',function(e){ if(e.ctrlKey) e.preventDefault(); },{passive:false});\n document.addEventListener('touchmove',function(e){ if(e.scale && e.scale !== 1) e.preventDefault(); },{passive:false});\n document.addEventListener('selectstart',prevent,{passive:false,capture:true});\n document.addEventListener('contextmenu',prevent,{passive:false,capture:true});\n document.addEventListener('copy',prevent,{passive:false,capture:true});\n document.addEventListener('cut',prevent,{passive:false,capture:true});\n document.addEventListener('paste',prevent,{passive:false,capture:true});\n document.addEventListener('dragstart',prevent,{passive:false,capture:true});\n // Belt-and-suspenders: aggressively clear any attempted selection\n var clearSel=function(){\n try{var sel=window.getSelection&&window.getSelection(); if(sel&&sel.removeAllRanges) sel.removeAllRanges();}catch(_){} }\n document.addEventListener('selectionchange',clearSel,{passive:true,capture:true});\n document.onselectstart=function(){ clearSel(); return false; };\n try{ document.documentElement.style.webkitUserSelect='none'; document.documentElement.style.userSelect='none'; }catch(_){ }\n try{ document.body.style.webkitUserSelect='none'; document.body.style.userSelect='none'; }catch(_){ }\n var __selTimer = setInterval(clearSel, 160);\n window.addEventListener('pagehide',function(){ try{ clearInterval(__selTimer); }catch(_){} });\n // Continuously enforce no-select on all elements and new nodes\n var enforceNoSelect = function(el){\n try{\n el.style && (el.style.webkitUserSelect='none', el.style.userSelect='none', el.style.webkitTouchCallout='none');\n el.setAttribute && (el.setAttribute('unselectable','on'), el.setAttribute('contenteditable','false'));\n }catch(_){}\n }\n try{\n var all=document.getElementsByTagName('*');\n for(var i=0;i<all.length;i++){ enforceNoSelect(all[i]); }\n var obs = new MutationObserver(function(muts){\n for(var j=0;j<muts.length;j++){\n var m=muts[j];\n if(m.type==='childList'){\n m.addedNodes && m.addedNodes.forEach && m.addedNodes.forEach(function(n){ if(n && n.nodeType===1){ enforceNoSelect(n); var q=n.getElementsByTagName? n.getElementsByTagName('*'): []; for(var k=0;k<q.length;k++){ enforceNoSelect(q[k]); }}});\n } else if(m.type==='attributes'){\n enforceNoSelect(m.target);\n }\n }\n });\n obs.observe(document.documentElement,{ childList:true, subtree:true, attributes:true, attributeFilter:['contenteditable','style'] });\n }catch(_){ }\n } catch(_) {}\n})(); true;\n";
3
3
  export declare const injectedNoSelect = "\n(function(){\n try {\n if (window.__rkNoSelectApplied) return true;\n window.__rkNoSelectApplied = true;\n var style = document.getElementById('rk-no-select-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'rk-no-select-style';\n style.innerHTML = \"\n * {\n user-select: none !important;\n -webkit-user-select: none !important;\n -webkit-touch-callout: none !important;\n }\n ::selection {\n background: transparent !important;\n }\n \";\n document.head.appendChild(style);\n }\n var prevent = function(e){ if(e && e.preventDefault) e.preventDefault(); return false; };\n document.addEventListener('contextmenu', prevent, { passive: false, capture: true });\n document.addEventListener('selectstart', prevent, { passive: false, capture: true });\n } catch (_) {}\n true;\n})();\n";
4
4
  export declare const injectedVarsHandler = "\n(function(){\n try {\n if (window.__rkVarsHandlerApplied) return true;\n window.__rkVarsHandlerApplied = true;\n \n // Handler function that updates variables and notifies the page\n window.__rkHandleVarsUpdate = function(vars) {\n if (!vars || typeof vars !== 'object') return;\n // Update the global variables object\n window.__rampkitVariables = vars;\n // Dispatch a custom event that the page's JS can listen to for re-rendering\n try {\n document.dispatchEvent(new CustomEvent('rampkit:vars-updated', { detail: vars }));\n } catch(e) {}\n // Also try calling a global handler if the page defined one\n try {\n if (typeof window.onRampkitVarsUpdate === 'function') {\n window.onRampkitVarsUpdate(vars);\n }\n } catch(e) {}\n };\n \n // Listen for message events from React Native\n document.addEventListener('message', function(event) {\n try {\n var data = event.data;\n if (data && data.type === 'rampkit:variables' && data.vars) {\n window.__rkHandleVarsUpdate(data.vars);\n }\n } catch(e) {}\n }, false);\n \n // Also listen on window for compatibility\n window.addEventListener('message', function(event) {\n try {\n var data = event.data;\n if (data && data.type === 'rampkit:variables' && data.vars) {\n window.__rkHandleVarsUpdate(data.vars);\n }\n } catch(e) {}\n }, false);\n } catch (_) {}\n true;\n})();\n";
5
- export declare const injectedTemplateResolver: string;
6
5
  export type ScreenPayload = {
7
6
  id: string;
8
7
  html: string;
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.injectedTemplateResolver = exports.injectedVarsHandler = exports.injectedNoSelect = exports.injectedHardening = void 0;
39
+ exports.injectedVarsHandler = exports.injectedNoSelect = exports.injectedHardening = void 0;
40
40
  exports.showRampkitOverlay = showRampkitOverlay;
41
41
  exports.hideRampkitOverlay = hideRampkitOverlay;
42
42
  exports.closeRampkitOverlay = closeRampkitOverlay;
@@ -173,84 +173,6 @@ exports.injectedVarsHandler = `
173
173
  true;
174
174
  })();
175
175
  `;
176
- // Template resolution script that replaces ${device.xxx} and ${user.xxx} with actual values
177
- // Built using string concatenation to avoid template literal escaping issues
178
- exports.injectedTemplateResolver = [
179
- "(function(){",
180
- " try {",
181
- " if (window.__rkTemplateResolverApplied) return true;",
182
- " window.__rkTemplateResolverApplied = true;",
183
- " ",
184
- " console.log('[Rampkit] Template resolver starting...');",
185
- " console.log('[Rampkit] rampkitContext:', JSON.stringify(window.rampkitContext));",
186
- " ",
187
- " function buildVarMap() {",
188
- " var vars = {};",
189
- " var ctx = window.rampkitContext || { device: {}, user: {} };",
190
- " var state = window.__rampkitVariables || {};",
191
- " if (ctx.device) {",
192
- " Object.keys(ctx.device).forEach(function(key) {",
193
- " vars['device.' + key] = ctx.device[key];",
194
- " });",
195
- " }",
196
- " if (ctx.user) {",
197
- " Object.keys(ctx.user).forEach(function(key) {",
198
- " vars['user.' + key] = ctx.user[key];",
199
- " });",
200
- " }",
201
- " Object.keys(state).forEach(function(key) {",
202
- " vars[key] = state[key];",
203
- " });",
204
- " console.log('[Rampkit] Variable map:', JSON.stringify(vars));",
205
- " return vars;",
206
- " }",
207
- " ",
208
- " function formatValue(value) {",
209
- " if (value === undefined || value === null) return '';",
210
- " if (typeof value === 'boolean') return value ? 'true' : 'false';",
211
- " if (typeof value === 'object') return JSON.stringify(value);",
212
- " return String(value);",
213
- " }",
214
- " ",
215
- " function resolveAllTemplates() {",
216
- " console.log('[Rampkit] Resolving templates...');",
217
- " var vars = buildVarMap();",
218
- " var pattern = /\\$\\{([A-Za-z_][A-Za-z0-9_.]*)\\}/g;",
219
- " var bodyHtml = document.body.innerHTML;",
220
- " var marker = String.fromCharCode(36, 123);", // $ = 36, { = 123
221
- " var hasTemplates = bodyHtml.indexOf(marker) !== -1;",
222
- " console.log('[Rampkit] Body has templates:', hasTemplates);",
223
- " if (hasTemplates) {",
224
- " var newHtml = bodyHtml.replace(pattern, function(match, varName) {",
225
- " console.log('[Rampkit] Found template:', match, 'varName:', varName);",
226
- " if (vars.hasOwnProperty(varName)) {",
227
- " var value = formatValue(vars[varName]);",
228
- " console.log('[Rampkit] Replacing with:', value);",
229
- " return value;",
230
- " }",
231
- " console.log('[Rampkit] No value found for:', varName);",
232
- " return match;",
233
- " });",
234
- " if (newHtml !== bodyHtml) {",
235
- " document.body.innerHTML = newHtml;",
236
- " console.log('[Rampkit] Templates resolved!');",
237
- " }",
238
- " }",
239
- " }",
240
- " ",
241
- " setTimeout(resolveAllTemplates, 50);",
242
- " setTimeout(resolveAllTemplates, 200);",
243
- " window.rampkitResolveTemplates = resolveAllTemplates;",
244
- " document.addEventListener('rampkit:vars-updated', function() {",
245
- " setTimeout(resolveAllTemplates, 0);",
246
- " });",
247
- " console.log('[Rampkit] Template resolver initialized');",
248
- " } catch(e) {",
249
- " console.log('[Rampkit] Template resolver error:', e);",
250
- " }",
251
- " true;",
252
- "})();",
253
- ].join("\n");
254
176
  function performRampkitHaptic(event) {
255
177
  if (!event || event.action !== "haptic") {
256
178
  // Backwards compatible default
@@ -300,13 +222,15 @@ function performRampkitHaptic(event) {
300
222
  }
301
223
  let sibling = null;
302
224
  let preloadSibling = null;
303
- const preloadCache = new Map();
225
+ // Cache is now disabled - always rebuild docs to ensure templates are resolved with current context
226
+ // const preloadCache = new Map<string, string[]>();
304
227
  let activeCloseHandler = null;
305
228
  function showRampkitOverlay(opts) {
306
- console.log("showRampkitOverlay");
229
+ console.log("[RampKit] showRampkitOverlay called, context:", opts.rampkitContext ? "present" : "missing");
307
230
  if (sibling)
308
231
  return; // already visible
309
- const prebuiltDocs = preloadCache.get(opts.onboardingId);
232
+ // Always build fresh docs to ensure templates are resolved with current context
233
+ const prebuiltDocs = undefined;
310
234
  sibling = new react_native_root_siblings_1.default(((0, jsx_runtime_1.jsx)(Overlay, { onboardingId: opts.onboardingId, screens: opts.screens, variables: opts.variables, requiredScripts: opts.requiredScripts, rampkitContext: opts.rampkitContext, prebuiltDocs: prebuiltDocs, onRequestClose: () => {
311
235
  var _a;
312
236
  activeCloseHandler = null;
@@ -336,14 +260,11 @@ function closeRampkitOverlay() {
336
260
  hideRampkitOverlay();
337
261
  }
338
262
  function preloadRampkitOverlay(opts) {
263
+ // Preloading is now simplified - just warm up the WebView process
339
264
  try {
340
- if (preloadCache.has(opts.onboardingId))
341
- return;
342
- const docs = opts.screens.map((s) => buildHtmlDocument(s, opts.variables, opts.requiredScripts, opts.rampkitContext));
343
- preloadCache.set(opts.onboardingId, docs);
344
- // Mount a hidden WebView to warm up the WebView process and cache
345
265
  if (preloadSibling)
346
266
  return;
267
+ const docs = opts.screens.map((s) => buildHtmlDocument(s, opts.variables, opts.requiredScripts, opts.rampkitContext));
347
268
  const HiddenPreloader = () => ((0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "none", style: {
348
269
  position: "absolute",
349
270
  width: 1,
@@ -351,17 +272,62 @@ function preloadRampkitOverlay(opts) {
351
272
  opacity: 0,
352
273
  top: -1000,
353
274
  left: -1000,
354
- }, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { originWhitelist: ["*"], source: { html: docs[0] || "<html></html>" }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler + exports.injectedTemplateResolver, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, hideKeyboardAccessoryView: true }) }));
275
+ }, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { originWhitelist: ["*"], source: { html: docs[0] || "<html></html>" }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, hideKeyboardAccessoryView: true }) }));
355
276
  preloadSibling = new react_native_root_siblings_1.default((0, jsx_runtime_1.jsx)(HiddenPreloader, {}));
356
277
  }
357
278
  catch (e) {
358
279
  // best-effort preloading; ignore errors
359
280
  }
360
281
  }
282
+ /**
283
+ * Resolve device/user templates in a string
284
+ * Replaces ${device.xxx} and ${user.xxx} with actual values from context
285
+ */
286
+ function resolveContextTemplates(text, context) {
287
+ if (!text || !text.includes("${"))
288
+ return text;
289
+ // Build variable map
290
+ const vars = {};
291
+ // Device vars
292
+ if (context.device) {
293
+ Object.entries(context.device).forEach(([key, value]) => {
294
+ vars[`device.${key}`] = value;
295
+ });
296
+ }
297
+ // User vars
298
+ if (context.user) {
299
+ Object.entries(context.user).forEach(([key, value]) => {
300
+ vars[`user.${key}`] = value;
301
+ });
302
+ }
303
+ console.log("[RampKit] Resolving templates with vars:", JSON.stringify(vars));
304
+ // Replace ${varName} patterns
305
+ return text.replace(/\$\{([A-Za-z_][A-Za-z0-9_.]*)\}/g, (match, varName) => {
306
+ if (vars.hasOwnProperty(varName)) {
307
+ const value = vars[varName];
308
+ console.log(`[RampKit] Replacing ${match} with:`, value);
309
+ if (value === undefined || value === null)
310
+ return "";
311
+ if (typeof value === "boolean")
312
+ return value ? "true" : "false";
313
+ if (typeof value === "object")
314
+ return JSON.stringify(value);
315
+ return String(value);
316
+ }
317
+ // Not a device/user var - leave for state variable handling
318
+ return match;
319
+ });
320
+ }
361
321
  function buildHtmlDocument(screen, variables, requiredScripts, rampkitContext) {
322
+ console.log("[RampKit] buildHtmlDocument called");
323
+ console.log("[RampKit] rampkitContext received:", rampkitContext ? JSON.stringify(rampkitContext).slice(0, 200) : "undefined");
362
324
  const css = screen.css || "";
363
- const html = screen.html || "";
325
+ let html = screen.html || "";
364
326
  const js = screen.js || "";
327
+ // Log if HTML contains device/user templates
328
+ if (html.includes("${device.") || html.includes("${user.")) {
329
+ console.log("[RampKit] HTML contains device/user templates");
330
+ }
365
331
  const scripts = (requiredScripts || [])
366
332
  .map((src) => `<script src="${src}"></script>`)
367
333
  .join("\n");
@@ -418,6 +384,15 @@ function buildHtmlDocument(screen, variables, requiredScripts, rampkitContext) {
418
384
  installedAt: new Date().toISOString(),
419
385
  },
420
386
  };
387
+ // Resolve device/user templates in HTML BEFORE sending to WebView
388
+ const originalHtml = html;
389
+ html = resolveContextTemplates(html, context);
390
+ if (originalHtml !== html) {
391
+ console.log("[RampKit] Templates were resolved in HTML");
392
+ }
393
+ else if (originalHtml.includes("${device.") || originalHtml.includes("${user.")) {
394
+ console.log("[RampKit] WARNING: HTML still contains unresolved device/user templates!");
395
+ }
421
396
  return `<!doctype html>
422
397
  <html>
423
398
  <head>
@@ -760,7 +735,7 @@ function Overlay(props) {
760
735
  styles.root,
761
736
  !visible && styles.invisible,
762
737
  visible && { opacity: overlayOpacity },
763
- ], pointerEvents: visible && !isClosing ? "auto" : "none", children: [(0, jsx_runtime_1.jsx)(react_native_pager_view_1.default, { ref: pagerRef, style: react_native_1.StyleSheet.absoluteFill, scrollEnabled: false, initialPage: 0, onPageSelected: onPageSelected, offscreenPageLimit: props.screens.length, overScrollMode: "never", children: docs.map((doc, i) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.page, renderToHardwareTextureAndroid: true, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { ref: (r) => (webviewsRef.current[i] = r), style: styles.webview, originWhitelist: ["*"], source: { html: doc }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler + exports.injectedTemplateResolver, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, overScrollMode: "never", scalesPageToFit: false, showsHorizontalScrollIndicator: false, dataDetectorTypes: "none", allowsLinkPreview: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, javaScriptEnabled: true, domStorageEnabled: true, hideKeyboardAccessoryView: true, onLoadEnd: () => {
738
+ ], pointerEvents: visible && !isClosing ? "auto" : "none", children: [(0, jsx_runtime_1.jsx)(react_native_pager_view_1.default, { ref: pagerRef, style: react_native_1.StyleSheet.absoluteFill, scrollEnabled: false, initialPage: 0, onPageSelected: onPageSelected, offscreenPageLimit: props.screens.length, overScrollMode: "never", children: docs.map((doc, i) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.page, renderToHardwareTextureAndroid: true, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { ref: (r) => (webviewsRef.current[i] = r), style: styles.webview, originWhitelist: ["*"], source: { html: doc }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, overScrollMode: "never", scalesPageToFit: false, showsHorizontalScrollIndicator: false, dataDetectorTypes: "none", allowsLinkPreview: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, javaScriptEnabled: true, domStorageEnabled: true, hideKeyboardAccessoryView: true, onLoadEnd: () => {
764
739
  setLoadedCount((c) => c + 1);
765
740
  if (i === 0) {
766
741
  setFirstPageLoaded(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rampkit-expo-dev",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "The Expo SDK for RampKit. Build, test, and personalize app onboardings with instant updates.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",