inital commit
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
(function () {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const token = params.get("token") || "";
|
||||
const origin = params.get("origin") || "";
|
||||
let seq = 0;
|
||||
const pending = new Map();
|
||||
|
||||
function base64ToArrayBuffer(base64) {
|
||||
const binary = atob(base64 || "");
|
||||
const len = binary.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
function makeResponse(payload) {
|
||||
const headers = new Map(Object.entries(payload.headers || {}));
|
||||
const bodyText = payload.bodyText || "";
|
||||
const bodyBase64 = payload.bodyBase64 || "";
|
||||
const response = {
|
||||
ok: Boolean(payload.ok),
|
||||
status: payload.status || 0,
|
||||
statusText: payload.statusText || "",
|
||||
headers,
|
||||
text: async () => bodyText,
|
||||
json: async () => JSON.parse(bodyText || "{}"),
|
||||
arrayBuffer: async () => base64ToArrayBuffer(bodyBase64),
|
||||
blob: async () => new Blob([base64ToArrayBuffer(bodyBase64)], {
|
||||
type: payload.contentType || "application/octet-stream",
|
||||
}),
|
||||
};
|
||||
return response;
|
||||
}
|
||||
|
||||
async function bridgeFetch(input, init = {}) {
|
||||
const requestUrl = typeof input === "string" ? input : input.url;
|
||||
const id = `req_${Date.now()}_${seq++}`;
|
||||
|
||||
const request = {
|
||||
url: requestUrl,
|
||||
method: init.method || "GET",
|
||||
headers: init.headers || {},
|
||||
body: init.body || undefined,
|
||||
};
|
||||
|
||||
const responsePromise = new Promise((resolve, reject) => {
|
||||
pending.set(id, { resolve, reject });
|
||||
window.parent.postMessage(
|
||||
{ type: "plugin-fetch", id, token, request },
|
||||
origin || "*",
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
if (!pending.has(id)) return;
|
||||
pending.delete(id);
|
||||
reject(new Error("plugin fetch timeout"));
|
||||
}, 30_000);
|
||||
});
|
||||
|
||||
const payload = await responsePromise;
|
||||
if (payload.error) {
|
||||
throw new Error(payload.message || payload.error);
|
||||
}
|
||||
return makeResponse(payload);
|
||||
}
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
const data = event.data;
|
||||
if (!data || data.type !== "plugin-fetch-response") return;
|
||||
const waiter = pending.get(data.id);
|
||||
if (!waiter) return;
|
||||
pending.delete(data.id);
|
||||
waiter.resolve(data);
|
||||
});
|
||||
|
||||
window.fetch = bridgeFetch;
|
||||
|
||||
let wsSeq = 0;
|
||||
const wsWaiters = new Map();
|
||||
const wsHandlers = new Map();
|
||||
|
||||
function createPluginWebSocket(url) {
|
||||
const id = `pws_${Date.now()}_${wsSeq++}`;
|
||||
const handlers = { onopen: null, onmessage: null, onclose: null, onerror: null };
|
||||
wsHandlers.set(id, handlers);
|
||||
|
||||
window.parent.postMessage(
|
||||
{ type: "plugin-ws-open", id, token, url },
|
||||
origin || "*",
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
set onopen(fn) {
|
||||
handlers.onopen = fn;
|
||||
},
|
||||
set onmessage(fn) {
|
||||
handlers.onmessage = fn;
|
||||
},
|
||||
set onclose(fn) {
|
||||
handlers.onclose = fn;
|
||||
},
|
||||
set onerror(fn) {
|
||||
handlers.onerror = fn;
|
||||
},
|
||||
send(data) {
|
||||
if (data instanceof ArrayBuffer) {
|
||||
const bytes = new Uint8Array(data);
|
||||
let binary = "";
|
||||
for (let i = 0; i < bytes.length; i += 1) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
window.parent.postMessage(
|
||||
{ type: "plugin-ws-send", id, token, kind: "binary", data: btoa(binary) },
|
||||
origin || "*",
|
||||
);
|
||||
return;
|
||||
}
|
||||
window.parent.postMessage(
|
||||
{ type: "plugin-ws-send", id, token, kind: "text", data: String(data) },
|
||||
origin || "*",
|
||||
);
|
||||
},
|
||||
close() {
|
||||
window.parent.postMessage(
|
||||
{ type: "plugin-ws-close", id, token },
|
||||
origin || "*",
|
||||
);
|
||||
wsHandlers.delete(id);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
const data = event.data;
|
||||
if (!data || !data.type) return;
|
||||
if (!data.id) return;
|
||||
const handlers = wsHandlers.get(data.id);
|
||||
if (!handlers) return;
|
||||
|
||||
if (data.type === "plugin-ws-opened") {
|
||||
handlers.onopen?.();
|
||||
return;
|
||||
}
|
||||
if (data.type === "plugin-ws-closed") {
|
||||
handlers.onclose?.();
|
||||
wsHandlers.delete(data.id);
|
||||
return;
|
||||
}
|
||||
if (data.type === "plugin-ws-error") {
|
||||
handlers.onerror?.(new Error(data.error || "ws_error"));
|
||||
return;
|
||||
}
|
||||
if (data.type === "plugin-ws-message") {
|
||||
if (data.payload?.kind === "binary") {
|
||||
const buffer = base64ToArrayBuffer(data.payload.data);
|
||||
handlers.onmessage?.({ data: buffer });
|
||||
} else {
|
||||
handlers.onmessage?.({ data: data.payload?.data ?? "" });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.createPluginWebSocket = createPluginWebSocket;
|
||||
})();
|
||||
Reference in New Issue
Block a user