@zubridge/electron
Version:
A streamlined state management library for Electron applications using Zustand.
702 lines (688 loc) • 21.2 kB
JavaScript
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 };