UNPKG

@zubridge/electron

Version:

A streamlined state management library for Electron applications using Zustand.

702 lines (688 loc) 21.2 kB
import { useStore } from 'zustand'; import { createStore as createStore$1 } from 'zustand/vanilla'; // Renderer-safe build with polyfilled Node.js modules // ../../node_modules/.pnpm/@wdio+logger@9.18.0/node_modules/@wdio/logger/build/browser.js var LOG_METHODS = ["error", "warn", "info", "debug", "trace", "silent"]; function getLogger(component) { return LOG_METHODS.reduce((acc, cur) => { const prop = cur; if (console[prop]) { acc[prop] = console[prop].bind(console, "".concat(component, ":")); } return acc; }, {}); } getLogger.setLevel = () => { }; getLogger.setLogLevelsConfig = () => { }; getLogger.setMaskingPatterns = () => { }; getLogger.waitForBuffer = () => { }; getLogger.clearLogger = () => { }; // ../../node_modules/.pnpm/ms@3.0.0-canary.202508261828/node_modules/ms/dist/index.js var e = 1e3; var t = e * 60; var n = t * 60; var r = n * 24; var i = r * 7; var a = r * 365.25; var o = a / 12; function s(e2, t2) { if (typeof e2 == `string`) return l(e2); if (typeof e2 == `number`) return p(e2, t2); throw Error(`Value provided to ms() must be a string or number. value=${JSON.stringify(e2)}`); } var c = s; function l(s2) { if (typeof s2 != `string` || s2.length === 0 || s2.length > 100) throw Error(`Value provided to ms.parse() must be a string with length between 1 and 99. value=${JSON.stringify(s2)}`); let c2 = /^(?<value>-?\d*\.?\d+) *(?<unit>milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)?$/i.exec(s2); if (!c2?.groups) return NaN; let { value: l2, unit: u = `ms` } = c2.groups, d2 = parseFloat(l2), f2 = u.toLowerCase(); switch (f2) { case `years`: case `year`: case `yrs`: case `yr`: case `y`: return d2 * a; case `months`: case `month`: case `mo`: return d2 * o; case `weeks`: case `week`: case `w`: return d2 * i; case `days`: case `day`: case `d`: return d2 * r; case `hours`: case `hour`: case `hrs`: case `hr`: case `h`: return d2 * n; case `minutes`: case `minute`: case `mins`: case `min`: case `m`: return d2 * t; case `seconds`: case `second`: case `secs`: case `sec`: case `s`: return d2 * e; case `milliseconds`: case `millisecond`: case `msecs`: case `msec`: case `ms`: return d2; default: throw Error(`Unknown unit "${f2}" provided to ms.parse(). value=${JSON.stringify(s2)}`); } } function d(s2) { let c2 = Math.abs(s2); return c2 >= a ? `${Math.round(s2 / a)}y` : c2 >= o ? `${Math.round(s2 / o)}mo` : c2 >= i ? `${Math.round(s2 / i)}w` : c2 >= r ? `${Math.round(s2 / r)}d` : c2 >= n ? `${Math.round(s2 / n)}h` : c2 >= t ? `${Math.round(s2 / t)}m` : c2 >= e ? `${Math.round(s2 / e)}s` : `${s2}ms`; } function f(s2) { let c2 = Math.abs(s2); return c2 >= a ? m(s2, c2, a, `year`) : c2 >= o ? m(s2, c2, o, `month`) : c2 >= i ? m(s2, c2, i, `week`) : c2 >= r ? m(s2, c2, r, `day`) : c2 >= n ? m(s2, c2, n, `hour`) : c2 >= t ? m(s2, c2, t, `minute`) : c2 >= e ? m(s2, c2, e, `second`) : `${s2} ms`; } function p(e2, t2) { if (typeof e2 != `number` || !Number.isFinite(e2)) throw Error(`Value provided to ms.format() must be of type number.`); return t2?.long ? f(e2) : d(e2); } function m(e2, t2, n2, r2) { let i2 = t2 >= n2 * 1.5; return `${Math.round(e2 / n2)} ${r2}${i2 ? `s` : ``}`; } // ../../node_modules/.pnpm/weald@1.0.6/node_modules/weald/dist/src/common.js function setup(env) { createDebug.debug = createDebug; createDebug.default = createDebug; createDebug.coerce = coerce; createDebug.disable = disable; createDebug.enable = enable; createDebug.enabled = enabled; createDebug.humanize = c; createDebug.destroy = destroy; Object.keys(env).forEach((key) => { createDebug[key] = env[key]; }); createDebug.names = []; createDebug.skips = []; createDebug.formatters = {}; function selectColor(namespace) { let hash = 0; for (let i2 = 0; i2 < namespace.length; i2++) { hash = (hash << 5) - hash + namespace.charCodeAt(i2); hash |= 0; } return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; } createDebug.selectColor = selectColor; function createDebug(namespace) { let prevTime; let enableOverride = null; let namespacesCache; let enabledCache; function debug(...args) { if (!debug.enabled) { return; } const self = debug; const curr = Number(/* @__PURE__ */ new Date()); const ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; args[0] = createDebug.coerce(args[0]); if (typeof args[0] !== "string") { args.unshift("%O"); } let index = 0; args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { if (match === "%%") { return "%"; } index++; const formatter = createDebug.formatters[format]; if (typeof formatter === "function") { const val = args[index]; match = formatter.call(self, val); args.splice(index, 1); index--; } return match; }); createDebug.formatArgs.call(self, args); const logFn = self.log || createDebug.log; logFn.apply(self, args); } debug.namespace = namespace; debug.useColors = createDebug.useColors(); debug.color = createDebug.selectColor(namespace); debug.extend = extend; debug.destroy = createDebug.destroy; Object.defineProperty(debug, "enabled", { enumerable: true, configurable: false, get: () => { if (enableOverride !== null) { return enableOverride; } if (namespacesCache !== createDebug.namespaces) { namespacesCache = createDebug.namespaces; enabledCache = createDebug.enabled(namespace); } return enabledCache; }, set: (v) => { enableOverride = v; } }); if (typeof createDebug.init === "function") { createDebug.init(debug); } return debug; } function extend(namespace, delimiter) { const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace); newDebug.log = this.log; return newDebug; } function enable(namespaces) { createDebug.save(namespaces); createDebug.namespaces = namespaces; createDebug.names = []; createDebug.skips = []; let i2; const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/); const len = split.length; for (i2 = 0; i2 < len; i2++) { if (!split[i2]) { continue; } namespaces = split[i2].replace(/\*/g, ".*?"); if (namespaces[0] === "-") { createDebug.skips.push(new RegExp("^" + namespaces.substr(1) + "$")); } else { createDebug.names.push(new RegExp("^" + namespaces + "$")); } } } function disable() { const namespaces = [ ...createDebug.names.map(toNamespace), ...createDebug.skips.map(toNamespace).map((namespace) => "-" + namespace) ].join(","); createDebug.enable(""); return namespaces; } function enabled(name) { if (name[name.length - 1] === "*") { return true; } let i2; let len; for (i2 = 0, len = createDebug.skips.length; i2 < len; i2++) { if (createDebug.skips[i2].test(name)) { return false; } } for (i2 = 0, len = createDebug.names.length; i2 < len; i2++) { if (createDebug.names[i2].test(name)) { return true; } } return false; } function toNamespace(regexp) { return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*"); } function coerce(val) { if (val instanceof Error) { return val.stack ?? val.message; } return val; } function destroy() { console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); } createDebug.setupFormatters(createDebug.formatters); createDebug.enable(createDebug.load()); return createDebug; } // ../../node_modules/.pnpm/weald@1.0.6/node_modules/weald/dist/src/browser.js var storage = localstorage(); var colors = [ "#0000CC", "#0000FF", "#0033CC", "#0033FF", "#0066CC", "#0066FF", "#0099CC", "#0099FF", "#00CC00", "#00CC33", "#00CC66", "#00CC99", "#00CCCC", "#00CCFF", "#3300CC", "#3300FF", "#3333CC", "#3333FF", "#3366CC", "#3366FF", "#3399CC", "#3399FF", "#33CC00", "#33CC33", "#33CC66", "#33CC99", "#33CCCC", "#33CCFF", "#6600CC", "#6600FF", "#6633CC", "#6633FF", "#66CC00", "#66CC33", "#9900CC", "#9900FF", "#9933CC", "#9933FF", "#99CC00", "#99CC33", "#CC0000", "#CC0033", "#CC0066", "#CC0099", "#CC00CC", "#CC00FF", "#CC3300", "#CC3333", "#CC3366", "#CC3399", "#CC33CC", "#CC33FF", "#CC6600", "#CC6633", "#CC9900", "#CC9933", "#CCCC00", "#CCCC33", "#FF0000", "#FF0033", "#FF0066", "#FF0099", "#FF00CC", "#FF00FF", "#FF3300", "#FF3333", "#FF3366", "#FF3399", "#FF33CC", "#FF33FF", "#FF6600", "#FF6633", "#FF9900", "#FF9933", "#FFCC00", "#FFCC33" ]; function useColors() { if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) { return true; } if (typeof navigator !== "undefined" && navigator.userAgent?.toLowerCase().match(/(edge|trident)\/(\d+)/) != null) { return false; } return typeof document !== "undefined" && document.documentElement?.style?.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 // @ts-expect-error window.console.firebug and window.console.exception are not in the types typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages typeof navigator !== "undefined" && navigator.userAgent?.toLowerCase().match(/firefox\/(\d+)/) != null && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker typeof navigator !== "undefined" && navigator.userAgent?.toLowerCase().match(/applewebkit\/(\d+)/); } function formatArgs(args) { args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + c(this.diff); if (!this.useColors) { return; } const c2 = "color: " + this.color; args.splice(1, 0, c2, "color: inherit"); let index = 0; let lastC = 0; args[0].replace(/%[a-zA-Z%]/g, (match) => { if (match === "%%") { return; } index++; if (match === "%c") { lastC = index; } }); args.splice(lastC, 0, c2); } var log = console.debug ?? console.log ?? (() => { }); function save(namespaces) { try { if (namespaces) { storage?.setItem("debug", namespaces); } else { storage?.removeItem("debug"); } } catch (error) { } } function load() { let r2; try { r2 = storage?.getItem("debug"); } catch (error) { } if (!r2 && typeof globalThis.process !== "undefined" && "env" in globalThis.process) { r2 = globalThis.process.env.DEBUG; } return r2; } function localstorage() { try { return localStorage; } catch (error) { } } function setupFormatters(formatters) { formatters.j = function(v) { try { return JSON.stringify(v); } catch (error) { return "[UnexpectedJSONParseError]: " + error.message; } }; } var browser_default = setup({ formatArgs, save, load, useColors, setupFormatters, colors, storage, log }); // ../../node_modules/.pnpm/weald@1.0.6/node_modules/weald/dist/src/index.js var src_default = browser_default; // ../core/dist/utils/debug.js var logger = getLogger("zubridge"); var debuggers = { core: src_default("zubridge:core"), ipc: src_default("zubridge:ipc"), store: src_default("zubridge:store"), adapters: src_default("zubridge:adapters"), windows: src_default("zubridge:windows"), serialization: src_default("zubridge:serialization") }; var dynamicDebuggers = /* @__PURE__ */ new Map(); function getDebugger(area) { if (area in debuggers) { return debuggers[area]; } if (!dynamicDebuggers.has(area)) { dynamicDebuggers.set(area, src_default(`zubridge:${area}`)); } const debugFn = dynamicDebuggers.get(area); if (!debugFn) throw new Error(`Failed to create debugger for area: ${area}`); return debugFn; } function debugLog(area, ...args) { const debugInstance = getDebugger(area); debugInstance(args); if (area.endsWith(":error")) { logger.error(area, ...args); } else if (area.endsWith(":warn")) { logger.warn(area, ...args); } else if (area.endsWith(":info")) { logger.info(area, ...args); } else { logger.debug(area, ...args); } } // src/renderer/subscriptionValidator.ts var cachedSubscriptions = []; var lastSubscriptionFetchTime = 0; var SUBSCRIPTION_CACHE_TTL = 1e3; var getSubscriptionAPI = () => { if (typeof window !== "undefined" && window.__zubridge_subscriptionValidator) { return window.__zubridge_subscriptionValidator; } return null; }; async function getWindowSubscriptions() { try { const now = Date.now(); if (cachedSubscriptions.length > 0 && now - lastSubscriptionFetchTime < SUBSCRIPTION_CACHE_TTL) { return cachedSubscriptions; } const api = getSubscriptionAPI(); if (api) { const result = await api.getWindowSubscriptions(); cachedSubscriptions = Array.isArray(result) ? result : []; lastSubscriptionFetchTime = now; return cachedSubscriptions; } debugLog("subscription:error", "Subscription validator API not available"); return []; } catch (error) { debugLog("subscription:error", "Error getting window subscriptions:", error); return []; } } async function isSubscribedToKey(key) { const api = getSubscriptionAPI(); if (api) { return api.isSubscribedToKey(key); } const subscriptions = await getWindowSubscriptions(); if (subscriptions.includes("*")) { return true; } if (subscriptions.includes(key)) { return true; } if (key.includes(".")) { const keyParts = key.split("."); for (let i2 = 1; i2 <= keyParts.length; i2++) { const parentKey = keyParts.slice(0, i2).join("."); if (subscriptions.includes(parentKey)) { return true; } } } for (const subscription of subscriptions) { if (key.startsWith(`${subscription}.`)) { return true; } } return false; } async function validateStateAccess(key, action) { if (!key) return; if (action && action.__bypassAccessControl === true) { debugLog( "subscription", `Access control bypass set on action ${action.type}, bypassing subscription validation for key: ${key}` ); return; } const isSubscribed = await isSubscribedToKey(key); if (!isSubscribed) { const subscriptions = await getWindowSubscriptions(); throw new Error( `Access denied: This window is not subscribed to state key '${key}'. Current subscriptions: ${subscriptions.join(", ") || "none"}` ); } } function stateKeyExists(state, key) { const api = getSubscriptionAPI(); if (api) { return api.stateKeyExists(state, key); } if (!key || !state) return false; const parts = key.split("."); let current = state; for (const part of parts) { if (current === void 0 || current === null || typeof current !== "object") { return false; } if (!(part in current)) { return false; } current = current[part]; } return true; } async function validateStateAccessWithExistence(state, key, action) { if (!stateKeyExists(state, key)) { throw new Error(`State key '${key}' does not exist in the store`); } await validateStateAccess(key, action); } // src/renderer/actionValidator.ts var actionToStateKeyMap = /* @__PURE__ */ new Map(); function registerActionMapping(actionType, stateKeys) { actionToStateKeyMap.set(actionType, stateKeys); debugLog("action-validator", `Registered action mapping: ${actionType} -> ${stateKeys.join(", ")}`); } function registerActionMappings(mappings) { Object.entries(mappings).forEach(([actionType, stateKeys]) => { registerActionMapping(actionType, stateKeys); }); const mappingCount = Object.keys(mappings).length; debugLog("action-validator", `Registered ${mappingCount} action mappings`); } function getAffectedStateKeys(actionType) { return actionToStateKeyMap.get(actionType) || []; } async function canDispatchAction(action) { if (action.__bypassAccessControl === true) { debugLog( "action-validator", `Access control bypass set on action ${action.type}, allowing dispatch` ); return true; } const actionType = action.type; const affectedKeys = getAffectedStateKeys(actionType); if (affectedKeys.length === 0) { debugLog("action-validator", `No mapping for action ${actionType}, allowing by default`); return true; } const subscriptions = await getWindowSubscriptions(); if (subscriptions.includes("*")) { return true; } const currentState = typeof window !== "undefined" && window.zubridge ? await window.zubridge.getState() : null; for (const key of affectedKeys) { if (!currentState || !stateKeyExists(currentState, key)) { debugLog("action-validator", `State key ${key} does not exist in the store`); return false; } const hasAccess = await isSubscribedToKey(key); if (!hasAccess) { debugLog( "action-validator", `Window lacks permission to affect key ${key} with action ${actionType}` ); return false; } } return true; } async function validateActionDispatch(action) { if (action.__bypassAccessControl === true) { debugLog( "action-validator", `Access control bypass set on action ${action.type}, bypassing action dispatch validation` ); return; } const actionType = action.type; const affectedKeys = getAffectedStateKeys(actionType); if (affectedKeys.length === 0) { debugLog("action-validator", `No mapping for action ${actionType}, allowing by default`); return; } const currentState = typeof window !== "undefined" && window.zubridge ? await window.zubridge.getState() : null; for (const key of affectedKeys) { if (!currentState || !stateKeyExists(currentState, key)) { throw new Error(`State key '${key}' does not exist in the store`); } } const canDispatch = await canDispatchAction(action); if (!canDispatch) { const subscriptions = await getWindowSubscriptions(); throw new Error( `Unauthorized action dispatch: This window cannot dispatch action '${action.type}' which affects state keys: ${affectedKeys.join(", ")}. Current subscriptions: ${subscriptions.join(", ") || "none"}` ); } } // src/utils/environment.ts var isDev = async () => { if (process.type !== "browser") { if (process.env.ELECTRON_IS_DEV === "0") { return false; } return true; } const { app } = await import('electron'); return !app.isPackaged || true; }; // src/index.ts var storeRegistry = /* @__PURE__ */ new WeakMap(); var createStore = (bridge) => { if (storeRegistry.has(bridge)) { return storeRegistry.get(bridge); } const newStore = createStore$1((setState) => { bridge.subscribe((state) => setState(state)); bridge.getState().then((state) => setState(state)); return {}; }); storeRegistry.set(bridge, newStore); return newStore; }; var createHandlers = () => { if (typeof window === "undefined" || !window.zubridge) { throw new Error( "Zubridge handlers not found in window. Make sure the preload script is properly set up." ); } return window.zubridge; }; var createUseStore = (customHandlers) => { const handlers = customHandlers || createHandlers(); const vanillaStore = createStore(handlers); const useBoundStore = (selector) => useStore(vanillaStore, selector); Object.assign(useBoundStore, vanillaStore); return useBoundStore; }; function useDispatch(customHandlers) { const handlers = customHandlers || createHandlers(); const dispatch = (action, payloadOrOptions, maybeOptions) => { if (typeof action === "string") { return handlers.dispatch(action, payloadOrOptions, maybeOptions); } if (typeof action === "function") { return handlers.dispatch(action, payloadOrOptions); } return handlers.dispatch(action, payloadOrOptions); }; return dispatch; } export { canDispatchAction, createHandlers, createUseStore, getAffectedStateKeys, getWindowSubscriptions, isDev, isSubscribedToKey, registerActionMapping, registerActionMappings, stateKeyExists, useDispatch, validateActionDispatch, validateStateAccess, validateStateAccessWithExistence };