UNPKG

@repugraf/cross-domain-storage

Version:

Enables shared cross domain localStorage and sessionStorage

140 lines (139 loc) 4.89 kB
import { createMessage, error, debugLog } from "./shared.js"; /** * Creates client instance * * Call `connect` to start communications with the server * * ```js * const client = getClient({ * domain: "https://www.example.com" * }); * * await client.connect(); * * await client.set("key", "val"); * * await client.get("key"); * ``` */ const getClient = (config) => { var _a, _b, _c, _d, _e; const timeout = (_a = config.timeout) !== null && _a !== void 0 ? _a : 10000; const debug = (_b = config.debug) !== null && _b !== void 0 ? _b : false; const target = (_c = config.target) !== null && _c !== void 0 ? _c : document.body; const duplicate = (_d = config.duplicate) !== null && _d !== void 0 ? _d : false; const fallback = (_e = config.fallback) !== null && _e !== void 0 ? _e : false; const iframe = document.createElement("iframe"); iframe.setAttribute("src", config.domain); iframe.style.width = "0"; iframe.style.height = "0"; iframe.style.display = "none"; iframe.id = "cross-domain-storage"; let isConnected = false; /** * Connects to specified domain * * Under the hood will append invisible iframe to `document.body` with `src` of specified domain * * @param _timeout Timeout after which the method will reject */ const connect = (_timeout = timeout) => { return new Promise((resolve, reject) => { const timeoutID = setTimeout(() => reject(false), _timeout); iframe.onload = () => { clearTimeout(timeoutID); isConnected = true; debugLog(debug, `Connected to server (${config.domain})`); resolve(); }; iframe.onerror = e => { clearTimeout(timeoutID); error(debug, `Failed to connect to server (${config.domain})`, e); reject(e); }; target.appendChild(iframe); }).catch(e => error(debug, e)); }; /** * Disconnect from specified domain * * Under the hood will remove invisible iframe from `document.body` */ const disconnect = () => { target.removeChild(iframe); isConnected = false; }; const set = (key, value, storageType) => { return handleOperation({ method: "set", key, value, storageType }); }; const get = (key, storageType) => { return handleOperation({ method: "get", key, storageType }); }; const remove = (key, storageType) => { return handleOperation({ method: "remove", key, storageType }); }; const handleOperation = async (props) => { var _a; const message = createMessage({ method: props.method, key: props.key, value: props.value, storageType: (_a = props.storageType) !== null && _a !== void 0 ? _a : "localStorage" }); try { const { result } = await new Promise((resolve, reject) => { const clearListener = () => window.removeEventListener("message", handler); const timeoutID = setTimeout(() => { clearListener(); reject(new Error(`Timeout (${timeout})`)); }, timeout); const handler = (e) => { const response = e.data; if (!response || response.source !== "cross-domain-storage" || message.id !== response.id) return; clearTimeout(timeoutID); return response.isError ? reject(new Error(response.result)) : resolve(response); }; window.addEventListener("message", handler); if (!isConnected || !iframe.contentWindow) { clearListener(); return reject(new Error("Not connected")); } iframe.contentWindow.postMessage(message, config.domain); if (duplicate) window[message.storageType][`${message.method}Item`](message.key, message.value); }); debugLog(debug, `[Client] Action executed: ${message.storageType}.${message.method}Item(${message.key}${message.value ? `, ${message.value}` : ""})`); return result; } catch (err) { error(debug, err); if (fallback) return window[message.storageType][`${message.method}Item`](message.key, message.value); } }; return { connect, disconnect, set, get, remove, get isConnected() { return isConnected; } }; }; export { getClient };