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.
Files changed (97) hide show
  1. package/CLAUDE.md +254 -0
  2. package/LICENSE +21 -0
  3. package/README.md +753 -0
  4. package/README.zh-CN.md +743 -0
  5. package/bun.lock +542 -0
  6. package/bunfig.toml +7 -0
  7. package/dist/jotai.cjs +106 -0
  8. package/dist/jotai.d.cts +48 -0
  9. package/dist/jotai.d.cts.map +1 -0
  10. package/dist/jotai.d.ts +48 -0
  11. package/dist/jotai.d.ts.map +1 -0
  12. package/dist/jotai.js +105 -0
  13. package/dist/jotai.js.map +1 -0
  14. package/dist/main.cjs +177 -0
  15. package/dist/main.d.cts +26 -0
  16. package/dist/main.d.cts.map +1 -0
  17. package/dist/main.d.ts +26 -0
  18. package/dist/main.d.ts.map +1 -0
  19. package/dist/main.js +177 -0
  20. package/dist/main.js.map +1 -0
  21. package/dist/preact.cjs +46 -0
  22. package/dist/preact.d.cts +11 -0
  23. package/dist/preact.d.cts.map +1 -0
  24. package/dist/preact.d.ts +11 -0
  25. package/dist/preact.d.ts.map +1 -0
  26. package/dist/preact.js +46 -0
  27. package/dist/preact.js.map +1 -0
  28. package/dist/preload.cjs +51 -0
  29. package/dist/preload.d.cts +20 -0
  30. package/dist/preload.d.cts.map +1 -0
  31. package/dist/preload.d.ts +20 -0
  32. package/dist/preload.d.ts.map +1 -0
  33. package/dist/preload.js +51 -0
  34. package/dist/preload.js.map +1 -0
  35. package/dist/react-query.cjs +113 -0
  36. package/dist/react-query.d.cts +58 -0
  37. package/dist/react-query.d.cts.map +1 -0
  38. package/dist/react-query.d.ts +58 -0
  39. package/dist/react-query.d.ts.map +1 -0
  40. package/dist/react-query.js +112 -0
  41. package/dist/react-query.js.map +1 -0
  42. package/dist/react.cjs +46 -0
  43. package/dist/react.d.cts +11 -0
  44. package/dist/react.d.cts.map +1 -0
  45. package/dist/react.d.ts +11 -0
  46. package/dist/react.d.ts.map +1 -0
  47. package/dist/react.js +46 -0
  48. package/dist/react.js.map +1 -0
  49. package/dist/redux.cjs +148 -0
  50. package/dist/redux.d.cts +80 -0
  51. package/dist/redux.d.cts.map +1 -0
  52. package/dist/redux.d.ts +80 -0
  53. package/dist/redux.d.ts.map +1 -0
  54. package/dist/redux.js +146 -0
  55. package/dist/redux.js.map +1 -0
  56. package/dist/renderer/index.cjs +7 -0
  57. package/dist/renderer/index.d.cts +23 -0
  58. package/dist/renderer/index.d.cts.map +1 -0
  59. package/dist/renderer/index.d.ts +23 -0
  60. package/dist/renderer/index.d.ts.map +1 -0
  61. package/dist/renderer/index.js +3 -0
  62. package/dist/renderer-C7zF3UQm.js +57 -0
  63. package/dist/renderer-C7zF3UQm.js.map +1 -0
  64. package/dist/renderer-D3YziJ_U.cjs +86 -0
  65. package/dist/solid.cjs +74 -0
  66. package/dist/solid.d.cts +13 -0
  67. package/dist/solid.d.cts.map +1 -0
  68. package/dist/solid.d.ts +13 -0
  69. package/dist/solid.d.ts.map +1 -0
  70. package/dist/solid.js +74 -0
  71. package/dist/solid.js.map +1 -0
  72. package/dist/svelte.cjs +63 -0
  73. package/dist/svelte.d.cts +14 -0
  74. package/dist/svelte.d.cts.map +1 -0
  75. package/dist/svelte.d.ts +14 -0
  76. package/dist/svelte.d.ts.map +1 -0
  77. package/dist/svelte.js +63 -0
  78. package/dist/svelte.js.map +1 -0
  79. package/dist/types-7wPPX0ty.d.ts +37 -0
  80. package/dist/types-7wPPX0ty.d.ts.map +1 -0
  81. package/dist/types-C18dHgLI.d.cts +37 -0
  82. package/dist/types-C18dHgLI.d.cts.map +1 -0
  83. package/dist/vue.cjs +69 -0
  84. package/dist/vue.d.cts +15 -0
  85. package/dist/vue.d.cts.map +1 -0
  86. package/dist/vue.d.ts +15 -0
  87. package/dist/vue.d.ts.map +1 -0
  88. package/dist/vue.js +70 -0
  89. package/dist/vue.js.map +1 -0
  90. package/dist/zustand.cjs +193 -0
  91. package/dist/zustand.d.cts +61 -0
  92. package/dist/zustand.d.cts.map +1 -0
  93. package/dist/zustand.d.ts +61 -0
  94. package/dist/zustand.d.ts.map +1 -0
  95. package/dist/zustand.js +191 -0
  96. package/dist/zustand.js.map +1 -0
  97. package/package.json +162 -0
package/CLAUDE.md ADDED
@@ -0,0 +1,254 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ This is a lightweight Electron state synchronization library that enables seamless data sharing between main and renderer processes. It supports React, Vue, Svelte, and SolidJS with automatic multi-window sync.
8
+
9
+ **Key characteristics:**
10
+ - Main process bundle: ~6.3KB, renderer bundles: 1.5-2.2KB per framework
11
+ - Three-tier architecture: main process → preload script → renderer layer
12
+ - IPC-based communication using typed channels
13
+ - Write permission control and validation from main process
14
+ - Automatic multi-window synchronization
15
+
16
+ ## Development Commands
17
+
18
+ ```bash
19
+ # Build all artifacts (main + preload + renderer integrations)
20
+ bun run build
21
+
22
+ # Build individual parts
23
+ bun run build:main # Main process (src/main.ts)
24
+ bun run build:preload # Preload script (src/preload.ts)
25
+ bun run build:renderer # All framework integrations
26
+
27
+ # Linting and formatting
28
+ bun run lint # Check code with oxlint
29
+ bun run lint:fix # Auto-fix linting issues
30
+ bun run format # Format code with oxfmt
31
+ bun run format:check # Check formatting
32
+ bun run check # Run both lint and format checks
33
+
34
+ # Testing
35
+ bun run test # Run unit tests (test/ directory)
36
+ bun run coverage # Run unit tests with coverage
37
+ bun run test:e2e # Run E2E tests (requires build first)
38
+
39
+ # Development
40
+ bun run dev # Run src/index.ts directly
41
+ ```
42
+
43
+ **Important:** E2E tests require the library to be built first (`bun run build`). Git hooks enforce this via lefthook (pre-push hook runs tests and build).
44
+
45
+ ## Architecture
46
+
47
+ ### Three-Tier Structure
48
+
49
+ **1. Main Process (`src/main.ts`)**
50
+ - Central state management hub with IPC handlers
51
+ - Controls write permissions via `allowRendererSet` flag
52
+ - Validates renderer writes via `resolveRendererValue` function
53
+ - Broadcasts updates to all subscribed windows
54
+ - Uses `WebContents` tracking for multi-window sync
55
+
56
+ **2. Preload Script (`src/preload.ts`)**
57
+ - Security bridge using `contextBridge.exposeInMainWorld`
58
+ - Exposes `SyncStateBridge` interface to renderer
59
+ - Handles IPC communication with type safety
60
+ - Entry point for `electron-state-sync/preload` export
61
+
62
+ **3. Renderer Layer (`src/renderer/`)**
63
+ - **Core**: `bridge.ts` resolves bridge instances, `index.ts` provides global config
64
+ - **Framework integrations**: Each framework has its own adapter
65
+ - `react.ts` - React hook with tuple return `[value, setter, isSynced]`
66
+ - `vue.ts` - Vue composable returning Ref with `isSynced` property
67
+ - `svelte.ts` - Svelte store with `isSynced` readable store
68
+ - `solid.ts` - Solid hook with tuple return similar to React
69
+
70
+ ### IPC Channel System (`src/channels.ts`)
71
+
72
+ Channel naming pattern: `${baseChannel}:${name}:action`
73
+
74
+ Five channels per state:
75
+ - `get` - Renderer fetches current value
76
+ - `set` - Renderer updates value (if allowed)
77
+ - `subscribe` - Window subscribes to updates
78
+ - `unsubscribe` - Window unsubscribes
79
+ - `update` - Main broadcasts changes to subscribers
80
+
81
+ **Example:** For `baseChannel="state"` and `name="counter"`:
82
+ - `state:counter:get`
83
+ - `state:counter:set`
84
+ - `state:counter:subscribe`
85
+ - `state:counter:unsubscribe`
86
+ - `state:counter:update`
87
+
88
+ ### Data Flow
89
+
90
+ 1. **Initialization**: Main creates state with `state({ name, initialValue })`
91
+ 2. **Subscription**: Renderer calls `bridge.subscribe()` via IPC
92
+ 3. **Initial Sync**: Renderer fetches current value via `bridge.get()`
93
+ 4. **Updates**:
94
+ - Main process calls `handle.set()` → broadcasts to all subscribers
95
+ - Renderer calls `bridge.set()` → validated by main → broadcast if valid
96
+ 5. **Multi-window**: All subscribed windows receive updates automatically
97
+
98
+ ### State Management Pattern
99
+
100
+ Each framework integration follows this pattern:
101
+ 1. Create local reactive state with `initialValue` (placeholder)
102
+ 2. Subscribe to main process updates in effect/hook
103
+ 3. Fetch initial value from main process on mount
104
+ 4. Local changes sync to main (if allowed)
105
+ 5. Remote updates from main override local state
106
+ 6. Track sync completion via `isSynced` flag
107
+
108
+ **Critical detail:** To prevent infinite loops when renderer writes are allowed:
109
+ - Frameworks track when applying remote updates
110
+ - Skip syncing back to main during remote update application
111
+ - React/Solid: Use refs/flags to track update source
112
+ - Vue: Compare resolved values before syncing
113
+ - Svelte: Track update state in callback closure
114
+
115
+ ## Build System
116
+
117
+ **Tool:** `tsdown` (TypeScript-to-JavaScript compiler)
118
+
119
+ Three separate build configurations:
120
+ - `tsdown.main.config.ts` - Builds main process entry
121
+ - `tsdown.preload.config.ts` - Builds preload script
122
+ - `tsdown.config.ts` - Builds all renderer framework integrations
123
+
124
+ **Output format:** Dual ESM + CJS with TypeScript definitions
125
+
126
+ **Package exports:** Each layer has its own export path:
127
+ - `electron-state-sync/main` → `dist/main.{js,cjs,d.ts}`
128
+ - `electron-state-sync/preload` → `dist/preload.{js,cjs,d.ts}`
129
+ - `electron-state-sync/renderer` → `dist/renderer/index.{js,cjs,d.ts}`
130
+ - `electron-state-sync/react` → `dist/react.{js,cjs,d.ts}`
131
+ - `electron-state-sync/vue` → `dist/vue.{js,cjs,d.ts}`
132
+ - `electron-state-sync/svelte` → `dist/svelte.{js,cjs,d.ts}`
133
+ - `electron-state-sync/solid` → `dist/solid.{js,cjs,d.ts}`
134
+
135
+ ## Testing
136
+
137
+ **Unit Tests (`test/`):**
138
+ - `channels.test.ts` - IPC channel generation logic
139
+ - `validation.test.ts` - Serialization validation
140
+ - `index.test.ts` - SyncStateError class
141
+ - `setup.ts` - Mocks Electron modules for unit tests
142
+ - `mocks.ts` - Test utilities
143
+
144
+ **E2E Tests (`e2e/`):**
145
+ - Uses Playwright with real Electron app
146
+ - `sync-state.spec.ts` - Tests all frameworks (React, Vue, Svelte, Solid)
147
+ - `main.mjs` - Electron main process for testing
148
+ - `renderer-*.js` - Framework-specific test renderers
149
+ - `preload.{cjs,mjs}` - Preload scripts for testing
150
+
151
+ **Key test utilities:**
152
+ - Unit tests mock Electron IPC to avoid needing real Electron
153
+ - E2E tests use `@playwright/test` with `electron` launcher
154
+ - Test framework selection via `--framework=` CLI argument
155
+ - Multi-window testing via `--windows=` argument
156
+
157
+ ## Code Conventions
158
+
159
+ ### Error Handling
160
+
161
+ Use `SyncStateError` for all library errors:
162
+ ```typescript
163
+ throw new SyncStateError("RENDERER_READONLY", message, {
164
+ stateName: options.name,
165
+ baseChannel: options.baseChannel ?? "state",
166
+ });
167
+ ```
168
+
169
+ Error codes:
170
+ - `RENDERER_READONLY` - Renderer write blocked by `allowRendererSet: false`
171
+ - `RENDERER_INVALID_VALUE` - Renderer write rejected by `resolveRendererValue`
172
+
173
+ ### Serialization Safety
174
+
175
+ All state values must be JSON-serializable. The library validates:
176
+ - No functions, Symbols, or BigInt
177
+ - No circular references
178
+ - Arrays and objects are recursively checked
179
+
180
+ **Validation function** (`src/main.ts`): Used to warn about invalid initial values but doesn't block them (renderer's initialValue is just a placeholder).
181
+
182
+ ### Framework-Specific Notes
183
+
184
+ **Vue only:**
185
+ - Supports `deep` option for object watching
186
+ - Converts Vue Proxies to raw values before IPC via `toRaw()`
187
+ - Watch flush mode set to `'sync'` for immediate updates
188
+
189
+ **React/Solid:**
190
+ - Return tuple `[value, setter, isSynced]` from hooks
191
+ - Use `useCallback`/`useMemo` to optimize bridge operations
192
+
193
+ **Svelte:**
194
+ - Returns custom store with `isSynced` as separate readable store
195
+ - Store cleanup handles unsubscribe automatically
196
+
197
+ ### Global Configuration
198
+
199
+ Both main and renderer support global config:
200
+ ```typescript
201
+ // Main process
202
+ import { initSyncStateMain } from "electron-state-sync/main";
203
+ initSyncStateMain({ baseChannel: "myapp", allowRendererSet: false });
204
+
205
+ // Renderer process
206
+ import { initSyncState } from "electron-state-sync/react";
207
+ initSyncState({ baseChannel: "myapp" });
208
+ ```
209
+
210
+ Global config is merged with per-state options, where per-state options take precedence.
211
+
212
+ ### Working with Bridges
213
+
214
+ The `SyncStateBridge` interface enables custom integrations:
215
+ ```typescript
216
+ interface SyncStateBridge {
217
+ get: <StateValue>(options: SyncStateChannelOptions) => Promise<StateValue>;
218
+ set: <StateValue>(options: SyncStateChannelOptions, value: StateValue) => Promise<void>;
219
+ subscribe: <StateValue>(options: SyncStateChannelOptions, listener: SyncStateListener<StateValue>) => () => void;
220
+ }
221
+ ```
222
+
223
+ Framework hooks accept optional `bridge` parameter for custom implementations or testing.
224
+
225
+ ## Important Files
226
+
227
+ - `src/main.ts` - Main process state management and IPC handlers
228
+ - `src/preload.ts` - Preload bridge exposure
229
+ - `src/channels.ts` - IPC channel naming
230
+ - `src/types.ts` - Core type definitions and SyncStateError
231
+ - `src/renderer/bridge.ts` - Bridge resolution logic
232
+ - `src/renderer/index.ts` - Global config for renderer
233
+ - `src/renderer/react.ts` - React integration
234
+ - `src/renderer/vue.ts` - Vue integration
235
+ - `src/renderer/svelte.ts` - Svelte integration
236
+ - `src/renderer/solid.ts` - SolidJS integration
237
+
238
+ ## Linting and Formatting
239
+
240
+ **Tools:**
241
+ - `oxlint` - Fast linting (used in CI and pre-commit hooks)
242
+ - `oxfmt` - Code formatting
243
+
244
+ **Common warnings that should be fixed:**
245
+ - `arrow-body-style` - Use concise arrow function bodies when possible
246
+ - `init-declarations` - Initialize variables on declaration
247
+ - `curly` - Use braces for all if statements
248
+ - `prefer-destructuring` - Use object destructuring
249
+
250
+ **Warnings to ignore (by convention):**
251
+ - `max-statements` - Complex functions are acceptable
252
+ - `max-lines-per-function` - Long functions are acceptable
253
+
254
+ Pre-commit hooks run `lint` and `format:check` in parallel. Pre-push hooks run `test` and `build`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.