@accelerated-agency/visual-editor 0.2.0 → 0.2.2
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/index.js +126 -72
- package/dist/vite.cjs +34 -12
- package/dist/vite.js +34 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,6 +5,11 @@ import { persist } from 'zustand/middleware';
|
|
|
5
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { HexColorPicker } from 'react-colorful';
|
|
7
7
|
|
|
8
|
+
var PERSIST_NAME_PREFIX = "conversion-editor-variations-v3-";
|
|
9
|
+
function variationsPersistStorageName(experimentId) {
|
|
10
|
+
const id = experimentId?.trim();
|
|
11
|
+
return `${PERSIST_NAME_PREFIX}${id && id.length > 0 ? id : "standalone"}`;
|
|
12
|
+
}
|
|
8
13
|
var VARIATION_COLORS = [
|
|
9
14
|
"#0084D1",
|
|
10
15
|
"#F54A00",
|
|
@@ -77,16 +82,12 @@ var useVariationsStore = create()(
|
|
|
77
82
|
}));
|
|
78
83
|
},
|
|
79
84
|
setActiveVariation: (id) => set({ activeVariationId: id }),
|
|
80
|
-
addMutationToActive: (mutation) => set((s) => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// Clear redo stack for active variation on new mutation
|
|
87
|
-
redoStacks: { ...s.redoStacks, [s.activeVariationId]: [] }
|
|
88
|
-
};
|
|
89
|
-
}),
|
|
85
|
+
addMutationToActive: (mutation) => set((s) => ({
|
|
86
|
+
variations: s.variations.map(
|
|
87
|
+
(v) => v.id === s.activeVariationId ? { ...v, mutations: [...v.mutations, mutation] } : v
|
|
88
|
+
),
|
|
89
|
+
redoStacks: { ...s.redoStacks, [s.activeVariationId]: [] }
|
|
90
|
+
})),
|
|
90
91
|
removeLastMutationFromActive: () => set((s) => {
|
|
91
92
|
const active = s.variations.find((v) => v.id === s.activeVariationId);
|
|
92
93
|
const removed = active?.mutations[active.mutations.length - 1];
|
|
@@ -149,7 +150,8 @@ var useVariationsStore = create()(
|
|
|
149
150
|
}
|
|
150
151
|
}),
|
|
151
152
|
{
|
|
152
|
-
name:
|
|
153
|
+
name: variationsPersistStorageName(void 0),
|
|
154
|
+
skipHydration: true,
|
|
153
155
|
partialize: (state) => ({
|
|
154
156
|
variations: state.variations,
|
|
155
157
|
activeVariationId: state.activeVariationId
|
|
@@ -157,6 +159,15 @@ var useVariationsStore = create()(
|
|
|
157
159
|
}
|
|
158
160
|
)
|
|
159
161
|
);
|
|
162
|
+
async function hydrateVariationsFromStorage(experimentId) {
|
|
163
|
+
useVariationsStore.persist.setOptions({
|
|
164
|
+
name: variationsPersistStorageName(experimentId)
|
|
165
|
+
});
|
|
166
|
+
try {
|
|
167
|
+
await useVariationsStore.persist.rehydrate();
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
160
171
|
function TopBar({
|
|
161
172
|
connectionStatus,
|
|
162
173
|
onLoadUrl,
|
|
@@ -1832,8 +1843,10 @@ function ElementIcon({ tag }) {
|
|
|
1832
1843
|
return /* @__PURE__ */ jsx("span", { className: "shrink-0 w-5 h-5 rounded flex items-center justify-center", style: { backgroundColor: bgColor }, children: /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("rect", { x: "1.5", y: "1.5", width: "9", height: "9", rx: "1.5", stroke: iconColor, strokeWidth: "1.2" }) }) });
|
|
1833
1844
|
}
|
|
1834
1845
|
var CHANNEL = "conversion-editor";
|
|
1846
|
+
var IFRAME_LOAD_GUARD_MS = 13e4;
|
|
1835
1847
|
function IframeCanvas({ url, password, proxyBaseUrl = "", onBridgeReady, onPong }) {
|
|
1836
1848
|
const iframeElRef = useRef(null);
|
|
1849
|
+
const loadGuardRef = useRef(null);
|
|
1837
1850
|
const setSelectedElement = useMutationsStore((s) => s.setSelectedElement);
|
|
1838
1851
|
const addMutationToActive = useVariationsStore((s) => s.addMutationToActive);
|
|
1839
1852
|
const [loading, setLoading] = useState(false);
|
|
@@ -1876,8 +1889,24 @@ function IframeCanvas({ url, password, proxyBaseUrl = "", onBridgeReady, onPong
|
|
|
1876
1889
|
return () => window.removeEventListener("message", handleMessage);
|
|
1877
1890
|
}, [handleMessage]);
|
|
1878
1891
|
useEffect(() => {
|
|
1879
|
-
if (url)
|
|
1892
|
+
if (!url) return;
|
|
1893
|
+
setLoading(true);
|
|
1894
|
+
if (loadGuardRef.current) clearTimeout(loadGuardRef.current);
|
|
1895
|
+
loadGuardRef.current = setTimeout(() => {
|
|
1896
|
+
loadGuardRef.current = null;
|
|
1897
|
+
setLoading(false);
|
|
1898
|
+
}, IFRAME_LOAD_GUARD_MS);
|
|
1899
|
+
return () => {
|
|
1900
|
+
if (loadGuardRef.current) clearTimeout(loadGuardRef.current);
|
|
1901
|
+
};
|
|
1880
1902
|
}, [url]);
|
|
1903
|
+
const clearLoadGuard = useCallback(() => {
|
|
1904
|
+
if (loadGuardRef.current) {
|
|
1905
|
+
clearTimeout(loadGuardRef.current);
|
|
1906
|
+
loadGuardRef.current = null;
|
|
1907
|
+
}
|
|
1908
|
+
setLoading(false);
|
|
1909
|
+
}, []);
|
|
1881
1910
|
let resolvedUrl;
|
|
1882
1911
|
if (url.toLowerCase() === "test") {
|
|
1883
1912
|
resolvedUrl = "/test";
|
|
@@ -1908,7 +1937,8 @@ function IframeCanvas({ url, password, proxyBaseUrl = "", onBridgeReady, onPong
|
|
|
1908
1937
|
src: resolvedUrl,
|
|
1909
1938
|
className: "w-full h-full border-0",
|
|
1910
1939
|
sandbox: "allow-scripts allow-same-origin allow-forms allow-popups",
|
|
1911
|
-
onLoad:
|
|
1940
|
+
onLoad: clearLoadGuard,
|
|
1941
|
+
onError: clearLoadGuard
|
|
1912
1942
|
}
|
|
1913
1943
|
)
|
|
1914
1944
|
] });
|
|
@@ -4059,6 +4089,9 @@ function sendToPlatform(type, payload) {
|
|
|
4059
4089
|
data: { channel: PLATFORM_CHANNEL, type, payload }
|
|
4060
4090
|
}));
|
|
4061
4091
|
}
|
|
4092
|
+
function experimentIframeContextKey(exp) {
|
|
4093
|
+
return [exp.experimentId ?? "", exp.pageUrl ?? "", exp.editorPassword ?? ""].join("");
|
|
4094
|
+
}
|
|
4062
4095
|
function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
4063
4096
|
const [url, setUrl] = useState("");
|
|
4064
4097
|
const [password, setPassword] = useState("");
|
|
@@ -4071,7 +4104,8 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4071
4104
|
);
|
|
4072
4105
|
const [experimentData, setExperimentData] = useState(null);
|
|
4073
4106
|
const experimentDataRef = useRef(null);
|
|
4074
|
-
const
|
|
4107
|
+
const lastAppliedRunKeyRef = useRef("");
|
|
4108
|
+
const iframeContextKeyRef = useRef("");
|
|
4075
4109
|
useEffect(() => {
|
|
4076
4110
|
experimentDataRef.current = experimentData;
|
|
4077
4111
|
}, [experimentData]);
|
|
@@ -4089,7 +4123,6 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4089
4123
|
const clearAll = useVariationsStore((s) => s.clearAll);
|
|
4090
4124
|
const setSelectedElement = useMutationsStore((s) => s.setSelectedElement);
|
|
4091
4125
|
const selectedElement = useMutationsStore((s) => s.selectedElement);
|
|
4092
|
-
const activeVariationId = useVariationsStore((s) => s.activeVariationId);
|
|
4093
4126
|
const variations = useVariationsStore((s) => s.variations);
|
|
4094
4127
|
const removeLastMutationFromActive = useVariationsStore((s) => s.removeLastMutationFromActive);
|
|
4095
4128
|
const removeMutationsForSelector = useVariationsStore((s) => s.removeMutationsForSelector);
|
|
@@ -4140,7 +4173,6 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4140
4173
|
}, [embedded, experimentData, toast, buildPlatformVariations]);
|
|
4141
4174
|
const pingIntervalRef = useRef(null);
|
|
4142
4175
|
const pongTimeoutRef = useRef(null);
|
|
4143
|
-
const syncDebounceRef = useRef(null);
|
|
4144
4176
|
const stopHeartbeat = useCallback(() => {
|
|
4145
4177
|
if (pingIntervalRef.current) {
|
|
4146
4178
|
clearInterval(pingIntervalRef.current);
|
|
@@ -4174,15 +4206,24 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4174
4206
|
},
|
|
4175
4207
|
[stopHeartbeat, clearAll, setSelectedElement]
|
|
4176
4208
|
);
|
|
4209
|
+
useEffect(() => {
|
|
4210
|
+
if (initialExperiment) return;
|
|
4211
|
+
let cancelled = false;
|
|
4212
|
+
void (async () => {
|
|
4213
|
+
await hydrateVariationsFromStorage(void 0);
|
|
4214
|
+
if (cancelled) return;
|
|
4215
|
+
})();
|
|
4216
|
+
return () => {
|
|
4217
|
+
cancelled = true;
|
|
4218
|
+
};
|
|
4219
|
+
}, [initialExperiment]);
|
|
4177
4220
|
useEffect(() => {
|
|
4178
4221
|
if (!initialExperiment) return;
|
|
4179
|
-
const
|
|
4180
|
-
if (
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
handleLoadUrl(initialExperiment.pageUrl, initialExperiment.editorPassword || void 0);
|
|
4185
|
-
}
|
|
4222
|
+
const runKey = JSON.stringify(initialExperiment);
|
|
4223
|
+
if (lastAppliedRunKeyRef.current === runKey) return;
|
|
4224
|
+
const ctx = experimentIframeContextKey(initialExperiment);
|
|
4225
|
+
const sameIframeContext = iframeContextKeyRef.current !== "" && iframeContextKeyRef.current === ctx;
|
|
4226
|
+
let cancelled = false;
|
|
4186
4227
|
const sourceVariations = Array.isArray(initialExperiment.variations) ? initialExperiment.variations : [];
|
|
4187
4228
|
const editorVariations = sourceVariations.map((v, idx) => {
|
|
4188
4229
|
let mutations = [];
|
|
@@ -4202,7 +4243,31 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4202
4243
|
mutations
|
|
4203
4244
|
};
|
|
4204
4245
|
});
|
|
4205
|
-
|
|
4246
|
+
setExperimentData(initialExperiment);
|
|
4247
|
+
if (sameIframeContext) {
|
|
4248
|
+
void (async () => {
|
|
4249
|
+
await hydrateVariationsFromStorage(initialExperiment.experimentId);
|
|
4250
|
+
if (cancelled) return;
|
|
4251
|
+
loadExperimentVariations(editorVariations);
|
|
4252
|
+
lastAppliedRunKeyRef.current = runKey;
|
|
4253
|
+
})();
|
|
4254
|
+
return () => {
|
|
4255
|
+
cancelled = true;
|
|
4256
|
+
};
|
|
4257
|
+
}
|
|
4258
|
+
if (initialExperiment.pageUrl) {
|
|
4259
|
+
handleLoadUrl(initialExperiment.pageUrl, initialExperiment.editorPassword || void 0);
|
|
4260
|
+
iframeContextKeyRef.current = ctx;
|
|
4261
|
+
}
|
|
4262
|
+
void (async () => {
|
|
4263
|
+
await hydrateVariationsFromStorage(initialExperiment.experimentId);
|
|
4264
|
+
if (cancelled) return;
|
|
4265
|
+
loadExperimentVariations(editorVariations);
|
|
4266
|
+
lastAppliedRunKeyRef.current = runKey;
|
|
4267
|
+
})();
|
|
4268
|
+
return () => {
|
|
4269
|
+
cancelled = true;
|
|
4270
|
+
};
|
|
4206
4271
|
}, [initialExperiment, handleLoadUrl, loadExperimentVariations]);
|
|
4207
4272
|
const handleBridgeReady = useCallback(() => {
|
|
4208
4273
|
setConnectionStatus("connected");
|
|
@@ -4232,33 +4297,44 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4232
4297
|
switch (msg.type) {
|
|
4233
4298
|
case "load-experiment": {
|
|
4234
4299
|
const data = msg.payload;
|
|
4235
|
-
setExperimentData(data);
|
|
4236
4300
|
experimentDataRef.current = data;
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
try {
|
|
4244
|
-
const chainSets = JSON.parse(v.changesets || "[]");
|
|
4245
|
-
if (Array.isArray(chainSets) && chainSets.length > 0) {
|
|
4246
|
-
mutations = convertChainSetsToMutations(chainSets);
|
|
4247
|
-
}
|
|
4248
|
-
} catch {
|
|
4301
|
+
const editorVariations = Array.isArray(data.variations) ? data.variations.map((v, idx) => {
|
|
4302
|
+
let mutations = [];
|
|
4303
|
+
try {
|
|
4304
|
+
const chainSets = JSON.parse(v.changesets || "[]");
|
|
4305
|
+
if (Array.isArray(chainSets) && chainSets.length > 0) {
|
|
4306
|
+
mutations = convertChainSetsToMutations(chainSets);
|
|
4249
4307
|
}
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4308
|
+
} catch {
|
|
4309
|
+
}
|
|
4310
|
+
return {
|
|
4311
|
+
id: v._id || `v_${idx}`,
|
|
4312
|
+
platformIid: v.iid,
|
|
4313
|
+
name: v.name,
|
|
4314
|
+
isControl: v.baseline || false,
|
|
4315
|
+
traffic_allocation: v.traffic_allocation,
|
|
4316
|
+
mutations
|
|
4317
|
+
};
|
|
4318
|
+
}) : [];
|
|
4319
|
+
setExperimentData(data);
|
|
4320
|
+
const ctx = experimentIframeContextKey(data);
|
|
4321
|
+
let reloadedIframe = false;
|
|
4322
|
+
if (data.pageUrl && !data.skipUrlReload) {
|
|
4323
|
+
if (iframeContextKeyRef.current !== ctx) {
|
|
4324
|
+
handleLoadUrl(data.pageUrl, data.editorPassword || void 0);
|
|
4325
|
+
iframeContextKeyRef.current = ctx;
|
|
4326
|
+
reloadedIframe = true;
|
|
4327
|
+
}
|
|
4260
4328
|
}
|
|
4261
|
-
|
|
4329
|
+
void (async () => {
|
|
4330
|
+
await hydrateVariationsFromStorage(data.experimentId);
|
|
4331
|
+
if (Array.isArray(data.variations)) {
|
|
4332
|
+
loadExperimentVariations(editorVariations);
|
|
4333
|
+
}
|
|
4334
|
+
if (reloadedIframe) {
|
|
4335
|
+
toast(`Loaded experiment: ${data.name || "Untitled"}`, "info");
|
|
4336
|
+
}
|
|
4337
|
+
})();
|
|
4262
4338
|
break;
|
|
4263
4339
|
}
|
|
4264
4340
|
case "request-save": {
|
|
@@ -4332,27 +4408,6 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
|
|
|
4332
4408
|
useEffect(() => {
|
|
4333
4409
|
sendToBridge({ type: "setMode", mode: interactionMode });
|
|
4334
4410
|
}, [interactionMode]);
|
|
4335
|
-
useEffect(() => {
|
|
4336
|
-
if (syncDebounceRef.current) {
|
|
4337
|
-
clearTimeout(syncDebounceRef.current);
|
|
4338
|
-
syncDebounceRef.current = null;
|
|
4339
|
-
}
|
|
4340
|
-
if (connectionStatus !== "connected") return;
|
|
4341
|
-
syncDebounceRef.current = setTimeout(() => {
|
|
4342
|
-
const currentActiveMutations = useVariationsStore.getState().getActiveMutations();
|
|
4343
|
-
sendToBridge({ type: "clearAllMutations" });
|
|
4344
|
-
if (currentActiveMutations.length > 0) {
|
|
4345
|
-
sendToBridge({ type: "applyMutationBatch", mutations: currentActiveMutations });
|
|
4346
|
-
}
|
|
4347
|
-
syncDebounceRef.current = null;
|
|
4348
|
-
}, 350);
|
|
4349
|
-
return () => {
|
|
4350
|
-
if (syncDebounceRef.current) {
|
|
4351
|
-
clearTimeout(syncDebounceRef.current);
|
|
4352
|
-
syncDebounceRef.current = null;
|
|
4353
|
-
}
|
|
4354
|
-
};
|
|
4355
|
-
}, [connectionStatus, activeVariationId, experimentData?.experimentId]);
|
|
4356
4411
|
useEffect(() => {
|
|
4357
4412
|
const handler = (e) => {
|
|
4358
4413
|
const meta = e.metaKey || e.ctrlKey;
|
|
@@ -4555,7 +4610,7 @@ function PlatformVisualEditor({
|
|
|
4555
4610
|
renderLoading,
|
|
4556
4611
|
renderError
|
|
4557
4612
|
}) {
|
|
4558
|
-
console.log(experiment);
|
|
4613
|
+
console.log("experiment", experiment);
|
|
4559
4614
|
const [editorReady, setEditorReady] = useState(false);
|
|
4560
4615
|
const [dirty, setDirty] = useState(false);
|
|
4561
4616
|
const dirtyRef = useRef(false);
|
|
@@ -4769,7 +4824,6 @@ function PlatformVisualEditorV2({
|
|
|
4769
4824
|
renderLoading,
|
|
4770
4825
|
renderError
|
|
4771
4826
|
}) {
|
|
4772
|
-
console.log(experiment);
|
|
4773
4827
|
const iframeRef = useRef(null);
|
|
4774
4828
|
const [editorReady, setEditorReady] = useState(false);
|
|
4775
4829
|
const [dirty, setDirty] = useState(false);
|
|
@@ -4959,4 +5013,4 @@ function PlatformVisualEditorV2({
|
|
|
4959
5013
|
] });
|
|
4960
5014
|
}
|
|
4961
5015
|
|
|
4962
|
-
export { EditorShell, PlatformVisualEditor, PlatformVisualEditorV2, ToastProvider, useToast };
|
|
5016
|
+
export { EditorShell, PlatformVisualEditor, PlatformVisualEditorV2, ToastProvider, hydrateVariationsFromStorage, useToast, variationsPersistStorageName };
|
package/dist/vite.cjs
CHANGED
|
@@ -1800,7 +1800,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
1800
1800
|
}
|
|
1801
1801
|
}
|
|
1802
1802
|
return async (req, res, next) => {
|
|
1803
|
-
|
|
1803
|
+
let pathname = (req.url || "").split("?")[0];
|
|
1804
1804
|
if (pathname === "/bridge.js") {
|
|
1805
1805
|
res.removeHeader("X-Frame-Options");
|
|
1806
1806
|
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
@@ -1937,8 +1937,8 @@ function createVisualEditorMiddleware(options) {
|
|
|
1937
1937
|
return;
|
|
1938
1938
|
}
|
|
1939
1939
|
if (pathname.startsWith("/api/proxy")) {
|
|
1940
|
-
|
|
1941
|
-
req.url
|
|
1940
|
+
req.url = (req.url || "").replace("/api/proxy", "/api/conversion-proxy");
|
|
1941
|
+
pathname = (req.url || "").split("?")[0];
|
|
1942
1942
|
}
|
|
1943
1943
|
if (pathname.startsWith("/api/conversion-proxy")) {
|
|
1944
1944
|
try {
|
|
@@ -2001,17 +2001,39 @@ function createVisualEditorMiddleware(options) {
|
|
|
2001
2001
|
}
|
|
2002
2002
|
if (chunks.length > 0) requestBody = Buffer.concat(chunks);
|
|
2003
2003
|
}
|
|
2004
|
-
const
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2004
|
+
const upstreamTimeoutMs = 12e4;
|
|
2005
|
+
const ac = new AbortController();
|
|
2006
|
+
const timeoutId = setTimeout(() => ac.abort(), upstreamTimeoutMs);
|
|
2007
|
+
let upstream;
|
|
2008
|
+
try {
|
|
2009
|
+
upstream = await fetch(targetUrl, {
|
|
2010
|
+
method,
|
|
2011
|
+
headers: fetchHeaders,
|
|
2012
|
+
body: requestBody ? Buffer.from(requestBody) : null,
|
|
2013
|
+
redirect: "follow",
|
|
2014
|
+
signal: ac.signal
|
|
2015
|
+
});
|
|
2016
|
+
} catch (fetchErr) {
|
|
2017
|
+
clearTimeout(timeoutId);
|
|
2018
|
+
const aborted = fetchErr?.name === "AbortError";
|
|
2019
|
+
res.statusCode = aborted ? 504 : 502;
|
|
2020
|
+
res.setHeader("Content-Type", "application/json");
|
|
2021
|
+
res.end(
|
|
2022
|
+
JSON.stringify({
|
|
2023
|
+
error: aborted ? `Upstream request timed out after ${upstreamTimeoutMs / 1e3}s` : fetchErr?.message || "Upstream fetch failed"
|
|
2024
|
+
})
|
|
2025
|
+
);
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
clearTimeout(timeoutId);
|
|
2010
2029
|
const responseContentType = upstream.headers.get("content-type") || "";
|
|
2011
2030
|
const isHtmlResponse = responseContentType.includes("text/html");
|
|
2031
|
+
const secFetchMode = (req.headers?.["sec-fetch-mode"] || "").toLowerCase();
|
|
2012
2032
|
const secFetchDest = (req.headers?.["sec-fetch-dest"] || "").toLowerCase();
|
|
2013
|
-
const
|
|
2014
|
-
|
|
2033
|
+
const isLikelyDocumentNavigation = secFetchMode === "navigate" || secFetchDest === "iframe" || secFetchDest === "document" || secFetchDest === "nested-document" || secFetchDest === "frame";
|
|
2034
|
+
const isLikelyFetchOrXHR = secFetchDest === "empty" && (secFetchMode === "cors" || secFetchMode === "same-origin" || secFetchMode === "no-cors");
|
|
2035
|
+
const shouldInjectHtmlBridge = isHtmlResponse && (isLikelyDocumentNavigation || !isLikelyFetchOrXHR);
|
|
2036
|
+
if (!isHtmlResponse || !shouldInjectHtmlBridge) {
|
|
2015
2037
|
const binary = Buffer.from(await upstream.arrayBuffer());
|
|
2016
2038
|
res.statusCode = upstream.status;
|
|
2017
2039
|
if (responseContentType) {
|
|
@@ -2044,7 +2066,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
2044
2066
|
html = html.replace("</head>", `${popupHideCss}
|
|
2045
2067
|
</head>`);
|
|
2046
2068
|
}
|
|
2047
|
-
const runtimeProxyScript = `<script>(function(){try{var TARGET_ORIGIN=${JSON.stringify(origin)};var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};var EMPTY_JSON_DATA="data:application/json;charset=utf-8,%7B%7D";function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#")||raw.startsWith("http")||raw.startsWith("//");}function toAbsoluteOriginUrl(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;var abs=new URL(raw,base);if(abs.origin!==TARGET_ORIGIN)return raw;return abs.toString();}catch(_){return raw;}}function resolveUrl(s){try{return new URL(s,window.location.href);}catch(_){return null;}}function isNestedMalformedProxy(u){if(!u)return false;var p=u.pathname||"";if(p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0)return false;return p.indexOf("api/conversion-proxy")!==-1;}function skipNestedProxyNetwork(s){var u=typeof s==="string"?resolveUrl(s):null;return u&&isNestedMalformedProxy(u);}function emptyJsonFetchResponse(){return Promise.resolve(new Response("{}",{status:200,headers:{"Content-Type":"application/json; charset=utf-8"}}));}if(window.fetch){var _fetch=window.fetch.bind(window);window.fetch=function(input,init){try{var rawUrl=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(rawUrl&&skipNestedProxyNetwork(rawUrl))return emptyJsonFetchResponse();if(typeof input==="string"){input=toAbsoluteOriginUrl(input);}else if(input&&input.url){var next=toAbsoluteOriginUrl(input.url);if(next!==input.url){input=new Request(next,input);}}var after=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(after&&skipNestedProxyNetwork(after))return emptyJsonFetchResponse();}catch(_){}return _fetch(input,init);};}if(window.XMLHttpRequest&&window.XMLHttpRequest.prototype&&window.XMLHttpRequest.prototype.open){var _open=window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open=function(method,url){try{var u=resolveUrl(String(url));if(u&&isNestedMalformedProxy(u)){arguments[1]=EMPTY_JSON_DATA;}else{arguments[1]=toAbsoluteOriginUrl(url);}}catch(_){}return _open.apply(this,arguments);};}if(window.navigator&&window.navigator.serviceWorker&&typeof window.navigator.serviceWorker.register==="function"){window.navigator.serviceWorker.register=function(){return Promise.resolve({scope:"disabled-in-editor-proxy"});};}}catch(_){}})();</script>`;
|
|
2069
|
+
const runtimeProxyScript = `<script>(function(){try{var TARGET_ORIGIN=${JSON.stringify(origin)};var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};var EMPTY_JSON_DATA="data:application/json;charset=utf-8,%7B%7D";function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#")||raw.startsWith("http")||raw.startsWith("//");}function toAbsoluteOriginUrl(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;var abs=new URL(raw,base);if(abs.origin!==TARGET_ORIGIN)return raw;return abs.toString();}catch(_){return raw;}}function resolveUrl(s){try{return new URL(s,window.location.href);}catch(_){return null;}}function isNestedMalformedProxy(u){if(!u)return false;var p=u.pathname||"";if(p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0)return false;return p.indexOf("api/conversion-proxy")!==-1;}function skipNestedProxyNetwork(s){var u=typeof s==="string"?resolveUrl(s):null;return u&&isNestedMalformedProxy(u);}function emptyJsonFetchResponse(){return Promise.resolve(new Response("{}",{status:200,headers:{"Content-Type":"application/json; charset=utf-8"}}));}if(window.fetch){var _fetch=window.fetch.bind(window);window.fetch=function(input,init){try{var rawUrl=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(rawUrl&&skipNestedProxyNetwork(rawUrl))return emptyJsonFetchResponse();if(typeof input==="string"){input=toAbsoluteOriginUrl(input);}else if(input&&input.url){var next=toAbsoluteOriginUrl(input.url);if(next!==input.url){input=new Request(next,input);}}var after=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(after&&skipNestedProxyNetwork(after))return emptyJsonFetchResponse();}catch(_){}return _fetch(input,init);};}if(window.XMLHttpRequest&&window.XMLHttpRequest.prototype&&window.XMLHttpRequest.prototype.open){var _open=window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open=function(method,url){try{var u=resolveUrl(String(url));if(u&&isNestedMalformedProxy(u)){arguments[1]=EMPTY_JSON_DATA;}else{arguments[1]=toAbsoluteOriginUrl(url);}}catch(_){}return _open.apply(this,arguments);};}if(window.navigator&&typeof window.navigator.sendBeacon==="function"){var _beacon=window.navigator.sendBeacon.bind(window.navigator);window.navigator.sendBeacon=function(url,data){try{if(skipNestedProxyNetwork(String(url)))return true;}catch(_){}return _beacon(url,data);};}if(window.navigator&&window.navigator.serviceWorker&&typeof window.navigator.serviceWorker.register==="function"){window.navigator.serviceWorker.register=function(){return Promise.resolve({scope:"disabled-in-editor-proxy"});};}}catch(_){}})();</script>`;
|
|
2048
2070
|
if (html.includes("</head>")) {
|
|
2049
2071
|
html = html.replace("</head>", `${runtimeProxyScript}
|
|
2050
2072
|
</head>`);
|
package/dist/vite.js
CHANGED
|
@@ -1792,7 +1792,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
1792
1792
|
}
|
|
1793
1793
|
}
|
|
1794
1794
|
return async (req, res, next) => {
|
|
1795
|
-
|
|
1795
|
+
let pathname = (req.url || "").split("?")[0];
|
|
1796
1796
|
if (pathname === "/bridge.js") {
|
|
1797
1797
|
res.removeHeader("X-Frame-Options");
|
|
1798
1798
|
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
@@ -1929,8 +1929,8 @@ function createVisualEditorMiddleware(options) {
|
|
|
1929
1929
|
return;
|
|
1930
1930
|
}
|
|
1931
1931
|
if (pathname.startsWith("/api/proxy")) {
|
|
1932
|
-
|
|
1933
|
-
req.url
|
|
1932
|
+
req.url = (req.url || "").replace("/api/proxy", "/api/conversion-proxy");
|
|
1933
|
+
pathname = (req.url || "").split("?")[0];
|
|
1934
1934
|
}
|
|
1935
1935
|
if (pathname.startsWith("/api/conversion-proxy")) {
|
|
1936
1936
|
try {
|
|
@@ -1993,17 +1993,39 @@ function createVisualEditorMiddleware(options) {
|
|
|
1993
1993
|
}
|
|
1994
1994
|
if (chunks.length > 0) requestBody = Buffer.concat(chunks);
|
|
1995
1995
|
}
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
1996
|
+
const upstreamTimeoutMs = 12e4;
|
|
1997
|
+
const ac = new AbortController();
|
|
1998
|
+
const timeoutId = setTimeout(() => ac.abort(), upstreamTimeoutMs);
|
|
1999
|
+
let upstream;
|
|
2000
|
+
try {
|
|
2001
|
+
upstream = await fetch(targetUrl, {
|
|
2002
|
+
method,
|
|
2003
|
+
headers: fetchHeaders,
|
|
2004
|
+
body: requestBody ? Buffer.from(requestBody) : null,
|
|
2005
|
+
redirect: "follow",
|
|
2006
|
+
signal: ac.signal
|
|
2007
|
+
});
|
|
2008
|
+
} catch (fetchErr) {
|
|
2009
|
+
clearTimeout(timeoutId);
|
|
2010
|
+
const aborted = fetchErr?.name === "AbortError";
|
|
2011
|
+
res.statusCode = aborted ? 504 : 502;
|
|
2012
|
+
res.setHeader("Content-Type", "application/json");
|
|
2013
|
+
res.end(
|
|
2014
|
+
JSON.stringify({
|
|
2015
|
+
error: aborted ? `Upstream request timed out after ${upstreamTimeoutMs / 1e3}s` : fetchErr?.message || "Upstream fetch failed"
|
|
2016
|
+
})
|
|
2017
|
+
);
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
clearTimeout(timeoutId);
|
|
2002
2021
|
const responseContentType = upstream.headers.get("content-type") || "";
|
|
2003
2022
|
const isHtmlResponse = responseContentType.includes("text/html");
|
|
2023
|
+
const secFetchMode = (req.headers?.["sec-fetch-mode"] || "").toLowerCase();
|
|
2004
2024
|
const secFetchDest = (req.headers?.["sec-fetch-dest"] || "").toLowerCase();
|
|
2005
|
-
const
|
|
2006
|
-
|
|
2025
|
+
const isLikelyDocumentNavigation = secFetchMode === "navigate" || secFetchDest === "iframe" || secFetchDest === "document" || secFetchDest === "nested-document" || secFetchDest === "frame";
|
|
2026
|
+
const isLikelyFetchOrXHR = secFetchDest === "empty" && (secFetchMode === "cors" || secFetchMode === "same-origin" || secFetchMode === "no-cors");
|
|
2027
|
+
const shouldInjectHtmlBridge = isHtmlResponse && (isLikelyDocumentNavigation || !isLikelyFetchOrXHR);
|
|
2028
|
+
if (!isHtmlResponse || !shouldInjectHtmlBridge) {
|
|
2007
2029
|
const binary = Buffer.from(await upstream.arrayBuffer());
|
|
2008
2030
|
res.statusCode = upstream.status;
|
|
2009
2031
|
if (responseContentType) {
|
|
@@ -2036,7 +2058,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
2036
2058
|
html = html.replace("</head>", `${popupHideCss}
|
|
2037
2059
|
</head>`);
|
|
2038
2060
|
}
|
|
2039
|
-
const runtimeProxyScript = `<script>(function(){try{var TARGET_ORIGIN=${JSON.stringify(origin)};var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};var EMPTY_JSON_DATA="data:application/json;charset=utf-8,%7B%7D";function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#")||raw.startsWith("http")||raw.startsWith("//");}function toAbsoluteOriginUrl(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;var abs=new URL(raw,base);if(abs.origin!==TARGET_ORIGIN)return raw;return abs.toString();}catch(_){return raw;}}function resolveUrl(s){try{return new URL(s,window.location.href);}catch(_){return null;}}function isNestedMalformedProxy(u){if(!u)return false;var p=u.pathname||"";if(p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0)return false;return p.indexOf("api/conversion-proxy")!==-1;}function skipNestedProxyNetwork(s){var u=typeof s==="string"?resolveUrl(s):null;return u&&isNestedMalformedProxy(u);}function emptyJsonFetchResponse(){return Promise.resolve(new Response("{}",{status:200,headers:{"Content-Type":"application/json; charset=utf-8"}}));}if(window.fetch){var _fetch=window.fetch.bind(window);window.fetch=function(input,init){try{var rawUrl=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(rawUrl&&skipNestedProxyNetwork(rawUrl))return emptyJsonFetchResponse();if(typeof input==="string"){input=toAbsoluteOriginUrl(input);}else if(input&&input.url){var next=toAbsoluteOriginUrl(input.url);if(next!==input.url){input=new Request(next,input);}}var after=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(after&&skipNestedProxyNetwork(after))return emptyJsonFetchResponse();}catch(_){}return _fetch(input,init);};}if(window.XMLHttpRequest&&window.XMLHttpRequest.prototype&&window.XMLHttpRequest.prototype.open){var _open=window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open=function(method,url){try{var u=resolveUrl(String(url));if(u&&isNestedMalformedProxy(u)){arguments[1]=EMPTY_JSON_DATA;}else{arguments[1]=toAbsoluteOriginUrl(url);}}catch(_){}return _open.apply(this,arguments);};}if(window.navigator&&window.navigator.serviceWorker&&typeof window.navigator.serviceWorker.register==="function"){window.navigator.serviceWorker.register=function(){return Promise.resolve({scope:"disabled-in-editor-proxy"});};}}catch(_){}})();</script>`;
|
|
2061
|
+
const runtimeProxyScript = `<script>(function(){try{var TARGET_ORIGIN=${JSON.stringify(origin)};var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};var EMPTY_JSON_DATA="data:application/json;charset=utf-8,%7B%7D";function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#")||raw.startsWith("http")||raw.startsWith("//");}function toAbsoluteOriginUrl(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")?TARGET_ORIGIN:TARGET_PAGE_URL;var abs=new URL(raw,base);if(abs.origin!==TARGET_ORIGIN)return raw;return abs.toString();}catch(_){return raw;}}function resolveUrl(s){try{return new URL(s,window.location.href);}catch(_){return null;}}function isNestedMalformedProxy(u){if(!u)return false;var p=u.pathname||"";if(p==="/api/conversion-proxy"||p.indexOf("/api/conversion-proxy/")===0)return false;return p.indexOf("api/conversion-proxy")!==-1;}function skipNestedProxyNetwork(s){var u=typeof s==="string"?resolveUrl(s):null;return u&&isNestedMalformedProxy(u);}function emptyJsonFetchResponse(){return Promise.resolve(new Response("{}",{status:200,headers:{"Content-Type":"application/json; charset=utf-8"}}));}if(window.fetch){var _fetch=window.fetch.bind(window);window.fetch=function(input,init){try{var rawUrl=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(rawUrl&&skipNestedProxyNetwork(rawUrl))return emptyJsonFetchResponse();if(typeof input==="string"){input=toAbsoluteOriginUrl(input);}else if(input&&input.url){var next=toAbsoluteOriginUrl(input.url);if(next!==input.url){input=new Request(next,input);}}var after=typeof input==="string"?input:(input&&input.url?String(input.url):"");if(after&&skipNestedProxyNetwork(after))return emptyJsonFetchResponse();}catch(_){}return _fetch(input,init);};}if(window.XMLHttpRequest&&window.XMLHttpRequest.prototype&&window.XMLHttpRequest.prototype.open){var _open=window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open=function(method,url){try{var u=resolveUrl(String(url));if(u&&isNestedMalformedProxy(u)){arguments[1]=EMPTY_JSON_DATA;}else{arguments[1]=toAbsoluteOriginUrl(url);}}catch(_){}return _open.apply(this,arguments);};}if(window.navigator&&typeof window.navigator.sendBeacon==="function"){var _beacon=window.navigator.sendBeacon.bind(window.navigator);window.navigator.sendBeacon=function(url,data){try{if(skipNestedProxyNetwork(String(url)))return true;}catch(_){}return _beacon(url,data);};}if(window.navigator&&window.navigator.serviceWorker&&typeof window.navigator.serviceWorker.register==="function"){window.navigator.serviceWorker.register=function(){return Promise.resolve({scope:"disabled-in-editor-proxy"});};}}catch(_){}})();</script>`;
|
|
2040
2062
|
if (html.includes("</head>")) {
|
|
2041
2063
|
html = html.replace("</head>", `${runtimeProxyScript}
|
|
2042
2064
|
</head>`);
|