@accelerated-agency/visual-editor 0.4.6 → 0.4.8

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 (3) hide show
  1. package/dist/vite.cjs +231 -104
  2. package/dist/vite.js +231 -104
  3. package/package.json +1 -1
package/dist/vite.cjs CHANGED
@@ -11,20 +11,6 @@ var fs__default = /*#__PURE__*/_interopDefault(fs);
11
11
  var path__default = /*#__PURE__*/_interopDefault(path);
12
12
 
13
13
  // src/visualEditorProxyPlugin.ts
14
- var SCRAPER_PROXY_HOST = process.env.SCRAPERAPI_PROXY_HOST || "proxy-server.scraperapi.com";
15
- var SCRAPER_PROXY_PORT = Number(process.env.SCRAPERAPI_PROXY_PORT || "8001");
16
- var SCRAPER_PROXY_USERNAME_BASE = process.env.SCRAPERAPI_PROXY_USERNAME_BASE || "scraperapi";
17
- var SCRAPER_PROXY_USERNAME_PARAMS = process.env.SCRAPERAPI_PROXY_USERNAME_PARAMS || "render=true.wait_for_selector=body.follow_redirect=false.keep_headers=true";
18
- var SCRAPER_PROXY_USERNAME = process.env.SCRAPERAPI_PROXY_USERNAME || [
19
- SCRAPER_PROXY_USERNAME_BASE,
20
- SCRAPER_PROXY_USERNAME_PARAMS
21
- ].filter(Boolean).join(".");
22
- var SCRAPER_PROXY_PASSWORD = process.env.SCRAPERAPI_PROXY_PASSWORD || process.env.SCRAPERAPI_API_KEY;
23
- var SCRAPER_API_ENDPOINT = process.env.SCRAPERAPI_ENDPOINT || "https://api.scraperapi.com/";
24
- var SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED_RAW = process.env.SCRAPERAPI_REQUEST_TLS_REJECT_UNAUTHORIZED || "false";
25
- var SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED = !["0", "false", "no"].includes(
26
- SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED_RAW.toLowerCase()
27
- );
28
14
  var DEFAULT_TRACKING_MARKERS = [
29
15
  "snowplow",
30
16
  "taboola",
@@ -122,30 +108,6 @@ function stripTrackingScriptsFromScrapedHtml(html, markers) {
122
108
  }
123
109
  return out;
124
110
  }
125
- var SCRAPER_BILLING_ERROR_RE = /exhausted the api credits|upgrade your subscription|enable overages|dashboard\.scraperapi\.com\/billing|insufficient credits|billing/i;
126
- var scraperProxyClientPromise = null;
127
- async function getScraperProxyClient() {
128
- if (scraperProxyClientPromise) return scraperProxyClientPromise;
129
- scraperProxyClientPromise = (async () => {
130
- if (!SCRAPER_PROXY_PASSWORD) return null;
131
- try {
132
- const undici = await import('undici');
133
- const proxyUrl = "http://" + encodeURIComponent(SCRAPER_PROXY_USERNAME) + ":" + encodeURIComponent(SCRAPER_PROXY_PASSWORD) + "@" + SCRAPER_PROXY_HOST + ":" + String(SCRAPER_PROXY_PORT);
134
- return {
135
- dispatcher: new undici.ProxyAgent({
136
- uri: proxyUrl,
137
- requestTls: {
138
- rejectUnauthorized: SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED
139
- }
140
- }),
141
- fetchFn: undici.fetch
142
- };
143
- } catch (_) {
144
- return null;
145
- }
146
- })();
147
- return scraperProxyClientPromise;
148
- }
149
111
  var iframeAlwaysShowCss = `<style id="__ce_force_show">
150
112
 
151
113
  </style>`;
@@ -3283,6 +3245,53 @@ function parseEditorUrlPayload(rawUrl) {
3283
3245
  password: nestedPassword || undefined,
3284
3246
  };
3285
3247
  }
3248
+ var path = parsed.pathname || '';
3249
+ var isProxyPath =
3250
+ path === '/api/conversion-proxy' ||
3251
+ path.indexOf('/api/conversion-proxy/') === 0 ||
3252
+ path.indexOf('api/conversion-proxy') !== -1;
3253
+ if (isProxyPath) {
3254
+ var fallbackUrl = (experimentData && experimentData.pageUrl) ? String(experimentData.pageUrl) : '';
3255
+ if (fallbackUrl) {
3256
+ var recovered = new URL(fallbackUrl, window.location.href);
3257
+ var qp = new URLSearchParams(parsed.search || '');
3258
+ qp.delete('url');
3259
+ qp.delete('password');
3260
+ qp.delete('conversionProxyBaseUrl');
3261
+ qp.delete('trackingMarkers');
3262
+ qp.delete('strictObserverFreeze');
3263
+ qp.delete('proxy');
3264
+ qp.delete('raw');
3265
+ recovered.search = '';
3266
+ qp.forEach(function(v, k) { recovered.searchParams.set(k, v); });
3267
+ recovered.hash = parsed.hash || '';
3268
+ return {
3269
+ url: recovered.toString(),
3270
+ password:
3271
+ (experimentData && experimentData.editorPassword)
3272
+ ? String(experimentData.editorPassword)
3273
+ : undefined,
3274
+ };
3275
+ }
3276
+ }
3277
+ var fallbackUrl2 = (experimentData && experimentData.pageUrl) ? String(experimentData.pageUrl) : '';
3278
+ if (fallbackUrl2) {
3279
+ try {
3280
+ var targetBase = new URL(fallbackUrl2, window.location.href);
3281
+ if (parsed.origin !== targetBase.origin) {
3282
+ var normalized = new URL(parsed.pathname || '/', targetBase.origin);
3283
+ normalized.search = parsed.search || '';
3284
+ normalized.hash = parsed.hash || '';
3285
+ return {
3286
+ url: normalized.toString(),
3287
+ password:
3288
+ (experimentData && experimentData.editorPassword)
3289
+ ? String(experimentData.editorPassword)
3290
+ : undefined,
3291
+ };
3292
+ }
3293
+ } catch(_) {}
3294
+ }
3286
3295
  return {
3287
3296
  url: parsed.toString(),
3288
3297
  password:
@@ -3301,6 +3310,12 @@ function emitEditorUrlChangedPayload(payload) {
3301
3310
  var key = String(payload.url || '') + '|' + String(payload.password || '');
3302
3311
  if (key === lastEditorUrlPayloadKey) return;
3303
3312
  lastEditorUrlPayloadKey = key;
3313
+ try {
3314
+ console.info('[V2 iframe-url]', {
3315
+ url: payload.url || '',
3316
+ passwordPresent: !!payload.password,
3317
+ });
3318
+ } catch(_) {}
3304
3319
  send('editor-url-changed', payload);
3305
3320
  }
3306
3321
  function emitEditorUrlChanged(rawUrl) {
@@ -5073,6 +5088,67 @@ function stripDataVveInstanceSubtree(root) {
5073
5088
  }
5074
5089
  }
5075
5090
 
5091
+ function cssEscapeIdent(raw) {
5092
+ var s = raw == null ? '' : String(raw);
5093
+ if (!s) return '';
5094
+ try {
5095
+ if (typeof CSS !== 'undefined' && CSS && typeof CSS.escape === 'function') {
5096
+ return CSS.escape(s);
5097
+ }
5098
+ } catch(_) {}
5099
+ // Fallback escape for identifiers when CSS.escape is unavailable.
5100
+ return s.replace(/[^a-zA-Z0-9_-]/g, function(ch) { return '\\\\' + ch; });
5101
+ }
5102
+
5103
+ function unescapeCssToken(token) {
5104
+ if (!token) return '';
5105
+ var s = String(token);
5106
+ var out = '';
5107
+ for (var i = 0; i < s.length; i++) {
5108
+ var ch = s.charAt(i);
5109
+ if (ch === '\\\\' && i + 1 < s.length) {
5110
+ out += s.charAt(i + 1);
5111
+ i += 1;
5112
+ continue;
5113
+ }
5114
+ out += ch;
5115
+ }
5116
+ return out;
5117
+ }
5118
+
5119
+ function isGeneratedClassToken(token) {
5120
+ var t = token == null ? '' : String(token).trim();
5121
+ if (!t) return true;
5122
+ if (t.indexOf('vve-') === 0) return true;
5123
+ // Tailwind arbitrary values / variant-heavy utilities are often unstable in selectors.
5124
+ if (
5125
+ t.indexOf('[') !== -1 ||
5126
+ t.indexOf(']') !== -1 ||
5127
+ t.indexOf('(') !== -1 ||
5128
+ t.indexOf(')') !== -1 ||
5129
+ t.indexOf('{') !== -1 ||
5130
+ t.indexOf('}') !== -1 ||
5131
+ t.indexOf(':') !== -1
5132
+ ) return true;
5133
+ // CSS-in-JS / runtime hash prefixes.
5134
+ if (/^(css|jsx|sc|emotion|styled|chakra|mantine|mui|ant)-/i.test(t)) return true;
5135
+ // Classnames with long hashy suffixes (framework/runtime generated).
5136
+ if (/[a-f0-9]{8,}/i.test(t)) return true;
5137
+ if (/^[a-zA-Z_-]+[0-9]{4,}[a-zA-Z0-9_-]*$/.test(t)) return true;
5138
+ if (/^[a-zA-Z0-9_-]{36,}$/.test(t)) return true;
5139
+ return false;
5140
+ }
5141
+
5142
+ function escapeSelectorClassTokens(sel) {
5143
+ if (!sel || typeof sel !== 'string') return '';
5144
+ return sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
5145
+ if (!cls) return _m;
5146
+ // Already escaped token; keep it as-is.
5147
+ if (cls.indexOf('\\\\') >= 0) return '.' + cls;
5148
+ return '.' + cssEscapeIdent(cls);
5149
+ });
5150
+ }
5151
+
5076
5152
  function buildSelector(el) {
5077
5153
  if (!el) return '';
5078
5154
  var doc = el.ownerDocument || document;
@@ -5084,16 +5160,16 @@ function buildSelector(el) {
5084
5160
  if (doc.querySelectorAll(attrSel).length === 1) return attrSel;
5085
5161
  } catch(_) {}
5086
5162
  }
5087
- if (el.id) return '#' + el.id;
5163
+ if (el.id) return '#' + cssEscapeIdent(el.id);
5088
5164
  var parts = [], node = el, depth = 0;
5089
5165
  while (node && node.nodeType === 1 && depth < 5) {
5090
- if (node.id) { parts.unshift('#' + node.id); break; }
5166
+ if (node.id) { parts.unshift('#' + cssEscapeIdent(node.id)); break; }
5091
5167
  var p = node.tagName.toLowerCase();
5092
5168
  if (node.classList && node.classList.length) {
5093
5169
  var clsArr = Array.from(node.classList).filter(function(c) {
5094
- return c.indexOf('vve-') !== 0;
5170
+ return c.indexOf('vve-') !== 0 && !isGeneratedClassToken(c);
5095
5171
  });
5096
- if (clsArr.length) p += '.' + clsArr.slice(0, 2).join('.');
5172
+ if (clsArr.length) p += '.' + clsArr.slice(0, 2).map(function(c) { return cssEscapeIdent(c); }).join('.');
5097
5173
  }
5098
5174
  var idx = 1, sib = node.previousElementSibling;
5099
5175
  while (sib) { if (sib.tagName === node.tagName) idx++; sib = sib.previousElementSibling; }
@@ -5105,17 +5181,32 @@ function buildSelector(el) {
5105
5181
  return parts.join(' > ');
5106
5182
  }
5107
5183
 
5184
+ function stripGeneratedSelectorClassTokens(sel) {
5185
+ if (!sel || typeof sel !== 'string') return '';
5186
+ var s = sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
5187
+ var raw = unescapeCssToken(cls);
5188
+ if (isGeneratedClassToken(raw)) return '';
5189
+ return '.' + cssEscapeIdent(raw);
5190
+ });
5191
+ return s
5192
+ .replace(/.{2,}/g, '.')
5193
+ .replace(/s{2,}/g, ' ')
5194
+ .replace(/s*>s*>/g, ' > ')
5195
+ .trim();
5196
+ }
5197
+
5108
5198
  /**
5109
5199
  * Strip editor-only .vve-* class tokens from a selector string (fixes DB rows saved while an element was selected).
5110
5200
  */
5111
5201
  function sanitizeSelectorForMatch(sel) {
5112
5202
  if (!sel || typeof sel !== 'string') return '';
5113
5203
  var s0 = sel.replace(/.vve-[a-zA-Z0-9_-]+/gi, '');
5204
+ s0 = stripGeneratedSelectorClassTokens(s0);
5114
5205
  var parts = s0.split(/s*>s*/).map(function(seg) {
5115
5206
  var t = seg.replace(/.+/g, '.').replace(/.$/, '');
5116
5207
  return t.trim();
5117
5208
  });
5118
- return parts.filter(Boolean).join(' > ');
5209
+ return escapeSelectorClassTokens(parts.filter(Boolean).join(' > '));
5119
5210
  }
5120
5211
 
5121
5212
  /** Drop the rightmost :nth-of-type(n) (hydration / layout often shifts sibling indices). */
@@ -5159,11 +5250,12 @@ function querySelectorResolved(iframeDoc, selector) {
5159
5250
  return null;
5160
5251
  }
5161
5252
  var alt = sanitizeSelectorForMatch(selector);
5253
+ var escaped = escapeSelectorClassTokens(alt || selector);
5162
5254
  // Prefer sanitized + nth relax FIRST: raw selectors often still contain .vve-* from
5163
5255
  // save-time selection; those only match after clicking (we re-add vve-selected).
5164
- var el = walkRelax(alt || selector);
5256
+ var el = walkRelax(escaped || alt || selector);
5165
5257
  if (el) return el;
5166
- if (alt !== selector) {
5258
+ if (alt !== selector || escaped !== selector) {
5167
5259
  el = walkRelax(selector);
5168
5260
  if (el) return el;
5169
5261
  }
@@ -5830,6 +5922,12 @@ window.addEventListener('load', function() {
5830
5922
  if (!d || d.channel !== 'vvveb-proxy-url' || d.type !== 'editor-url-changed') return;
5831
5923
  if (!iframe || !iframe.contentWindow || ev.source !== iframe.contentWindow) return;
5832
5924
  var payload = d.payload || {};
5925
+ try {
5926
+ console.info('[V2 iframe-url bridge]', {
5927
+ url: payload.url || '',
5928
+ passwordPresent: !!payload.password,
5929
+ });
5930
+ } catch(_) {}
5833
5931
  emitEditorUrlChangedPayload({
5834
5932
  url: payload.url || undefined,
5835
5933
  password: payload.password || undefined,
@@ -6119,8 +6217,6 @@ function createVisualEditorMiddleware(options) {
6119
6217
  extraTrackingMarkersForRequest
6120
6218
  );
6121
6219
  const strictFreezeParam = (url.searchParams.get("strictObserverFreeze") || "").toLowerCase();
6122
- const proxyParam = (url.searchParams.get("proxy") || "").toLowerCase();
6123
- const useScraperProxy = proxyParam === "1" || proxyParam === "true" || proxyParam === "yes";
6124
6220
  const strictObserverFreezeForRequest = strictFreezeParam === "1" || strictFreezeParam === "true" || strictFreezeParam === "yes" ? true : strictFreezeParam === "0" || strictFreezeParam === "false" || strictFreezeParam === "no" ? false : strictObserverFreeze;
6125
6221
  if (!targetUrl) {
6126
6222
  res.statusCode = 400;
@@ -6130,40 +6226,7 @@ function createVisualEditorMiddleware(options) {
6130
6226
  const parsed = new URL(targetUrl);
6131
6227
  const origin = parsed.origin;
6132
6228
  const method = (req.method || "GET").toUpperCase();
6133
- const scraperProxyClient = useScraperProxy ? await getScraperProxyClient() : null;
6134
- if (useScraperProxy && !scraperProxyClient) {
6135
- res.statusCode = 500;
6136
- res.setHeader("Content-Type", "application/json");
6137
- res.end(
6138
- JSON.stringify({
6139
- error: "ScraperAPI proxy is not configured. Set SCRAPERAPI_PROXY_PASSWORD or SCRAPERAPI_API_KEY."
6140
- })
6141
- );
6142
- return;
6143
- }
6144
6229
  const directFetch = (input, init = {}) => fetch(input, init);
6145
- const scraperFetch = (input, init = {}) => {
6146
- if (!SCRAPER_PROXY_PASSWORD) return directFetch(input, init);
6147
- if (!useScraperProxy || !scraperProxyClient) {
6148
- const scraperUrl = new URL(SCRAPER_API_ENDPOINT);
6149
- scraperUrl.searchParams.set("api_key", SCRAPER_PROXY_PASSWORD);
6150
- scraperUrl.searchParams.set("url", input);
6151
- return fetch(scraperUrl.toString(), init);
6152
- }
6153
- return scraperProxyClient.fetchFn(input, {
6154
- ...init,
6155
- dispatcher: scraperProxyClient.dispatcher
6156
- });
6157
- };
6158
- const shouldFallbackFromScraper = async (resp) => {
6159
- try {
6160
- if (resp.ok) return false;
6161
- const text = await resp.clone().text();
6162
- return SCRAPER_BILLING_ERROR_RE.test(String(text || ""));
6163
- } catch (_) {
6164
- return false;
6165
- }
6166
- };
6167
6230
  const workerRawFetch = (input, init = {}) => {
6168
6231
  const workerUrl = new URL("/api/conversion-proxy", conversionProxyBaseUrlForRequest);
6169
6232
  workerUrl.searchParams.set("url", input);
@@ -6196,17 +6259,6 @@ function createVisualEditorMiddleware(options) {
6196
6259
  }
6197
6260
  return workerRawFetch(input, init);
6198
6261
  }
6199
- const primary = await scraperFetch(input, init);
6200
- if (!await shouldFallbackFromScraper(primary)) {
6201
- return primary;
6202
- }
6203
- try {
6204
- console.warn("[conversion-proxy] ScraperAPI billing/quota detected; falling back to direct fetch", {
6205
- url: input,
6206
- mode: useScraperProxy ? "proxy" : "simple"
6207
- });
6208
- } catch (_) {
6209
- }
6210
6262
  return directFetch(input, init);
6211
6263
  };
6212
6264
  const headers = {
@@ -6278,16 +6330,8 @@ function createVisualEditorMiddleware(options) {
6278
6330
  res.end(
6279
6331
  JSON.stringify({
6280
6332
  error: aborted ? `Upstream request timed out after ${upstreamTimeoutMs / 1e3}s` : fetchErr?.message || "Upstream fetch failed",
6281
- phase: useScraperProxy ? "scraperapi-proxy-fetch" : "scraperapi-simple-fetch",
6282
- fetchMode: useScraperProxy ? "proxy" : "simple",
6283
- scraperapi: {
6284
- host: SCRAPER_PROXY_HOST,
6285
- port: SCRAPER_PROXY_PORT,
6286
- username: SCRAPER_PROXY_USERNAME,
6287
- endpoint: SCRAPER_API_ENDPOINT,
6288
- hasProxyPassword: Boolean(SCRAPER_PROXY_PASSWORD),
6289
- requestTlsRejectUnauthorized: SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED
6290
- },
6333
+ phase: conversionProxyBaseUrlForRequest ? "worker-raw-fetch" : "direct-upstream-fetch",
6334
+ fetchMode: conversionProxyBaseUrlForRequest ? "worker" : "direct",
6291
6335
  cause: fetchCause && typeof fetchCause === "object" ? {
6292
6336
  name: fetchCause.name,
6293
6337
  code: fetchCause.code,
@@ -6473,8 +6517,60 @@ try{
6473
6517
  }
6474
6518
  }
6475
6519
  }catch(_){}
6476
- function getEditorUrlPayload(){try{var u=new URL(window.location.href);var nested=u.searchParams.get("url");return{url:nested||u.toString(),password:u.searchParams.get("password")||undefined};}catch(_){return null;}}
6477
- function notifyEditorUrlChanged(){try{var payload=getEditorUrlPayload();if(!payload)return;if(window.parent){window.parent.postMessage({channel:PARENT_URL_CHANNEL,type:"editor-url-changed",payload:payload},"*");}}catch(_){}}
6520
+ function getEditorUrlPayload(){
6521
+ try{
6522
+ var u=new URL(window.location.href);
6523
+ var nested=u.searchParams.get("url");
6524
+ if(nested){
6525
+ return {url:nested,password:u.searchParams.get("password")||undefined};
6526
+ }
6527
+ var p=u.pathname||"";
6528
+ var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
6529
+ if(isProxyPath){
6530
+ var recoveredBase=new URL(TARGET_PAGE_URL,window.location.href);
6531
+ var qp=new URLSearchParams(u.search||"");
6532
+ qp.delete("url");
6533
+ qp.delete("password");
6534
+ qp.delete("conversionProxyBaseUrl");
6535
+ qp.delete("trackingMarkers");
6536
+ qp.delete("strictObserverFreeze");
6537
+ qp.delete("proxy");
6538
+ qp.delete("raw");
6539
+ recoveredBase.search="";
6540
+ qp.forEach(function(v,k){recoveredBase.searchParams.set(k,v);});
6541
+ recoveredBase.hash=u.hash||"";
6542
+ return {url:recoveredBase.toString(),password:u.searchParams.get("password")||undefined};
6543
+ }
6544
+ try{
6545
+ var targetBase2=new URL(TARGET_PAGE_URL,window.location.href);
6546
+ if(u.origin!==targetBase2.origin){
6547
+ var normalized2=new URL((u.pathname||"/"),targetBase2.origin);
6548
+ normalized2.search=u.search||"";
6549
+ normalized2.hash=u.hash||"";
6550
+ return {url:normalized2.toString(),password:u.searchParams.get("password")||undefined};
6551
+ }
6552
+ }catch(_){}
6553
+ return {url:u.toString(),password:u.searchParams.get("password")||undefined};
6554
+ }catch(_){
6555
+ return null;
6556
+ }
6557
+ }
6558
+ function notifyEditorUrlChanged(){
6559
+ try{
6560
+ var payload=getEditorUrlPayload();
6561
+ if(!payload) return;
6562
+ try{
6563
+ console.info("[conversion-proxy] iframe url rendered",{
6564
+ current:window.location.href,
6565
+ target:payload.url||"",
6566
+ passwordPresent:!!payload.password
6567
+ });
6568
+ }catch(_){}
6569
+ if(window.parent){
6570
+ window.parent.postMessage({channel:PARENT_URL_CHANNEL,type:"editor-url-changed",payload:payload},"*");
6571
+ }
6572
+ }catch(_){}
6573
+ }
6478
6574
  try{notifyEditorUrlChanged();}catch(_){}
6479
6575
  try{if(window.history&&typeof window.history.pushState==="function"){var nativePushState=window.history.pushState;window.history.pushState=function(){var ret=nativePushState.apply(window.history,arguments);setTimeout(notifyEditorUrlChanged,0);return ret;};}}catch(_){}
6480
6576
  try{if(window.history&&typeof window.history.replaceState==="function"){var nativeReplaceState=window.history.replaceState;window.history.replaceState=function(){var ret=nativeReplaceState.apply(window.history,arguments);setTimeout(notifyEditorUrlChanged,0);return ret;};}}catch(_){}
@@ -6482,7 +6578,29 @@ try{window.addEventListener("popstate",notifyEditorUrlChanged,true);}catch(_){}
6482
6578
  try{window.addEventListener("hashchange",notifyEditorUrlChanged,true);}catch(_){}
6483
6579
  function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
6484
6580
  function toAbsolute(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")||raw.startsWith("//")?TARGET_ORIGIN:TARGET_PAGE_URL;return new URL(raw,base).toString();}catch(_){return raw;}}
6485
- function toProxy(raw){if(isSkippable(raw))return null;var abs=toAbsolute(raw);if(!abs||typeof abs!=="string")return null;try{var parsed=new URL(abs);if(parsed.origin!==TARGET_ORIGIN)return null;var root="/api/conversion-proxy";return root+"?password="+encodeURIComponent(PROXY_PASSWORD||"")+"&url="+encodeURIComponent(parsed.toString());}catch(_){return null;}}
6581
+ function toProxy(raw){
6582
+ if(isSkippable(raw))return null;
6583
+ var abs=toAbsolute(raw);
6584
+ if(!abs||typeof abs!=="string")return null;
6585
+ try{
6586
+ var parsed=new URL(abs);
6587
+ // If input is already a proxy URL, unwrap to its target url first
6588
+ // to avoid nested /api/conversion-proxy?url=/api/conversion-proxy?... chains.
6589
+ var p=(parsed.pathname||"");
6590
+ var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
6591
+ if(isProxyPath){
6592
+ var nested=parsed.searchParams.get("url")||"";
6593
+ if(nested){
6594
+ try{parsed=new URL(nested);}catch(_){}
6595
+ }
6596
+ }
6597
+ if(parsed.origin!==TARGET_ORIGIN)return null;
6598
+ var root="/api/conversion-proxy";
6599
+ return root+"?password="+encodeURIComponent(PROXY_PASSWORD||"")+"&url="+encodeURIComponent(parsed.toString());
6600
+ }catch(_){
6601
+ return null;
6602
+ }
6603
+ }
6486
6604
  var nativeAssign=window.location.assign?window.location.assign.bind(window.location):null;
6487
6605
  var nativeReplace=window.location.replace?window.location.replace.bind(window.location):null;
6488
6606
  function safeNavigate(raw,mode){var abs=toAbsolute(raw);var prox=toProxy(raw);if(!prox){try{console.warn("[conversion-proxy] redirect blocked",{mode:mode,requested:raw,resolved:abs,origin:TARGET_ORIGIN});}catch(_){}return false;}try{console.info("[conversion-proxy] redirect intercepted",{mode:mode,requested:raw,resolved:abs,proxied:prox});if(mode==="replace"&&nativeReplace){nativeReplace(prox);return true;}if(nativeAssign){nativeAssign(prox);return true;}window.location.href=prox;return true;}catch(err){try{console.warn("[conversion-proxy] redirect interception failed",{mode:mode,requested:raw,resolved:abs,proxied:prox,error:err&&err.message?err.message:String(err)});}catch(_){}return false;}}
@@ -6556,6 +6674,15 @@ function toProxyNetworkUrl(raw){
6556
6674
  try{
6557
6675
  var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;
6558
6676
  var abs=new URL(raw,base);
6677
+ // Unwrap already-proxied URLs first to avoid nested proxy urls.
6678
+ var p0=abs.pathname||"";
6679
+ var isProxyPath0=p0==="/api/conversion-proxy"||p0.indexOf("/api/conversion-proxy/")===0||p0.indexOf("api/conversion-proxy")!==-1;
6680
+ if(isProxyPath0){
6681
+ var nested0=abs.searchParams.get("url")||"";
6682
+ if(nested0){
6683
+ try{abs=new URL(nested0);}catch(_){}
6684
+ }
6685
+ }
6559
6686
  // Some embedded scripts build absolute requests against editor origin
6560
6687
  // (e.g. https://localhost:4001/api/unstable/graphql.json). Remap those
6561
6688
  // paths to target origin first, then proxy as usual.
package/dist/vite.js CHANGED
@@ -3,20 +3,6 @@ import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
 
5
5
  // src/visualEditorProxyPlugin.ts
6
- var SCRAPER_PROXY_HOST = process.env.SCRAPERAPI_PROXY_HOST || "proxy-server.scraperapi.com";
7
- var SCRAPER_PROXY_PORT = Number(process.env.SCRAPERAPI_PROXY_PORT || "8001");
8
- var SCRAPER_PROXY_USERNAME_BASE = process.env.SCRAPERAPI_PROXY_USERNAME_BASE || "scraperapi";
9
- var SCRAPER_PROXY_USERNAME_PARAMS = process.env.SCRAPERAPI_PROXY_USERNAME_PARAMS || "render=true.wait_for_selector=body.follow_redirect=false.keep_headers=true";
10
- var SCRAPER_PROXY_USERNAME = process.env.SCRAPERAPI_PROXY_USERNAME || [
11
- SCRAPER_PROXY_USERNAME_BASE,
12
- SCRAPER_PROXY_USERNAME_PARAMS
13
- ].filter(Boolean).join(".");
14
- var SCRAPER_PROXY_PASSWORD = process.env.SCRAPERAPI_PROXY_PASSWORD || process.env.SCRAPERAPI_API_KEY;
15
- var SCRAPER_API_ENDPOINT = process.env.SCRAPERAPI_ENDPOINT || "https://api.scraperapi.com/";
16
- var SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED_RAW = process.env.SCRAPERAPI_REQUEST_TLS_REJECT_UNAUTHORIZED || "false";
17
- var SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED = !["0", "false", "no"].includes(
18
- SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED_RAW.toLowerCase()
19
- );
20
6
  var DEFAULT_TRACKING_MARKERS = [
21
7
  "snowplow",
22
8
  "taboola",
@@ -114,30 +100,6 @@ function stripTrackingScriptsFromScrapedHtml(html, markers) {
114
100
  }
115
101
  return out;
116
102
  }
117
- var SCRAPER_BILLING_ERROR_RE = /exhausted the api credits|upgrade your subscription|enable overages|dashboard\.scraperapi\.com\/billing|insufficient credits|billing/i;
118
- var scraperProxyClientPromise = null;
119
- async function getScraperProxyClient() {
120
- if (scraperProxyClientPromise) return scraperProxyClientPromise;
121
- scraperProxyClientPromise = (async () => {
122
- if (!SCRAPER_PROXY_PASSWORD) return null;
123
- try {
124
- const undici = await import('undici');
125
- const proxyUrl = "http://" + encodeURIComponent(SCRAPER_PROXY_USERNAME) + ":" + encodeURIComponent(SCRAPER_PROXY_PASSWORD) + "@" + SCRAPER_PROXY_HOST + ":" + String(SCRAPER_PROXY_PORT);
126
- return {
127
- dispatcher: new undici.ProxyAgent({
128
- uri: proxyUrl,
129
- requestTls: {
130
- rejectUnauthorized: SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED
131
- }
132
- }),
133
- fetchFn: undici.fetch
134
- };
135
- } catch (_) {
136
- return null;
137
- }
138
- })();
139
- return scraperProxyClientPromise;
140
- }
141
103
  var iframeAlwaysShowCss = `<style id="__ce_force_show">
142
104
 
143
105
  </style>`;
@@ -3275,6 +3237,53 @@ function parseEditorUrlPayload(rawUrl) {
3275
3237
  password: nestedPassword || undefined,
3276
3238
  };
3277
3239
  }
3240
+ var path = parsed.pathname || '';
3241
+ var isProxyPath =
3242
+ path === '/api/conversion-proxy' ||
3243
+ path.indexOf('/api/conversion-proxy/') === 0 ||
3244
+ path.indexOf('api/conversion-proxy') !== -1;
3245
+ if (isProxyPath) {
3246
+ var fallbackUrl = (experimentData && experimentData.pageUrl) ? String(experimentData.pageUrl) : '';
3247
+ if (fallbackUrl) {
3248
+ var recovered = new URL(fallbackUrl, window.location.href);
3249
+ var qp = new URLSearchParams(parsed.search || '');
3250
+ qp.delete('url');
3251
+ qp.delete('password');
3252
+ qp.delete('conversionProxyBaseUrl');
3253
+ qp.delete('trackingMarkers');
3254
+ qp.delete('strictObserverFreeze');
3255
+ qp.delete('proxy');
3256
+ qp.delete('raw');
3257
+ recovered.search = '';
3258
+ qp.forEach(function(v, k) { recovered.searchParams.set(k, v); });
3259
+ recovered.hash = parsed.hash || '';
3260
+ return {
3261
+ url: recovered.toString(),
3262
+ password:
3263
+ (experimentData && experimentData.editorPassword)
3264
+ ? String(experimentData.editorPassword)
3265
+ : undefined,
3266
+ };
3267
+ }
3268
+ }
3269
+ var fallbackUrl2 = (experimentData && experimentData.pageUrl) ? String(experimentData.pageUrl) : '';
3270
+ if (fallbackUrl2) {
3271
+ try {
3272
+ var targetBase = new URL(fallbackUrl2, window.location.href);
3273
+ if (parsed.origin !== targetBase.origin) {
3274
+ var normalized = new URL(parsed.pathname || '/', targetBase.origin);
3275
+ normalized.search = parsed.search || '';
3276
+ normalized.hash = parsed.hash || '';
3277
+ return {
3278
+ url: normalized.toString(),
3279
+ password:
3280
+ (experimentData && experimentData.editorPassword)
3281
+ ? String(experimentData.editorPassword)
3282
+ : undefined,
3283
+ };
3284
+ }
3285
+ } catch(_) {}
3286
+ }
3278
3287
  return {
3279
3288
  url: parsed.toString(),
3280
3289
  password:
@@ -3293,6 +3302,12 @@ function emitEditorUrlChangedPayload(payload) {
3293
3302
  var key = String(payload.url || '') + '|' + String(payload.password || '');
3294
3303
  if (key === lastEditorUrlPayloadKey) return;
3295
3304
  lastEditorUrlPayloadKey = key;
3305
+ try {
3306
+ console.info('[V2 iframe-url]', {
3307
+ url: payload.url || '',
3308
+ passwordPresent: !!payload.password,
3309
+ });
3310
+ } catch(_) {}
3296
3311
  send('editor-url-changed', payload);
3297
3312
  }
3298
3313
  function emitEditorUrlChanged(rawUrl) {
@@ -5065,6 +5080,67 @@ function stripDataVveInstanceSubtree(root) {
5065
5080
  }
5066
5081
  }
5067
5082
 
5083
+ function cssEscapeIdent(raw) {
5084
+ var s = raw == null ? '' : String(raw);
5085
+ if (!s) return '';
5086
+ try {
5087
+ if (typeof CSS !== 'undefined' && CSS && typeof CSS.escape === 'function') {
5088
+ return CSS.escape(s);
5089
+ }
5090
+ } catch(_) {}
5091
+ // Fallback escape for identifiers when CSS.escape is unavailable.
5092
+ return s.replace(/[^a-zA-Z0-9_-]/g, function(ch) { return '\\\\' + ch; });
5093
+ }
5094
+
5095
+ function unescapeCssToken(token) {
5096
+ if (!token) return '';
5097
+ var s = String(token);
5098
+ var out = '';
5099
+ for (var i = 0; i < s.length; i++) {
5100
+ var ch = s.charAt(i);
5101
+ if (ch === '\\\\' && i + 1 < s.length) {
5102
+ out += s.charAt(i + 1);
5103
+ i += 1;
5104
+ continue;
5105
+ }
5106
+ out += ch;
5107
+ }
5108
+ return out;
5109
+ }
5110
+
5111
+ function isGeneratedClassToken(token) {
5112
+ var t = token == null ? '' : String(token).trim();
5113
+ if (!t) return true;
5114
+ if (t.indexOf('vve-') === 0) return true;
5115
+ // Tailwind arbitrary values / variant-heavy utilities are often unstable in selectors.
5116
+ if (
5117
+ t.indexOf('[') !== -1 ||
5118
+ t.indexOf(']') !== -1 ||
5119
+ t.indexOf('(') !== -1 ||
5120
+ t.indexOf(')') !== -1 ||
5121
+ t.indexOf('{') !== -1 ||
5122
+ t.indexOf('}') !== -1 ||
5123
+ t.indexOf(':') !== -1
5124
+ ) return true;
5125
+ // CSS-in-JS / runtime hash prefixes.
5126
+ if (/^(css|jsx|sc|emotion|styled|chakra|mantine|mui|ant)-/i.test(t)) return true;
5127
+ // Classnames with long hashy suffixes (framework/runtime generated).
5128
+ if (/[a-f0-9]{8,}/i.test(t)) return true;
5129
+ if (/^[a-zA-Z_-]+[0-9]{4,}[a-zA-Z0-9_-]*$/.test(t)) return true;
5130
+ if (/^[a-zA-Z0-9_-]{36,}$/.test(t)) return true;
5131
+ return false;
5132
+ }
5133
+
5134
+ function escapeSelectorClassTokens(sel) {
5135
+ if (!sel || typeof sel !== 'string') return '';
5136
+ return sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
5137
+ if (!cls) return _m;
5138
+ // Already escaped token; keep it as-is.
5139
+ if (cls.indexOf('\\\\') >= 0) return '.' + cls;
5140
+ return '.' + cssEscapeIdent(cls);
5141
+ });
5142
+ }
5143
+
5068
5144
  function buildSelector(el) {
5069
5145
  if (!el) return '';
5070
5146
  var doc = el.ownerDocument || document;
@@ -5076,16 +5152,16 @@ function buildSelector(el) {
5076
5152
  if (doc.querySelectorAll(attrSel).length === 1) return attrSel;
5077
5153
  } catch(_) {}
5078
5154
  }
5079
- if (el.id) return '#' + el.id;
5155
+ if (el.id) return '#' + cssEscapeIdent(el.id);
5080
5156
  var parts = [], node = el, depth = 0;
5081
5157
  while (node && node.nodeType === 1 && depth < 5) {
5082
- if (node.id) { parts.unshift('#' + node.id); break; }
5158
+ if (node.id) { parts.unshift('#' + cssEscapeIdent(node.id)); break; }
5083
5159
  var p = node.tagName.toLowerCase();
5084
5160
  if (node.classList && node.classList.length) {
5085
5161
  var clsArr = Array.from(node.classList).filter(function(c) {
5086
- return c.indexOf('vve-') !== 0;
5162
+ return c.indexOf('vve-') !== 0 && !isGeneratedClassToken(c);
5087
5163
  });
5088
- if (clsArr.length) p += '.' + clsArr.slice(0, 2).join('.');
5164
+ if (clsArr.length) p += '.' + clsArr.slice(0, 2).map(function(c) { return cssEscapeIdent(c); }).join('.');
5089
5165
  }
5090
5166
  var idx = 1, sib = node.previousElementSibling;
5091
5167
  while (sib) { if (sib.tagName === node.tagName) idx++; sib = sib.previousElementSibling; }
@@ -5097,17 +5173,32 @@ function buildSelector(el) {
5097
5173
  return parts.join(' > ');
5098
5174
  }
5099
5175
 
5176
+ function stripGeneratedSelectorClassTokens(sel) {
5177
+ if (!sel || typeof sel !== 'string') return '';
5178
+ var s = sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
5179
+ var raw = unescapeCssToken(cls);
5180
+ if (isGeneratedClassToken(raw)) return '';
5181
+ return '.' + cssEscapeIdent(raw);
5182
+ });
5183
+ return s
5184
+ .replace(/.{2,}/g, '.')
5185
+ .replace(/s{2,}/g, ' ')
5186
+ .replace(/s*>s*>/g, ' > ')
5187
+ .trim();
5188
+ }
5189
+
5100
5190
  /**
5101
5191
  * Strip editor-only .vve-* class tokens from a selector string (fixes DB rows saved while an element was selected).
5102
5192
  */
5103
5193
  function sanitizeSelectorForMatch(sel) {
5104
5194
  if (!sel || typeof sel !== 'string') return '';
5105
5195
  var s0 = sel.replace(/.vve-[a-zA-Z0-9_-]+/gi, '');
5196
+ s0 = stripGeneratedSelectorClassTokens(s0);
5106
5197
  var parts = s0.split(/s*>s*/).map(function(seg) {
5107
5198
  var t = seg.replace(/.+/g, '.').replace(/.$/, '');
5108
5199
  return t.trim();
5109
5200
  });
5110
- return parts.filter(Boolean).join(' > ');
5201
+ return escapeSelectorClassTokens(parts.filter(Boolean).join(' > '));
5111
5202
  }
5112
5203
 
5113
5204
  /** Drop the rightmost :nth-of-type(n) (hydration / layout often shifts sibling indices). */
@@ -5151,11 +5242,12 @@ function querySelectorResolved(iframeDoc, selector) {
5151
5242
  return null;
5152
5243
  }
5153
5244
  var alt = sanitizeSelectorForMatch(selector);
5245
+ var escaped = escapeSelectorClassTokens(alt || selector);
5154
5246
  // Prefer sanitized + nth relax FIRST: raw selectors often still contain .vve-* from
5155
5247
  // save-time selection; those only match after clicking (we re-add vve-selected).
5156
- var el = walkRelax(alt || selector);
5248
+ var el = walkRelax(escaped || alt || selector);
5157
5249
  if (el) return el;
5158
- if (alt !== selector) {
5250
+ if (alt !== selector || escaped !== selector) {
5159
5251
  el = walkRelax(selector);
5160
5252
  if (el) return el;
5161
5253
  }
@@ -5822,6 +5914,12 @@ window.addEventListener('load', function() {
5822
5914
  if (!d || d.channel !== 'vvveb-proxy-url' || d.type !== 'editor-url-changed') return;
5823
5915
  if (!iframe || !iframe.contentWindow || ev.source !== iframe.contentWindow) return;
5824
5916
  var payload = d.payload || {};
5917
+ try {
5918
+ console.info('[V2 iframe-url bridge]', {
5919
+ url: payload.url || '',
5920
+ passwordPresent: !!payload.password,
5921
+ });
5922
+ } catch(_) {}
5825
5923
  emitEditorUrlChangedPayload({
5826
5924
  url: payload.url || undefined,
5827
5925
  password: payload.password || undefined,
@@ -6111,8 +6209,6 @@ function createVisualEditorMiddleware(options) {
6111
6209
  extraTrackingMarkersForRequest
6112
6210
  );
6113
6211
  const strictFreezeParam = (url.searchParams.get("strictObserverFreeze") || "").toLowerCase();
6114
- const proxyParam = (url.searchParams.get("proxy") || "").toLowerCase();
6115
- const useScraperProxy = proxyParam === "1" || proxyParam === "true" || proxyParam === "yes";
6116
6212
  const strictObserverFreezeForRequest = strictFreezeParam === "1" || strictFreezeParam === "true" || strictFreezeParam === "yes" ? true : strictFreezeParam === "0" || strictFreezeParam === "false" || strictFreezeParam === "no" ? false : strictObserverFreeze;
6117
6213
  if (!targetUrl) {
6118
6214
  res.statusCode = 400;
@@ -6122,40 +6218,7 @@ function createVisualEditorMiddleware(options) {
6122
6218
  const parsed = new URL(targetUrl);
6123
6219
  const origin = parsed.origin;
6124
6220
  const method = (req.method || "GET").toUpperCase();
6125
- const scraperProxyClient = useScraperProxy ? await getScraperProxyClient() : null;
6126
- if (useScraperProxy && !scraperProxyClient) {
6127
- res.statusCode = 500;
6128
- res.setHeader("Content-Type", "application/json");
6129
- res.end(
6130
- JSON.stringify({
6131
- error: "ScraperAPI proxy is not configured. Set SCRAPERAPI_PROXY_PASSWORD or SCRAPERAPI_API_KEY."
6132
- })
6133
- );
6134
- return;
6135
- }
6136
6221
  const directFetch = (input, init = {}) => fetch(input, init);
6137
- const scraperFetch = (input, init = {}) => {
6138
- if (!SCRAPER_PROXY_PASSWORD) return directFetch(input, init);
6139
- if (!useScraperProxy || !scraperProxyClient) {
6140
- const scraperUrl = new URL(SCRAPER_API_ENDPOINT);
6141
- scraperUrl.searchParams.set("api_key", SCRAPER_PROXY_PASSWORD);
6142
- scraperUrl.searchParams.set("url", input);
6143
- return fetch(scraperUrl.toString(), init);
6144
- }
6145
- return scraperProxyClient.fetchFn(input, {
6146
- ...init,
6147
- dispatcher: scraperProxyClient.dispatcher
6148
- });
6149
- };
6150
- const shouldFallbackFromScraper = async (resp) => {
6151
- try {
6152
- if (resp.ok) return false;
6153
- const text = await resp.clone().text();
6154
- return SCRAPER_BILLING_ERROR_RE.test(String(text || ""));
6155
- } catch (_) {
6156
- return false;
6157
- }
6158
- };
6159
6222
  const workerRawFetch = (input, init = {}) => {
6160
6223
  const workerUrl = new URL("/api/conversion-proxy", conversionProxyBaseUrlForRequest);
6161
6224
  workerUrl.searchParams.set("url", input);
@@ -6188,17 +6251,6 @@ function createVisualEditorMiddleware(options) {
6188
6251
  }
6189
6252
  return workerRawFetch(input, init);
6190
6253
  }
6191
- const primary = await scraperFetch(input, init);
6192
- if (!await shouldFallbackFromScraper(primary)) {
6193
- return primary;
6194
- }
6195
- try {
6196
- console.warn("[conversion-proxy] ScraperAPI billing/quota detected; falling back to direct fetch", {
6197
- url: input,
6198
- mode: useScraperProxy ? "proxy" : "simple"
6199
- });
6200
- } catch (_) {
6201
- }
6202
6254
  return directFetch(input, init);
6203
6255
  };
6204
6256
  const headers = {
@@ -6270,16 +6322,8 @@ function createVisualEditorMiddleware(options) {
6270
6322
  res.end(
6271
6323
  JSON.stringify({
6272
6324
  error: aborted ? `Upstream request timed out after ${upstreamTimeoutMs / 1e3}s` : fetchErr?.message || "Upstream fetch failed",
6273
- phase: useScraperProxy ? "scraperapi-proxy-fetch" : "scraperapi-simple-fetch",
6274
- fetchMode: useScraperProxy ? "proxy" : "simple",
6275
- scraperapi: {
6276
- host: SCRAPER_PROXY_HOST,
6277
- port: SCRAPER_PROXY_PORT,
6278
- username: SCRAPER_PROXY_USERNAME,
6279
- endpoint: SCRAPER_API_ENDPOINT,
6280
- hasProxyPassword: Boolean(SCRAPER_PROXY_PASSWORD),
6281
- requestTlsRejectUnauthorized: SCRAPER_REQUEST_TLS_REJECT_UNAUTHORIZED
6282
- },
6325
+ phase: conversionProxyBaseUrlForRequest ? "worker-raw-fetch" : "direct-upstream-fetch",
6326
+ fetchMode: conversionProxyBaseUrlForRequest ? "worker" : "direct",
6283
6327
  cause: fetchCause && typeof fetchCause === "object" ? {
6284
6328
  name: fetchCause.name,
6285
6329
  code: fetchCause.code,
@@ -6465,8 +6509,60 @@ try{
6465
6509
  }
6466
6510
  }
6467
6511
  }catch(_){}
6468
- function getEditorUrlPayload(){try{var u=new URL(window.location.href);var nested=u.searchParams.get("url");return{url:nested||u.toString(),password:u.searchParams.get("password")||undefined};}catch(_){return null;}}
6469
- function notifyEditorUrlChanged(){try{var payload=getEditorUrlPayload();if(!payload)return;if(window.parent){window.parent.postMessage({channel:PARENT_URL_CHANNEL,type:"editor-url-changed",payload:payload},"*");}}catch(_){}}
6512
+ function getEditorUrlPayload(){
6513
+ try{
6514
+ var u=new URL(window.location.href);
6515
+ var nested=u.searchParams.get("url");
6516
+ if(nested){
6517
+ return {url:nested,password:u.searchParams.get("password")||undefined};
6518
+ }
6519
+ var p=u.pathname||"";
6520
+ var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
6521
+ if(isProxyPath){
6522
+ var recoveredBase=new URL(TARGET_PAGE_URL,window.location.href);
6523
+ var qp=new URLSearchParams(u.search||"");
6524
+ qp.delete("url");
6525
+ qp.delete("password");
6526
+ qp.delete("conversionProxyBaseUrl");
6527
+ qp.delete("trackingMarkers");
6528
+ qp.delete("strictObserverFreeze");
6529
+ qp.delete("proxy");
6530
+ qp.delete("raw");
6531
+ recoveredBase.search="";
6532
+ qp.forEach(function(v,k){recoveredBase.searchParams.set(k,v);});
6533
+ recoveredBase.hash=u.hash||"";
6534
+ return {url:recoveredBase.toString(),password:u.searchParams.get("password")||undefined};
6535
+ }
6536
+ try{
6537
+ var targetBase2=new URL(TARGET_PAGE_URL,window.location.href);
6538
+ if(u.origin!==targetBase2.origin){
6539
+ var normalized2=new URL((u.pathname||"/"),targetBase2.origin);
6540
+ normalized2.search=u.search||"";
6541
+ normalized2.hash=u.hash||"";
6542
+ return {url:normalized2.toString(),password:u.searchParams.get("password")||undefined};
6543
+ }
6544
+ }catch(_){}
6545
+ return {url:u.toString(),password:u.searchParams.get("password")||undefined};
6546
+ }catch(_){
6547
+ return null;
6548
+ }
6549
+ }
6550
+ function notifyEditorUrlChanged(){
6551
+ try{
6552
+ var payload=getEditorUrlPayload();
6553
+ if(!payload) return;
6554
+ try{
6555
+ console.info("[conversion-proxy] iframe url rendered",{
6556
+ current:window.location.href,
6557
+ target:payload.url||"",
6558
+ passwordPresent:!!payload.password
6559
+ });
6560
+ }catch(_){}
6561
+ if(window.parent){
6562
+ window.parent.postMessage({channel:PARENT_URL_CHANNEL,type:"editor-url-changed",payload:payload},"*");
6563
+ }
6564
+ }catch(_){}
6565
+ }
6470
6566
  try{notifyEditorUrlChanged();}catch(_){}
6471
6567
  try{if(window.history&&typeof window.history.pushState==="function"){var nativePushState=window.history.pushState;window.history.pushState=function(){var ret=nativePushState.apply(window.history,arguments);setTimeout(notifyEditorUrlChanged,0);return ret;};}}catch(_){}
6472
6568
  try{if(window.history&&typeof window.history.replaceState==="function"){var nativeReplaceState=window.history.replaceState;window.history.replaceState=function(){var ret=nativeReplaceState.apply(window.history,arguments);setTimeout(notifyEditorUrlChanged,0);return ret;};}}catch(_){}
@@ -6474,7 +6570,29 @@ try{window.addEventListener("popstate",notifyEditorUrlChanged,true);}catch(_){}
6474
6570
  try{window.addEventListener("hashchange",notifyEditorUrlChanged,true);}catch(_){}
6475
6571
  function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
6476
6572
  function toAbsolute(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")||raw.startsWith("//")?TARGET_ORIGIN:TARGET_PAGE_URL;return new URL(raw,base).toString();}catch(_){return raw;}}
6477
- function toProxy(raw){if(isSkippable(raw))return null;var abs=toAbsolute(raw);if(!abs||typeof abs!=="string")return null;try{var parsed=new URL(abs);if(parsed.origin!==TARGET_ORIGIN)return null;var root="/api/conversion-proxy";return root+"?password="+encodeURIComponent(PROXY_PASSWORD||"")+"&url="+encodeURIComponent(parsed.toString());}catch(_){return null;}}
6573
+ function toProxy(raw){
6574
+ if(isSkippable(raw))return null;
6575
+ var abs=toAbsolute(raw);
6576
+ if(!abs||typeof abs!=="string")return null;
6577
+ try{
6578
+ var parsed=new URL(abs);
6579
+ // If input is already a proxy URL, unwrap to its target url first
6580
+ // to avoid nested /api/conversion-proxy?url=/api/conversion-proxy?... chains.
6581
+ var p=(parsed.pathname||"");
6582
+ var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
6583
+ if(isProxyPath){
6584
+ var nested=parsed.searchParams.get("url")||"";
6585
+ if(nested){
6586
+ try{parsed=new URL(nested);}catch(_){}
6587
+ }
6588
+ }
6589
+ if(parsed.origin!==TARGET_ORIGIN)return null;
6590
+ var root="/api/conversion-proxy";
6591
+ return root+"?password="+encodeURIComponent(PROXY_PASSWORD||"")+"&url="+encodeURIComponent(parsed.toString());
6592
+ }catch(_){
6593
+ return null;
6594
+ }
6595
+ }
6478
6596
  var nativeAssign=window.location.assign?window.location.assign.bind(window.location):null;
6479
6597
  var nativeReplace=window.location.replace?window.location.replace.bind(window.location):null;
6480
6598
  function safeNavigate(raw,mode){var abs=toAbsolute(raw);var prox=toProxy(raw);if(!prox){try{console.warn("[conversion-proxy] redirect blocked",{mode:mode,requested:raw,resolved:abs,origin:TARGET_ORIGIN});}catch(_){}return false;}try{console.info("[conversion-proxy] redirect intercepted",{mode:mode,requested:raw,resolved:abs,proxied:prox});if(mode==="replace"&&nativeReplace){nativeReplace(prox);return true;}if(nativeAssign){nativeAssign(prox);return true;}window.location.href=prox;return true;}catch(err){try{console.warn("[conversion-proxy] redirect interception failed",{mode:mode,requested:raw,resolved:abs,proxied:prox,error:err&&err.message?err.message:String(err)});}catch(_){}return false;}}
@@ -6548,6 +6666,15 @@ function toProxyNetworkUrl(raw){
6548
6666
  try{
6549
6667
  var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;
6550
6668
  var abs=new URL(raw,base);
6669
+ // Unwrap already-proxied URLs first to avoid nested proxy urls.
6670
+ var p0=abs.pathname||"";
6671
+ var isProxyPath0=p0==="/api/conversion-proxy"||p0.indexOf("/api/conversion-proxy/")===0||p0.indexOf("api/conversion-proxy")!==-1;
6672
+ if(isProxyPath0){
6673
+ var nested0=abs.searchParams.get("url")||"";
6674
+ if(nested0){
6675
+ try{abs=new URL(nested0);}catch(_){}
6676
+ }
6677
+ }
6551
6678
  // Some embedded scripts build absolute requests against editor origin
6552
6679
  // (e.g. https://localhost:4001/api/unstable/graphql.json). Remap those
6553
6680
  // paths to target origin first, then proxy as usual.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accelerated-agency/visual-editor",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "private": false,
5
5
  "description": "Conversion visual editor as a reusable React package",
6
6
  "type": "module",