@accelerated-agency/visual-editor 0.4.6 → 0.4.7
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/dist/vite.cjs +204 -104
- package/dist/vite.js +204 -104
- 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,35 @@ 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
|
+
}
|
|
3286
3277
|
return {
|
|
3287
3278
|
url: parsed.toString(),
|
|
3288
3279
|
password:
|
|
@@ -3301,6 +3292,12 @@ function emitEditorUrlChangedPayload(payload) {
|
|
|
3301
3292
|
var key = String(payload.url || '') + '|' + String(payload.password || '');
|
|
3302
3293
|
if (key === lastEditorUrlPayloadKey) return;
|
|
3303
3294
|
lastEditorUrlPayloadKey = key;
|
|
3295
|
+
try {
|
|
3296
|
+
console.info('[V2 iframe-url]', {
|
|
3297
|
+
url: payload.url || '',
|
|
3298
|
+
passwordPresent: !!payload.password,
|
|
3299
|
+
});
|
|
3300
|
+
} catch(_) {}
|
|
3304
3301
|
send('editor-url-changed', payload);
|
|
3305
3302
|
}
|
|
3306
3303
|
function emitEditorUrlChanged(rawUrl) {
|
|
@@ -5073,6 +5070,67 @@ function stripDataVveInstanceSubtree(root) {
|
|
|
5073
5070
|
}
|
|
5074
5071
|
}
|
|
5075
5072
|
|
|
5073
|
+
function cssEscapeIdent(raw) {
|
|
5074
|
+
var s = raw == null ? '' : String(raw);
|
|
5075
|
+
if (!s) return '';
|
|
5076
|
+
try {
|
|
5077
|
+
if (typeof CSS !== 'undefined' && CSS && typeof CSS.escape === 'function') {
|
|
5078
|
+
return CSS.escape(s);
|
|
5079
|
+
}
|
|
5080
|
+
} catch(_) {}
|
|
5081
|
+
// Fallback escape for identifiers when CSS.escape is unavailable.
|
|
5082
|
+
return s.replace(/[^a-zA-Z0-9_-]/g, function(ch) { return '\\\\' + ch; });
|
|
5083
|
+
}
|
|
5084
|
+
|
|
5085
|
+
function unescapeCssToken(token) {
|
|
5086
|
+
if (!token) return '';
|
|
5087
|
+
var s = String(token);
|
|
5088
|
+
var out = '';
|
|
5089
|
+
for (var i = 0; i < s.length; i++) {
|
|
5090
|
+
var ch = s.charAt(i);
|
|
5091
|
+
if (ch === '\\\\' && i + 1 < s.length) {
|
|
5092
|
+
out += s.charAt(i + 1);
|
|
5093
|
+
i += 1;
|
|
5094
|
+
continue;
|
|
5095
|
+
}
|
|
5096
|
+
out += ch;
|
|
5097
|
+
}
|
|
5098
|
+
return out;
|
|
5099
|
+
}
|
|
5100
|
+
|
|
5101
|
+
function isGeneratedClassToken(token) {
|
|
5102
|
+
var t = token == null ? '' : String(token).trim();
|
|
5103
|
+
if (!t) return true;
|
|
5104
|
+
if (t.indexOf('vve-') === 0) return true;
|
|
5105
|
+
// Tailwind arbitrary values / variant-heavy utilities are often unstable in selectors.
|
|
5106
|
+
if (
|
|
5107
|
+
t.indexOf('[') !== -1 ||
|
|
5108
|
+
t.indexOf(']') !== -1 ||
|
|
5109
|
+
t.indexOf('(') !== -1 ||
|
|
5110
|
+
t.indexOf(')') !== -1 ||
|
|
5111
|
+
t.indexOf('{') !== -1 ||
|
|
5112
|
+
t.indexOf('}') !== -1 ||
|
|
5113
|
+
t.indexOf(':') !== -1
|
|
5114
|
+
) return true;
|
|
5115
|
+
// CSS-in-JS / runtime hash prefixes.
|
|
5116
|
+
if (/^(css|jsx|sc|emotion|styled|chakra|mantine|mui|ant)-/i.test(t)) return true;
|
|
5117
|
+
// Classnames with long hashy suffixes (framework/runtime generated).
|
|
5118
|
+
if (/[a-f0-9]{8,}/i.test(t)) return true;
|
|
5119
|
+
if (/^[a-zA-Z_-]+[0-9]{4,}[a-zA-Z0-9_-]*$/.test(t)) return true;
|
|
5120
|
+
if (/^[a-zA-Z0-9_-]{36,}$/.test(t)) return true;
|
|
5121
|
+
return false;
|
|
5122
|
+
}
|
|
5123
|
+
|
|
5124
|
+
function escapeSelectorClassTokens(sel) {
|
|
5125
|
+
if (!sel || typeof sel !== 'string') return '';
|
|
5126
|
+
return sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
|
|
5127
|
+
if (!cls) return _m;
|
|
5128
|
+
// Already escaped token; keep it as-is.
|
|
5129
|
+
if (cls.indexOf('\\\\') >= 0) return '.' + cls;
|
|
5130
|
+
return '.' + cssEscapeIdent(cls);
|
|
5131
|
+
});
|
|
5132
|
+
}
|
|
5133
|
+
|
|
5076
5134
|
function buildSelector(el) {
|
|
5077
5135
|
if (!el) return '';
|
|
5078
5136
|
var doc = el.ownerDocument || document;
|
|
@@ -5084,16 +5142,16 @@ function buildSelector(el) {
|
|
|
5084
5142
|
if (doc.querySelectorAll(attrSel).length === 1) return attrSel;
|
|
5085
5143
|
} catch(_) {}
|
|
5086
5144
|
}
|
|
5087
|
-
if (el.id) return '#' + el.id;
|
|
5145
|
+
if (el.id) return '#' + cssEscapeIdent(el.id);
|
|
5088
5146
|
var parts = [], node = el, depth = 0;
|
|
5089
5147
|
while (node && node.nodeType === 1 && depth < 5) {
|
|
5090
|
-
if (node.id) { parts.unshift('#' + node.id); break; }
|
|
5148
|
+
if (node.id) { parts.unshift('#' + cssEscapeIdent(node.id)); break; }
|
|
5091
5149
|
var p = node.tagName.toLowerCase();
|
|
5092
5150
|
if (node.classList && node.classList.length) {
|
|
5093
5151
|
var clsArr = Array.from(node.classList).filter(function(c) {
|
|
5094
|
-
return c.indexOf('vve-') !== 0;
|
|
5152
|
+
return c.indexOf('vve-') !== 0 && !isGeneratedClassToken(c);
|
|
5095
5153
|
});
|
|
5096
|
-
if (clsArr.length) p += '.' + clsArr.slice(0, 2).join('.');
|
|
5154
|
+
if (clsArr.length) p += '.' + clsArr.slice(0, 2).map(function(c) { return cssEscapeIdent(c); }).join('.');
|
|
5097
5155
|
}
|
|
5098
5156
|
var idx = 1, sib = node.previousElementSibling;
|
|
5099
5157
|
while (sib) { if (sib.tagName === node.tagName) idx++; sib = sib.previousElementSibling; }
|
|
@@ -5105,17 +5163,32 @@ function buildSelector(el) {
|
|
|
5105
5163
|
return parts.join(' > ');
|
|
5106
5164
|
}
|
|
5107
5165
|
|
|
5166
|
+
function stripGeneratedSelectorClassTokens(sel) {
|
|
5167
|
+
if (!sel || typeof sel !== 'string') return '';
|
|
5168
|
+
var s = sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
|
|
5169
|
+
var raw = unescapeCssToken(cls);
|
|
5170
|
+
if (isGeneratedClassToken(raw)) return '';
|
|
5171
|
+
return '.' + cssEscapeIdent(raw);
|
|
5172
|
+
});
|
|
5173
|
+
return s
|
|
5174
|
+
.replace(/.{2,}/g, '.')
|
|
5175
|
+
.replace(/s{2,}/g, ' ')
|
|
5176
|
+
.replace(/s*>s*>/g, ' > ')
|
|
5177
|
+
.trim();
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5108
5180
|
/**
|
|
5109
5181
|
* Strip editor-only .vve-* class tokens from a selector string (fixes DB rows saved while an element was selected).
|
|
5110
5182
|
*/
|
|
5111
5183
|
function sanitizeSelectorForMatch(sel) {
|
|
5112
5184
|
if (!sel || typeof sel !== 'string') return '';
|
|
5113
5185
|
var s0 = sel.replace(/.vve-[a-zA-Z0-9_-]+/gi, '');
|
|
5186
|
+
s0 = stripGeneratedSelectorClassTokens(s0);
|
|
5114
5187
|
var parts = s0.split(/s*>s*/).map(function(seg) {
|
|
5115
5188
|
var t = seg.replace(/.+/g, '.').replace(/.$/, '');
|
|
5116
5189
|
return t.trim();
|
|
5117
5190
|
});
|
|
5118
|
-
return parts.filter(Boolean).join(' > ');
|
|
5191
|
+
return escapeSelectorClassTokens(parts.filter(Boolean).join(' > '));
|
|
5119
5192
|
}
|
|
5120
5193
|
|
|
5121
5194
|
/** Drop the rightmost :nth-of-type(n) (hydration / layout often shifts sibling indices). */
|
|
@@ -5159,11 +5232,12 @@ function querySelectorResolved(iframeDoc, selector) {
|
|
|
5159
5232
|
return null;
|
|
5160
5233
|
}
|
|
5161
5234
|
var alt = sanitizeSelectorForMatch(selector);
|
|
5235
|
+
var escaped = escapeSelectorClassTokens(alt || selector);
|
|
5162
5236
|
// Prefer sanitized + nth relax FIRST: raw selectors often still contain .vve-* from
|
|
5163
5237
|
// save-time selection; those only match after clicking (we re-add vve-selected).
|
|
5164
|
-
var el = walkRelax(alt || selector);
|
|
5238
|
+
var el = walkRelax(escaped || alt || selector);
|
|
5165
5239
|
if (el) return el;
|
|
5166
|
-
if (alt !== selector) {
|
|
5240
|
+
if (alt !== selector || escaped !== selector) {
|
|
5167
5241
|
el = walkRelax(selector);
|
|
5168
5242
|
if (el) return el;
|
|
5169
5243
|
}
|
|
@@ -5830,6 +5904,12 @@ window.addEventListener('load', function() {
|
|
|
5830
5904
|
if (!d || d.channel !== 'vvveb-proxy-url' || d.type !== 'editor-url-changed') return;
|
|
5831
5905
|
if (!iframe || !iframe.contentWindow || ev.source !== iframe.contentWindow) return;
|
|
5832
5906
|
var payload = d.payload || {};
|
|
5907
|
+
try {
|
|
5908
|
+
console.info('[V2 iframe-url bridge]', {
|
|
5909
|
+
url: payload.url || '',
|
|
5910
|
+
passwordPresent: !!payload.password,
|
|
5911
|
+
});
|
|
5912
|
+
} catch(_) {}
|
|
5833
5913
|
emitEditorUrlChangedPayload({
|
|
5834
5914
|
url: payload.url || undefined,
|
|
5835
5915
|
password: payload.password || undefined,
|
|
@@ -6119,8 +6199,6 @@ function createVisualEditorMiddleware(options) {
|
|
|
6119
6199
|
extraTrackingMarkersForRequest
|
|
6120
6200
|
);
|
|
6121
6201
|
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
6202
|
const strictObserverFreezeForRequest = strictFreezeParam === "1" || strictFreezeParam === "true" || strictFreezeParam === "yes" ? true : strictFreezeParam === "0" || strictFreezeParam === "false" || strictFreezeParam === "no" ? false : strictObserverFreeze;
|
|
6125
6203
|
if (!targetUrl) {
|
|
6126
6204
|
res.statusCode = 400;
|
|
@@ -6130,40 +6208,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
6130
6208
|
const parsed = new URL(targetUrl);
|
|
6131
6209
|
const origin = parsed.origin;
|
|
6132
6210
|
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
6211
|
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
6212
|
const workerRawFetch = (input, init = {}) => {
|
|
6168
6213
|
const workerUrl = new URL("/api/conversion-proxy", conversionProxyBaseUrlForRequest);
|
|
6169
6214
|
workerUrl.searchParams.set("url", input);
|
|
@@ -6196,17 +6241,6 @@ function createVisualEditorMiddleware(options) {
|
|
|
6196
6241
|
}
|
|
6197
6242
|
return workerRawFetch(input, init);
|
|
6198
6243
|
}
|
|
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
6244
|
return directFetch(input, init);
|
|
6211
6245
|
};
|
|
6212
6246
|
const headers = {
|
|
@@ -6278,16 +6312,8 @@ function createVisualEditorMiddleware(options) {
|
|
|
6278
6312
|
res.end(
|
|
6279
6313
|
JSON.stringify({
|
|
6280
6314
|
error: aborted ? `Upstream request timed out after ${upstreamTimeoutMs / 1e3}s` : fetchErr?.message || "Upstream fetch failed",
|
|
6281
|
-
phase:
|
|
6282
|
-
fetchMode:
|
|
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
|
-
},
|
|
6315
|
+
phase: conversionProxyBaseUrlForRequest ? "worker-raw-fetch" : "direct-upstream-fetch",
|
|
6316
|
+
fetchMode: conversionProxyBaseUrlForRequest ? "worker" : "direct",
|
|
6291
6317
|
cause: fetchCause && typeof fetchCause === "object" ? {
|
|
6292
6318
|
name: fetchCause.name,
|
|
6293
6319
|
code: fetchCause.code,
|
|
@@ -6473,8 +6499,51 @@ try{
|
|
|
6473
6499
|
}
|
|
6474
6500
|
}
|
|
6475
6501
|
}catch(_){}
|
|
6476
|
-
function getEditorUrlPayload(){
|
|
6477
|
-
|
|
6502
|
+
function getEditorUrlPayload(){
|
|
6503
|
+
try{
|
|
6504
|
+
var u=new URL(window.location.href);
|
|
6505
|
+
var nested=u.searchParams.get("url");
|
|
6506
|
+
if(nested){
|
|
6507
|
+
return {url:nested,password:u.searchParams.get("password")||undefined};
|
|
6508
|
+
}
|
|
6509
|
+
var p=u.pathname||"";
|
|
6510
|
+
var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
|
|
6511
|
+
if(isProxyPath){
|
|
6512
|
+
var recoveredBase=new URL(TARGET_PAGE_URL,window.location.href);
|
|
6513
|
+
var qp=new URLSearchParams(u.search||"");
|
|
6514
|
+
qp.delete("url");
|
|
6515
|
+
qp.delete("password");
|
|
6516
|
+
qp.delete("conversionProxyBaseUrl");
|
|
6517
|
+
qp.delete("trackingMarkers");
|
|
6518
|
+
qp.delete("strictObserverFreeze");
|
|
6519
|
+
qp.delete("proxy");
|
|
6520
|
+
qp.delete("raw");
|
|
6521
|
+
recoveredBase.search="";
|
|
6522
|
+
qp.forEach(function(v,k){recoveredBase.searchParams.set(k,v);});
|
|
6523
|
+
recoveredBase.hash=u.hash||"";
|
|
6524
|
+
return {url:recoveredBase.toString(),password:u.searchParams.get("password")||undefined};
|
|
6525
|
+
}
|
|
6526
|
+
return {url:u.toString(),password:u.searchParams.get("password")||undefined};
|
|
6527
|
+
}catch(_){
|
|
6528
|
+
return null;
|
|
6529
|
+
}
|
|
6530
|
+
}
|
|
6531
|
+
function notifyEditorUrlChanged(){
|
|
6532
|
+
try{
|
|
6533
|
+
var payload=getEditorUrlPayload();
|
|
6534
|
+
if(!payload) return;
|
|
6535
|
+
try{
|
|
6536
|
+
console.info("[conversion-proxy] iframe url rendered",{
|
|
6537
|
+
current:window.location.href,
|
|
6538
|
+
target:payload.url||"",
|
|
6539
|
+
passwordPresent:!!payload.password
|
|
6540
|
+
});
|
|
6541
|
+
}catch(_){}
|
|
6542
|
+
if(window.parent){
|
|
6543
|
+
window.parent.postMessage({channel:PARENT_URL_CHANNEL,type:"editor-url-changed",payload:payload},"*");
|
|
6544
|
+
}
|
|
6545
|
+
}catch(_){}
|
|
6546
|
+
}
|
|
6478
6547
|
try{notifyEditorUrlChanged();}catch(_){}
|
|
6479
6548
|
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
6549
|
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 +6551,29 @@ try{window.addEventListener("popstate",notifyEditorUrlChanged,true);}catch(_){}
|
|
|
6482
6551
|
try{window.addEventListener("hashchange",notifyEditorUrlChanged,true);}catch(_){}
|
|
6483
6552
|
function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
|
|
6484
6553
|
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){
|
|
6554
|
+
function toProxy(raw){
|
|
6555
|
+
if(isSkippable(raw))return null;
|
|
6556
|
+
var abs=toAbsolute(raw);
|
|
6557
|
+
if(!abs||typeof abs!=="string")return null;
|
|
6558
|
+
try{
|
|
6559
|
+
var parsed=new URL(abs);
|
|
6560
|
+
// If input is already a proxy URL, unwrap to its target url first
|
|
6561
|
+
// to avoid nested /api/conversion-proxy?url=/api/conversion-proxy?... chains.
|
|
6562
|
+
var p=(parsed.pathname||"");
|
|
6563
|
+
var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
|
|
6564
|
+
if(isProxyPath){
|
|
6565
|
+
var nested=parsed.searchParams.get("url")||"";
|
|
6566
|
+
if(nested){
|
|
6567
|
+
try{parsed=new URL(nested);}catch(_){}
|
|
6568
|
+
}
|
|
6569
|
+
}
|
|
6570
|
+
if(parsed.origin!==TARGET_ORIGIN)return null;
|
|
6571
|
+
var root="/api/conversion-proxy";
|
|
6572
|
+
return root+"?password="+encodeURIComponent(PROXY_PASSWORD||"")+"&url="+encodeURIComponent(parsed.toString());
|
|
6573
|
+
}catch(_){
|
|
6574
|
+
return null;
|
|
6575
|
+
}
|
|
6576
|
+
}
|
|
6486
6577
|
var nativeAssign=window.location.assign?window.location.assign.bind(window.location):null;
|
|
6487
6578
|
var nativeReplace=window.location.replace?window.location.replace.bind(window.location):null;
|
|
6488
6579
|
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 +6647,15 @@ function toProxyNetworkUrl(raw){
|
|
|
6556
6647
|
try{
|
|
6557
6648
|
var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;
|
|
6558
6649
|
var abs=new URL(raw,base);
|
|
6650
|
+
// Unwrap already-proxied URLs first to avoid nested proxy urls.
|
|
6651
|
+
var p0=abs.pathname||"";
|
|
6652
|
+
var isProxyPath0=p0==="/api/conversion-proxy"||p0.indexOf("/api/conversion-proxy/")===0||p0.indexOf("api/conversion-proxy")!==-1;
|
|
6653
|
+
if(isProxyPath0){
|
|
6654
|
+
var nested0=abs.searchParams.get("url")||"";
|
|
6655
|
+
if(nested0){
|
|
6656
|
+
try{abs=new URL(nested0);}catch(_){}
|
|
6657
|
+
}
|
|
6658
|
+
}
|
|
6559
6659
|
// Some embedded scripts build absolute requests against editor origin
|
|
6560
6660
|
// (e.g. https://localhost:4001/api/unstable/graphql.json). Remap those
|
|
6561
6661
|
// 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,35 @@ 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
|
+
}
|
|
3278
3269
|
return {
|
|
3279
3270
|
url: parsed.toString(),
|
|
3280
3271
|
password:
|
|
@@ -3293,6 +3284,12 @@ function emitEditorUrlChangedPayload(payload) {
|
|
|
3293
3284
|
var key = String(payload.url || '') + '|' + String(payload.password || '');
|
|
3294
3285
|
if (key === lastEditorUrlPayloadKey) return;
|
|
3295
3286
|
lastEditorUrlPayloadKey = key;
|
|
3287
|
+
try {
|
|
3288
|
+
console.info('[V2 iframe-url]', {
|
|
3289
|
+
url: payload.url || '',
|
|
3290
|
+
passwordPresent: !!payload.password,
|
|
3291
|
+
});
|
|
3292
|
+
} catch(_) {}
|
|
3296
3293
|
send('editor-url-changed', payload);
|
|
3297
3294
|
}
|
|
3298
3295
|
function emitEditorUrlChanged(rawUrl) {
|
|
@@ -5065,6 +5062,67 @@ function stripDataVveInstanceSubtree(root) {
|
|
|
5065
5062
|
}
|
|
5066
5063
|
}
|
|
5067
5064
|
|
|
5065
|
+
function cssEscapeIdent(raw) {
|
|
5066
|
+
var s = raw == null ? '' : String(raw);
|
|
5067
|
+
if (!s) return '';
|
|
5068
|
+
try {
|
|
5069
|
+
if (typeof CSS !== 'undefined' && CSS && typeof CSS.escape === 'function') {
|
|
5070
|
+
return CSS.escape(s);
|
|
5071
|
+
}
|
|
5072
|
+
} catch(_) {}
|
|
5073
|
+
// Fallback escape for identifiers when CSS.escape is unavailable.
|
|
5074
|
+
return s.replace(/[^a-zA-Z0-9_-]/g, function(ch) { return '\\\\' + ch; });
|
|
5075
|
+
}
|
|
5076
|
+
|
|
5077
|
+
function unescapeCssToken(token) {
|
|
5078
|
+
if (!token) return '';
|
|
5079
|
+
var s = String(token);
|
|
5080
|
+
var out = '';
|
|
5081
|
+
for (var i = 0; i < s.length; i++) {
|
|
5082
|
+
var ch = s.charAt(i);
|
|
5083
|
+
if (ch === '\\\\' && i + 1 < s.length) {
|
|
5084
|
+
out += s.charAt(i + 1);
|
|
5085
|
+
i += 1;
|
|
5086
|
+
continue;
|
|
5087
|
+
}
|
|
5088
|
+
out += ch;
|
|
5089
|
+
}
|
|
5090
|
+
return out;
|
|
5091
|
+
}
|
|
5092
|
+
|
|
5093
|
+
function isGeneratedClassToken(token) {
|
|
5094
|
+
var t = token == null ? '' : String(token).trim();
|
|
5095
|
+
if (!t) return true;
|
|
5096
|
+
if (t.indexOf('vve-') === 0) return true;
|
|
5097
|
+
// Tailwind arbitrary values / variant-heavy utilities are often unstable in selectors.
|
|
5098
|
+
if (
|
|
5099
|
+
t.indexOf('[') !== -1 ||
|
|
5100
|
+
t.indexOf(']') !== -1 ||
|
|
5101
|
+
t.indexOf('(') !== -1 ||
|
|
5102
|
+
t.indexOf(')') !== -1 ||
|
|
5103
|
+
t.indexOf('{') !== -1 ||
|
|
5104
|
+
t.indexOf('}') !== -1 ||
|
|
5105
|
+
t.indexOf(':') !== -1
|
|
5106
|
+
) return true;
|
|
5107
|
+
// CSS-in-JS / runtime hash prefixes.
|
|
5108
|
+
if (/^(css|jsx|sc|emotion|styled|chakra|mantine|mui|ant)-/i.test(t)) return true;
|
|
5109
|
+
// Classnames with long hashy suffixes (framework/runtime generated).
|
|
5110
|
+
if (/[a-f0-9]{8,}/i.test(t)) return true;
|
|
5111
|
+
if (/^[a-zA-Z_-]+[0-9]{4,}[a-zA-Z0-9_-]*$/.test(t)) return true;
|
|
5112
|
+
if (/^[a-zA-Z0-9_-]{36,}$/.test(t)) return true;
|
|
5113
|
+
return false;
|
|
5114
|
+
}
|
|
5115
|
+
|
|
5116
|
+
function escapeSelectorClassTokens(sel) {
|
|
5117
|
+
if (!sel || typeof sel !== 'string') return '';
|
|
5118
|
+
return sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
|
|
5119
|
+
if (!cls) return _m;
|
|
5120
|
+
// Already escaped token; keep it as-is.
|
|
5121
|
+
if (cls.indexOf('\\\\') >= 0) return '.' + cls;
|
|
5122
|
+
return '.' + cssEscapeIdent(cls);
|
|
5123
|
+
});
|
|
5124
|
+
}
|
|
5125
|
+
|
|
5068
5126
|
function buildSelector(el) {
|
|
5069
5127
|
if (!el) return '';
|
|
5070
5128
|
var doc = el.ownerDocument || document;
|
|
@@ -5076,16 +5134,16 @@ function buildSelector(el) {
|
|
|
5076
5134
|
if (doc.querySelectorAll(attrSel).length === 1) return attrSel;
|
|
5077
5135
|
} catch(_) {}
|
|
5078
5136
|
}
|
|
5079
|
-
if (el.id) return '#' + el.id;
|
|
5137
|
+
if (el.id) return '#' + cssEscapeIdent(el.id);
|
|
5080
5138
|
var parts = [], node = el, depth = 0;
|
|
5081
5139
|
while (node && node.nodeType === 1 && depth < 5) {
|
|
5082
|
-
if (node.id) { parts.unshift('#' + node.id); break; }
|
|
5140
|
+
if (node.id) { parts.unshift('#' + cssEscapeIdent(node.id)); break; }
|
|
5083
5141
|
var p = node.tagName.toLowerCase();
|
|
5084
5142
|
if (node.classList && node.classList.length) {
|
|
5085
5143
|
var clsArr = Array.from(node.classList).filter(function(c) {
|
|
5086
|
-
return c.indexOf('vve-') !== 0;
|
|
5144
|
+
return c.indexOf('vve-') !== 0 && !isGeneratedClassToken(c);
|
|
5087
5145
|
});
|
|
5088
|
-
if (clsArr.length) p += '.' + clsArr.slice(0, 2).join('.');
|
|
5146
|
+
if (clsArr.length) p += '.' + clsArr.slice(0, 2).map(function(c) { return cssEscapeIdent(c); }).join('.');
|
|
5089
5147
|
}
|
|
5090
5148
|
var idx = 1, sib = node.previousElementSibling;
|
|
5091
5149
|
while (sib) { if (sib.tagName === node.tagName) idx++; sib = sib.previousElementSibling; }
|
|
@@ -5097,17 +5155,32 @@ function buildSelector(el) {
|
|
|
5097
5155
|
return parts.join(' > ');
|
|
5098
5156
|
}
|
|
5099
5157
|
|
|
5158
|
+
function stripGeneratedSelectorClassTokens(sel) {
|
|
5159
|
+
if (!sel || typeof sel !== 'string') return '';
|
|
5160
|
+
var s = sel.replace(/.((?:\\.|[^s>+~:#.])+)/g, function(_m, cls) {
|
|
5161
|
+
var raw = unescapeCssToken(cls);
|
|
5162
|
+
if (isGeneratedClassToken(raw)) return '';
|
|
5163
|
+
return '.' + cssEscapeIdent(raw);
|
|
5164
|
+
});
|
|
5165
|
+
return s
|
|
5166
|
+
.replace(/.{2,}/g, '.')
|
|
5167
|
+
.replace(/s{2,}/g, ' ')
|
|
5168
|
+
.replace(/s*>s*>/g, ' > ')
|
|
5169
|
+
.trim();
|
|
5170
|
+
}
|
|
5171
|
+
|
|
5100
5172
|
/**
|
|
5101
5173
|
* Strip editor-only .vve-* class tokens from a selector string (fixes DB rows saved while an element was selected).
|
|
5102
5174
|
*/
|
|
5103
5175
|
function sanitizeSelectorForMatch(sel) {
|
|
5104
5176
|
if (!sel || typeof sel !== 'string') return '';
|
|
5105
5177
|
var s0 = sel.replace(/.vve-[a-zA-Z0-9_-]+/gi, '');
|
|
5178
|
+
s0 = stripGeneratedSelectorClassTokens(s0);
|
|
5106
5179
|
var parts = s0.split(/s*>s*/).map(function(seg) {
|
|
5107
5180
|
var t = seg.replace(/.+/g, '.').replace(/.$/, '');
|
|
5108
5181
|
return t.trim();
|
|
5109
5182
|
});
|
|
5110
|
-
return parts.filter(Boolean).join(' > ');
|
|
5183
|
+
return escapeSelectorClassTokens(parts.filter(Boolean).join(' > '));
|
|
5111
5184
|
}
|
|
5112
5185
|
|
|
5113
5186
|
/** Drop the rightmost :nth-of-type(n) (hydration / layout often shifts sibling indices). */
|
|
@@ -5151,11 +5224,12 @@ function querySelectorResolved(iframeDoc, selector) {
|
|
|
5151
5224
|
return null;
|
|
5152
5225
|
}
|
|
5153
5226
|
var alt = sanitizeSelectorForMatch(selector);
|
|
5227
|
+
var escaped = escapeSelectorClassTokens(alt || selector);
|
|
5154
5228
|
// Prefer sanitized + nth relax FIRST: raw selectors often still contain .vve-* from
|
|
5155
5229
|
// save-time selection; those only match after clicking (we re-add vve-selected).
|
|
5156
|
-
var el = walkRelax(alt || selector);
|
|
5230
|
+
var el = walkRelax(escaped || alt || selector);
|
|
5157
5231
|
if (el) return el;
|
|
5158
|
-
if (alt !== selector) {
|
|
5232
|
+
if (alt !== selector || escaped !== selector) {
|
|
5159
5233
|
el = walkRelax(selector);
|
|
5160
5234
|
if (el) return el;
|
|
5161
5235
|
}
|
|
@@ -5822,6 +5896,12 @@ window.addEventListener('load', function() {
|
|
|
5822
5896
|
if (!d || d.channel !== 'vvveb-proxy-url' || d.type !== 'editor-url-changed') return;
|
|
5823
5897
|
if (!iframe || !iframe.contentWindow || ev.source !== iframe.contentWindow) return;
|
|
5824
5898
|
var payload = d.payload || {};
|
|
5899
|
+
try {
|
|
5900
|
+
console.info('[V2 iframe-url bridge]', {
|
|
5901
|
+
url: payload.url || '',
|
|
5902
|
+
passwordPresent: !!payload.password,
|
|
5903
|
+
});
|
|
5904
|
+
} catch(_) {}
|
|
5825
5905
|
emitEditorUrlChangedPayload({
|
|
5826
5906
|
url: payload.url || undefined,
|
|
5827
5907
|
password: payload.password || undefined,
|
|
@@ -6111,8 +6191,6 @@ function createVisualEditorMiddleware(options) {
|
|
|
6111
6191
|
extraTrackingMarkersForRequest
|
|
6112
6192
|
);
|
|
6113
6193
|
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
6194
|
const strictObserverFreezeForRequest = strictFreezeParam === "1" || strictFreezeParam === "true" || strictFreezeParam === "yes" ? true : strictFreezeParam === "0" || strictFreezeParam === "false" || strictFreezeParam === "no" ? false : strictObserverFreeze;
|
|
6117
6195
|
if (!targetUrl) {
|
|
6118
6196
|
res.statusCode = 400;
|
|
@@ -6122,40 +6200,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
6122
6200
|
const parsed = new URL(targetUrl);
|
|
6123
6201
|
const origin = parsed.origin;
|
|
6124
6202
|
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
6203
|
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
6204
|
const workerRawFetch = (input, init = {}) => {
|
|
6160
6205
|
const workerUrl = new URL("/api/conversion-proxy", conversionProxyBaseUrlForRequest);
|
|
6161
6206
|
workerUrl.searchParams.set("url", input);
|
|
@@ -6188,17 +6233,6 @@ function createVisualEditorMiddleware(options) {
|
|
|
6188
6233
|
}
|
|
6189
6234
|
return workerRawFetch(input, init);
|
|
6190
6235
|
}
|
|
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
6236
|
return directFetch(input, init);
|
|
6203
6237
|
};
|
|
6204
6238
|
const headers = {
|
|
@@ -6270,16 +6304,8 @@ function createVisualEditorMiddleware(options) {
|
|
|
6270
6304
|
res.end(
|
|
6271
6305
|
JSON.stringify({
|
|
6272
6306
|
error: aborted ? `Upstream request timed out after ${upstreamTimeoutMs / 1e3}s` : fetchErr?.message || "Upstream fetch failed",
|
|
6273
|
-
phase:
|
|
6274
|
-
fetchMode:
|
|
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
|
-
},
|
|
6307
|
+
phase: conversionProxyBaseUrlForRequest ? "worker-raw-fetch" : "direct-upstream-fetch",
|
|
6308
|
+
fetchMode: conversionProxyBaseUrlForRequest ? "worker" : "direct",
|
|
6283
6309
|
cause: fetchCause && typeof fetchCause === "object" ? {
|
|
6284
6310
|
name: fetchCause.name,
|
|
6285
6311
|
code: fetchCause.code,
|
|
@@ -6465,8 +6491,51 @@ try{
|
|
|
6465
6491
|
}
|
|
6466
6492
|
}
|
|
6467
6493
|
}catch(_){}
|
|
6468
|
-
function getEditorUrlPayload(){
|
|
6469
|
-
|
|
6494
|
+
function getEditorUrlPayload(){
|
|
6495
|
+
try{
|
|
6496
|
+
var u=new URL(window.location.href);
|
|
6497
|
+
var nested=u.searchParams.get("url");
|
|
6498
|
+
if(nested){
|
|
6499
|
+
return {url:nested,password:u.searchParams.get("password")||undefined};
|
|
6500
|
+
}
|
|
6501
|
+
var p=u.pathname||"";
|
|
6502
|
+
var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
|
|
6503
|
+
if(isProxyPath){
|
|
6504
|
+
var recoveredBase=new URL(TARGET_PAGE_URL,window.location.href);
|
|
6505
|
+
var qp=new URLSearchParams(u.search||"");
|
|
6506
|
+
qp.delete("url");
|
|
6507
|
+
qp.delete("password");
|
|
6508
|
+
qp.delete("conversionProxyBaseUrl");
|
|
6509
|
+
qp.delete("trackingMarkers");
|
|
6510
|
+
qp.delete("strictObserverFreeze");
|
|
6511
|
+
qp.delete("proxy");
|
|
6512
|
+
qp.delete("raw");
|
|
6513
|
+
recoveredBase.search="";
|
|
6514
|
+
qp.forEach(function(v,k){recoveredBase.searchParams.set(k,v);});
|
|
6515
|
+
recoveredBase.hash=u.hash||"";
|
|
6516
|
+
return {url:recoveredBase.toString(),password:u.searchParams.get("password")||undefined};
|
|
6517
|
+
}
|
|
6518
|
+
return {url:u.toString(),password:u.searchParams.get("password")||undefined};
|
|
6519
|
+
}catch(_){
|
|
6520
|
+
return null;
|
|
6521
|
+
}
|
|
6522
|
+
}
|
|
6523
|
+
function notifyEditorUrlChanged(){
|
|
6524
|
+
try{
|
|
6525
|
+
var payload=getEditorUrlPayload();
|
|
6526
|
+
if(!payload) return;
|
|
6527
|
+
try{
|
|
6528
|
+
console.info("[conversion-proxy] iframe url rendered",{
|
|
6529
|
+
current:window.location.href,
|
|
6530
|
+
target:payload.url||"",
|
|
6531
|
+
passwordPresent:!!payload.password
|
|
6532
|
+
});
|
|
6533
|
+
}catch(_){}
|
|
6534
|
+
if(window.parent){
|
|
6535
|
+
window.parent.postMessage({channel:PARENT_URL_CHANNEL,type:"editor-url-changed",payload:payload},"*");
|
|
6536
|
+
}
|
|
6537
|
+
}catch(_){}
|
|
6538
|
+
}
|
|
6470
6539
|
try{notifyEditorUrlChanged();}catch(_){}
|
|
6471
6540
|
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
6541
|
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 +6543,29 @@ try{window.addEventListener("popstate",notifyEditorUrlChanged,true);}catch(_){}
|
|
|
6474
6543
|
try{window.addEventListener("hashchange",notifyEditorUrlChanged,true);}catch(_){}
|
|
6475
6544
|
function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
|
|
6476
6545
|
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){
|
|
6546
|
+
function toProxy(raw){
|
|
6547
|
+
if(isSkippable(raw))return null;
|
|
6548
|
+
var abs=toAbsolute(raw);
|
|
6549
|
+
if(!abs||typeof abs!=="string")return null;
|
|
6550
|
+
try{
|
|
6551
|
+
var parsed=new URL(abs);
|
|
6552
|
+
// If input is already a proxy URL, unwrap to its target url first
|
|
6553
|
+
// to avoid nested /api/conversion-proxy?url=/api/conversion-proxy?... chains.
|
|
6554
|
+
var p=(parsed.pathname||"");
|
|
6555
|
+
var isProxyPath=p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0||p.indexOf("api/conversion-proxy")!==-1;
|
|
6556
|
+
if(isProxyPath){
|
|
6557
|
+
var nested=parsed.searchParams.get("url")||"";
|
|
6558
|
+
if(nested){
|
|
6559
|
+
try{parsed=new URL(nested);}catch(_){}
|
|
6560
|
+
}
|
|
6561
|
+
}
|
|
6562
|
+
if(parsed.origin!==TARGET_ORIGIN)return null;
|
|
6563
|
+
var root="/api/conversion-proxy";
|
|
6564
|
+
return root+"?password="+encodeURIComponent(PROXY_PASSWORD||"")+"&url="+encodeURIComponent(parsed.toString());
|
|
6565
|
+
}catch(_){
|
|
6566
|
+
return null;
|
|
6567
|
+
}
|
|
6568
|
+
}
|
|
6478
6569
|
var nativeAssign=window.location.assign?window.location.assign.bind(window.location):null;
|
|
6479
6570
|
var nativeReplace=window.location.replace?window.location.replace.bind(window.location):null;
|
|
6480
6571
|
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 +6639,15 @@ function toProxyNetworkUrl(raw){
|
|
|
6548
6639
|
try{
|
|
6549
6640
|
var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;
|
|
6550
6641
|
var abs=new URL(raw,base);
|
|
6642
|
+
// Unwrap already-proxied URLs first to avoid nested proxy urls.
|
|
6643
|
+
var p0=abs.pathname||"";
|
|
6644
|
+
var isProxyPath0=p0==="/api/conversion-proxy"||p0.indexOf("/api/conversion-proxy/")===0||p0.indexOf("api/conversion-proxy")!==-1;
|
|
6645
|
+
if(isProxyPath0){
|
|
6646
|
+
var nested0=abs.searchParams.get("url")||"";
|
|
6647
|
+
if(nested0){
|
|
6648
|
+
try{abs=new URL(nested0);}catch(_){}
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6551
6651
|
// Some embedded scripts build absolute requests against editor origin
|
|
6552
6652
|
// (e.g. https://localhost:4001/api/unstable/graphql.json). Remap those
|
|
6553
6653
|
// paths to target origin first, then proxy as usual.
|