electron-state-sync 1.1.0
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/CLAUDE.md +254 -0
- package/LICENSE +21 -0
- package/README.md +753 -0
- package/README.zh-CN.md +743 -0
- package/bun.lock +542 -0
- package/bunfig.toml +7 -0
- package/dist/jotai.cjs +106 -0
- package/dist/jotai.d.cts +48 -0
- package/dist/jotai.d.cts.map +1 -0
- package/dist/jotai.d.ts +48 -0
- package/dist/jotai.d.ts.map +1 -0
- package/dist/jotai.js +105 -0
- package/dist/jotai.js.map +1 -0
- package/dist/main.cjs +177 -0
- package/dist/main.d.cts +26 -0
- package/dist/main.d.cts.map +1 -0
- package/dist/main.d.ts +26 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +177 -0
- package/dist/main.js.map +1 -0
- package/dist/preact.cjs +46 -0
- package/dist/preact.d.cts +11 -0
- package/dist/preact.d.cts.map +1 -0
- package/dist/preact.d.ts +11 -0
- package/dist/preact.d.ts.map +1 -0
- package/dist/preact.js +46 -0
- package/dist/preact.js.map +1 -0
- package/dist/preload.cjs +51 -0
- package/dist/preload.d.cts +20 -0
- package/dist/preload.d.cts.map +1 -0
- package/dist/preload.d.ts +20 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +51 -0
- package/dist/preload.js.map +1 -0
- package/dist/react-query.cjs +113 -0
- package/dist/react-query.d.cts +58 -0
- package/dist/react-query.d.cts.map +1 -0
- package/dist/react-query.d.ts +58 -0
- package/dist/react-query.d.ts.map +1 -0
- package/dist/react-query.js +112 -0
- package/dist/react-query.js.map +1 -0
- package/dist/react.cjs +46 -0
- package/dist/react.d.cts +11 -0
- package/dist/react.d.cts.map +1 -0
- package/dist/react.d.ts +11 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +46 -0
- package/dist/react.js.map +1 -0
- package/dist/redux.cjs +148 -0
- package/dist/redux.d.cts +80 -0
- package/dist/redux.d.cts.map +1 -0
- package/dist/redux.d.ts +80 -0
- package/dist/redux.d.ts.map +1 -0
- package/dist/redux.js +146 -0
- package/dist/redux.js.map +1 -0
- package/dist/renderer/index.cjs +7 -0
- package/dist/renderer/index.d.cts +23 -0
- package/dist/renderer/index.d.cts.map +1 -0
- package/dist/renderer/index.d.ts +23 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +3 -0
- package/dist/renderer-C7zF3UQm.js +57 -0
- package/dist/renderer-C7zF3UQm.js.map +1 -0
- package/dist/renderer-D3YziJ_U.cjs +86 -0
- package/dist/solid.cjs +74 -0
- package/dist/solid.d.cts +13 -0
- package/dist/solid.d.cts.map +1 -0
- package/dist/solid.d.ts +13 -0
- package/dist/solid.d.ts.map +1 -0
- package/dist/solid.js +74 -0
- package/dist/solid.js.map +1 -0
- package/dist/svelte.cjs +63 -0
- package/dist/svelte.d.cts +14 -0
- package/dist/svelte.d.cts.map +1 -0
- package/dist/svelte.d.ts +14 -0
- package/dist/svelte.d.ts.map +1 -0
- package/dist/svelte.js +63 -0
- package/dist/svelte.js.map +1 -0
- package/dist/types-7wPPX0ty.d.ts +37 -0
- package/dist/types-7wPPX0ty.d.ts.map +1 -0
- package/dist/types-C18dHgLI.d.cts +37 -0
- package/dist/types-C18dHgLI.d.cts.map +1 -0
- package/dist/vue.cjs +69 -0
- package/dist/vue.d.cts +15 -0
- package/dist/vue.d.cts.map +1 -0
- package/dist/vue.d.ts +15 -0
- package/dist/vue.d.ts.map +1 -0
- package/dist/vue.js +70 -0
- package/dist/vue.js.map +1 -0
- package/dist/zustand.cjs +193 -0
- package/dist/zustand.d.cts +61 -0
- package/dist/zustand.d.cts.map +1 -0
- package/dist/zustand.d.ts +61 -0
- package/dist/zustand.d.ts.map +1 -0
- package/dist/zustand.js +191 -0
- package/dist/zustand.js.map +1 -0
- package/package.json +162 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solid.js","names":[],"sources":["../src/renderer/solid.ts"],"sourcesContent":["import { createSignal, onCleanup, onMount, type Accessor } from \"solid-js\";\n\nimport type { SyncStateChannelOptions } from \"../channels\";\nimport type { SyncStateBridge } from \"../types\";\nimport { resolveSyncStateBridge } from \"./bridge\";\nimport { getGlobalConfig } from \"./index\";\n\nexport interface UseSyncStateSolidOptions extends SyncStateChannelOptions {\n bridge?: SyncStateBridge;\n}\n\n// Solid Setter type\nexport type SyncStateSolidSetter<StateValue> = (\n value: StateValue | ((prev: StateValue) => StateValue),\n) => StateValue;\n\n// Solid Hook return type\nexport type UseSyncStateSolidResult<StateValue> = readonly [\n Accessor<StateValue>,\n SyncStateSolidSetter<StateValue>,\n Accessor<boolean>,\n];\n\n// Remote update tracker\ninterface RemoteUpdateTracker<StateValue> {\n // Apply main process update\n applyRemoteValue: (value: StateValue) => void;\n // Check if should skip local sync\n shouldSkipLocalSync: () => boolean;\n}\n\nconst createChannelOptions = (options: UseSyncStateSolidOptions): SyncStateChannelOptions => {\n const globalConfig = getGlobalConfig();\n return {\n baseChannel: options.baseChannel ?? globalConfig.baseChannel,\n name: options.name,\n };\n};\n\nconst createRemoteUpdateTracker = <StateValue>(\n setState: (value: StateValue) => StateValue,\n setIsSynced: (value: boolean) => void,\n): RemoteUpdateTracker<StateValue> => {\n // Whether update is from main process\n let isRemoteUpdate = false;\n\n const applyRemoteValue = (value: StateValue): void => {\n isRemoteUpdate = true;\n setState(value);\n // Mark first sync as completed\n setIsSynced(true);\n // Reset immediately since Solid doesn't have a watch that consumes this synchronously\n isRemoteUpdate = false;\n };\n\n const shouldSkipLocalSync = (): boolean => {\n if (!isRemoteUpdate) {\n return false;\n }\n isRemoteUpdate = false;\n return true;\n };\n\n return {\n applyRemoteValue,\n shouldSkipLocalSync,\n };\n};\n\n// Sync Setter options\ninterface SyncSetterOptions<StateValue> {\n accessor: Accessor<StateValue>;\n setState: (value: StateValue) => StateValue;\n bridge: SyncStateBridge;\n channelOptions: SyncStateChannelOptions;\n tracker: RemoteUpdateTracker<StateValue>;\n}\n\nconst createSyncSetter =\n <StateValue>(options: SyncSetterOptions<StateValue>): SyncStateSolidSetter<StateValue> =>\n (value: StateValue | ((prev: StateValue) => StateValue)) => {\n const { accessor, bridge, channelOptions, setState, tracker } = options;\n let nextValue = value as StateValue;\n\n if (typeof value === \"function\") {\n nextValue = (value as (prev: StateValue) => StateValue)(accessor());\n }\n\n if (tracker.shouldSkipLocalSync()) {\n return setState(nextValue);\n }\n\n const resultValue = setState(nextValue);\n void bridge.set(channelOptions, nextValue);\n return resultValue;\n };\n\nexport const useSyncState = <StateValue>(\n initialValue: StateValue,\n options: UseSyncStateSolidOptions,\n): UseSyncStateSolidResult<StateValue> => {\n const [rawStateValue, rawSetStateValue] = createSignal<StateValue>(initialValue as StateValue);\n const stateValue = rawStateValue as Accessor<StateValue>;\n // First sync completion flag\n const [isSynced, setIsSynced] = createSignal(false);\n const setStateValue = (value: StateValue): StateValue => {\n (rawSetStateValue as (nextValue: StateValue) => void)(value);\n return value;\n };\n const globalConfig = getGlobalConfig();\n const bridge = resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);\n const channelOptions = createChannelOptions(options);\n const tracker = createRemoteUpdateTracker(setStateValue, setIsSynced);\n const setAndSync = createSyncSetter<StateValue>({\n accessor: stateValue,\n bridge,\n channelOptions,\n setState: setStateValue,\n tracker,\n });\n\n onMount(() => {\n const unsubscribe = bridge.subscribe(channelOptions, tracker.applyRemoteValue);\n void bridge.get<StateValue>(channelOptions).then(tracker.applyRemoteValue);\n\n onCleanup(() => {\n unsubscribe();\n });\n });\n\n return [stateValue, setAndSync, isSynced] as const;\n};\n\n// Backward compatibility alias\nexport { useSyncState as useSyncStateSolid };\n"],"mappings":";;;;AA+BA,MAAM,wBAAwB,YAA+D;CAC3F,MAAM,eAAe,iBAAiB;AACtC,QAAO;EACL,aAAa,QAAQ,eAAe,aAAa;EACjD,MAAM,QAAQ;EACf;;AAGH,MAAM,6BACJ,UACA,gBACoC;CAEpC,IAAI,iBAAiB;CAErB,MAAM,oBAAoB,UAA4B;AACpD,mBAAiB;AACjB,WAAS,MAAM;AAEf,cAAY,KAAK;AAEjB,mBAAiB;;CAGnB,MAAM,4BAAqC;AACzC,MAAI,CAAC,eACH,QAAO;AAET,mBAAiB;AACjB,SAAO;;AAGT,QAAO;EACL;EACA;EACD;;AAYH,MAAM,oBACS,aACZ,UAA2D;CAC1D,MAAM,EAAE,UAAU,QAAQ,gBAAgB,UAAU,YAAY;CAChE,IAAI,YAAY;AAEhB,KAAI,OAAO,UAAU,WACnB,aAAa,MAA2C,UAAU,CAAC;AAGrE,KAAI,QAAQ,qBAAqB,CAC/B,QAAO,SAAS,UAAU;CAG5B,MAAM,cAAc,SAAS,UAAU;AACvC,CAAK,OAAO,IAAI,gBAAgB,UAAU;AAC1C,QAAO;;AAGX,MAAa,gBACX,cACA,YACwC;CACxC,MAAM,CAAC,eAAe,oBAAoB,aAAyB,aAA2B;CAC9F,MAAM,aAAa;CAEnB,MAAM,CAAC,UAAU,eAAe,aAAa,MAAM;CACnD,MAAM,iBAAiB,UAAkC;AACvD,EAAC,iBAAqD,MAAM;AAC5D,SAAO;;CAET,MAAM,eAAe,iBAAiB;CACtC,MAAM,SAAS,uBAAuB,QAAQ,UAAU,aAAa,OAAO;CAC5E,MAAM,iBAAiB,qBAAqB,QAAQ;CACpD,MAAM,UAAU,0BAA0B,eAAe,YAAY;CACrE,MAAM,aAAa,iBAA6B;EAC9C,UAAU;EACV;EACA;EACA,UAAU;EACV;EACD,CAAC;AAEF,eAAc;EACZ,MAAM,cAAc,OAAO,UAAU,gBAAgB,QAAQ,iBAAiB;AAC9E,EAAK,OAAO,IAAgB,eAAe,CAAC,KAAK,QAAQ,iBAAiB;AAE1E,kBAAgB;AACd,gBAAa;IACb;GACF;AAEF,QAAO;EAAC;EAAY;EAAY;EAAS"}
|
package/dist/svelte.cjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const require_renderer = require('./renderer-D3YziJ_U.cjs');
|
|
2
|
+
let svelte_store = require("svelte/store");
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/svelte.ts
|
|
5
|
+
const createChannelOptions = (options) => {
|
|
6
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
7
|
+
return {
|
|
8
|
+
baseChannel: options.baseChannel ?? globalConfig.baseChannel,
|
|
9
|
+
name: options.name
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const createRemoteUpdateTracker = (setStoreValue, isSynced) => {
|
|
13
|
+
let isRemoteUpdate = false;
|
|
14
|
+
const applyRemoteValue = (value) => {
|
|
15
|
+
isRemoteUpdate = true;
|
|
16
|
+
setStoreValue(value);
|
|
17
|
+
isSynced.set(true);
|
|
18
|
+
isRemoteUpdate = false;
|
|
19
|
+
};
|
|
20
|
+
const shouldSkipLocalSync = () => {
|
|
21
|
+
if (!isRemoteUpdate) return false;
|
|
22
|
+
isRemoteUpdate = false;
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
applyRemoteValue,
|
|
27
|
+
shouldSkipLocalSync
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const useSyncState = (initialValue, options) => {
|
|
31
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
32
|
+
const bridge = require_renderer.resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);
|
|
33
|
+
const channelOptions = createChannelOptions(options);
|
|
34
|
+
const isSynced = (0, svelte_store.writable)(false);
|
|
35
|
+
let tracker = void 0;
|
|
36
|
+
const store = (0, svelte_store.writable)(initialValue, (setStoreValue) => {
|
|
37
|
+
tracker = createRemoteUpdateTracker(setStoreValue, isSynced);
|
|
38
|
+
const unsubscribe = bridge.subscribe(channelOptions, tracker.applyRemoteValue);
|
|
39
|
+
bridge.get(channelOptions).then(tracker.applyRemoteValue);
|
|
40
|
+
return () => {
|
|
41
|
+
unsubscribe();
|
|
42
|
+
tracker = void 0;
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
const set = (value) => {
|
|
46
|
+
if (tracker?.shouldSkipLocalSync()) return;
|
|
47
|
+
store.set(value);
|
|
48
|
+
bridge.set(channelOptions, value);
|
|
49
|
+
};
|
|
50
|
+
const update = (updater) => {
|
|
51
|
+
set(updater((0, svelte_store.get)(store)));
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
isSynced,
|
|
55
|
+
set,
|
|
56
|
+
subscribe: store.subscribe,
|
|
57
|
+
update
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
exports.createSyncStateStore = useSyncState;
|
|
63
|
+
exports.useSyncState = useSyncState;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { a as SyncStateChannelOptions, t as SyncStateBridge } from "./types-C18dHgLI.cjs";
|
|
2
|
+
import { Readable, Writable } from "svelte/store";
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/svelte.d.ts
|
|
5
|
+
interface SyncStateSvelteOptions extends SyncStateChannelOptions {
|
|
6
|
+
bridge?: SyncStateBridge;
|
|
7
|
+
}
|
|
8
|
+
interface SyncStateStore<StateValue> extends Writable<StateValue> {
|
|
9
|
+
isSynced: Readable<boolean>;
|
|
10
|
+
}
|
|
11
|
+
declare const useSyncState: <StateValue>(initialValue: StateValue, options: SyncStateSvelteOptions) => SyncStateStore<StateValue>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { SyncStateStore, SyncStateSvelteOptions, useSyncState as createSyncStateStore, useSyncState };
|
|
14
|
+
//# sourceMappingURL=svelte.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte.d.cts","names":[],"sources":["../src/renderer/svelte.ts"],"sourcesContent":[],"mappings":";;;;UAOiB,sBAAA,SAA+B;EAA/B,MAAA,CAAA,EACN,eADM;AASjB;AAA6D,UAA5C,cAA4C,CAAA,UAAA,CAAA,SAAT,QAAS,CAAA,UAAA,CAAA,CAAA;EACjD,QAAA,EAAA,QAAA,CAAA,OAAA,CAAA;;AADgD,cAwC/C,YAxC+C,EAAA,CAAA,UAAA,CAAA,CAAA,YAAA,EAyC5C,UAzC4C,EAAA,OAAA,EA0CjD,sBA1CiD,EAAA,GA2CzD,cA3CyD,CA2C1C,UA3C0C,CAAA"}
|
package/dist/svelte.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { a as SyncStateChannelOptions, t as SyncStateBridge } from "./types-7wPPX0ty.js";
|
|
2
|
+
import { Readable, Writable } from "svelte/store";
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/svelte.d.ts
|
|
5
|
+
interface SyncStateSvelteOptions extends SyncStateChannelOptions {
|
|
6
|
+
bridge?: SyncStateBridge;
|
|
7
|
+
}
|
|
8
|
+
interface SyncStateStore<StateValue> extends Writable<StateValue> {
|
|
9
|
+
isSynced: Readable<boolean>;
|
|
10
|
+
}
|
|
11
|
+
declare const useSyncState: <StateValue>(initialValue: StateValue, options: SyncStateSvelteOptions) => SyncStateStore<StateValue>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { SyncStateStore, SyncStateSvelteOptions, useSyncState as createSyncStateStore, useSyncState };
|
|
14
|
+
//# sourceMappingURL=svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte.d.ts","names":[],"sources":["../src/renderer/svelte.ts"],"sourcesContent":[],"mappings":";;;;UAOiB,sBAAA,SAA+B;EAA/B,MAAA,CAAA,EACN,eADM;AASjB;AAA6D,UAA5C,cAA4C,CAAA,UAAA,CAAA,SAAT,QAAS,CAAA,UAAA,CAAA,CAAA;EACjD,QAAA,EAAA,QAAA,CAAA,OAAA,CAAA;;AADgD,cAwC/C,YAxC+C,EAAA,CAAA,UAAA,CAAA,CAAA,YAAA,EAyC5C,UAzC4C,EAAA,OAAA,EA0CjD,sBA1CiD,EAAA,GA2CzD,cA3CyD,CA2C1C,UA3C0C,CAAA"}
|
package/dist/svelte.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { a as resolveSyncStateBridge, t as getGlobalConfig } from "./renderer-C7zF3UQm.js";
|
|
2
|
+
import { get, writable } from "svelte/store";
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/svelte.ts
|
|
5
|
+
const createChannelOptions = (options) => {
|
|
6
|
+
const globalConfig = getGlobalConfig();
|
|
7
|
+
return {
|
|
8
|
+
baseChannel: options.baseChannel ?? globalConfig.baseChannel,
|
|
9
|
+
name: options.name
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const createRemoteUpdateTracker = (setStoreValue, isSynced) => {
|
|
13
|
+
let isRemoteUpdate = false;
|
|
14
|
+
const applyRemoteValue = (value) => {
|
|
15
|
+
isRemoteUpdate = true;
|
|
16
|
+
setStoreValue(value);
|
|
17
|
+
isSynced.set(true);
|
|
18
|
+
isRemoteUpdate = false;
|
|
19
|
+
};
|
|
20
|
+
const shouldSkipLocalSync = () => {
|
|
21
|
+
if (!isRemoteUpdate) return false;
|
|
22
|
+
isRemoteUpdate = false;
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
applyRemoteValue,
|
|
27
|
+
shouldSkipLocalSync
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const useSyncState = (initialValue, options) => {
|
|
31
|
+
const globalConfig = getGlobalConfig();
|
|
32
|
+
const bridge = resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);
|
|
33
|
+
const channelOptions = createChannelOptions(options);
|
|
34
|
+
const isSynced = writable(false);
|
|
35
|
+
let tracker = void 0;
|
|
36
|
+
const store = writable(initialValue, (setStoreValue) => {
|
|
37
|
+
tracker = createRemoteUpdateTracker(setStoreValue, isSynced);
|
|
38
|
+
const unsubscribe = bridge.subscribe(channelOptions, tracker.applyRemoteValue);
|
|
39
|
+
bridge.get(channelOptions).then(tracker.applyRemoteValue);
|
|
40
|
+
return () => {
|
|
41
|
+
unsubscribe();
|
|
42
|
+
tracker = void 0;
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
const set = (value) => {
|
|
46
|
+
if (tracker?.shouldSkipLocalSync()) return;
|
|
47
|
+
store.set(value);
|
|
48
|
+
bridge.set(channelOptions, value);
|
|
49
|
+
};
|
|
50
|
+
const update = (updater) => {
|
|
51
|
+
set(updater(get(store)));
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
isSynced,
|
|
55
|
+
set,
|
|
56
|
+
subscribe: store.subscribe,
|
|
57
|
+
update
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { useSyncState as createSyncStateStore, useSyncState };
|
|
63
|
+
//# sourceMappingURL=svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte.js","names":[],"sources":["../src/renderer/svelte.ts"],"sourcesContent":["import { get, writable, type Readable, type Updater, type Writable } from \"svelte/store\";\n\nimport type { SyncStateChannelOptions } from \"../channels\";\nimport type { SyncStateBridge } from \"../types\";\nimport { resolveSyncStateBridge } from \"./bridge\";\nimport { getGlobalConfig } from \"./index\";\n\nexport interface SyncStateSvelteOptions extends SyncStateChannelOptions {\n bridge?: SyncStateBridge;\n}\n\ninterface RemoteUpdateTracker<StateValue> {\n applyRemoteValue: (value: StateValue) => void;\n shouldSkipLocalSync: () => boolean;\n}\n\nexport interface SyncStateStore<StateValue> extends Writable<StateValue> {\n isSynced: Readable<boolean>;\n}\n\nconst createChannelOptions = (options: SyncStateSvelteOptions): SyncStateChannelOptions => {\n const globalConfig = getGlobalConfig();\n return {\n baseChannel: options.baseChannel ?? globalConfig.baseChannel,\n name: options.name,\n };\n};\n\nconst createRemoteUpdateTracker = <StateValue>(\n setStoreValue: (value: StateValue) => void,\n isSynced: Writable<boolean>,\n): RemoteUpdateTracker<StateValue> => {\n let isRemoteUpdate = false;\n\n const applyRemoteValue = (value: StateValue): void => {\n isRemoteUpdate = true;\n setStoreValue(value);\n isSynced.set(true);\n // Reset immediately since Svelte doesn't have a watch that consumes this synchronously\n isRemoteUpdate = false;\n };\n\n const shouldSkipLocalSync = (): boolean => {\n if (!isRemoteUpdate) {\n return false;\n }\n isRemoteUpdate = false;\n return true;\n };\n\n return {\n applyRemoteValue,\n shouldSkipLocalSync,\n };\n};\n\nexport const useSyncState = <StateValue>(\n initialValue: StateValue,\n options: SyncStateSvelteOptions,\n): SyncStateStore<StateValue> => {\n const globalConfig = getGlobalConfig();\n const bridge = resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);\n const channelOptions = createChannelOptions(options);\n const isSynced = writable(false);\n let tracker: RemoteUpdateTracker<StateValue> | undefined = undefined;\n\n const store = writable(initialValue, (setStoreValue) => {\n tracker = createRemoteUpdateTracker(setStoreValue, isSynced);\n const unsubscribe = bridge.subscribe(channelOptions, tracker.applyRemoteValue);\n void bridge.get<StateValue>(channelOptions).then(tracker.applyRemoteValue);\n\n return () => {\n unsubscribe();\n tracker = undefined;\n };\n });\n\n const set = (value: StateValue): void => {\n if (tracker?.shouldSkipLocalSync()) {\n return;\n }\n\n store.set(value);\n void bridge.set(channelOptions, value);\n };\n\n const update = (updater: Updater<StateValue>): void => {\n const nextValue = updater(get(store));\n set(nextValue);\n };\n\n return {\n isSynced,\n set,\n subscribe: store.subscribe,\n update,\n };\n};\n\n// Backward compatibility alias\nexport { useSyncState as createSyncStateStore };\n"],"mappings":";;;;AAoBA,MAAM,wBAAwB,YAA6D;CACzF,MAAM,eAAe,iBAAiB;AACtC,QAAO;EACL,aAAa,QAAQ,eAAe,aAAa;EACjD,MAAM,QAAQ;EACf;;AAGH,MAAM,6BACJ,eACA,aACoC;CACpC,IAAI,iBAAiB;CAErB,MAAM,oBAAoB,UAA4B;AACpD,mBAAiB;AACjB,gBAAc,MAAM;AACpB,WAAS,IAAI,KAAK;AAElB,mBAAiB;;CAGnB,MAAM,4BAAqC;AACzC,MAAI,CAAC,eACH,QAAO;AAET,mBAAiB;AACjB,SAAO;;AAGT,QAAO;EACL;EACA;EACD;;AAGH,MAAa,gBACX,cACA,YAC+B;CAC/B,MAAM,eAAe,iBAAiB;CACtC,MAAM,SAAS,uBAAuB,QAAQ,UAAU,aAAa,OAAO;CAC5E,MAAM,iBAAiB,qBAAqB,QAAQ;CACpD,MAAM,WAAW,SAAS,MAAM;CAChC,IAAI,UAAuD;CAE3D,MAAM,QAAQ,SAAS,eAAe,kBAAkB;AACtD,YAAU,0BAA0B,eAAe,SAAS;EAC5D,MAAM,cAAc,OAAO,UAAU,gBAAgB,QAAQ,iBAAiB;AAC9E,EAAK,OAAO,IAAgB,eAAe,CAAC,KAAK,QAAQ,iBAAiB;AAE1E,eAAa;AACX,gBAAa;AACb,aAAU;;GAEZ;CAEF,MAAM,OAAO,UAA4B;AACvC,MAAI,SAAS,qBAAqB,CAChC;AAGF,QAAM,IAAI,MAAM;AAChB,EAAK,OAAO,IAAI,gBAAgB,MAAM;;CAGxC,MAAM,UAAU,YAAuC;AAErD,MADkB,QAAQ,IAAI,MAAM,CAAC,CACvB;;AAGhB,QAAO;EACL;EACA;EACA,WAAW,MAAM;EACjB;EACD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/channels.d.ts
|
|
2
|
+
interface SyncStateChannelOptions {
|
|
3
|
+
name: string;
|
|
4
|
+
baseChannel?: string;
|
|
5
|
+
}
|
|
6
|
+
interface SyncStateChannels {
|
|
7
|
+
getChannel: string;
|
|
8
|
+
setChannel: string;
|
|
9
|
+
subscribeChannel: string;
|
|
10
|
+
unsubscribeChannel: string;
|
|
11
|
+
updateChannel: string;
|
|
12
|
+
}
|
|
13
|
+
declare const createSyncStateChannels: (options: SyncStateChannelOptions) => SyncStateChannels;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/types.d.ts
|
|
16
|
+
type SyncStateListener<StateValue> = (value: StateValue) => void;
|
|
17
|
+
type SyncStateErrorCode = "RENDERER_READONLY" | "RENDERER_INVALID_VALUE";
|
|
18
|
+
declare class SyncStateError extends Error {
|
|
19
|
+
readonly code: SyncStateErrorCode;
|
|
20
|
+
readonly stateName?: string;
|
|
21
|
+
readonly baseChannel?: string;
|
|
22
|
+
readonly cause?: Error;
|
|
23
|
+
constructor(code: SyncStateErrorCode, message: string, context?: {
|
|
24
|
+
stateName?: string;
|
|
25
|
+
baseChannel?: string;
|
|
26
|
+
cause?: Error;
|
|
27
|
+
});
|
|
28
|
+
getFullMessage(): string;
|
|
29
|
+
}
|
|
30
|
+
interface SyncStateBridge {
|
|
31
|
+
get: <StateValue>(options: SyncStateChannelOptions) => Promise<StateValue>;
|
|
32
|
+
set: <StateValue>(options: SyncStateChannelOptions, value: StateValue) => Promise<void>;
|
|
33
|
+
subscribe: <StateValue>(options: SyncStateChannelOptions, listener: SyncStateListener<StateValue>) => () => void;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { SyncStateChannelOptions as a, SyncStateListener as i, SyncStateError as n, SyncStateChannels as o, SyncStateErrorCode as r, createSyncStateChannels as s, SyncStateBridge as t };
|
|
37
|
+
//# sourceMappingURL=types-7wPPX0ty.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-7wPPX0ty.d.ts","names":[],"sources":["../src/channels.ts","../src/types.ts"],"sourcesContent":[],"mappings":";UAAiB,uBAAA;EAAA,IAAA,EAAA,MAAA;EAKA,WAAA,CAAA,EAAA,MAAiB;AAQlC;UARiB,iBAAA;;;ECHL,gBAAA,EAAA,MAAiB;EAEjB,kBAAA,EAAA,MAAkB;EAEjB,aAAA,EAAA,MAAe;;AAIF,cDGb,uBCHa,EAAA,CAAA,OAAA,EDGuB,uBCHvB,EAAA,GDGiD,iBCHjD;;;ADVT,KCEL,iBDFK,CAAuB,UAAA,CAAA,GAAA,CAAA,KAAA,ECEY,UDFZ,EAAA,GAAA,IAAA;AAKvB,KCDL,kBAAA,GDCsB,mBAAA,GAAA,wBAAA;AAQrB,cCPA,cAAA,SAAuB,KAAA,CDOa;iBCNzB;;;EALZ,SAAA,KAAA,CAAA,EAQc,KARG;EAEjB,WAAA,CAAA,IAAA,EASF,kBAToB,EAAA,OAAA,EAAA,MAAA,EAAA,OASpB,CAToB,EAAA;IAEjB,SAAA,CAAA,EAAA,MAAe;IACJ,WAAA,CAAA,EAAA,MAAA;IAGE,KAAA,CAAA,EAKwC,KALxC;EAGhB,CAAA;EAEwD,cAAA,CAAA,CAAA,EAAA,MAAA;;AATzB,UAsCxB,eAAA,CAtCwB;EAsCxB,GAAA,EAAA,CAAA,UAAA,CAAA,CAAe,OAAA,EACH,uBADG,EAAA,GACyB,OADzB,CACiC,UADjC,CAAA;EACH,GAAA,EAAA,CAAA,UAAA,CAAA,CAAA,OAAA,EACA,uBADA,EAAA,KAAA,EACgC,UADhC,EAAA,GAC+C,OAD/C,CAAA,IAAA,CAAA;EAAoC,SAAA,EAAA,CAAA,UAAA,CAAA,CAAA,OAAA,EAGpD,uBAHoD,EAAA,QAAA,EAInD,iBAJmD,CAIjC,UAJiC,CAAA,EAAA,GAAA,GAAA,GAAA,IAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/channels.d.ts
|
|
2
|
+
interface SyncStateChannelOptions {
|
|
3
|
+
name: string;
|
|
4
|
+
baseChannel?: string;
|
|
5
|
+
}
|
|
6
|
+
interface SyncStateChannels {
|
|
7
|
+
getChannel: string;
|
|
8
|
+
setChannel: string;
|
|
9
|
+
subscribeChannel: string;
|
|
10
|
+
unsubscribeChannel: string;
|
|
11
|
+
updateChannel: string;
|
|
12
|
+
}
|
|
13
|
+
declare const createSyncStateChannels: (options: SyncStateChannelOptions) => SyncStateChannels;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/types.d.ts
|
|
16
|
+
type SyncStateListener<StateValue> = (value: StateValue) => void;
|
|
17
|
+
type SyncStateErrorCode = "RENDERER_READONLY" | "RENDERER_INVALID_VALUE";
|
|
18
|
+
declare class SyncStateError extends Error {
|
|
19
|
+
readonly code: SyncStateErrorCode;
|
|
20
|
+
readonly stateName?: string;
|
|
21
|
+
readonly baseChannel?: string;
|
|
22
|
+
readonly cause?: Error;
|
|
23
|
+
constructor(code: SyncStateErrorCode, message: string, context?: {
|
|
24
|
+
stateName?: string;
|
|
25
|
+
baseChannel?: string;
|
|
26
|
+
cause?: Error;
|
|
27
|
+
});
|
|
28
|
+
getFullMessage(): string;
|
|
29
|
+
}
|
|
30
|
+
interface SyncStateBridge {
|
|
31
|
+
get: <StateValue>(options: SyncStateChannelOptions) => Promise<StateValue>;
|
|
32
|
+
set: <StateValue>(options: SyncStateChannelOptions, value: StateValue) => Promise<void>;
|
|
33
|
+
subscribe: <StateValue>(options: SyncStateChannelOptions, listener: SyncStateListener<StateValue>) => () => void;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { SyncStateChannelOptions as a, SyncStateListener as i, SyncStateError as n, SyncStateChannels as o, SyncStateErrorCode as r, createSyncStateChannels as s, SyncStateBridge as t };
|
|
37
|
+
//# sourceMappingURL=types-C18dHgLI.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-C18dHgLI.d.cts","names":[],"sources":["../src/channels.ts","../src/types.ts"],"sourcesContent":[],"mappings":";UAAiB,uBAAA;EAAA,IAAA,EAAA,MAAA;EAKA,WAAA,CAAA,EAAA,MAAiB;AAQlC;UARiB,iBAAA;;;ECHL,gBAAA,EAAA,MAAiB;EAEjB,kBAAA,EAAA,MAAkB;EAEjB,aAAA,EAAA,MAAe;;AAIF,cDGb,uBCHa,EAAA,CAAA,OAAA,EDGuB,uBCHvB,EAAA,GDGiD,iBCHjD;;;ADVT,KCEL,iBDFK,CAAuB,UAAA,CAAA,GAAA,CAAA,KAAA,ECEY,UDFZ,EAAA,GAAA,IAAA;AAKvB,KCDL,kBAAA,GDCsB,mBAAA,GAAA,wBAAA;AAQrB,cCPA,cAAA,SAAuB,KAAA,CDOa;iBCNzB;;;EALZ,SAAA,KAAA,CAAA,EAQc,KARG;EAEjB,WAAA,CAAA,IAAA,EASF,kBAToB,EAAA,OAAA,EAAA,MAAA,EAAA,OASpB,CAToB,EAAA;IAEjB,SAAA,CAAA,EAAA,MAAe;IACJ,WAAA,CAAA,EAAA,MAAA;IAGE,KAAA,CAAA,EAKwC,KALxC;EAGhB,CAAA;EAEwD,cAAA,CAAA,CAAA,EAAA,MAAA;;AATzB,UAsCxB,eAAA,CAtCwB;EAsCxB,GAAA,EAAA,CAAA,UAAA,CAAA,CAAe,OAAA,EACH,uBADG,EAAA,GACyB,OADzB,CACiC,UADjC,CAAA;EACH,GAAA,EAAA,CAAA,UAAA,CAAA,CAAA,OAAA,EACA,uBADA,EAAA,KAAA,EACgC,UADhC,EAAA,GAC+C,OAD/C,CAAA,IAAA,CAAA;EAAoC,SAAA,EAAA,CAAA,UAAA,CAAA,CAAA,OAAA,EAGpD,uBAHoD,EAAA,QAAA,EAInD,iBAJmD,CAIjC,UAJiC,CAAA,EAAA,GAAA,GAAA,GAAA,IAAA"}
|
package/dist/vue.cjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const require_renderer = require('./renderer-D3YziJ_U.cjs');
|
|
2
|
+
let vue = require("vue");
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/vue.ts
|
|
5
|
+
const createChannelOptions = (options) => {
|
|
6
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
7
|
+
return {
|
|
8
|
+
baseChannel: options.baseChannel ?? globalConfig.baseChannel,
|
|
9
|
+
name: options.name
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const resolveSyncValue = (value) => {
|
|
13
|
+
if (value !== null && typeof value === "object" && (0, vue.isProxy)(value)) return (0, vue.toRaw)(value);
|
|
14
|
+
return value;
|
|
15
|
+
};
|
|
16
|
+
const createRemoteUpdateTracker = (stateRef, isSynced) => {
|
|
17
|
+
let isApplyingRemoteValue = false;
|
|
18
|
+
let lastRemoteValue = void 0;
|
|
19
|
+
const applyRemoteValue = (value) => {
|
|
20
|
+
const resolvedValue = resolveSyncValue(value);
|
|
21
|
+
isApplyingRemoteValue = true;
|
|
22
|
+
lastRemoteValue = resolvedValue;
|
|
23
|
+
stateRef.value = resolvedValue;
|
|
24
|
+
isSynced.value = true;
|
|
25
|
+
isApplyingRemoteValue = false;
|
|
26
|
+
};
|
|
27
|
+
const shouldSkipLocalSync = (currentValue) => isApplyingRemoteValue && resolveSyncValue(currentValue) === lastRemoteValue;
|
|
28
|
+
return {
|
|
29
|
+
applyRemoteValue,
|
|
30
|
+
shouldSkipLocalSync
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const createStateWatcher = ({ bridge, channelOptions, deep, stateRef, tracker }) => (0, vue.watch)(stateRef, (nextValue) => {
|
|
34
|
+
if (tracker.shouldSkipLocalSync(nextValue)) return;
|
|
35
|
+
bridge.set(channelOptions, resolveSyncValue(nextValue));
|
|
36
|
+
}, {
|
|
37
|
+
deep: Boolean(deep),
|
|
38
|
+
flush: "sync"
|
|
39
|
+
});
|
|
40
|
+
const useSyncState = (initialValue, options) => {
|
|
41
|
+
const stateRef = (0, vue.ref)(initialValue);
|
|
42
|
+
const isSynced = (0, vue.ref)(false);
|
|
43
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
44
|
+
const bridge = require_renderer.resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);
|
|
45
|
+
const channelOptions = createChannelOptions(options);
|
|
46
|
+
const tracker = createRemoteUpdateTracker(stateRef, isSynced);
|
|
47
|
+
const stopWatcher = createStateWatcher({
|
|
48
|
+
bridge,
|
|
49
|
+
channelOptions,
|
|
50
|
+
deep: options.deep,
|
|
51
|
+
stateRef,
|
|
52
|
+
tracker
|
|
53
|
+
});
|
|
54
|
+
let unsubscribe = void 0;
|
|
55
|
+
(0, vue.onMounted)(() => {
|
|
56
|
+
unsubscribe = bridge.subscribe(channelOptions, tracker.applyRemoteValue);
|
|
57
|
+
bridge.get(channelOptions).then(tracker.applyRemoteValue);
|
|
58
|
+
});
|
|
59
|
+
(0, vue.onBeforeUnmount)(() => {
|
|
60
|
+
stopWatcher();
|
|
61
|
+
unsubscribe?.();
|
|
62
|
+
unsubscribe = void 0;
|
|
63
|
+
});
|
|
64
|
+
stateRef.isSynced = isSynced;
|
|
65
|
+
return stateRef;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
exports.useSyncState = useSyncState;
|
package/dist/vue.d.cts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { a as SyncStateChannelOptions, t as SyncStateBridge } from "./types-C18dHgLI.cjs";
|
|
2
|
+
import { Ref } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/vue.d.ts
|
|
5
|
+
interface UseSyncStateOptions extends SyncStateChannelOptions {
|
|
6
|
+
bridge?: SyncStateBridge;
|
|
7
|
+
deep?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface SyncStateRef<StateValue> extends Ref<StateValue> {
|
|
10
|
+
isSynced: Ref<boolean>;
|
|
11
|
+
}
|
|
12
|
+
declare const useSyncState: <StateValue>(initialValue: StateValue, options: UseSyncStateOptions) => SyncStateRef<StateValue>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { SyncStateRef, UseSyncStateOptions, useSyncState };
|
|
15
|
+
//# sourceMappingURL=vue.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.d.cts","names":[],"sources":["../src/renderer/vue.ts"],"sourcesContent":[],"mappings":";;;;UAOiB,mBAAA,SAA4B;EAA5B,MAAA,CAAA,EACN,eAD0B;EAqFpB,IAAA,CAAA,EAAA,OAAA;;AACL,UADK,YACL,CAAA,UAAA,CAAA,SADsC,GACtC,CAD0C,UAC1C,CAAA,CAAA;EADsC,QAAA,EACtC,GADsC,CAAA,OAAA,CAAA;;AAIrC,cAAA,YAkCZ,EAAA,CAAA,UAAA,CAAA,CAAA,YAAA,EAjCe,UAiCf,EAAA,OAAA,EAhCU,mBAgCV,EAAA,GA/BE,YA+BF,CA/Be,UA+Bf,CAAA"}
|
package/dist/vue.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { a as SyncStateChannelOptions, t as SyncStateBridge } from "./types-7wPPX0ty.js";
|
|
2
|
+
import { Ref } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/vue.d.ts
|
|
5
|
+
interface UseSyncStateOptions extends SyncStateChannelOptions {
|
|
6
|
+
bridge?: SyncStateBridge;
|
|
7
|
+
deep?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface SyncStateRef<StateValue> extends Ref<StateValue> {
|
|
10
|
+
isSynced: Ref<boolean>;
|
|
11
|
+
}
|
|
12
|
+
declare const useSyncState: <StateValue>(initialValue: StateValue, options: UseSyncStateOptions) => SyncStateRef<StateValue>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { SyncStateRef, UseSyncStateOptions, useSyncState };
|
|
15
|
+
//# sourceMappingURL=vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.d.ts","names":[],"sources":["../src/renderer/vue.ts"],"sourcesContent":[],"mappings":";;;;UAOiB,mBAAA,SAA4B;EAA5B,MAAA,CAAA,EACN,eAD0B;EAqFpB,IAAA,CAAA,EAAA,OAAA;;AACL,UADK,YACL,CAAA,UAAA,CAAA,SADsC,GACtC,CAD0C,UAC1C,CAAA,CAAA;EADsC,QAAA,EACtC,GADsC,CAAA,OAAA,CAAA;;AAIrC,cAAA,YAkCZ,EAAA,CAAA,UAAA,CAAA,CAAA,YAAA,EAjCe,UAiCf,EAAA,OAAA,EAhCU,mBAgCV,EAAA,GA/BE,YA+BF,CA/Be,UA+Bf,CAAA"}
|
package/dist/vue.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { a as resolveSyncStateBridge, t as getGlobalConfig } from "./renderer-C7zF3UQm.js";
|
|
2
|
+
import { isProxy, onBeforeUnmount, onMounted, ref, toRaw, watch } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/renderer/vue.ts
|
|
5
|
+
const createChannelOptions = (options) => {
|
|
6
|
+
const globalConfig = getGlobalConfig();
|
|
7
|
+
return {
|
|
8
|
+
baseChannel: options.baseChannel ?? globalConfig.baseChannel,
|
|
9
|
+
name: options.name
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const resolveSyncValue = (value) => {
|
|
13
|
+
if (value !== null && typeof value === "object" && isProxy(value)) return toRaw(value);
|
|
14
|
+
return value;
|
|
15
|
+
};
|
|
16
|
+
const createRemoteUpdateTracker = (stateRef, isSynced) => {
|
|
17
|
+
let isApplyingRemoteValue = false;
|
|
18
|
+
let lastRemoteValue = void 0;
|
|
19
|
+
const applyRemoteValue = (value) => {
|
|
20
|
+
const resolvedValue = resolveSyncValue(value);
|
|
21
|
+
isApplyingRemoteValue = true;
|
|
22
|
+
lastRemoteValue = resolvedValue;
|
|
23
|
+
stateRef.value = resolvedValue;
|
|
24
|
+
isSynced.value = true;
|
|
25
|
+
isApplyingRemoteValue = false;
|
|
26
|
+
};
|
|
27
|
+
const shouldSkipLocalSync = (currentValue) => isApplyingRemoteValue && resolveSyncValue(currentValue) === lastRemoteValue;
|
|
28
|
+
return {
|
|
29
|
+
applyRemoteValue,
|
|
30
|
+
shouldSkipLocalSync
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const createStateWatcher = ({ bridge, channelOptions, deep, stateRef, tracker }) => watch(stateRef, (nextValue) => {
|
|
34
|
+
if (tracker.shouldSkipLocalSync(nextValue)) return;
|
|
35
|
+
bridge.set(channelOptions, resolveSyncValue(nextValue));
|
|
36
|
+
}, {
|
|
37
|
+
deep: Boolean(deep),
|
|
38
|
+
flush: "sync"
|
|
39
|
+
});
|
|
40
|
+
const useSyncState = (initialValue, options) => {
|
|
41
|
+
const stateRef = ref(initialValue);
|
|
42
|
+
const isSynced = ref(false);
|
|
43
|
+
const globalConfig = getGlobalConfig();
|
|
44
|
+
const bridge = resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);
|
|
45
|
+
const channelOptions = createChannelOptions(options);
|
|
46
|
+
const tracker = createRemoteUpdateTracker(stateRef, isSynced);
|
|
47
|
+
const stopWatcher = createStateWatcher({
|
|
48
|
+
bridge,
|
|
49
|
+
channelOptions,
|
|
50
|
+
deep: options.deep,
|
|
51
|
+
stateRef,
|
|
52
|
+
tracker
|
|
53
|
+
});
|
|
54
|
+
let unsubscribe = void 0;
|
|
55
|
+
onMounted(() => {
|
|
56
|
+
unsubscribe = bridge.subscribe(channelOptions, tracker.applyRemoteValue);
|
|
57
|
+
bridge.get(channelOptions).then(tracker.applyRemoteValue);
|
|
58
|
+
});
|
|
59
|
+
onBeforeUnmount(() => {
|
|
60
|
+
stopWatcher();
|
|
61
|
+
unsubscribe?.();
|
|
62
|
+
unsubscribe = void 0;
|
|
63
|
+
});
|
|
64
|
+
stateRef.isSynced = isSynced;
|
|
65
|
+
return stateRef;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
export { useSyncState };
|
|
70
|
+
//# sourceMappingURL=vue.js.map
|
package/dist/vue.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.js","names":[],"sources":["../src/renderer/vue.ts"],"sourcesContent":["import { isProxy, onBeforeUnmount, onMounted, ref, toRaw, watch, type Ref } from \"vue\";\n\nimport type { SyncStateChannelOptions } from \"../channels\";\nimport type { SyncStateBridge } from \"../types\";\nimport { resolveSyncStateBridge } from \"./bridge\";\nimport { getGlobalConfig } from \"./index\";\n\nexport interface UseSyncStateOptions extends SyncStateChannelOptions {\n bridge?: SyncStateBridge;\n deep?: boolean;\n}\n\ninterface RemoteUpdateTracker<StateValue> {\n applyRemoteValue: (value: StateValue) => void;\n shouldSkipLocalSync: (currentValue: StateValue) => boolean;\n}\n\nconst createChannelOptions = (options: UseSyncStateOptions): SyncStateChannelOptions => {\n const globalConfig = getGlobalConfig();\n return {\n baseChannel: options.baseChannel ?? globalConfig.baseChannel,\n name: options.name,\n };\n};\n\n// Convert Vue Proxy to serializable raw value\nconst resolveSyncValue = <StateValue>(value: StateValue): StateValue => {\n if (value !== null && typeof value === \"object\" && isProxy(value)) {\n return toRaw(value) as StateValue;\n }\n\n return value;\n};\n\nconst createRemoteUpdateTracker = <StateValue>(\n stateRef: Ref<StateValue>,\n isSynced: Ref<boolean>,\n): RemoteUpdateTracker<StateValue> => {\n let isApplyingRemoteValue = false;\n // Record the last raw value synced from main process\n let lastRemoteValue: StateValue | undefined = undefined;\n\n const applyRemoteValue = (value: StateValue): void => {\n // Convert remote synced value to raw object\n const resolvedValue = resolveSyncValue(value);\n isApplyingRemoteValue = true;\n lastRemoteValue = resolvedValue;\n stateRef.value = resolvedValue;\n isSynced.value = true;\n isApplyingRemoteValue = false;\n };\n\n const shouldSkipLocalSync = (currentValue: StateValue): boolean =>\n // Skip sync if we're currently applying a remote value AND it matches\n // This prevents infinite loops while allowing all local updates to sync\n isApplyingRemoteValue && resolveSyncValue(currentValue) === lastRemoteValue;\n\n return {\n applyRemoteValue,\n shouldSkipLocalSync,\n };\n};\n\ninterface StateWatcherOptions<StateValue> {\n bridge: SyncStateBridge;\n channelOptions: SyncStateChannelOptions;\n stateRef: Ref<StateValue>;\n tracker: RemoteUpdateTracker<StateValue>;\n deep?: boolean;\n}\n\nconst createStateWatcher = <StateValue>({\n bridge,\n channelOptions,\n deep,\n stateRef,\n tracker,\n}: StateWatcherOptions<StateValue>): (() => void) =>\n watch(\n stateRef,\n (nextValue: StateValue) => {\n if (tracker.shouldSkipLocalSync(nextValue)) {\n return;\n }\n void bridge.set(channelOptions, resolveSyncValue(nextValue));\n },\n {\n deep: Boolean(deep),\n flush: \"sync\",\n },\n );\n\nexport interface SyncStateRef<StateValue> extends Ref<StateValue> {\n isSynced: Ref<boolean>;\n}\n\nexport const useSyncState = <StateValue>(\n initialValue: StateValue,\n options: UseSyncStateOptions,\n): SyncStateRef<StateValue> => {\n const stateRef = ref(initialValue) as unknown as SyncStateRef<StateValue>;\n const isSynced = ref(false);\n const globalConfig = getGlobalConfig();\n const bridge = resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);\n const channelOptions = createChannelOptions(options);\n const tracker = createRemoteUpdateTracker(stateRef, isSynced);\n const stopWatcher = createStateWatcher({\n bridge,\n channelOptions,\n deep: options.deep,\n stateRef,\n tracker,\n });\n\n let unsubscribe: (() => void) | undefined = undefined;\n\n onMounted(() => {\n unsubscribe = bridge.subscribe(channelOptions, tracker.applyRemoteValue);\n void bridge.get<StateValue>(channelOptions).then(tracker.applyRemoteValue);\n });\n\n onBeforeUnmount(() => {\n stopWatcher();\n unsubscribe?.();\n unsubscribe = undefined;\n });\n\n stateRef.isSynced = isSynced;\n\n return stateRef;\n};\n"],"mappings":";;;;AAiBA,MAAM,wBAAwB,YAA0D;CACtF,MAAM,eAAe,iBAAiB;AACtC,QAAO;EACL,aAAa,QAAQ,eAAe,aAAa;EACjD,MAAM,QAAQ;EACf;;AAIH,MAAM,oBAAgC,UAAkC;AACtE,KAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,QAAQ,MAAM,CAC/D,QAAO,MAAM,MAAM;AAGrB,QAAO;;AAGT,MAAM,6BACJ,UACA,aACoC;CACpC,IAAI,wBAAwB;CAE5B,IAAI,kBAA0C;CAE9C,MAAM,oBAAoB,UAA4B;EAEpD,MAAM,gBAAgB,iBAAiB,MAAM;AAC7C,0BAAwB;AACxB,oBAAkB;AAClB,WAAS,QAAQ;AACjB,WAAS,QAAQ;AACjB,0BAAwB;;CAG1B,MAAM,uBAAuB,iBAG3B,yBAAyB,iBAAiB,aAAa,KAAK;AAE9D,QAAO;EACL;EACA;EACD;;AAWH,MAAM,sBAAkC,EACtC,QACA,gBACA,MACA,UACA,cAEA,MACE,WACC,cAA0B;AACzB,KAAI,QAAQ,oBAAoB,UAAU,CACxC;AAEF,CAAK,OAAO,IAAI,gBAAgB,iBAAiB,UAAU,CAAC;GAE9D;CACE,MAAM,QAAQ,KAAK;CACnB,OAAO;CACR,CACF;AAMH,MAAa,gBACX,cACA,YAC6B;CAC7B,MAAM,WAAW,IAAI,aAAa;CAClC,MAAM,WAAW,IAAI,MAAM;CAC3B,MAAM,eAAe,iBAAiB;CACtC,MAAM,SAAS,uBAAuB,QAAQ,UAAU,aAAa,OAAO;CAC5E,MAAM,iBAAiB,qBAAqB,QAAQ;CACpD,MAAM,UAAU,0BAA0B,UAAU,SAAS;CAC7D,MAAM,cAAc,mBAAmB;EACrC;EACA;EACA,MAAM,QAAQ;EACd;EACA;EACD,CAAC;CAEF,IAAI,cAAwC;AAE5C,iBAAgB;AACd,gBAAc,OAAO,UAAU,gBAAgB,QAAQ,iBAAiB;AACxE,EAAK,OAAO,IAAgB,eAAe,CAAC,KAAK,QAAQ,iBAAiB;GAC1E;AAEF,uBAAsB;AACpB,eAAa;AACb,iBAAe;AACf,gBAAc;GACd;AAEF,UAAS,WAAW;AAEpB,QAAO"}
|
package/dist/zustand.cjs
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
const require_renderer = require('./renderer-D3YziJ_U.cjs');
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let zustand_vanilla = require("zustand/vanilla");
|
|
4
|
+
|
|
5
|
+
//#region src/renderer/zustand.ts
|
|
6
|
+
const createChannelOptions = (options) => {
|
|
7
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
8
|
+
return {
|
|
9
|
+
baseChannel: options.baseChannel ?? globalConfig.baseChannel,
|
|
10
|
+
name: options.name
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Zustand middleware that syncs store state with Electron main process
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { create } from 'zustand';
|
|
19
|
+
* import { syncStateMiddleware } from 'electron-state-sync/zustand';
|
|
20
|
+
*
|
|
21
|
+
* const useStore = create(
|
|
22
|
+
* syncStateMiddleware({ name: 'counter' })((set) => ({
|
|
23
|
+
* count: 0,
|
|
24
|
+
* increment: () => set((state) => ({ count: state.count + 1 }))
|
|
25
|
+
* }))
|
|
26
|
+
* );
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
const syncStateMiddleware = (options) => (config) => (set, get, api) => {
|
|
30
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
31
|
+
const bridge = require_renderer.resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);
|
|
32
|
+
const channelOptions = createChannelOptions(options);
|
|
33
|
+
let isApplyingRemoteValue = false;
|
|
34
|
+
const originalSetState = api.setState;
|
|
35
|
+
api.setState = (partial, replace) => {
|
|
36
|
+
const currentState = get();
|
|
37
|
+
if (isApplyingRemoteValue) {
|
|
38
|
+
isApplyingRemoteValue = false;
|
|
39
|
+
return originalSetState(partial, replace);
|
|
40
|
+
}
|
|
41
|
+
let nextState;
|
|
42
|
+
if (typeof partial === "function") nextState = partial(currentState);
|
|
43
|
+
else if (replace) nextState = partial;
|
|
44
|
+
else nextState = {
|
|
45
|
+
...currentState,
|
|
46
|
+
...partial
|
|
47
|
+
};
|
|
48
|
+
const dataOnlyState = Object.fromEntries(Object.entries(nextState).filter(([_, v]) => typeof v !== "function"));
|
|
49
|
+
bridge.set(channelOptions, dataOnlyState);
|
|
50
|
+
return originalSetState(partial, replace);
|
|
51
|
+
};
|
|
52
|
+
const applyRemoteValue = (value) => {
|
|
53
|
+
isApplyingRemoteValue = true;
|
|
54
|
+
const currentState = get();
|
|
55
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) api.setState({
|
|
56
|
+
...currentState,
|
|
57
|
+
...value
|
|
58
|
+
}, true);
|
|
59
|
+
else {
|
|
60
|
+
const keys = Object.keys(currentState).filter((k) => typeof currentState[k] !== "function");
|
|
61
|
+
if (keys.length === 1) api.setState({
|
|
62
|
+
...currentState,
|
|
63
|
+
[keys[0]]: value
|
|
64
|
+
}, true);
|
|
65
|
+
else api.setState(value, true);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const unsubscribe = bridge.subscribe(channelOptions, applyRemoteValue);
|
|
69
|
+
bridge.get(channelOptions).then(applyRemoteValue);
|
|
70
|
+
const store = config(set, get, api);
|
|
71
|
+
const storeApi = api;
|
|
72
|
+
if (storeApi.destroy) {
|
|
73
|
+
const originalDestroy = storeApi.destroy;
|
|
74
|
+
storeApi.destroy = () => {
|
|
75
|
+
unsubscribe();
|
|
76
|
+
originalDestroy();
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return store;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Hook to access sync state for a Zustand store
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* import { useSyncState } from 'electron-state-sync/zustand';
|
|
87
|
+
*
|
|
88
|
+
* function App() {
|
|
89
|
+
* const { state, isSynced } = useSyncState({ name: 'counter' });
|
|
90
|
+
* return <div>{state?.count}</div>;
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
const useSyncState = (options) => {
|
|
95
|
+
const [isSynced, setIsSynced] = (0, react.useState)(false);
|
|
96
|
+
const [state, setState] = (0, react.useState)();
|
|
97
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
98
|
+
const bridge = require_renderer.resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);
|
|
99
|
+
const channelOptions = (0, react.useMemo)(() => createChannelOptions(options), [options.baseChannel, options.name]);
|
|
100
|
+
(0, react.useEffect)(() => {
|
|
101
|
+
const unsubscribe = bridge.subscribe(channelOptions, (value) => {
|
|
102
|
+
setState(value);
|
|
103
|
+
setIsSynced(true);
|
|
104
|
+
});
|
|
105
|
+
bridge.get(channelOptions).then((value) => {
|
|
106
|
+
setState(value);
|
|
107
|
+
setIsSynced(true);
|
|
108
|
+
});
|
|
109
|
+
return () => {
|
|
110
|
+
unsubscribe();
|
|
111
|
+
};
|
|
112
|
+
}, [bridge, channelOptions]);
|
|
113
|
+
return {
|
|
114
|
+
isSynced,
|
|
115
|
+
state
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Create a synced Zustand store (hook-less version for vanilla JS)
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* import { createSyncedStore } from 'electron-state-sync/zustand';
|
|
124
|
+
*
|
|
125
|
+
* const store = createSyncedStore(
|
|
126
|
+
* { count: 0 },
|
|
127
|
+
* { name: 'counter' },
|
|
128
|
+
* (set) => ({
|
|
129
|
+
* increment: () => set((state) => ({ count: state.count + 1 }))
|
|
130
|
+
* })
|
|
131
|
+
* );
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
const createSyncedStore = (initialState, options) => {
|
|
135
|
+
const globalConfig = require_renderer.getGlobalConfig();
|
|
136
|
+
const bridge = require_renderer.resolveSyncStateBridge(options.bridge ?? globalConfig.bridge);
|
|
137
|
+
const channelOptions = createChannelOptions(options);
|
|
138
|
+
let isApplyingRemoteValue = false;
|
|
139
|
+
const store = (0, zustand_vanilla.createStore)()(() => initialState);
|
|
140
|
+
const originalSetState = store.setState.bind(store);
|
|
141
|
+
store.setState = (partial, replace) => {
|
|
142
|
+
const currentState = store.getState();
|
|
143
|
+
if (isApplyingRemoteValue) {
|
|
144
|
+
isApplyingRemoteValue = false;
|
|
145
|
+
return originalSetState(partial, replace);
|
|
146
|
+
}
|
|
147
|
+
let nextState;
|
|
148
|
+
if (typeof partial === "function") nextState = partial(currentState);
|
|
149
|
+
else if (replace) nextState = partial;
|
|
150
|
+
else nextState = {
|
|
151
|
+
...currentState,
|
|
152
|
+
...partial
|
|
153
|
+
};
|
|
154
|
+
const dataOnlyState = Object.fromEntries(Object.entries(nextState).filter(([_, v]) => typeof v !== "function"));
|
|
155
|
+
bridge.set(channelOptions, dataOnlyState);
|
|
156
|
+
return originalSetState(partial, replace);
|
|
157
|
+
};
|
|
158
|
+
const applyRemoteValue = (value) => {
|
|
159
|
+
isApplyingRemoteValue = true;
|
|
160
|
+
const currentState = store.getState();
|
|
161
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) store.setState({
|
|
162
|
+
...currentState,
|
|
163
|
+
...value
|
|
164
|
+
});
|
|
165
|
+
else {
|
|
166
|
+
const keys = Object.keys(currentState).filter((k) => typeof currentState[k] !== "function");
|
|
167
|
+
if (keys.length === 1) store.setState({
|
|
168
|
+
...currentState,
|
|
169
|
+
[keys[0]]: value
|
|
170
|
+
});
|
|
171
|
+
else store.setState(value);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const unsubscribe = bridge.subscribe(channelOptions, applyRemoteValue);
|
|
175
|
+
bridge.get(channelOptions).then(applyRemoteValue);
|
|
176
|
+
const storeWithCleanup = store;
|
|
177
|
+
if (storeWithCleanup.destroy) {
|
|
178
|
+
const originalDestroy = storeWithCleanup.destroy;
|
|
179
|
+
storeWithCleanup.destroy = () => {
|
|
180
|
+
unsubscribe();
|
|
181
|
+
originalDestroy();
|
|
182
|
+
};
|
|
183
|
+
} else storeWithCleanup.destroy = () => {
|
|
184
|
+
unsubscribe();
|
|
185
|
+
};
|
|
186
|
+
return store;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
//#endregion
|
|
190
|
+
exports.createSyncedStore = createSyncedStore;
|
|
191
|
+
exports.syncStateMiddleware = syncStateMiddleware;
|
|
192
|
+
exports.useSyncState = useSyncState;
|
|
193
|
+
exports.useZustandSyncState = useSyncState;
|